oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1//go:build browser && darwin
2
3package callstest
4
5import (
6 "context"
7 "os"
8 "os/exec"
9 "path/filepath"
10 "strings"
11 "testing"
12 "time"
13)
14
15// The audio test's record-and-play-back path, through the shipping
16// capture chain: LiveKit's engine module in manual rendering with the
17// synthetic voice as its microphone, WebRTC's processing module, and the
18// capture hook's noise reduction (DeepFilterNet when
19// CALLS_DEEPFILTER_MODEL is set, as make native-browser sets it). No
20// server and no browser; it sits with the native gates because it runs
21// the same CallsInterop build.
22//
23// Five runs must each print PASS: noise reduction on, off, and a control
24// with a silent microphone that must be called silent; then the test's
25// start on the real device module (not manual rendering), which must ask
26// the module to record, and its control, 0.3.7's start, which never did.
27// Manual rendering runs the input whenever the output runs, so the first
28// three pass whether or not the test opens a microphone; the last two are
29// what would have caught the 0.3.6/0.3.7 test that never opened one. A run
30// that prints nothing is a failure, not a pass.
31func TestNativeMacAudioSelfTest(t *testing.T) {
32 root, err := filepath.Abs("../..")
33 if err != nil {
34 t.Fatal(err)
35 }
36 binary := filepath.Join(root, "native/apple/.build/debug/CallsInterop")
37 if _, err := os.Stat(binary); err != nil {
38 t.Fatal("build native interop executable with swift build --package-path native/apple --product CallsInterop first")
39 }
40 ctx, cancel := context.WithTimeout(t.Context(), 90*time.Second)
41 defer cancel()
42 out, err := exec.CommandContext(ctx, binary, "--audio-selftest").CombinedOutput()
43 var passes, fails []string
44 for _, line := range strings.Split(string(out), "\n") {
45 switch {
46 case strings.HasPrefix(line, "PASS audio self-test"):
47 passes = append(passes, line)
48 case strings.HasPrefix(line, "FAIL audio self-test"):
49 fails = append(fails, line)
50 }
51 }
52 for _, line := range append(passes, fails...) {
53 t.Log(line)
54 }
55 if err != nil || len(fails) > 0 || len(passes) != 5 {
56 t.Fatalf("audio self-test: %d passed, %d failed, exit %v\n%s", len(passes), len(fails), err, out)
57 }
58 for _, want := range []string{
59 "(noise reduction on)", "(noise reduction off)", "control (a silent microphone)",
60 "control (0.3.7's start on a real device never opens the microphone)",
61 "on a real device (asks the module to record)",
62 } {
63 found := false
64 for _, line := range passes {
65 found = found || strings.Contains(line, want)
66 }
67 if !found {
68 t.Errorf("no PASS line for %s", want)
69 }
70 }
71}
72
73// The main thread is never held longer than 100 ms while the audio
74// starts: Test audio before a call (plain, with a Bluetooth headset's
75// profile switch simulated as a 1.5 s hold of the engine's start on
76// WebRTC's worker thread, and closed partway through that start), and a
77// call's audio as a join starts it, with the same switch. A watchdog in
78// CallsInterop times the main queue every 5 ms, on the real engine
79// module; a test process has no microphone permission, so the module
80// refuses at the very end, after everything that used to block.
81//
82// 0.3.8 held it 0.1–0.7 s for the plain start and about 1.5 s with the
83// switch (Paul's "pause, then a beachball", 2026-09-27). A control run
84// blocks the main thread on purpose and must be seen, so a pass means the
85// watchdog was looking. Five runs, five PASS lines.
86func TestNativeMacAudioStartStall(t *testing.T) {
87 root, err := filepath.Abs("../..")
88 if err != nil {
89 t.Fatal(err)
90 }
91 binary := filepath.Join(root, "native/apple/.build/debug/CallsInterop")
92 if _, err := os.Stat(binary); err != nil {
93 t.Fatal("build native interop executable with swift build --package-path native/apple --product CallsInterop first")
94 }
95 ctx, cancel := context.WithTimeout(t.Context(), 90*time.Second)
96 defer cancel()
97 out, err := exec.CommandContext(ctx, binary, "--audio-start-stall").CombinedOutput()
98 var passes, fails []string
99 for _, line := range strings.Split(string(out), "\n") {
100 switch {
101 case strings.HasPrefix(line, "PASS "):
102 passes = append(passes, line)
103 case strings.HasPrefix(line, "FAIL "):
104 fails = append(fails, line)
105 }
106 }
107 for _, line := range append(passes, fails...) {
108 t.Log(line)
109 }
110 if err != nil || len(fails) > 0 || len(passes) != 5 {
111 t.Fatalf("audio start stall: %d passed, %d failed, exit %v\n%s", len(passes), len(fails), err, out)
112 }
113 for _, want := range []string{
114 "audio start stall control", "PASS Test audio start:", "Test audio start, Bluetooth switch simulated",
115 "Test audio closed while its microphone starts", "Call join audio start, Bluetooth switch simulated",
116 } {
117 found := false
118 for _, line := range passes {
119 found = found || strings.Contains(line, want)
120 }
121 if !found {
122 t.Errorf("no PASS line for %s", want)
123 }
124 }
125}
126