2 changed files with 67 additions and 0 deletions
@ -0,0 +1,36 @@
|
||||
package com.utun.chat.data |
||||
|
||||
/** Только playback worker. Ошибка ограничивает повтор в текущем маршруте, не в следующем. */ |
||||
internal class CallOutputRecovery { |
||||
var deviceId = Int.MIN_VALUE |
||||
private set |
||||
var failures = 0 |
||||
private set |
||||
|
||||
fun route(id: Int): Boolean { |
||||
if (id == deviceId) return false |
||||
deviceId = id; failures = 0 |
||||
return true |
||||
} |
||||
|
||||
fun failed(): Boolean { failures++; return failures <= 2 } |
||||
val tryFast: Boolean get() = failures == 0 |
||||
} |
||||
|
||||
/** Локальный счётчик нового устройства начинается с нуля; общий PCM-счётчик не сбрасывается. */ |
||||
internal class CallOutputPosition { |
||||
var base = 0L |
||||
private set |
||||
private var head = CallPlaybackHead() |
||||
|
||||
fun reset() { base = 0; head = CallPlaybackHead() } |
||||
|
||||
fun reopen(written: Long) { |
||||
require(written >= base) |
||||
base = written |
||||
head = CallPlaybackHead() |
||||
} |
||||
|
||||
fun played(raw: Int, written: Long): Long? = head.update(raw, written - base)?.let { base + it } |
||||
fun timestamp(local: Long): Long = if (local < 0) -1 else base + local |
||||
} |
||||
@ -0,0 +1,31 @@
|
||||
package com.utun.chat.data |
||||
|
||||
import org.junit.Assert.* |
||||
import org.junit.Test |
||||
|
||||
class CallOutputRecoveryTest { |
||||
@Test fun routeChangeRestoresFastAttemptButDuplicateConfirmationDoesNot() { |
||||
val p = CallOutputRecovery() |
||||
assertTrue(p.route(3)); assertTrue(p.tryFast) |
||||
assertTrue(p.failed()); assertFalse(p.tryFast) |
||||
assertFalse(p.route(3)); assertFalse(p.tryFast) |
||||
assertTrue(p.failed()); assertFalse(p.failed()) |
||||
assertTrue(p.route(2)); assertTrue(p.tryFast); assertEquals(0, p.failures) |
||||
assertTrue(p.route(3)); assertTrue(p.tryFast) |
||||
} |
||||
|
||||
@Test fun replacementKeepsAcceptedPcmPositionAndPartialBlockRemainder() { |
||||
val p = CallOutputPosition() |
||||
assertEquals(480L, p.played(480, 960)) |
||||
// 288 сэмплов следующего блока успели записаться; остаток 672 ещё принадлежит worker. |
||||
val accepted = 960L + 288 |
||||
p.reopen(accepted) |
||||
assertEquals(accepted, p.played(0, accepted)) |
||||
assertEquals(accepted + 96, p.played(96, accepted + 672)) |
||||
assertEquals(accepted + 48, p.timestamp(48)) |
||||
assertEquals(-1L, p.timestamp(-1)) |
||||
assertNull(p.played(1000, accepted + 672)) |
||||
p.reopen(1920) |
||||
assertEquals(1920L, p.played(0, 1920)) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue