package kotlinx.coroutines.debug import kotlinx.coroutines.testing.* import kotlinx.coroutines.* import org.junit.Test import java.util.concurrent.* import java.util.concurrent.atomic.AtomicBoolean import kotlin.test.* class DebugProbesTest : DebugTestBase() { private fun CoroutineScope.createDeferred(): Deferred<*> = async(NonCancellable) { throw ExecutionException(null) } @Test fun testAsync() = runTest { val deferred = createDeferred() val traces = listOf( "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:14)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt:49)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt:44)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsync\$1.invokeSuspend(DebugProbesTest.kt:17)\n", "Caused by: java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:14)\n" + "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)" ) nestedMethod(deferred, traces) deferred.join() } @Test fun testAsyncWithProbes() = DebugProbes.withDebugProbes { DebugProbes.sanitizeStackTraces = false runTest { val deferred = createDeferred() val traces = listOf( "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsyncWithProbes\$1\$1.invokeSuspend(DebugProbesTest.kt:62)", "Caused by: java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt)\n" + "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt)\n" ) nestedMethod(deferred, traces) deferred.join() } } @Test fun testAsyncWithSanitizedProbes() = DebugProbes.withDebugProbes { DebugProbes.sanitizeStackTraces = true runTest { val deferred = createDeferred() val traces = listOf( "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:16)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt:71)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt:66)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsyncWithSanitizedProbes\$1\$1.invokeSuspend(DebugProbesTest.kt:87)", "Caused by: java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:16)\n" + "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)\n" ) nestedMethod(deferred, traces) deferred.join() } } private suspend fun nestedMethod(deferred: Deferred<*>, traces: List) { oneMoreNestedMethod(deferred, traces) assertTrue(true) // Prevent tail-call optimization } private suspend fun oneMoreNestedMethod(deferred: Deferred<*>, traces: List) { try { deferred.await() expectUnreached() } catch (e: ExecutionException) { verifyStackTrace(e, traces) } } @Test fun testMultipleConsecutiveProbeResumed() = runTest { val job = launch { expect(1) foo() expect(4) delay(Long.MAX_VALUE) expectUnreached() } yield() yield() expect(5) val infos = DebugProbes.dumpCoroutinesInfo() assertEquals(2, infos.size) assertEquals(setOf(State.RUNNING, State.SUSPENDED), infos.map { it.state }.toSet()) job.cancel() finish(6) } @Test fun testMultipleConsecutiveProbeResumedAndLaterRunning() = runTest { val reachedActiveStage = AtomicBoolean(false) val job = launch(Dispatchers.Default) { expect(1) foo() expect(4) yield() reachedActiveStage.set(true) while (isActive) { // Spin until test is done } } while (!reachedActiveStage.get()) { delay(10) } expect(5) val infos = DebugProbes.dumpCoroutinesInfo() assertEquals(2, infos.size) assertEquals(setOf(State.RUNNING, State.RUNNING), infos.map { it.state }.toSet()) job.cancel() finish(6) } private suspend fun foo() { bar() // Kill TCO expect(3) } private suspend fun bar() { yield() expect(2) } }