ci(android): print full assertion messages for failing tests
android / Build + lint + test (push) Failing after 3m11s
android / Build + lint + test (push) Failing after 3m11s
CI run 3161 reported seven failures as bare "java.lang.AssertionError at
UpdateVeilControllerTest.kt:87" — and line 87 is the test's own `fun ... =
runTest {` line, not the assertion. Gradle picks the first stack frame
belonging to the test class, and assertions inside a `runTest { }` lambda
live in a generated suspend-lambda class that gets filtered out, so every
failure in a coroutine test collapses to the function declaration. With the
HTML report unreachable from CI, that leaves nothing to debug from.
testLogging with exceptionFormat = FULL prints the assertion message and the
whole stack trace for failures, which is what makes a coroutine-test failure
diagnosable at all here.
Also drop the NonCancellable floor-join from UpdateVeilController's finally.
Honouring the minimum hold while the scope is being torn down is pointless —
nothing is left to render the veil — and a finally that suspends is a finally
that can resist cancellation. The floor is now awaited in the try instead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -210,4 +210,17 @@ dependencies {
|
||||
debugImplementation(libs.compose.ui.test.manifest)
|
||||
}
|
||||
|
||||
tasks.withType<Test> { useJUnitPlatform() }
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
// Print the assertion message + full stack trace for failures. The
|
||||
// default console output gives only "AssertionError at Foo.kt:12", and
|
||||
// for a failure inside a `runTest { }` lambda even that line collapses
|
||||
// to the test function's own line (the assertion frames live in the
|
||||
// suspend-lambda class, which Gradle filters out) — leaving nothing to
|
||||
// debug from when the HTML report isn't reachable, as in CI.
|
||||
testLogging {
|
||||
events("failed")
|
||||
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
showStackTraces = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.fabledsword.minstrel.shared
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -14,7 +13,6 @@ import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
// Once raised, the veil stays up at least this long. Without a floor a
|
||||
@@ -166,15 +164,15 @@ class UpdateVeilController(
|
||||
// here before the raiser has been dispatched, tear the session
|
||||
// down, and leave the churn uncovered.
|
||||
withTimeoutOrNull(timings.maxHoldMs) { awaitSettled() }
|
||||
// Honour the no-flash minimum before lowering. Deliberately in
|
||||
// the try and not the finally: on cancellation the scope is
|
||||
// going away and nothing will render the veil, so the floor is
|
||||
// pointless there — and a finally that suspends is a finally
|
||||
// that can resist teardown.
|
||||
if (raised.isCompleted) floor.join()
|
||||
} finally {
|
||||
raiser.cancel()
|
||||
ceiling.cancel()
|
||||
if (raised.isCompleted) {
|
||||
// NonCancellable so the floor is honoured (and the veil
|
||||
// always cleared) even while the scope is torn down; the
|
||||
// floor job dies with the scope, so this cannot hang.
|
||||
withContext(NonCancellable) { floor.join() }
|
||||
}
|
||||
floor.cancel()
|
||||
visibleInternal.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user