forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmic_arbiter_test.dart
More file actions
153 lines (134 loc) · 4.25 KB
/
Copy pathmic_arbiter_test.dart
File metadata and controls
153 lines (134 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:omi/services/mic/mic_arbiter.dart';
import 'package:omi/services/services.dart';
class FakeMic implements IMicRecorderService {
int startCalls = 0;
int startBatchCalls = 0;
int stopCalls = 0;
bool failNextStart = false;
bool failNextStartBatch = false;
Function()? capturedOnStop;
@override
Future<void> start({
required Function(Uint8List bytes) onByteReceived,
Function()? onRecording,
Function()? onStop,
Function()? onInitializing,
Function()? onStalled,
Function(bool began)? onInterruption,
}) async {
startCalls++;
if (failNextStart) {
failNextStart = false;
throw Exception('recorder failed to start');
}
capturedOnStop = onStop;
}
@override
Future<void> startBatch({
Function()? onStop,
Function(bool began)? onInterruption,
Function()? onBatchStalled,
Function(String code, String message)? onError,
}) async {
startBatchCalls++;
if (failNextStartBatch) {
failNextStartBatch = false;
throw Exception('batch recorder failed to start');
}
capturedOnStop = onStop;
}
@override
void stop() {
stopCalls++;
capturedOnStop?.call();
capturedOnStop = null;
}
@override
void probeStallAfterForeground() {}
}
void main() {
group('MicArbiter', () {
test('same owner can re-acquire, different owner cannot', () {
final arbiter = MicArbiter();
expect(arbiter.tryAcquire('a'), isTrue);
expect(arbiter.tryAcquire('a'), isTrue);
expect(arbiter.tryAcquire('b'), isFalse);
arbiter.release('a');
expect(arbiter.tryAcquire('b'), isTrue);
});
test('release by non-owner is ignored', () {
final arbiter = MicArbiter();
arbiter.tryAcquire('a');
arbiter.release('b');
expect(arbiter.owner, 'a');
});
});
group('ArbitratedMic', () {
late MicArbiter arbiter;
late FakeMic micA;
late FakeMic micB;
late ArbitratedMic a;
late ArbitratedMic b;
setUp(() {
arbiter = MicArbiter();
micA = FakeMic();
micB = FakeMic();
a = ArbitratedMic(inner: micA, arbiter: arbiter, owner: 'conversation');
b = ArbitratedMic(inner: micB, arbiter: arbiter, owner: 'mic');
});
Future<void> startMic(ArbitratedMic mic) => mic.start(onByteReceived: (_) {});
test('second stack contends while first holds the mic', () async {
await startMic(a);
expect(() => startMic(b), throwsStateError);
expect(micB.startCalls, 0);
});
test('stop releases so the other stack can start', () async {
await startMic(a);
a.stop();
await startMic(b);
expect(micB.startCalls, 1);
});
test('start failure releases the arbiter and rethrows', () async {
micA.failNextStart = true;
await expectLater(startMic(a), throwsException);
await startMic(b);
expect(micB.startCalls, 1);
});
test('natural stop (inner onStop) releases without an explicit stop()', () async {
var stopped = false;
await a.start(onByteReceived: (_) {}, onStop: () => stopped = true);
// Simulate the recorder retiring itself (e.g. watchdog kill).
micA.capturedOnStop!.call();
expect(stopped, isTrue);
await startMic(b);
expect(micB.startCalls, 1);
});
test('startBatch contends while the other stack holds the mic', () async {
await startMic(b);
expect(() => a.startBatch(), throwsStateError);
expect(micA.startBatchCalls, 0);
});
test('startBatch failure releases the arbiter and rethrows', () async {
micA.failNextStartBatch = true;
await expectLater(a.startBatch(), throwsException);
await startMic(b);
expect(micB.startCalls, 1);
});
test('stop after startBatch releases so the other stack can start', () async {
await a.startBatch();
expect(micA.startBatchCalls, 1);
a.stop();
await startMic(b);
expect(micB.startCalls, 1);
});
test('natural stop from batch onStop releases the arbiter', () async {
await a.startBatch();
// Native terminal stop wired through the wrapped onStop.
micA.capturedOnStop!.call();
await startMic(b);
expect(micB.startCalls, 1);
});
});
}