mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
test(sidecar): cover WdaRecovery restart and retry policy
This commit is contained in:
1 parent
30d4f4f475
commit
693b86fc61
1 file changed
+135
@@ -0,0 +1,135 @@
|
||||
package dev.sanderling.sidecar
|
||||
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class WdaRecoveryTest {
|
||||
|
||||
private fun recovery(
|
||||
isAlive: () -> Boolean,
|
||||
restart: () -> Unit,
|
||||
) = WdaRecovery(isAlive = isAlive, restart = restart, log = {})
|
||||
|
||||
@Test fun aliveChannelSkipsRestartAndRetriesReads() {
|
||||
val restarts = AtomicInteger(0)
|
||||
val recovery = recovery(isAlive = { true }, restart = { restarts.incrementAndGet() })
|
||||
var calls = 0
|
||||
|
||||
val result = recovery.run(replay = true) {
|
||||
calls++
|
||||
if (calls == 1) throw IOException("connection reset")
|
||||
"ok"
|
||||
}
|
||||
|
||||
assertEquals("ok", result)
|
||||
assertEquals(2, calls)
|
||||
assertEquals(0, restarts.get())
|
||||
}
|
||||
|
||||
@Test fun aliveChannelSurfacesUnavailableForActions() {
|
||||
val restarts = AtomicInteger(0)
|
||||
val recovery = recovery(isAlive = { true }, restart = { restarts.incrementAndGet() })
|
||||
|
||||
val thrown = assertFailsWith<io.grpc.StatusRuntimeException> {
|
||||
recovery.run(replay = false) { throw IOException("connection reset") }
|
||||
}
|
||||
|
||||
assertEquals(io.grpc.Status.Code.UNAVAILABLE, thrown.status.code)
|
||||
assertEquals(0, restarts.get())
|
||||
}
|
||||
|
||||
@Test fun deadChannelRestartsOnceThenRetries() {
|
||||
val alive = AtomicBoolean(false)
|
||||
val restarts = AtomicInteger(0)
|
||||
val recovery = recovery(
|
||||
isAlive = { alive.get() },
|
||||
restart = {
|
||||
restarts.incrementAndGet()
|
||||
alive.set(true)
|
||||
},
|
||||
)
|
||||
var calls = 0
|
||||
|
||||
val result = recovery.run(replay = true) {
|
||||
calls++
|
||||
if (calls == 1) throw IOException("connection refused")
|
||||
"ok"
|
||||
}
|
||||
|
||||
assertEquals("ok", result)
|
||||
assertEquals(1, restarts.get())
|
||||
}
|
||||
|
||||
@Test fun concurrentFailuresRestartOnly() {
|
||||
val alive = AtomicBoolean(false)
|
||||
val restarts = AtomicInteger(0)
|
||||
val recovery = recovery(
|
||||
isAlive = { alive.get() },
|
||||
restart = {
|
||||
Thread.sleep(100)
|
||||
restarts.incrementAndGet()
|
||||
alive.set(true)
|
||||
},
|
||||
)
|
||||
val started = CountDownLatch(2)
|
||||
val threads = (1..2).map {
|
||||
thread {
|
||||
started.countDown()
|
||||
started.await()
|
||||
recovery.run(replay = true) {
|
||||
if (!alive.get()) throw IOException("connection refused")
|
||||
"ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
threads.forEach { it.join() }
|
||||
|
||||
assertEquals(1, restarts.get())
|
||||
}
|
||||
|
||||
@Test fun restartFailureSurfacesWdaReconnectFailed() {
|
||||
val recovery = recovery(
|
||||
isAlive = { false },
|
||||
restart = { throw IllegalStateException("xcodebuild died") },
|
||||
)
|
||||
|
||||
val thrown = assertFailsWith<IllegalStateException> {
|
||||
recovery.run(replay = true) { throw IOException("connection refused") }
|
||||
}
|
||||
|
||||
assertTrue(thrown.message.orEmpty().contains("WDA reconnect failed"))
|
||||
}
|
||||
|
||||
@Test fun nonIoFailurePropagatesWithoutRecovery() {
|
||||
val restarts = AtomicInteger(0)
|
||||
val probes = AtomicInteger(0)
|
||||
val recovery = recovery(
|
||||
isAlive = { probes.incrementAndGet() > 0 },
|
||||
restart = { restarts.incrementAndGet() },
|
||||
)
|
||||
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
recovery.run(replay = true) { throw IllegalArgumentException("bad selector") }
|
||||
}
|
||||
|
||||
assertEquals(0, restarts.get())
|
||||
assertEquals(0, probes.get())
|
||||
}
|
||||
|
||||
@Test fun readRetryFailureSurfacesUnavailable() {
|
||||
val recovery = recovery(isAlive = { true }, restart = {})
|
||||
|
||||
val thrown = assertFailsWith<io.grpc.StatusRuntimeException> {
|
||||
recovery.run(replay = true) { throw IOException("connection reset") }
|
||||
}
|
||||
|
||||
assertEquals(io.grpc.Status.Code.UNAVAILABLE, thrown.status.code)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user