@mentra/acs-meeting 3.2.0-dev.222 → 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,322 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.telemetry
|
|
2
|
-
|
|
3
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
class PipelineStatsTest {
|
|
7
|
-
@Test
|
|
8
|
-
fun conservationHoldsAcrossEveryDisposition() {
|
|
9
|
-
val stats = PipelineStats()
|
|
10
|
-
repeat(5) { stats.onSink() }
|
|
11
|
-
stats.onQueued(); stats.onSub()
|
|
12
|
-
stats.onDropSize()
|
|
13
|
-
stats.onDropBusy()
|
|
14
|
-
stats.onDropNullI420()
|
|
15
|
-
stats.onDropPaced()
|
|
16
|
-
assertThat(stats.conserved()).isTrue()
|
|
17
|
-
assertThat(stats.sinkCount()).isEqualTo(5)
|
|
18
|
-
assertThat(stats.subCount()).isEqualTo(1)
|
|
19
|
-
assertThat(stats.dropCount()).isEqualTo(4)
|
|
20
|
-
assertThat(stats.inFlightCount()).isEqualTo(0)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
@Test
|
|
24
|
-
fun latencyStraddleIsOneHundredPercentOverTenTicks() {
|
|
25
|
-
var clock = 0L
|
|
26
|
-
val stats = PipelineStats { clock }
|
|
27
|
-
val ticks = ArrayList<String>()
|
|
28
|
-
stats.onSink()
|
|
29
|
-
stats.onQueued()
|
|
30
|
-
clock += 1000
|
|
31
|
-
ticks.add(stats.tick())
|
|
32
|
-
stats.onSub()
|
|
33
|
-
repeat(9) {
|
|
34
|
-
clock += 1000
|
|
35
|
-
ticks.add(stats.tick())
|
|
36
|
-
}
|
|
37
|
-
assertThat(stats.subCount()).isEqualTo(stats.sinkCount())
|
|
38
|
-
assertThat(stats.conserved()).isTrue()
|
|
39
|
-
val last = ticks.last()
|
|
40
|
-
assertThat(last).contains("cum{sink=1 sub=1 drop=0 inFlight=0}")
|
|
41
|
-
assertThat(last).doesNotContain("CONSERVE_FAIL")
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
@Test
|
|
45
|
-
fun cumulativeCountersNeverDecrease() {
|
|
46
|
-
var clock = 0L
|
|
47
|
-
val stats = PipelineStats { clock }
|
|
48
|
-
stats.onSink(); stats.onQueued(); stats.onSub()
|
|
49
|
-
clock += 1000
|
|
50
|
-
val first = stats.tick()
|
|
51
|
-
clock += 1000
|
|
52
|
-
val idle = stats.tick()
|
|
53
|
-
assertThat(extractCum(first)).isEqualTo(extractCum(idle))
|
|
54
|
-
assertThat(stats.sinkCount()).isEqualTo(1)
|
|
55
|
-
assertThat(stats.subCount()).isEqualTo(1)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
@Test
|
|
59
|
-
fun idleTickStillEmitsSinkZero() {
|
|
60
|
-
var clock = 0L
|
|
61
|
-
val stats = PipelineStats { clock }
|
|
62
|
-
clock += 1000
|
|
63
|
-
val line = stats.tick()
|
|
64
|
-
assertThat(line).startsWith("P6 ladder")
|
|
65
|
-
assertThat(line).contains("sink=0.0")
|
|
66
|
-
assertThat(line).contains("dec=na")
|
|
67
|
-
assertThat(line).contains("wire=0x0@na kbps=na codec=na")
|
|
68
|
-
assertThat(line).contains("drop{")
|
|
69
|
-
assertThat(line).contains("pace=0")
|
|
70
|
-
assertThat(line).contains("ms{")
|
|
71
|
-
assertThat(line).contains("chroma{")
|
|
72
|
-
assertThat(line).contains("cum{sink=0 sub=0 drop=0 inFlight=0}")
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@Test
|
|
76
|
-
fun queuedFrameCountsAsInFlightUntilSettled() {
|
|
77
|
-
val stats = PipelineStats()
|
|
78
|
-
stats.onSink()
|
|
79
|
-
stats.onQueued()
|
|
80
|
-
assertThat(stats.conserved()).isTrue()
|
|
81
|
-
assertThat(stats.inFlightCount()).isEqualTo(1)
|
|
82
|
-
stats.onSub()
|
|
83
|
-
assertThat(stats.inFlightCount()).isEqualTo(0)
|
|
84
|
-
assertThat(stats.conserved()).isTrue()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* The device logged 14 CONSERVE_FAIL lines on a pipeline with zero drops.
|
|
89
|
-
* Cause was a tracked inFlight counter read mid-settle, not lost frames.
|
|
90
|
-
*/
|
|
91
|
-
@Test
|
|
92
|
-
fun concurrentSendsNeverReportConserveFail() {
|
|
93
|
-
val stats = PipelineStats { 0L }
|
|
94
|
-
val sender = Thread {
|
|
95
|
-
repeat(20_000) {
|
|
96
|
-
stats.onSink()
|
|
97
|
-
stats.onQueued()
|
|
98
|
-
stats.onSub()
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
val failures = ArrayList<String>()
|
|
102
|
-
val reader = Thread {
|
|
103
|
-
repeat(20_000) {
|
|
104
|
-
if (!stats.conserved()) failures.add("inFlight=${stats.inFlightCount()}")
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
sender.start(); reader.start()
|
|
108
|
-
sender.join(); reader.join()
|
|
109
|
-
assertThat(failures).isEmpty()
|
|
110
|
-
assertThat(stats.inFlightCount()).isEqualTo(0)
|
|
111
|
-
assertThat(stats.tick()).doesNotContain("CONSERVE_FAIL")
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
@Test
|
|
115
|
-
fun recvHealthReportsDeltasNotAbsolutes() {
|
|
116
|
-
val stats = PipelineStats { 0L }
|
|
117
|
-
assertThat(stats.recvLabel()).isEqualTo("recv{na}")
|
|
118
|
-
stats.setRecvHealth(
|
|
119
|
-
PipelineStats.RecvHealth(assembled = 100, dropped = 5, packetsLost = 40, nack = 7, pli = 2, freezes = 1),
|
|
120
|
-
)
|
|
121
|
-
assertThat(stats.recvLabel()).contains("drop=5", "lost=40", "nack=7", "pli=2", "freeze=1")
|
|
122
|
-
stats.setRecvHealth(
|
|
123
|
-
PipelineStats.RecvHealth(assembled = 115, dropped = 5, packetsLost = 52, nack = 9, pli = 2, freezes = 3),
|
|
124
|
-
)
|
|
125
|
-
assertThat(stats.recvLabel()).contains("drop=0", "lost=12", "nack=2", "pli=0", "freeze=2")
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
@Test
|
|
129
|
-
fun recvHealthAveragesPerFrameTimings() {
|
|
130
|
-
val stats = PipelineStats { 0L }
|
|
131
|
-
stats.setRecvHealth(
|
|
132
|
-
PipelineStats.RecvHealth(
|
|
133
|
-
assembled = 200,
|
|
134
|
-
decodeSec = 0.8,
|
|
135
|
-
jitterBufferSec = 12.0,
|
|
136
|
-
jitterBufferEmits = 200,
|
|
137
|
-
jitter = 0.015,
|
|
138
|
-
),
|
|
139
|
-
)
|
|
140
|
-
val label = stats.recvLabel()
|
|
141
|
-
assertThat(label).contains("decMs=4.0")
|
|
142
|
-
assertThat(label).contains("jbMs=60.0")
|
|
143
|
-
assertThat(label).contains("jit=15.0")
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
@Test
|
|
147
|
-
fun recvHealthToleratesMissingMembers() {
|
|
148
|
-
val stats = PipelineStats { 0L }
|
|
149
|
-
stats.setRecvHealth(PipelineStats.RecvHealth())
|
|
150
|
-
val label = stats.recvLabel()
|
|
151
|
-
assertThat(label).contains("drop=na", "lost=na", "decMs=na", "jbMs=na", "decImpl=na")
|
|
152
|
-
assertThat(stats.tick()).contains("recv{")
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
@Test
|
|
156
|
-
fun recvHealthPrintsDecoderImplementation() {
|
|
157
|
-
val stats = PipelineStats { 0L }
|
|
158
|
-
stats.setRecvHealth(PipelineStats.RecvHealth(decImpl = "OMX.qcom.video.decoder.avc"))
|
|
159
|
-
assertThat(stats.recvLabel()).contains("decImpl=OMX.qcom.video.decoder.avc")
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
@Test
|
|
163
|
-
fun ladderIncludesArmLabel() {
|
|
164
|
-
var clock = 0L
|
|
165
|
-
val stats = PipelineStats { clock }
|
|
166
|
-
clock += 1000
|
|
167
|
-
assertThat(stats.tick()).contains("arm=whep")
|
|
168
|
-
stats.arm = "synthetic"
|
|
169
|
-
clock += 1000
|
|
170
|
-
assertThat(stats.tick()).contains("arm=synthetic")
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
@Test
|
|
174
|
-
fun ladderExposesCpuAttributionFields() {
|
|
175
|
-
var clock = 0L
|
|
176
|
-
val stats = PipelineStats { clock }
|
|
177
|
-
stats.toI420.record(4_000_000)
|
|
178
|
-
stats.sinkCb.record(8_000_000)
|
|
179
|
-
stats.split.record(1_000_000)
|
|
180
|
-
stats.onDestAlloc()
|
|
181
|
-
stats.onPlaneAlloc()
|
|
182
|
-
stats.onPlaneAlloc()
|
|
183
|
-
clock += 1000
|
|
184
|
-
val line = stats.tick()
|
|
185
|
-
assertThat(line).contains("i420P95=4.0")
|
|
186
|
-
assertThat(line).contains("sinkCbP95=8.0")
|
|
187
|
-
assertThat(line).contains("splitP95=1.0")
|
|
188
|
-
assertThat(line).contains("alloc{dest=1 plane=2}")
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
@Test
|
|
192
|
-
fun ladderCarriesPathBufStrideZcAndCopyP95Na() {
|
|
193
|
-
var clock = 0L
|
|
194
|
-
val stats = PipelineStats { clock }
|
|
195
|
-
clock += 1000
|
|
196
|
-
val idle = stats.tick()
|
|
197
|
-
assertThat(idle).contains("path{mode=texture copy=packsplit pix=i420}")
|
|
198
|
-
assertThat(idle).contains("wire=0x0@na kbps=na codec=na")
|
|
199
|
-
assertThat(idle).contains("buf{tex=0 i420=0 other=0}")
|
|
200
|
-
assertThat(idle).contains("stride{y=0 u=0 v=0 tight=0 padded=0}")
|
|
201
|
-
assertThat(idle).contains("zc{on=0 used=0 fell=0 padded=0 heldMax=0 timeout=0}")
|
|
202
|
-
assertThat(idle).contains("copyP95=na")
|
|
203
|
-
|
|
204
|
-
stats.pathMode = "bytebuf"
|
|
205
|
-
stats.pathCopy = "nv12"
|
|
206
|
-
stats.pix = "nv12"
|
|
207
|
-
stats.codecName = "h264 sw"
|
|
208
|
-
stats.onFrameBuffer("tex")
|
|
209
|
-
stats.onFrameBuffer("i420")
|
|
210
|
-
stats.onFrameBuffer("i420")
|
|
211
|
-
stats.onFrameBuffer("nv12")
|
|
212
|
-
stats.onStrides(1280, 640, 640, 1280)
|
|
213
|
-
stats.onStrides(1280, 768, 768, 1280)
|
|
214
|
-
stats.zcOn = 1
|
|
215
|
-
stats.onZcUsed(1)
|
|
216
|
-
stats.onZcUsed(2)
|
|
217
|
-
stats.onZcFell()
|
|
218
|
-
stats.onZcPadded()
|
|
219
|
-
stats.onZcTimeout()
|
|
220
|
-
stats.copy.record(3_000_000)
|
|
221
|
-
clock += 1000
|
|
222
|
-
val line = stats.tick()
|
|
223
|
-
assertThat(line).contains("path{mode=bytebuf copy=nv12 pix=nv12}")
|
|
224
|
-
assertThat(line).contains("codec=h264_sw")
|
|
225
|
-
assertThat(line).contains("buf{tex=1 i420=2 other=1}")
|
|
226
|
-
assertThat(line).contains("stride{y=1280 u=768 v=768 tight=1 padded=1}")
|
|
227
|
-
assertThat(line).contains("zc{on=1 used=2 fell=1 padded=1 heldMax=2 timeout=1}")
|
|
228
|
-
assertThat(line).contains("copyP95=3.0")
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
@Test
|
|
232
|
-
fun ladderPrintsAcsWireGeometryAndBitrate() {
|
|
233
|
-
var clock = 0L
|
|
234
|
-
val stats = PipelineStats { clock }
|
|
235
|
-
stats.wireWidth = 960
|
|
236
|
-
stats.wireHeight = 540
|
|
237
|
-
stats.wireFps = 29.8
|
|
238
|
-
stats.wireBitrateBps = 1_480_000
|
|
239
|
-
clock += 1000
|
|
240
|
-
stats.codecName = "H264SkypeEncoder_HW"
|
|
241
|
-
assertThat(stats.tick()).contains("wire=960x540@29.8 kbps=1480 codec=H264SkypeEncoder_HW")
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
@Test
|
|
245
|
-
fun ladderExposesRecvRateSeparateFromDecode() {
|
|
246
|
-
var clock = 0L
|
|
247
|
-
val stats = PipelineStats { clock }
|
|
248
|
-
stats.recvFps = 14.8
|
|
249
|
-
stats.decodedFps = 8.1
|
|
250
|
-
clock += 1000
|
|
251
|
-
val line = stats.tick()
|
|
252
|
-
assertThat(line).contains("recv=14.8")
|
|
253
|
-
assertThat(line).contains("dec=8.1")
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
@Test
|
|
257
|
-
fun ladderReportsFrameAgeAndEndToEndLatency() {
|
|
258
|
-
var clock = 0L
|
|
259
|
-
val stats = PipelineStats { clock }
|
|
260
|
-
stats.age.record(40_000_000)
|
|
261
|
-
stats.e2e.record(95_000_000)
|
|
262
|
-
clock += 1000
|
|
263
|
-
val line = stats.tick()
|
|
264
|
-
assertThat(line).contains("ageP50=40.0")
|
|
265
|
-
assertThat(line).contains("e2eP50=95.0")
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* A backed-up ACS send thread has no other symptom on this ladder: `sendP95` times the ACS call
|
|
270
|
-
* itself, so the wait in front of it has to be its own measurement.
|
|
271
|
-
*/
|
|
272
|
-
@Test
|
|
273
|
-
fun ladderSeparatesSenderQueueWaitFromTheAcsCall() {
|
|
274
|
-
var clock = 0L
|
|
275
|
-
val stats = PipelineStats { clock }
|
|
276
|
-
stats.queue.record(30_000_000)
|
|
277
|
-
stats.send.record(2_000_000)
|
|
278
|
-
clock += 1000
|
|
279
|
-
val line = stats.tick()
|
|
280
|
-
assertThat(line).contains("queueP95=30.0")
|
|
281
|
-
assertThat(line).contains("sendP95=2.0")
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
@Test
|
|
285
|
-
fun slowestStageNamesTheWorstPhoneSideStage() {
|
|
286
|
-
val stats = PipelineStats { 0 }
|
|
287
|
-
stats.scale.record(2_000_000)
|
|
288
|
-
stats.toI420.record(9_000_000)
|
|
289
|
-
stats.copy.record(1_000_000)
|
|
290
|
-
stats.send.record(12_000_000)
|
|
291
|
-
assertThat(stats.slowestStage()).isEqualTo("send=12.0")
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/** Ranking must compare numbers: as strings "9.0" sorts above "12.0". */
|
|
295
|
-
@Test
|
|
296
|
-
fun slowestStageRanksNumericallyNotLexically() {
|
|
297
|
-
val stats = PipelineStats { 0 }
|
|
298
|
-
stats.toI420.record(9_000_000)
|
|
299
|
-
stats.copy.record(12_000_000)
|
|
300
|
-
assertThat(stats.slowestStage()).isEqualTo("copy=12.0")
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Frame age is dominated by capture, encode and the network, so blaming a phone-side stage for it
|
|
305
|
-
* would make every healthy call accuse a stage this device does not run.
|
|
306
|
-
*/
|
|
307
|
-
@Test
|
|
308
|
-
fun slowestStageIgnoresUpstreamFrameAge() {
|
|
309
|
-
val stats = PipelineStats { 0 }
|
|
310
|
-
stats.age.record(400_000_000)
|
|
311
|
-
stats.copy.record(3_000_000)
|
|
312
|
-
assertThat(stats.slowestStage()).isEqualTo("copy=3.0")
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
@Test
|
|
316
|
-
fun slowestStageIsNotAvailableBeforeAnySamples() {
|
|
317
|
-
assertThat(PipelineStats { 0 }.slowestStage()).isEqualTo("na")
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private fun extractCum(line: String): String =
|
|
321
|
-
Regex("""cum\{[^}]+\}""").find(line)?.value ?: line
|
|
322
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.telemetry
|
|
2
|
-
|
|
3
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
class ProcessCpuTest {
|
|
7
|
-
@Test
|
|
8
|
-
fun percentMatchesTopStyleOverOneSecond() {
|
|
9
|
-
assertThat(ProcessCpu.percent(cpuDeltaMs = 1120, wallDeltaMs = 1000)).isEqualTo(112.0)
|
|
10
|
-
assertThat(ProcessCpu.percent(cpuDeltaMs = 200, wallDeltaMs = 1000)).isEqualTo(20.0)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
@Test
|
|
14
|
-
fun labelFormatsNaUntilFirstSample() {
|
|
15
|
-
assertThat(ProcessCpu.label(null, 8)).isEqualTo("cpu{proc=na cores=8}")
|
|
16
|
-
assertThat(ProcessCpu.label(112.4, 8)).isEqualTo("cpu{proc=112.4 cores=8}")
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.telemetry
|
|
2
|
-
|
|
3
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
import java.util.concurrent.CountDownLatch
|
|
6
|
-
import java.util.concurrent.Executors
|
|
7
|
-
import java.util.concurrent.TimeUnit
|
|
8
|
-
|
|
9
|
-
class RingPercentileTest {
|
|
10
|
-
@Test
|
|
11
|
-
fun emptyIsNa() {
|
|
12
|
-
assertThat(RingPercentile().p50()).isEqualTo("na")
|
|
13
|
-
assertThat(RingPercentile().p95()).isEqualTo("na")
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
@Test
|
|
17
|
-
fun knownInputs() {
|
|
18
|
-
val ring = RingPercentile(8)
|
|
19
|
-
ring.record(10_000_000)
|
|
20
|
-
ring.record(20_000_000)
|
|
21
|
-
ring.record(40_000_000)
|
|
22
|
-
assertThat(ring.p50()).isEqualTo("20.0")
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
@Test
|
|
26
|
-
fun wraparoundKeepsNewest() {
|
|
27
|
-
val ring = RingPercentile(3)
|
|
28
|
-
repeat(6) { index -> ring.record((index + 1) * 1_000_000L) }
|
|
29
|
-
assertThat(ring.size()).isEqualTo(3)
|
|
30
|
-
assertThat(ring.p50()).isEqualTo("5.0")
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
@Test
|
|
34
|
-
fun concurrentRecordDoesNotCorruptCount() {
|
|
35
|
-
val ring = RingPercentile(32)
|
|
36
|
-
val pool = Executors.newFixedThreadPool(4)
|
|
37
|
-
val latch = CountDownLatch(4)
|
|
38
|
-
repeat(4) {
|
|
39
|
-
pool.execute {
|
|
40
|
-
repeat(50) { ring.record(1_000_000) }
|
|
41
|
-
latch.countDown()
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue()
|
|
45
|
-
pool.shutdown()
|
|
46
|
-
assertThat(ring.size()).isEqualTo(32)
|
|
47
|
-
assertThat(ring.p50()).isNotEqualTo("na")
|
|
48
|
-
}
|
|
49
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.telemetry
|
|
2
|
-
|
|
3
|
-
import com.mentra.acsmeeting.telemetry.VideoRateVerdict.Bound
|
|
4
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
5
|
-
import org.junit.Test
|
|
6
|
-
|
|
7
|
-
class VideoRateVerdictTest {
|
|
8
|
-
/** 540p15 against a starved 1.5 Mbps budget — the grant Mentra Call used to send. */
|
|
9
|
-
private fun verdict(
|
|
10
|
-
advertisedFps: Double = 15.0,
|
|
11
|
-
sinkFps: Double = 15.0,
|
|
12
|
-
admittedFps: Double = 15.0,
|
|
13
|
-
wireFps: Double? = 15.0,
|
|
14
|
-
wireBitrateBps: Long? = 1_400_000,
|
|
15
|
-
budgetBps: Int = 1_500_000,
|
|
16
|
-
cpuPercent: Double? = 55.0,
|
|
17
|
-
) = VideoRateVerdict.of(
|
|
18
|
-
advertisedFps = advertisedFps,
|
|
19
|
-
sinkFps = sinkFps,
|
|
20
|
-
admittedFps = admittedFps,
|
|
21
|
-
wireFps = wireFps,
|
|
22
|
-
wireBitrateBps = wireBitrateBps,
|
|
23
|
-
budgetBps = budgetBps,
|
|
24
|
-
cpuPercent = cpuPercent,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
@Test
|
|
28
|
-
fun aWireHoldingTheAdvertisedRateIsOk() {
|
|
29
|
-
assertThat(verdict()).isEqualTo(Bound.OK)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The verdict that decides whether "the call quality is low" is answered by raising the grant.
|
|
34
|
-
* Every frame is on the wire, so nothing here is short — but the bits are pressed against the
|
|
35
|
-
* ceiling, which is the one condition under which a bigger number changes the picture. Without
|
|
36
|
-
* this the same tick reads OK and the ceiling never enters the conversation.
|
|
37
|
-
*/
|
|
38
|
-
@Test
|
|
39
|
-
fun aFullRateRidingTheCeilingIsNamedAsCapBound() {
|
|
40
|
-
assertThat(verdict(wireBitrateBps = 1_480_000)).isEqualTo(Bound.CAP_BOUND)
|
|
41
|
-
// The controller routinely overshoots the grant; that is still the ceiling binding.
|
|
42
|
-
assertThat(verdict(wireBitrateBps = 1_623_000)).isEqualTo(Bound.CAP_BOUND)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Headroom under the grant at a full rate is a healthy stream, not a ceiling problem. */
|
|
46
|
-
@Test
|
|
47
|
-
fun aFullRateWithBitsToSpareIsStillPlainOk() {
|
|
48
|
-
assertThat(verdict(wireBitrateBps = 1_100_000)).isEqualTo(Bound.OK)
|
|
49
|
-
assertThat(verdict(wireBitrateBps = null)).isEqualTo(Bound.OK)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Cap-bound is about the ceiling, and a short frame rate means something else is wrong first.
|
|
54
|
-
* Raising the grant for a stream that cannot fill the one it has buys nothing.
|
|
55
|
-
*/
|
|
56
|
-
@Test
|
|
57
|
-
fun aShortRateIsDiagnosedBeforeTheCeilingIsConsidered() {
|
|
58
|
-
assertThat(verdict(wireFps = 8.0, wireBitrateBps = 1_480_000, cpuPercent = 92.0))
|
|
59
|
-
.isEqualTo(Bound.ENCODER_SHORT)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The exact shape of the greedy-gate bug: the glasses supplied 14.6 fps, the
|
|
64
|
-
* pacer admitted 7.3 against an advertised 8, and every counter downstream
|
|
65
|
-
* looked like an encoder problem. It was ours.
|
|
66
|
-
*/
|
|
67
|
-
@Test
|
|
68
|
-
fun aGateThatDropsFramesTheSourceSuppliedIsBlamedOnThePacer() {
|
|
69
|
-
assertThat(verdict(advertisedFps = 8.0, sinkFps = 14.6, admittedFps = 7.3, wireFps = 7.0))
|
|
70
|
-
.isEqualTo(Bound.PACER_SHORT)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@Test
|
|
74
|
-
fun aShortSourceIsNotBlamedOnAnythingDownstream() {
|
|
75
|
-
assertThat(verdict(sinkFps = 6.0, admittedFps = 6.0, wireFps = 6.0))
|
|
76
|
-
.isEqualTo(Bound.SOURCE_SHORT)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* A source a little under the advertise is not the pacer's fault. Scoring the
|
|
81
|
-
* gate against the advertise alone would read every slightly slow glasses leg
|
|
82
|
-
* as our own bug and send the next fix to the wrong place.
|
|
83
|
-
*/
|
|
84
|
-
@Test
|
|
85
|
-
fun thePacerIsNotBlamedForFramesTheSourceNeverSent() {
|
|
86
|
-
assertThat(verdict(sinkFps = 13.0, admittedFps = 13.0, wireFps = 13.0))
|
|
87
|
-
.isEqualTo(Bound.OK)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* The two verdicts that matter, because they want opposite fixes. Same short
|
|
92
|
-
* wire rate, same delivered rate; only CPU and the bitrate headroom differ.
|
|
93
|
-
*/
|
|
94
|
-
@Test
|
|
95
|
-
fun aBusyCpuMeansTheEncoderIsTheCeiling() {
|
|
96
|
-
assertThat(verdict(wireFps = 8.0, cpuPercent = 92.0)).isEqualTo(Bound.ENCODER_SHORT)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
@Test
|
|
100
|
-
fun sparecpuAndACollapsedBitrateMeansTheRateControllerIsThrottling() {
|
|
101
|
-
assertThat(verdict(wireFps = 8.0, wireBitrateBps = 496_000, cpuPercent = 50.0))
|
|
102
|
-
.isEqualTo(Bound.BITRATE_STARVED)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** Spare CPU but the budget is being spent: the encoder really is the limit. */
|
|
106
|
-
@Test
|
|
107
|
-
fun sparecpuWithTheBudgetSpentIsStillTheEncoder() {
|
|
108
|
-
assertThat(verdict(wireFps = 8.0, wireBitrateBps = 1_450_000, cpuPercent = 50.0))
|
|
109
|
-
.isEqualTo(Bound.ENCODER_SHORT)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
@Test
|
|
113
|
-
fun aMissingWireReportIsNotAVerdict() {
|
|
114
|
-
assertThat(verdict(wireFps = null)).isEqualTo(Bound.UNKNOWN)
|
|
115
|
-
assertThat(verdict(advertisedFps = 0.0)).isEqualTo(Bound.UNKNOWN)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** Every bound carries an action, or the line makes the reader open this file. */
|
|
119
|
-
@Test
|
|
120
|
-
fun everyBoundHasANonEmptyHint() {
|
|
121
|
-
for (bound in Bound.entries) {
|
|
122
|
-
assertThat(VideoRateVerdict.hint(bound)).isNotBlank()
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
@Test
|
|
127
|
-
fun theLinePrintsTheInputsBesideTheVerdict() {
|
|
128
|
-
val line = VideoRateVerdict.line(
|
|
129
|
-
advertisedFps = 15.0,
|
|
130
|
-
sinkFps = 14.6,
|
|
131
|
-
admittedFps = 7.3,
|
|
132
|
-
wireFps = 7.0,
|
|
133
|
-
wireBitrateBps = 496_000,
|
|
134
|
-
budgetBps = 1_500_000,
|
|
135
|
-
cpuPercent = 61.3,
|
|
136
|
-
codecName = "h264 sw",
|
|
137
|
-
)
|
|
138
|
-
assertThat(line).startsWith("P7 rate bound=PACER_SHORT")
|
|
139
|
-
assertThat(line).contains("advertised=15.0")
|
|
140
|
-
assertThat(line).contains("sink=14.6")
|
|
141
|
-
assertThat(line).contains("admitted=7.3")
|
|
142
|
-
assertThat(line).contains("wire=7.0")
|
|
143
|
-
assertThat(line).contains("kbps=496")
|
|
144
|
-
assertThat(line).contains("budgetKbps=1500")
|
|
145
|
-
assertThat(line).contains("cpu=61.3")
|
|
146
|
-
assertThat(line).contains("codec=h264_sw")
|
|
147
|
-
assertThat(line).contains(VideoRateVerdict.hint(Bound.PACER_SHORT))
|
|
148
|
-
}
|
|
149
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.trace
|
|
2
|
-
|
|
3
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Covers the pure formatter. The `Log.i` wrapper is a one-liner and is exercised on device.
|
|
8
|
-
*/
|
|
9
|
-
class SoftApTraceTest {
|
|
10
|
-
|
|
11
|
-
@Test
|
|
12
|
-
fun `format carries the grep marker`() {
|
|
13
|
-
assertThat(SoftApTrace.format("t1", "scoped_network_available", 12))
|
|
14
|
-
.contains("SOFTAP_TRACE")
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
@Test
|
|
18
|
-
fun `format includes traceId stage and elapsed`() {
|
|
19
|
-
assertThat(SoftApTrace.format("abc123", "ice_selected_pair", 450))
|
|
20
|
-
.isEqualTo("[SOFTAP_TRACE] traceId=abc123 stage=ice_selected_pair elapsedMs=450")
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
@Test
|
|
24
|
-
fun `format omits traceId when absent`() {
|
|
25
|
-
assertThat(SoftApTrace.format("", "boot", 0))
|
|
26
|
-
.isEqualTo("[SOFTAP_TRACE] stage=boot elapsedMs=0")
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@Test
|
|
30
|
-
fun `format appends fields in order`() {
|
|
31
|
-
assertThat(SoftApTrace.format("t", "whip_request", 7, "method" to "POST", "status" to 201))
|
|
32
|
-
.endsWith("method=POST status=201")
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
@Test
|
|
36
|
-
fun `sanitize redacts the hotspot password`() {
|
|
37
|
-
assertThat(SoftApTrace.sanitize("password", "hunter2")).isEqualTo("<redacted>")
|
|
38
|
-
assertThat(SoftApTrace.sanitize("hotspotPassword", "hunter2")).isEqualTo("<redacted>")
|
|
39
|
-
assertThat(SoftApTrace.sanitize("psk", "hunter2")).isEqualTo("<redacted>")
|
|
40
|
-
assertThat(SoftApTrace.sanitize("passphrase", "hunter2")).isEqualTo("<redacted>")
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@Test
|
|
44
|
-
fun `sanitize redacts tokens and credentials`() {
|
|
45
|
-
assertThat(SoftApTrace.sanitize("token", "eyJhbGciOi")).isEqualTo("<redacted>")
|
|
46
|
-
assertThat(SoftApTrace.sanitize("acsToken", "eyJhbGciOi")).isEqualTo("<redacted>")
|
|
47
|
-
assertThat(SoftApTrace.sanitize("Authorization", "Bearer x")).isEqualTo("<redacted>")
|
|
48
|
-
assertThat(SoftApTrace.sanitize("meetingUrl", "https://teams.microsoft.com/l/x"))
|
|
49
|
-
.isEqualTo("<redacted>")
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@Test
|
|
53
|
-
fun `format never leaks a secret value into the line`() {
|
|
54
|
-
val line = SoftApTrace.format(
|
|
55
|
-
"t", "hotspot_started", 5,
|
|
56
|
-
"ssid" to "MentraLive", "password" to "hunter2",
|
|
57
|
-
)
|
|
58
|
-
assertThat(line).doesNotContain("hunter2")
|
|
59
|
-
assertThat(line).contains("ssid=MentraLive")
|
|
60
|
-
assertThat(line).contains("password=<redacted>")
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@Test
|
|
64
|
-
fun `sanitize keeps the local whip url which is diagnostic`() {
|
|
65
|
-
assertThat(SoftApTrace.sanitize("whipUrl", "http://192.168.43.20:8790/whip/abc"))
|
|
66
|
-
.isEqualTo("http://192.168.43.20:8790/whip/abc")
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
@Test
|
|
70
|
-
fun `sanitize strips query strings that may carry tokens`() {
|
|
71
|
-
assertThat(SoftApTrace.sanitize("playbackUrl", "https://example.com/live?token=secret123"))
|
|
72
|
-
.isEqualTo("https://example.com/live?<redacted>")
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@Test
|
|
76
|
-
fun `sanitize strips userinfo from urls`() {
|
|
77
|
-
val result = SoftApTrace.sanitize("endpoint", "https://user:pw@example.com/x")
|
|
78
|
-
assertThat(result).doesNotContain("pw@")
|
|
79
|
-
assertThat(result).contains("<redacted>@example.com/x")
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
@Test
|
|
83
|
-
fun `sanitize quotes values containing spaces`() {
|
|
84
|
-
assertThat(SoftApTrace.sanitize("reason", "two words")).isEqualTo("\"two words\"")
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@Test
|
|
88
|
-
fun `sanitize renders null and empty`() {
|
|
89
|
-
assertThat(SoftApTrace.sanitize("reason", null)).isEqualTo("null")
|
|
90
|
-
assertThat(SoftApTrace.sanitize("reason", "")).isEqualTo("\"\"")
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
@Test
|
|
94
|
-
fun `newTraceId produces distinct ids`() {
|
|
95
|
-
assertThat(SoftApTrace.newTraceId()).isNotEqualTo(SoftApTrace.newTraceId())
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
package com.mentra.acsmeeting.video
|
|
2
|
-
|
|
3
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
class FrameGeometryTest {
|
|
7
|
-
@Test
|
|
8
|
-
fun rotationZeroUsesBufferSize() {
|
|
9
|
-
val size = FrameGeometry.packSize(1280, 720, 0)
|
|
10
|
-
assertThat(size.width).isEqualTo(1280)
|
|
11
|
-
assertThat(size.height).isEqualTo(720)
|
|
12
|
-
assertThat(size.rotationNonZero).isFalse()
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
@Test
|
|
16
|
-
fun rotation90KeepsBufferCoordinatesNotRotatedDisplaySize() {
|
|
17
|
-
val bufferW = 1280
|
|
18
|
-
val bufferH = 720
|
|
19
|
-
val rotatedWidth = bufferH
|
|
20
|
-
val rotatedHeight = bufferW
|
|
21
|
-
val size = FrameGeometry.packSize(bufferW, bufferH, 90)
|
|
22
|
-
assertThat(size.width).isEqualTo(bufferW)
|
|
23
|
-
assertThat(size.height).isEqualTo(bufferH)
|
|
24
|
-
assertThat(size.rotationNonZero).isTrue()
|
|
25
|
-
assertThat(size.width).isNotEqualTo(rotatedWidth)
|
|
26
|
-
assertThat(size.height).isNotEqualTo(rotatedHeight)
|
|
27
|
-
val overrun = I420Packer.packedSize(rotatedWidth, rotatedHeight)
|
|
28
|
-
val actual = I420Packer.packedSize(size.width, size.height)
|
|
29
|
-
assertThat(overrun).isEqualTo(actual)
|
|
30
|
-
val planeIfRotated = rotatedHeight * 1280
|
|
31
|
-
assertThat(planeIfRotated).isGreaterThan(bufferW * bufferH)
|
|
32
|
-
}
|
|
33
|
-
}
|