@mentra/acs-meeting 3.2.0-dev.223 → 3.2.0-dev.225
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/mentra/acsmeeting/AcsMeetingModule.kt +10 -10
- package/android/src/main/java/com/mentra/acsmeeting/AcsMeetingSession.kt +37 -37
- package/android/src/main/java/com/mentra/acsmeeting/audio/UplinkSender.kt +4 -4
- package/android/src/main/java/com/mentra/acsmeeting/source/MeetingVideoSourceSpec.kt +3 -0
- package/android/src/main/java/com/mentra/acsmeeting/video/AcsFrameSender.kt +12 -10
- package/android/src/main/java/com/mentra/acsmeeting/video/I420FormatSpec.kt +2 -0
- package/android/src/main/java/com/mentra/acsmeeting/video/Nv12Packer.kt +2 -0
- package/android/src/test/java/com/mentra/acsmeeting/source/{AcsInvestigationTest.kt → MediaDiagnosticsTest.kt} +10 -10
- package/android/src/test/java/com/mentra/acsmeeting/source/MeetingVideoSourceSpecTest.kt +3 -0
- package/android/src/test/java/com/mentra/acsmeeting/telemetry/VideoQualityTest.kt +2 -2
- package/android/src/test/java/com/mentra/acsmeeting/video/I420FormatSpecTest.kt +2 -0
- package/android/src/test/java/com/mentra/acsmeeting/video/VideoProfileTest.kt +2 -2
- package/build/AcsMeeting.types.d.ts +10 -1
- package/build/AcsMeeting.types.d.ts.map +1 -1
- package/build/AcsMeeting.types.js.map +1 -1
- package/build/AcsMeetingModule.d.ts +2 -2
- package/build/AcsMeetingModule.d.ts.map +1 -1
- package/build/AcsMeetingModule.js.map +1 -1
- package/ios/AcsMeeting.podspec +1 -1
- package/ios/AcsMeetingModule.swift +284 -148
- package/ios/PolicyKit/Package.swift +2 -1
- package/ios/PolicyKit/Sources/AcsAudioPolicy/CallbackOperation.swift +60 -0
- package/ios/PolicyKit/Sources/AcsAudioPolicy/CapturePolicy.swift +5 -0
- package/ios/PolicyKit/Tests/AcsAudioPolicyTests/AcsAudioPolicyTests.swift +1 -0
- package/ios/PolicyKit/Tests/AcsAudioPolicyTests/CallAgentRetirementTests.swift +113 -0
- package/package.json +3 -2
- package/src/AcsMeeting.types.ts +4 -1
- package/src/AcsMeetingModule.ts +2 -2
- package/android/src/androidTest/java/com/mentra/acsmeeting/network/SoftApFeasibilityGateTest.kt +0 -291
- package/android/src/main/java/com/mentra/acsmeeting/network/InternetHold.kt +0 -501
- package/android/src/main/java/com/mentra/acsmeeting/network/Ipv4Prefix.kt +0 -51
- package/android/src/main/java/com/mentra/acsmeeting/network/NetworkFacts.kt +0 -81
- package/android/src/main/java/com/mentra/acsmeeting/network/ScopedNetworkChangeDetector.kt +0 -355
- package/android/src/main/java/com/mentra/acsmeeting/network/ScopedNetworkError.kt +0 -109
- package/android/src/main/java/com/mentra/acsmeeting/network/ScopedNetworkObserver.kt +0 -160
- package/android/src/main/java/com/mentra/acsmeeting/network/ScopedNetworkReadiness.kt +0 -103
- package/android/src/main/java/com/mentra/acsmeeting/network/ScopedNetworkRequestSpec.kt +0 -53
- package/android/src/main/java/com/mentra/acsmeeting/network/ScopedNetworkState.kt +0 -127
- package/android/src/main/java/com/mentra/acsmeeting/network/ScopedSoftApNetwork.kt +0 -573
- package/android/src/main/java/com/mentra/acsmeeting/source/BitmapFont.kt +0 -99
- package/android/src/main/java/com/mentra/acsmeeting/source/CloudflareWhepSource.kt +0 -487
- package/android/src/main/java/com/mentra/acsmeeting/source/DecodedTrackRelay.kt +0 -147
- package/android/src/main/java/com/mentra/acsmeeting/source/FirstFrameGate.kt +0 -45
- package/android/src/main/java/com/mentra/acsmeeting/source/GlassesMediaSource.kt +0 -143
- package/android/src/main/java/com/mentra/acsmeeting/source/LocalWhipIngestSource.kt +0 -772
- package/android/src/main/java/com/mentra/acsmeeting/source/MediaListeners.kt +0 -57
- package/android/src/main/java/com/mentra/acsmeeting/source/SdpAdapter.kt +0 -17
- package/android/src/main/java/com/mentra/acsmeeting/source/SelectedIcePair.kt +0 -139
- package/android/src/main/java/com/mentra/acsmeeting/source/SoftApIceDiagnosis.kt +0 -144
- package/android/src/main/java/com/mentra/acsmeeting/source/SoftApIcePolicy.kt +0 -46
- package/android/src/main/java/com/mentra/acsmeeting/source/SoftApSdpGuard.kt +0 -160
- package/android/src/main/java/com/mentra/acsmeeting/source/SyntheticFrameFactory.kt +0 -205
- package/android/src/main/java/com/mentra/acsmeeting/source/SyntheticI420Source.kt +0 -214
- package/android/src/main/java/com/mentra/acsmeeting/source/TrackRegistry.kt +0 -33
- package/android/src/main/java/com/mentra/acsmeeting/source/VideoSourceArm.kt +0 -110
- package/android/src/main/java/com/mentra/acsmeeting/source/WhipIngestProtocol.kt +0 -207
- package/android/src/main/java/com/mentra/acsmeeting/source/WhipIngestServer.kt +0 -356
- package/android/src/main/java/com/mentra/acsmeeting/telemetry/AvSyncProbe.kt +0 -155
- package/android/src/main/java/com/mentra/acsmeeting/telemetry/ChromaProbe.kt +0 -78
- package/android/src/main/java/com/mentra/acsmeeting/telemetry/PipelineStats.kt +0 -372
- package/android/src/main/java/com/mentra/acsmeeting/telemetry/PipelineTicker.kt +0 -79
- package/android/src/main/java/com/mentra/acsmeeting/telemetry/ProcessCpu.kt +0 -15
- package/android/src/main/java/com/mentra/acsmeeting/telemetry/RingPercentile.kt +0 -48
- package/android/src/main/java/com/mentra/acsmeeting/telemetry/VideoQuality.kt +0 -178
- package/android/src/main/java/com/mentra/acsmeeting/telemetry/VideoRateVerdict.kt +0 -145
- package/android/src/main/java/com/mentra/acsmeeting/trace/SoftApTrace.kt +0 -143
- package/android/src/main/java/com/mentra/acsmeeting/video/FrameGeometry.kt +0 -22
- package/android/src/main/java/com/mentra/acsmeeting/video/I420Packer.kt +0 -72
- package/android/src/test/java/com/mentra/acsmeeting/network/InternetHoldLifecycleTest.kt +0 -102
- package/android/src/test/java/com/mentra/acsmeeting/network/InternetHoldTest.kt +0 -148
- package/android/src/test/java/com/mentra/acsmeeting/network/Ipv4PrefixTest.kt +0 -59
- package/android/src/test/java/com/mentra/acsmeeting/network/NetworkFactsTest.kt +0 -59
- package/android/src/test/java/com/mentra/acsmeeting/network/ScopedLocalNetworkPermissionTest.kt +0 -35
- package/android/src/test/java/com/mentra/acsmeeting/network/ScopedNetworkChangeDetectorTest.kt +0 -165
- package/android/src/test/java/com/mentra/acsmeeting/network/ScopedNetworkErrorTest.kt +0 -96
- package/android/src/test/java/com/mentra/acsmeeting/network/ScopedNetworkLifecycleTest.kt +0 -187
- package/android/src/test/java/com/mentra/acsmeeting/network/ScopedNetworkObserverTest.kt +0 -172
- package/android/src/test/java/com/mentra/acsmeeting/network/ScopedNetworkReadinessTest.kt +0 -141
- package/android/src/test/java/com/mentra/acsmeeting/network/ScopedNetworkRequestSpecTest.kt +0 -69
- package/android/src/test/java/com/mentra/acsmeeting/source/BitmapFontTest.kt +0 -22
- package/android/src/test/java/com/mentra/acsmeeting/source/DecodedTrackRelayTest.kt +0 -41
- package/android/src/test/java/com/mentra/acsmeeting/source/FirstFrameGateTest.kt +0 -60
- package/android/src/test/java/com/mentra/acsmeeting/source/GlassesMediaSourceTest.kt +0 -151
- package/android/src/test/java/com/mentra/acsmeeting/source/I420PlanesTest.kt +0 -67
- package/android/src/test/java/com/mentra/acsmeeting/source/SelectedIcePairTest.kt +0 -165
- package/android/src/test/java/com/mentra/acsmeeting/source/SoftApIceDiagnosisTest.kt +0 -148
- package/android/src/test/java/com/mentra/acsmeeting/source/SoftApIcePolicyTest.kt +0 -76
- package/android/src/test/java/com/mentra/acsmeeting/source/SoftApSdpGuardTest.kt +0 -275
- package/android/src/test/java/com/mentra/acsmeeting/source/SyntheticFrameFactoryTest.kt +0 -193
- package/android/src/test/java/com/mentra/acsmeeting/source/SyntheticI420SourceTest.kt +0 -176
- package/android/src/test/java/com/mentra/acsmeeting/source/TrackRegistryTest.kt +0 -45
- package/android/src/test/java/com/mentra/acsmeeting/source/WhipIngestProtocolTest.kt +0 -356
- package/android/src/test/java/com/mentra/acsmeeting/source/WhipIngestServerLoopbackTest.kt +0 -411
- package/android/src/test/java/com/mentra/acsmeeting/telemetry/AvSyncProbeTest.kt +0 -83
- package/android/src/test/java/com/mentra/acsmeeting/telemetry/ChromaProbeTest.kt +0 -61
- package/android/src/test/java/com/mentra/acsmeeting/telemetry/PipelineStatsTest.kt +0 -322
- package/android/src/test/java/com/mentra/acsmeeting/telemetry/ProcessCpuTest.kt +0 -18
- package/android/src/test/java/com/mentra/acsmeeting/telemetry/RingPercentileTest.kt +0 -49
- package/android/src/test/java/com/mentra/acsmeeting/telemetry/VideoRateVerdictTest.kt +0 -149
- package/android/src/test/java/com/mentra/acsmeeting/trace/SoftApTraceTest.kt +0 -97
- package/android/src/test/java/com/mentra/acsmeeting/video/FrameGeometryTest.kt +0 -33
- package/android/src/test/java/com/mentra/acsmeeting/video/I420PackerTest.kt +0 -126
- package/ios/PolicyKit/Sources/AcsAudioPolicy/GlassesMediaSource.swift +0 -68
- package/ios/WhepVideoSource.swift +0 -356
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.network
|
|
2
|
-
|
|
3
|
-
import com.mentra.acsmeeting.network.ScopedNetworkState.Failure
|
|
4
|
-
import com.mentra.acsmeeting.network.ScopedNetworkState.Phase
|
|
5
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
6
|
-
import org.junit.Test
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* ConnectivityManager gives no ordering or at-most-once guarantee, so these cover the awkward
|
|
10
|
-
* orderings that would otherwise only show up on a device: duplicate callbacks, a callback from a
|
|
11
|
-
* request that was already replaced, and a timeout racing a late onAvailable.
|
|
12
|
-
*/
|
|
13
|
-
class ScopedNetworkLifecycleTest {
|
|
14
|
-
|
|
15
|
-
@Test
|
|
16
|
-
fun `starts idle`() {
|
|
17
|
-
val state = ScopedNetworkState()
|
|
18
|
-
assertThat(state.phase).isEqualTo(Phase.IDLE)
|
|
19
|
-
assertThat(state.failure).isEqualTo(Failure.NONE)
|
|
20
|
-
assertThat(state.isActive).isFalse()
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
@Test
|
|
24
|
-
fun `happy path is idle to requesting to available`() {
|
|
25
|
-
val state = ScopedNetworkState()
|
|
26
|
-
val generation = state.startRequest()
|
|
27
|
-
assertThat(state.phase).isEqualTo(Phase.REQUESTING)
|
|
28
|
-
|
|
29
|
-
assertThat(state.onAvailable(generation)).isTrue()
|
|
30
|
-
assertThat(state.phase).isEqualTo(Phase.AVAILABLE)
|
|
31
|
-
assertThat(state.failure).isEqualTo(Failure.NONE)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
@Test
|
|
35
|
-
fun `a duplicate onAvailable does not re-transition`() {
|
|
36
|
-
val state = ScopedNetworkState()
|
|
37
|
-
val generation = state.startRequest()
|
|
38
|
-
|
|
39
|
-
assertThat(state.onAvailable(generation)).isTrue()
|
|
40
|
-
assertThat(state.onAvailable(generation)).isFalse()
|
|
41
|
-
assertThat(state.phase).isEqualTo(Phase.AVAILABLE)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
@Test
|
|
45
|
-
fun `losing an available network reports LOST`() {
|
|
46
|
-
val state = ScopedNetworkState()
|
|
47
|
-
val generation = state.startRequest()
|
|
48
|
-
state.onAvailable(generation)
|
|
49
|
-
|
|
50
|
-
assertThat(state.onLost(generation)).isTrue()
|
|
51
|
-
assertThat(state.phase).isEqualTo(Phase.LOST)
|
|
52
|
-
assertThat(state.failure).isEqualTo(Failure.LOST)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
@Test
|
|
56
|
-
fun `a second onLost is a no-op`() {
|
|
57
|
-
val state = ScopedNetworkState()
|
|
58
|
-
val generation = state.startRequest()
|
|
59
|
-
state.onAvailable(generation)
|
|
60
|
-
state.onLost(generation)
|
|
61
|
-
|
|
62
|
-
assertThat(state.onLost(generation)).isFalse()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
@Test
|
|
66
|
-
fun `timeout while still requesting fails as TIMEOUT`() {
|
|
67
|
-
val state = ScopedNetworkState()
|
|
68
|
-
val generation = state.startRequest()
|
|
69
|
-
|
|
70
|
-
assertThat(state.onTimeout(generation)).isTrue()
|
|
71
|
-
assertThat(state.phase).isEqualTo(Phase.LOST)
|
|
72
|
-
assertThat(state.failure).isEqualTo(Failure.TIMEOUT)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** The join succeeded just before the watchdog fired; the network must stay usable. */
|
|
76
|
-
@Test
|
|
77
|
-
fun `a timeout arriving after onAvailable is ignored`() {
|
|
78
|
-
val state = ScopedNetworkState()
|
|
79
|
-
val generation = state.startRequest()
|
|
80
|
-
state.onAvailable(generation)
|
|
81
|
-
|
|
82
|
-
assertThat(state.onTimeout(generation)).isFalse()
|
|
83
|
-
assertThat(state.phase).isEqualTo(Phase.AVAILABLE)
|
|
84
|
-
assertThat(state.failure).isEqualTo(Failure.NONE)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@Test
|
|
88
|
-
fun `onUnavailable fails as UNAVAILABLE`() {
|
|
89
|
-
val state = ScopedNetworkState()
|
|
90
|
-
val generation = state.startRequest()
|
|
91
|
-
|
|
92
|
-
assertThat(state.onUnavailable(generation)).isTrue()
|
|
93
|
-
assertThat(state.failure).isEqualTo(Failure.UNAVAILABLE)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
@Test
|
|
97
|
-
fun `a SecurityException maps to PERMISSION_DENIED`() {
|
|
98
|
-
val state = ScopedNetworkState()
|
|
99
|
-
val generation = state.startRequest()
|
|
100
|
-
|
|
101
|
-
assertThat(state.onRequestFailed(generation, permissionDenied = true)).isTrue()
|
|
102
|
-
assertThat(state.failure).isEqualTo(Failure.PERMISSION_DENIED)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
@Test
|
|
106
|
-
fun `any other request throw maps to REQUEST_FAILED`() {
|
|
107
|
-
val state = ScopedNetworkState()
|
|
108
|
-
val generation = state.startRequest()
|
|
109
|
-
|
|
110
|
-
assertThat(state.onRequestFailed(generation, permissionDenied = false)).isTrue()
|
|
111
|
-
assertThat(state.failure).isEqualTo(Failure.REQUEST_FAILED)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// -----------------------------------------------------------------
|
|
115
|
-
// Stale generations
|
|
116
|
-
// -----------------------------------------------------------------
|
|
117
|
-
|
|
118
|
-
@Test
|
|
119
|
-
fun `callbacks from a superseded request are rejected`() {
|
|
120
|
-
val state = ScopedNetworkState()
|
|
121
|
-
val first = state.startRequest()
|
|
122
|
-
val second = state.startRequest()
|
|
123
|
-
assertThat(second).isNotEqualTo(first)
|
|
124
|
-
|
|
125
|
-
assertThat(state.onAvailable(first)).isFalse()
|
|
126
|
-
assertThat(state.onLost(first)).isFalse()
|
|
127
|
-
assertThat(state.onUnavailable(first)).isFalse()
|
|
128
|
-
assertThat(state.onTimeout(first)).isFalse()
|
|
129
|
-
assertThat(state.phase).isEqualTo(Phase.REQUESTING)
|
|
130
|
-
|
|
131
|
-
assertThat(state.onAvailable(second)).isTrue()
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
@Test
|
|
135
|
-
fun `a late onLost after release does not resurrect state`() {
|
|
136
|
-
val state = ScopedNetworkState()
|
|
137
|
-
val generation = state.startRequest()
|
|
138
|
-
state.onAvailable(generation)
|
|
139
|
-
state.release()
|
|
140
|
-
|
|
141
|
-
assertThat(state.onLost(generation)).isFalse()
|
|
142
|
-
assertThat(state.failure).isEqualTo(Failure.NONE)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
@Test
|
|
146
|
-
fun `release is idempotent`() {
|
|
147
|
-
val state = ScopedNetworkState()
|
|
148
|
-
val generation = state.startRequest()
|
|
149
|
-
state.onAvailable(generation)
|
|
150
|
-
|
|
151
|
-
assertThat(state.release()).isTrue()
|
|
152
|
-
assertThat(state.release()).isFalse()
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
@Test
|
|
156
|
-
fun `release from idle is a no-op`() {
|
|
157
|
-
assertThat(ScopedNetworkState().release()).isFalse()
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
@Test
|
|
161
|
-
fun `reset returns the machine to idle for reuse`() {
|
|
162
|
-
val state = ScopedNetworkState()
|
|
163
|
-
val generation = state.startRequest()
|
|
164
|
-
state.onLost(generation)
|
|
165
|
-
state.reset()
|
|
166
|
-
|
|
167
|
-
assertThat(state.phase).isEqualTo(Phase.IDLE)
|
|
168
|
-
assertThat(state.failure).isEqualTo(Failure.NONE)
|
|
169
|
-
|
|
170
|
-
val next = state.startRequest()
|
|
171
|
-
assertThat(state.onAvailable(next)).isTrue()
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** Ten start/stop cycles must leave no residue, matching the rejoin soak requirement. */
|
|
175
|
-
@Test
|
|
176
|
-
fun `ten join and release cycles end clean`() {
|
|
177
|
-
val state = ScopedNetworkState()
|
|
178
|
-
repeat(10) {
|
|
179
|
-
val generation = state.startRequest()
|
|
180
|
-
assertThat(state.onAvailable(generation)).isTrue()
|
|
181
|
-
assertThat(state.release()).isTrue()
|
|
182
|
-
state.reset()
|
|
183
|
-
assertThat(state.phase).isEqualTo(Phase.IDLE)
|
|
184
|
-
}
|
|
185
|
-
assertThat(state.generation).isEqualTo(10)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.network
|
|
2
|
-
|
|
3
|
-
import com.mentra.acsmeeting.network.ScopedNetworkChangeDetector.ScopedInterface
|
|
4
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
5
|
-
import org.junit.Test
|
|
6
|
-
import org.webrtc.NetworkChangeDetector
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The takeover keeps a process-wide monitor from answering SoftAP with a leftover cellular handle;
|
|
10
|
-
* the restore keeps that takeover from breaking the next WHEP or LiveKit connection in the same
|
|
11
|
-
* process. Both halves are tested against a recording observer rather than the on-device gate.
|
|
12
|
-
*/
|
|
13
|
-
class ScopedNetworkObserverTest {
|
|
14
|
-
|
|
15
|
-
private val connected = mutableListOf<Long>()
|
|
16
|
-
private val disconnected = mutableListOf<Long>()
|
|
17
|
-
private var scoped: ScopedInterface? = softAp()
|
|
18
|
-
|
|
19
|
-
private val downstream =
|
|
20
|
-
object : NetworkChangeDetector.Observer() {
|
|
21
|
-
override fun onConnectionTypeChanged(connectionType: NetworkChangeDetector.ConnectionType) = Unit
|
|
22
|
-
|
|
23
|
-
override fun onNetworkConnect(networkInfo: NetworkChangeDetector.NetworkInformation) {
|
|
24
|
-
connected.add(networkInfo.handle)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
override fun onNetworkDisconnect(networkHandle: Long) {
|
|
28
|
-
disconnected.add(networkHandle)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
override fun onNetworkPreference(
|
|
32
|
-
types: MutableList<NetworkChangeDetector.ConnectionType>,
|
|
33
|
-
preference: Int,
|
|
34
|
-
) = Unit
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private val observer = ScopedNetworkObserver(downstream) { scoped }
|
|
38
|
-
|
|
39
|
-
private fun ipv4(a: Int, b: Int, c: Int, d: Int) =
|
|
40
|
-
byteArrayOf(a.toByte(), b.toByte(), c.toByte(), d.toByte())
|
|
41
|
-
|
|
42
|
-
private fun softAp() = ScopedInterface("wlan0", 99L, listOf(ipv4(192, 168, 43, 79)))
|
|
43
|
-
|
|
44
|
-
private fun hotspot() = ScopedNetworkChangeDetector.toNetworkInformation(softAp())!!
|
|
45
|
-
|
|
46
|
-
private fun info(name: String, handle: Long, address: ByteArray = ipv4(10, 1, 2, 3)) =
|
|
47
|
-
NetworkChangeDetector.NetworkInformation(
|
|
48
|
-
name,
|
|
49
|
-
NetworkChangeDetector.ConnectionType.CONNECTION_4G,
|
|
50
|
-
NetworkChangeDetector.ConnectionType.CONNECTION_NONE,
|
|
51
|
-
handle,
|
|
52
|
-
arrayOf(NetworkChangeDetector.IPAddress(address)),
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
@Test
|
|
56
|
-
fun `a stock connect is swallowed while SoftAP is joined`() {
|
|
57
|
-
observer.onNetworkConnect(info("rmnet_data0", 7L))
|
|
58
|
-
|
|
59
|
-
assertThat(connected).isEmpty()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The monitor is already running when a SoftAP call starts: ACS joins over cellular first, so
|
|
64
|
-
* those handles are in the native list before ingest exists. They have to be dropped.
|
|
65
|
-
*/
|
|
66
|
-
@Test
|
|
67
|
-
fun `publish drops networks announced before the join and connects only the inventory`() {
|
|
68
|
-
observer.onNetworkConnect(info("rmnet_data0", 7L))
|
|
69
|
-
observer.onNetworkConnect(info("wlan0", 8L, ipv4(192, 168, 50, 7)))
|
|
70
|
-
|
|
71
|
-
observer.publish(listOf(hotspot()))
|
|
72
|
-
|
|
73
|
-
assertThat(disconnected).containsExactly(7L, 8L)
|
|
74
|
-
assertThat(connected).containsExactly(99L)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@Test
|
|
78
|
-
fun `a second publish of the same inventory is a no-op`() {
|
|
79
|
-
observer.publish(listOf(hotspot()))
|
|
80
|
-
connected.clear()
|
|
81
|
-
|
|
82
|
-
observer.publish(listOf(hotspot()))
|
|
83
|
-
|
|
84
|
-
assertThat(connected).isEmpty()
|
|
85
|
-
assertThat(disconnected).isEmpty()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Teardown followed by ordinary WebRTC. Resuming pass-through alone would leave these networks
|
|
90
|
-
* missing until the OS happened to re-announce them, and leave the synthetic entry in place.
|
|
91
|
-
*/
|
|
92
|
-
@Test
|
|
93
|
-
fun `teardown drops the synthetic entry and replays the suppressed networks`() {
|
|
94
|
-
observer.onNetworkConnect(info("rmnet_data0", 7L))
|
|
95
|
-
observer.publish(listOf(hotspot()))
|
|
96
|
-
connected.clear()
|
|
97
|
-
disconnected.clear()
|
|
98
|
-
|
|
99
|
-
scoped = null
|
|
100
|
-
observer.restore()
|
|
101
|
-
|
|
102
|
-
assertThat(disconnected).containsExactly(99L)
|
|
103
|
-
assertThat(connected).containsExactly(7L)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** A network that genuinely went away while SoftAP was up must not come back in the replay. */
|
|
107
|
-
@Test
|
|
108
|
-
fun `a network lost during the call is not replayed on teardown`() {
|
|
109
|
-
observer.onNetworkConnect(info("rmnet_data0", 7L))
|
|
110
|
-
observer.onNetworkConnect(info("wlan0", 8L, ipv4(192, 168, 50, 7)))
|
|
111
|
-
observer.publish(listOf(hotspot()))
|
|
112
|
-
observer.onNetworkDisconnect(8L)
|
|
113
|
-
connected.clear()
|
|
114
|
-
|
|
115
|
-
scoped = null
|
|
116
|
-
observer.restore()
|
|
117
|
-
|
|
118
|
-
assertThat(connected).containsExactly(7L)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@Test
|
|
122
|
-
fun `restore is idempotent and a no-op when ownership was never taken`() {
|
|
123
|
-
scoped = null
|
|
124
|
-
|
|
125
|
-
observer.restore()
|
|
126
|
-
observer.restore()
|
|
127
|
-
|
|
128
|
-
assertThat(connected).isEmpty()
|
|
129
|
-
assertThat(disconnected).isEmpty()
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Restoration is also driven from the OS side, since after teardown it may be the only caller. */
|
|
133
|
-
@Test
|
|
134
|
-
fun `a stock callback after teardown restores before forwarding, and forwards once`() {
|
|
135
|
-
observer.onNetworkConnect(info("rmnet_data0", 7L))
|
|
136
|
-
observer.publish(listOf(hotspot()))
|
|
137
|
-
connected.clear()
|
|
138
|
-
disconnected.clear()
|
|
139
|
-
|
|
140
|
-
scoped = null
|
|
141
|
-
observer.onNetworkConnect(info("wlan0", 8L, ipv4(192, 168, 50, 7)))
|
|
142
|
-
|
|
143
|
-
assertThat(disconnected).containsExactly(99L)
|
|
144
|
-
assertThat(connected).containsExactly(7L, 8L)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
@Test
|
|
148
|
-
fun `stock callbacks pass through once the hotspot is gone`() {
|
|
149
|
-
scoped = null
|
|
150
|
-
|
|
151
|
-
observer.onNetworkConnect(info("rmnet_data0", 7L))
|
|
152
|
-
|
|
153
|
-
assertThat(connected).containsExactly(7L)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/** Rejoin is part of the acceptance bar, so the takeover has to re-arm after a restore. */
|
|
157
|
-
@Test
|
|
158
|
-
fun `an immediate rejoin takes the monitor back over`() {
|
|
159
|
-
observer.onNetworkConnect(info("rmnet_data0", 7L))
|
|
160
|
-
observer.publish(listOf(hotspot()))
|
|
161
|
-
scoped = null
|
|
162
|
-
observer.restore()
|
|
163
|
-
connected.clear()
|
|
164
|
-
disconnected.clear()
|
|
165
|
-
|
|
166
|
-
scoped = softAp()
|
|
167
|
-
observer.publish(listOf(hotspot()))
|
|
168
|
-
|
|
169
|
-
assertThat(disconnected).containsExactly(7L)
|
|
170
|
-
assertThat(connected).containsExactly(99L)
|
|
171
|
-
}
|
|
172
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.network
|
|
2
|
-
|
|
3
|
-
import com.mentra.acsmeeting.network.ScopedNetworkReadiness.Verdict
|
|
4
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
5
|
-
import org.junit.Test
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The race these cases pin cost a misdiagnosis: ingest started on `onAvailable`, the hotspot address
|
|
9
|
-
* was not yet in the kernel table, and the resulting candidate-less answer was blamed on socket
|
|
10
|
-
* binding. Readiness now has to agree from three sources, and the missing one has to be named.
|
|
11
|
-
*/
|
|
12
|
-
class ScopedNetworkReadinessTest {
|
|
13
|
-
|
|
14
|
-
private val readiness = ScopedNetworkReadiness()
|
|
15
|
-
|
|
16
|
-
private fun joined() {
|
|
17
|
-
readiness.onAvailable()
|
|
18
|
-
readiness.onCapabilities()
|
|
19
|
-
readiness.onLinkProperties("wlan0", "192.168.43.79")
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@Test
|
|
23
|
-
fun `a fresh gate waits for onAvailable`() {
|
|
24
|
-
assertThat(readiness.verdict()).isEqualTo(Verdict.Waiting("onAvailable"))
|
|
25
|
-
assertThat(readiness.callbacksSatisfied()).isFalse
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Android's guidance, and the reason `onAvailable` alone was not enough. */
|
|
29
|
-
@Test
|
|
30
|
-
fun `onAvailable alone is not ready`() {
|
|
31
|
-
readiness.onAvailable()
|
|
32
|
-
|
|
33
|
-
assertThat(readiness.verdict()).isEqualTo(Verdict.Waiting("onCapabilitiesChanged"))
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
@Test
|
|
37
|
-
fun `capabilities without an address is not ready`() {
|
|
38
|
-
readiness.onAvailable()
|
|
39
|
-
readiness.onCapabilities()
|
|
40
|
-
|
|
41
|
-
assertThat(readiness.verdict()).isEqualTo(Verdict.Waiting("an IPv4 address from onLinkPropertiesChanged"))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** The whole point: the callbacks can all agree while ICE still has nothing to gather. */
|
|
45
|
-
@Test
|
|
46
|
-
fun `every callback reporting is not ready until the kernel table has the address`() {
|
|
47
|
-
joined()
|
|
48
|
-
|
|
49
|
-
assertThat(readiness.callbacksSatisfied()).isTrue
|
|
50
|
-
assertThat(readiness.verdict())
|
|
51
|
-
.isEqualTo(Verdict.Waiting("the kernel interface table to carry 192.168.43.79"))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
@Test
|
|
55
|
-
fun `the address appearing in the table late completes readiness`() {
|
|
56
|
-
joined()
|
|
57
|
-
|
|
58
|
-
readiness.onInterfaceTable("wlan0")
|
|
59
|
-
|
|
60
|
-
assertThat(readiness.verdict()).isEqualTo(Verdict.Ready("192.168.43.79", "wlan0", "wlan0"))
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* A name disagreement is reported, not blocked on: ICE gathers per address, so the address being
|
|
65
|
-
* in the table is what matters. Blocking here would fail calls that actually work.
|
|
66
|
-
*/
|
|
67
|
-
@Test
|
|
68
|
-
fun `the kernel owning the address under another name is still ready`() {
|
|
69
|
-
joined()
|
|
70
|
-
|
|
71
|
-
readiness.onInterfaceTable("wlan1")
|
|
72
|
-
|
|
73
|
-
val verdict = readiness.verdict() as Verdict.Ready
|
|
74
|
-
assertThat(verdict.interfaceName).isEqualTo("wlan0")
|
|
75
|
-
assertThat(verdict.owner).isEqualTo("wlan1")
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** A later poll that no longer finds the address must un-ready the gate, not latch. */
|
|
79
|
-
@Test
|
|
80
|
-
fun `the address leaving the table takes readiness away again`() {
|
|
81
|
-
joined()
|
|
82
|
-
readiness.onInterfaceTable("wlan0")
|
|
83
|
-
|
|
84
|
-
readiness.onInterfaceTable(null)
|
|
85
|
-
|
|
86
|
-
assertThat(readiness.verdict()).isInstanceOf(Verdict.Waiting::class.java)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** LinkProperties is a whole-state snapshot, so a later one without an address means gone. */
|
|
90
|
-
@Test
|
|
91
|
-
fun `a link properties update without an address clears the observation`() {
|
|
92
|
-
joined()
|
|
93
|
-
readiness.onInterfaceTable("wlan0")
|
|
94
|
-
|
|
95
|
-
readiness.onLinkProperties("wlan0", null)
|
|
96
|
-
|
|
97
|
-
assertThat(readiness.callbacksSatisfied()).isFalse
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** The synchronous read inside `onAvailable` is a hint; it must not erase a real callback. */
|
|
101
|
-
@Test
|
|
102
|
-
fun `seeding with no address does not clear what a callback reported`() {
|
|
103
|
-
readiness.onLinkProperties("wlan0", "192.168.43.79")
|
|
104
|
-
|
|
105
|
-
readiness.seedLinkProperties(null, null)
|
|
106
|
-
|
|
107
|
-
assertThat(readiness.observations.linkAddress).isEqualTo("192.168.43.79")
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
@Test
|
|
111
|
-
fun `seeding supplies the address when the callback has not arrived`() {
|
|
112
|
-
readiness.onAvailable()
|
|
113
|
-
readiness.seedLinkProperties("wlan0", "192.168.43.79")
|
|
114
|
-
readiness.onCapabilities()
|
|
115
|
-
|
|
116
|
-
assertThat(readiness.callbacksSatisfied()).isTrue
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** Rejoin reuses the instance, so a stale Ready from the previous call would be fatal. */
|
|
120
|
-
@Test
|
|
121
|
-
fun `reset returns the gate to waiting for onAvailable`() {
|
|
122
|
-
joined()
|
|
123
|
-
readiness.onInterfaceTable("wlan0")
|
|
124
|
-
|
|
125
|
-
readiness.reset()
|
|
126
|
-
|
|
127
|
-
assertThat(readiness.verdict()).isEqualTo(Verdict.Waiting("onAvailable"))
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
@Test
|
|
131
|
-
fun `callbacks arriving out of order still resolve`() {
|
|
132
|
-
readiness.onCapabilities()
|
|
133
|
-
readiness.onLinkProperties("wlan0", "192.168.43.79")
|
|
134
|
-
readiness.onInterfaceTable("wlan0")
|
|
135
|
-
assertThat(readiness.verdict()).isEqualTo(Verdict.Waiting("onAvailable"))
|
|
136
|
-
|
|
137
|
-
readiness.onAvailable()
|
|
138
|
-
|
|
139
|
-
assertThat(readiness.verdict()).isInstanceOf(Verdict.Ready::class.java)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.network
|
|
2
|
-
|
|
3
|
-
import android.net.NetworkCapabilities
|
|
4
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
5
|
-
import org.junit.Test
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Guards the one property the whole dual-homing design rests on: the SoftAP request must not ask
|
|
9
|
-
* for `NET_CAPABILITY_INTERNET`. If that regresses, Android can promote the glasses hotspot to the
|
|
10
|
-
* default network and ACS traffic silently stops using cellular.
|
|
11
|
-
*/
|
|
12
|
-
class ScopedNetworkRequestSpecTest {
|
|
13
|
-
|
|
14
|
-
@Test
|
|
15
|
-
fun `the softap request removes the internet capability`() {
|
|
16
|
-
val spec = ScopedNetworkRequestSpec.forSoftAp("MentraLive-1234", "hunter2")
|
|
17
|
-
|
|
18
|
-
assertThat(spec.removedCapabilities).contains(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
|
19
|
-
assertThat(spec.avoidsInternetCapability).isTrue()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@Test
|
|
23
|
-
fun `the softap request is wifi only`() {
|
|
24
|
-
val spec = ScopedNetworkRequestSpec.forSoftAp("MentraLive-1234", "hunter2")
|
|
25
|
-
|
|
26
|
-
assertThat(spec.transportTypes).containsExactly(NetworkCapabilities.TRANSPORT_WIFI)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@Test
|
|
30
|
-
fun `ssid and passphrase are carried through`() {
|
|
31
|
-
val spec = ScopedNetworkRequestSpec.forSoftAp("MentraLive-1234", "hunter2")
|
|
32
|
-
|
|
33
|
-
assertThat(spec.ssid).isEqualTo("MentraLive-1234")
|
|
34
|
-
assertThat(spec.passphrase).isEqualTo("hunter2")
|
|
35
|
-
assertThat(spec.hasPassphrase).isTrue()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@Test
|
|
39
|
-
fun `an open network reports no passphrase`() {
|
|
40
|
-
assertThat(ScopedNetworkRequestSpec.forSoftAp("Open-AP", "").hasPassphrase).isFalse()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@Test
|
|
44
|
-
fun `the default timeout matches the bluetooth sdk hotspot join`() {
|
|
45
|
-
assertThat(ScopedNetworkRequestSpec.forSoftAp("x", "y").timeoutMs).isEqualTo(30_000)
|
|
46
|
-
assertThat(ScopedNetworkRequestSpec.DEFAULT_TIMEOUT_MS).isEqualTo(30_000)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
@Test
|
|
50
|
-
fun `the timeout is overridable`() {
|
|
51
|
-
assertThat(ScopedNetworkRequestSpec.forSoftAp("x", "y", timeoutMs = 5_000).timeoutMs)
|
|
52
|
-
.isEqualTo(5_000)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** A spec that kept the internet capability must not read as safe. */
|
|
56
|
-
@Test
|
|
57
|
-
fun `avoidsInternetCapability is false when the capability is not removed`() {
|
|
58
|
-
val unsafe =
|
|
59
|
-
ScopedNetworkRequestSpec(
|
|
60
|
-
ssid = "x",
|
|
61
|
-
passphrase = "y",
|
|
62
|
-
transportTypes = setOf(NetworkCapabilities.TRANSPORT_WIFI),
|
|
63
|
-
removedCapabilities = emptySet(),
|
|
64
|
-
timeoutMs = 1_000,
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
assertThat(unsafe.avoidsInternetCapability).isFalse()
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.source
|
|
2
|
-
|
|
3
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
import java.nio.ByteBuffer
|
|
6
|
-
|
|
7
|
-
class BitmapFontTest {
|
|
8
|
-
@Test
|
|
9
|
-
fun drawsOnPixelsForDigitEight() {
|
|
10
|
-
val width = 80
|
|
11
|
-
val height = 40
|
|
12
|
-
val dest = ByteBuffer.allocate(width * height)
|
|
13
|
-
BitmapFont.draw(dest, width, height, "8", 0, 0, 2, 0xEB.toByte())
|
|
14
|
-
var on = 0
|
|
15
|
-
for (i in 0 until dest.capacity()) {
|
|
16
|
-
if (dest.get(i) == 0xEB.toByte()) on += 1
|
|
17
|
-
}
|
|
18
|
-
assertThat(on).isGreaterThan(0)
|
|
19
|
-
assertThat(BitmapFont.textWidth(1, 2)).isEqualTo(5 * 2)
|
|
20
|
-
assertThat(BitmapFont.textHeight(2)).isEqualTo(7 * 2)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.source
|
|
2
|
-
|
|
3
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Covers the scale decision the relay makes on every frame.
|
|
8
|
-
*
|
|
9
|
-
* This code was previously inline in [CloudflareWhepSource] with no coverage at all, and it is
|
|
10
|
-
* decided per frame on the decode thread. Both directions are invisible when wrong: an unnecessary
|
|
11
|
-
* scale is a silent per-frame CPU cost, and a skipped one is a plane-size mismatch at the ACS
|
|
12
|
-
* sender rather than an exception here.
|
|
13
|
-
*/
|
|
14
|
-
class DecodedTrackRelayTest {
|
|
15
|
-
|
|
16
|
-
@Test
|
|
17
|
-
fun `no target size means ACS has not negotiated yet, so the buffer passes through`() {
|
|
18
|
-
assertThat(DecodedTrackRelay.needsScale(null, 1280, 720)).isFalse()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@Test
|
|
22
|
-
fun `a target matching the buffer needs no scale`() {
|
|
23
|
-
assertThat(DecodedTrackRelay.needsScale(TargetSize(1280, 720), 1280, 720)).isFalse()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@Test
|
|
27
|
-
fun `a differing width or height needs a scale`() {
|
|
28
|
-
assertThat(DecodedTrackRelay.needsScale(TargetSize(640, 720), 1280, 720)).isTrue()
|
|
29
|
-
assertThat(DecodedTrackRelay.needsScale(TargetSize(1280, 360), 1280, 720)).isTrue()
|
|
30
|
-
assertThat(DecodedTrackRelay.needsScale(TargetSize(640, 360), 1280, 720)).isTrue()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* A transposed target is a real size change, not a rotation to be ignored. The relay works in
|
|
35
|
-
* unrotated buffer coordinates, so 720x1280 against a 1280x720 buffer must scale.
|
|
36
|
-
*/
|
|
37
|
-
@Test
|
|
38
|
-
fun `a transposed target still needs a scale`() {
|
|
39
|
-
assertThat(DecodedTrackRelay.needsScale(TargetSize(720, 1280), 1280, 720)).isTrue()
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.source
|
|
2
|
-
|
|
3
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
class FirstFrameGateTest {
|
|
7
|
-
@Test
|
|
8
|
-
fun answerAloneDoesNotPromote() {
|
|
9
|
-
val gate = FirstFrameGate()
|
|
10
|
-
gate.arm(1)
|
|
11
|
-
// The answer is armed, not live: nothing has reached the sink yet.
|
|
12
|
-
assertThat(gate.expired(1)).isTrue()
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
@Test
|
|
16
|
-
fun firstFrameForTheArmedGenerationPromotesExactlyOnce() {
|
|
17
|
-
val gate = FirstFrameGate()
|
|
18
|
-
gate.arm(7)
|
|
19
|
-
assertThat(gate.onFrame(7)).isTrue()
|
|
20
|
-
assertThat(gate.onFrame(7)).isFalse()
|
|
21
|
-
assertThat(gate.expired(7)).isFalse()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@Test
|
|
25
|
-
fun framesFromAPeerBeingTornDownNeverPromote() {
|
|
26
|
-
val gate = FirstFrameGate()
|
|
27
|
-
gate.arm(2)
|
|
28
|
-
assertThat(gate.onFrame(1)).isFalse()
|
|
29
|
-
assertThat(gate.expired(2)).isTrue()
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@Test
|
|
33
|
-
fun deadlineForASupersededGenerationDoesNotFailTheNewPeer() {
|
|
34
|
-
val gate = FirstFrameGate()
|
|
35
|
-
gate.arm(3)
|
|
36
|
-
gate.arm(4)
|
|
37
|
-
assertThat(gate.expired(3)).isFalse()
|
|
38
|
-
assertThat(gate.expired(4)).isTrue()
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@Test
|
|
42
|
-
fun iceRecoveryRearmsSoTheNextFramePromotesAgain() {
|
|
43
|
-
val gate = FirstFrameGate()
|
|
44
|
-
gate.arm(5)
|
|
45
|
-
assertThat(gate.onFrame(5)).isTrue()
|
|
46
|
-
// ICE bounced and came back on the same peer: LIVE has to be re-earned.
|
|
47
|
-
gate.arm(5)
|
|
48
|
-
assertThat(gate.expired(5)).isTrue()
|
|
49
|
-
assertThat(gate.onFrame(5)).isTrue()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@Test
|
|
53
|
-
fun resetClearsTheArmSoAStoppedSourceCannotBeFailedLater() {
|
|
54
|
-
val gate = FirstFrameGate()
|
|
55
|
-
gate.arm(6)
|
|
56
|
-
gate.reset()
|
|
57
|
-
assertThat(gate.expired(6)).isFalse()
|
|
58
|
-
assertThat(gate.onFrame(6)).isFalse()
|
|
59
|
-
}
|
|
60
|
-
}
|