Test router cleanup barriers in call and shared media owners
This commit is contained in:
1 parent
b3e1356d16
commit
48ab63a9d3
2 files changed
+66
No files matched your search
+25
@@ -1,6 +1,7 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
import android.content.Context
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.media.AudioDeviceInfo
|
||||
import android.media.AudioManager
|
||||
import android.media.AudioRecord
|
||||
@@ -29,6 +30,7 @@ class CallAudioLifecycleTest {
|
||||
Build.VERSION.SDK_INT = 30
|
||||
ChatApplication.instance = Context()
|
||||
AudioManager.shared = AudioManager()
|
||||
BluetoothAdapter.current = null
|
||||
AudioTrack.created.clear(); AudioTrack.nextFailures = emptySet(); AudioTrack.minimumBuffer = 1920
|
||||
AudioRecord.created.clear(); AudioRecord.nextFailures = emptySet()
|
||||
NativeLib.callAudioCommands.clear(); NativeLib.callAudioFailures = emptySet(); NativeLib.onCallFeed = {}
|
||||
@@ -224,4 +226,27 @@ class CallAudioLifecycleTest {
|
||||
assertEquals(AudioManager.MODE_IN_COMMUNICATION, manager.mode)
|
||||
assertEquals(false, field("modeOwned")); assertEquals(1, stops())
|
||||
}
|
||||
|
||||
@Test fun pendingRouterCleanupBlocksNativeStopWithoutSkippingModeOrDeviceRelease() {
|
||||
start(); manager.failures = setOf("unregister")
|
||||
rejected { engine.stop() }
|
||||
assertTrue(AudioRecord.created.single().released); assertTrue(AudioTrack.created.single().released)
|
||||
assertEquals(AudioManager.MODE_NORMAL, manager.mode); assertEquals(0, stops())
|
||||
assertTrue(engine.router.cleanupPending); assertTrue(diagnostic("router stop failed"))
|
||||
rejected { engine.start(8) }
|
||||
assertEquals(listOf("start" to 7L), NativeLib.callAudioCommands.toList())
|
||||
manager.failures = emptySet(); engine.stop()
|
||||
assertFalse(engine.router.cleanupPending); assertEquals(1, stops())
|
||||
}
|
||||
|
||||
@Test fun failedTelecomHandoffReportsFailureAndRetainsFallbackForCleanup() {
|
||||
Build.VERSION.SDK_INT = 36
|
||||
manager.devices += AudioDeviceInfo(11, AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, true, false, "earpiece")
|
||||
engine.router.setRoute(AudioRoute.EARPIECE); start()
|
||||
manager.failures = setOf("clear")
|
||||
engine.bindTelecom(UtunConnection()); main.dispatchAll()
|
||||
assertTrue(failures.any { "Telecom routing handoff failed" in it }); assertNull(engine.router.telecom)
|
||||
rejected { engine.stop() }; assertEquals(0, stops())
|
||||
manager.failures = emptySet(); engine.stop(); assertEquals(1, stops())
|
||||
}
|
||||
}
|
||||
+41
@@ -361,6 +361,47 @@ class MediaAudioSessionTest {
|
||||
assertTrue(tracks.all { it.listeners.isEmpty() })
|
||||
}
|
||||
|
||||
@Test fun failedRouterCloseKeepsExecutorAvailableAndRejectsNewConsumersUntilRetry() {
|
||||
start()
|
||||
val callback = audio.callbacks.single()
|
||||
audio.failures = setOf("unregister")
|
||||
assertThrows(IllegalStateException::class.java) { background { owner.close() } }
|
||||
assertFalse(clock.isShutdown); assertEquals(setOf(callback), audio.callbacks)
|
||||
assertThrows(IllegalStateException::class.java) { owner.subscribe("radio") }
|
||||
assertThrows(IllegalStateException::class.java) { background { owner.attachOutput(track()) } }
|
||||
assertThrows(IllegalStateException::class.java) { background { owner.awaitIdle() } }
|
||||
val registrations = audio.callbackHistory.size
|
||||
owner.refresh(); owner.select(MediaAudioProfile.WIRED); callback.onAudioDevicesAdded(emptyArray()); drain()
|
||||
assertEquals(registrations, audio.callbackHistory.size)
|
||||
audio.failures = emptySet(); background { owner.close() }
|
||||
assertTrue(clock.isShutdown); assertTrue(audio.callbacks.isEmpty())
|
||||
diagnostic("session router cleanup failed")
|
||||
}
|
||||
|
||||
@Test fun bluetoothCloseFailureKeepsSessionCloseRetryable() {
|
||||
val adapter = BluetoothAdapter().also { BluetoothAdapter.current = it }
|
||||
try {
|
||||
start(); adapter.connect(0); drain()
|
||||
adapter.closeFailures = adapter.live.toSet()
|
||||
assertThrows(IllegalStateException::class.java) { background { owner.close() } }
|
||||
assertFalse(clock.isShutdown); assertEquals(1, adapter.live.size); assertTrue(audio.callbacks.isEmpty())
|
||||
adapter.closeFailures = emptySet(); background { owner.close() }
|
||||
assertTrue(clock.isShutdown); assertTrue(adapter.live.isEmpty())
|
||||
} finally { adapter.closeFailures = emptySet() }
|
||||
}
|
||||
|
||||
@Test fun failedHardwareCloseDoesNotSkipRouterCleanupOrDisableRetry() {
|
||||
start(); val sub = subscribe()
|
||||
AndroidMicrophoneCapture.failClose = true
|
||||
sub.close(); drain()
|
||||
assertThrows(IllegalStateException::class.java) { background { owner.close() } }
|
||||
assertFalse(clock.isShutdown); assertTrue(audio.callbacks.isEmpty())
|
||||
assertTrue(AndroidMicrophoneCapture.opened.any { !it.closed })
|
||||
AndroidMicrophoneCapture.failClose = false; background { owner.close() }
|
||||
assertTrue(clock.isShutdown); assertTrue(AndroidMicrophoneCapture.opened.all { it.closed })
|
||||
diagnostic("session hardware cleanup failed")
|
||||
}
|
||||
|
||||
@Test fun callReservationBlocksSelectionCaptureAndOutput() {
|
||||
start(); CallController.blocked = true
|
||||
select(MediaAudioProfile.WIRED)
|
||||
|
||||
Reference in new issue
Block a user