Files
subtracks/app/src/test/kotlin/com/subtracks/TestSupport.kt
T
austinried 6f997dceb9 test: dump thread stacks when an await times out
The playback/queue wall-clock awaits have flaked on the 4-vCPU GitHub
runner at exactly the 30s limit, a different test each time. That is a
stall, not slowness: the suite finishes ~80s for 795 tests, so no test is
anywhere near 30s normally. The JUnit XML holds the stacks but is not
uploaded, leaving the failures undiagnosable from the CI log. Put every
thread's stack in the failure message, which Gradle prints.
2026-10-11 06:27:42 +00:00

32 lines
1.2 KiB
Kotlin

package com.subtracks
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.runBlocking
import org.junit.Assert.fail
const val TEST_TIMEOUT_MS = 30_000L
fun cancelAndJoinBlocking(vararg scopes: CoroutineScope) {
runBlocking { scopes.forEach { it.coroutineContext[Job]?.cancelAndJoin() } }
}
fun awaitUntil(
message: String,
predicate: () -> Boolean,
) {
val deadline = System.nanoTime() + TEST_TIMEOUT_MS * 1_000_000
while (!predicate() && System.nanoTime() < deadline) Thread.sleep(10)
if (predicate()) return
// A wall-clock wait that expires means something stalled rather than ran slow: the suite
// finishes in milliseconds per test. Print every thread's stack in the failure message so the
// stall is diagnosable from the CI log, which shows exception messages but not the test XML.
fail("Timed out after ${TEST_TIMEOUT_MS}ms $message\n\n${threadDump()}")
}
private fun threadDump(): String =
Thread.getAllStackTraces().entries.joinToString("\n\n") { (thread, frames) ->
"${thread.name} [${thread.state}]\n" + frames.joinToString("\n") { " at $it" }
}