Model recoverable alert playback and focus cleanup failures

This commit is contained in:
evgeny committed 2026-10-28 14:43:27 +01:00
1 parent edc6b32fa7
commit 7ed1a63b36
2 files changed
+25 -6

No files matched your search

@@ -35,7 +35,7 @@ class AudioFocusRequest private constructor(val attributes: AudioAttributes?, va
}
/** Управляемые lifecycle failures; callbacks намеренно сохраняются после release для теста запоздалой доставки. */
class MediaPlayer private constructor(val resourceId: Int, val effectiveAttributes: AudioAttributes, private val failures: Set<String>) {
class MediaPlayer private constructor(val resourceId: Int, val effectiveAttributes: AudioAttributes, var failures: Set<String>) {
companion object {
val created = mutableListOf<MediaPlayer>()
var nextFailures = emptySet<String>()
@@ -52,6 +52,8 @@ class MediaPlayer private constructor(val resourceId: Int, val effectiveAttribut
}
var released = false
private set
var playing = false
private set
var startCalls = 0
private set
var releaseCalls = 0
@@ -70,12 +72,14 @@ class MediaPlayer private constructor(val resourceId: Int, val effectiveAttribut
fun setAudioAttributes(attributes: AudioAttributes) { fail("attributes") }
fun setOnCompletionListener(listener: (MediaPlayer) -> Unit) { fail("completion listener"); completion = listener }
fun setOnErrorListener(listener: (MediaPlayer, Int, Int) -> Boolean) { fail("error listener"); error = listener }
fun start() { startCalls++; fail("start") }
fun start() { startCalls++; fail("start"); playing = true; fail("start after") }
fun stop() { fail("stop") }
fun release() {
releaseCalls++
fail("release")
check("release" !in failures) { "release failed" }
released = true
playing = false
check("release after" !in failures) { "release failed after effect" }
}
fun complete() { completion?.invoke(this) }
fun playbackError(what: Int = 100, extra: Int = -1004): Boolean {
@@ -85,15 +89,23 @@ class MediaPlayer private constructor(val resourceId: Int, val effectiveAttribut
}
}
class Ringtone internal constructor(private val failures: Set<String>) {
class Ringtone internal constructor(var failures: Set<String>) {
var audioAttributes: AudioAttributes? = null
set(value) { check("attributes" !in failures) { "ringtone attributes failed" }; field = value }
var isLooping = false
set(value) { check("loop" !in failures) { "ringtone loop failed" }; field = value }
var playCalls = 0
var stopCalls = 0
fun play() { playCalls++; check("play" !in failures) { "ringtone play failed" } }
fun stop() { stopCalls++; check("stop" !in failures) { "ringtone stop failed" } }
var playing = false
private set
fun play() {
playCalls++; check("play" !in failures) { "ringtone play failed" }
playing = true; check("play after" !in failures) { "ringtone play failed after effect" }
}
fun stop() {
stopCalls++; check("stop" !in failures) { "ringtone stop failed" }
playing = false; check("stop after" !in failures) { "ringtone stop failed after effect" }
}
}
object RingtoneManager {
@@ -35,17 +35,24 @@ class AudioManager {
var focusResult = AUDIOFOCUS_REQUEST_GRANTED
var abandonResult = AUDIOFOCUS_REQUEST_GRANTED
var failFocus = false
var failFocusAfter = false
var failAbandon = false
var failAbandonAfter = false
val focusHistory = mutableListOf<AudioFocusRequest>()
val abandoned = mutableListOf<AudioFocusRequest>()
val focusOwners = mutableSetOf<AudioFocusRequest>()
fun requestAudioFocus(request: AudioFocusRequest): Int {
focusHistory.add(request)
check(!failFocus) { "focus unavailable" }
if (focusResult == AUDIOFOCUS_REQUEST_GRANTED) focusOwners.add(request)
check(!failFocusAfter) { "focus failed after effect" }
return focusResult
}
fun abandonAudioFocusRequest(request: AudioFocusRequest): Int {
abandoned.add(request)
check(!failAbandon) { "abandon unavailable" }
if (abandonResult == AUDIOFOCUS_REQUEST_GRANTED) focusOwners.remove(request)
check(!failAbandonAfter) { "abandon failed after effect" }
return abandonResult
}
val operations = mutableListOf<String>()