From 6f997dceb93a6f10fb5a2e169ad62b874372222e Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Sun, 11 Oct 2026 06:27:42 +0000 Subject: [PATCH] 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. --- app/src/test/kotlin/com/subtracks/TestSupport.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/test/kotlin/com/subtracks/TestSupport.kt b/app/src/test/kotlin/com/subtracks/TestSupport.kt index 943ac7f7..9e680ade 100644 --- a/app/src/test/kotlin/com/subtracks/TestSupport.kt +++ b/app/src/test/kotlin/com/subtracks/TestSupport.kt @@ -4,7 +4,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertTrue +import org.junit.Assert.fail const val TEST_TIMEOUT_MS = 30_000L @@ -18,5 +18,14 @@ fun awaitUntil( ) { val deadline = System.nanoTime() + TEST_TIMEOUT_MS * 1_000_000 while (!predicate() && System.nanoTime() < deadline) Thread.sleep(10) - assertTrue("Timed out after ${TEST_TIMEOUT_MS}ms $message", predicate()) + 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" } + }