Test router cleanup barriers in call and shared media owners

This commit is contained in:
evgeny committed 2026-10-29 13:34:23 +01:00
1 parent b3e1356d16
commit 48ab63a9d3
2 files changed
+66

No files matched your search

@@ -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())
}
}
@@ -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)