tapTest
The mandatory entry point for suspending framework calls in JUnit tests: @Test fun x(device: Device) = tapTest { ... } (a block body works too). tapTest returns Unit, so the test method compiles to a void JUnit can discover whatever the block's last expression is. JUnit test methods are not suspend, so this is the real-time runBlocking-style bridge onto the extension-owned per-test context (root job + TapContext); it never uses virtual time because ADB, devices and command deadlines are external real-time systems.
Fails immediately with TapUsageException when called without an active @TapTest binding (no TapExtension, or outside the test invocation), or when nested (tapTest inside tapTest/tapScope). Suspending Device/App/Element calls from a coroutine that does not inherit this context (for example GlobalScope) fail in the SDK for the same reason: they would otherwise run uncancelled beside the test. Plain metadata-only tests stay possible — they just cannot touch a live Device outside this bridge.
JUnit @Timeout/thread interruption cancels the blocked runBlocking, which cancels the root job and every in-flight RPC; AfterEach then performs bounded non-cancellable teardown. The interrupt is consumed here (never re-asserted): AfterEach runs its own runBlocking boundaries on this same JUnit callback thread, and a set interrupt flag would abort teardown before every device is closed. The original InterruptedException stays the primary failure as the cause, with cleanup failures suppressed into it. Sibling failure inside coroutineScope/async in block cancels the other device's work by structured-concurrency rules.