1 package kotlinx.coroutines.debug 2 3 import kotlinx.coroutines.testing.* 4 import kotlinx.coroutines.* 5 import org.junit.Test 6 import java.util.concurrent.* 7 import java.util.concurrent.atomic.AtomicBoolean 8 import kotlin.test.* 9 10 class DebugProbesTest : DebugTestBase() { 11 createDeferrednull12 private fun CoroutineScope.createDeferred(): Deferred<*> = async(NonCancellable) { 13 throw ExecutionException(null) 14 } 15 16 @Test <lambda>null17 fun testAsync() = runTest { 18 val deferred = createDeferred() 19 val traces = listOf( 20 "java.util.concurrent.ExecutionException\n" + 21 "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:14)\n" + 22 "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + 23 "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt:49)\n" + 24 "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt:44)\n" + 25 "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsync\$1.invokeSuspend(DebugProbesTest.kt:17)\n", 26 "Caused by: java.util.concurrent.ExecutionException\n" + 27 "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:14)\n" + 28 "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)" 29 ) 30 nestedMethod(deferred, traces) 31 deferred.join() 32 } 33 34 @Test <lambda>null35 fun testAsyncWithProbes() = DebugProbes.withDebugProbes { 36 DebugProbes.sanitizeStackTraces = false 37 runTest { 38 val deferred = createDeferred() 39 val traces = listOf( 40 "java.util.concurrent.ExecutionException\n" + 41 "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt)\n" + 42 "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + 43 "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt)\n" + 44 "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt)\n" + 45 "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsyncWithProbes\$1\$1.invokeSuspend(DebugProbesTest.kt:62)", 46 "Caused by: java.util.concurrent.ExecutionException\n" + 47 "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt)\n" + 48 "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt)\n" 49 ) 50 nestedMethod(deferred, traces) 51 deferred.join() 52 } 53 } 54 55 @Test <lambda>null56 fun testAsyncWithSanitizedProbes() = DebugProbes.withDebugProbes { 57 DebugProbes.sanitizeStackTraces = true 58 runTest { 59 val deferred = createDeferred() 60 val traces = listOf( 61 "java.util.concurrent.ExecutionException\n" + 62 "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:16)\n" + 63 "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + 64 "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt:71)\n" + 65 "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt:66)\n" + 66 "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsyncWithSanitizedProbes\$1\$1.invokeSuspend(DebugProbesTest.kt:87)", 67 "Caused by: java.util.concurrent.ExecutionException\n" + 68 "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:16)\n" + 69 "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)\n" 70 ) 71 nestedMethod(deferred, traces) 72 deferred.join() 73 } 74 } 75 nestedMethodnull76 private suspend fun nestedMethod(deferred: Deferred<*>, traces: List<String>) { 77 oneMoreNestedMethod(deferred, traces) 78 assertTrue(true) // Prevent tail-call optimization 79 } 80 oneMoreNestedMethodnull81 private suspend fun oneMoreNestedMethod(deferred: Deferred<*>, traces: List<String>) { 82 try { 83 deferred.await() 84 expectUnreached() 85 } catch (e: ExecutionException) { 86 verifyStackTrace(e, traces) 87 } 88 } 89 90 @Test testMultipleConsecutiveProbeResumednull91 fun testMultipleConsecutiveProbeResumed() = runTest { 92 val job = launch { 93 expect(1) 94 foo() 95 expect(4) 96 delay(Long.MAX_VALUE) 97 expectUnreached() 98 } 99 yield() 100 yield() 101 expect(5) 102 val infos = DebugProbes.dumpCoroutinesInfo() 103 assertEquals(2, infos.size) 104 assertEquals(setOf(State.RUNNING, State.SUSPENDED), infos.map { it.state }.toSet()) 105 job.cancel() 106 finish(6) 107 } 108 109 @Test <lambda>null110 fun testMultipleConsecutiveProbeResumedAndLaterRunning() = runTest { 111 val reachedActiveStage = AtomicBoolean(false) 112 val job = launch(Dispatchers.Default) { 113 expect(1) 114 foo() 115 expect(4) 116 yield() 117 reachedActiveStage.set(true) 118 while (isActive) { 119 // Spin until test is done 120 } 121 } 122 while (!reachedActiveStage.get()) { 123 delay(10) 124 } 125 expect(5) 126 val infos = DebugProbes.dumpCoroutinesInfo() 127 assertEquals(2, infos.size) 128 assertEquals(setOf(State.RUNNING, State.RUNNING), infos.map { it.state }.toSet()) 129 job.cancel() 130 finish(6) 131 } 132 foonull133 private suspend fun foo() { 134 bar() 135 // Kill TCO 136 expect(3) 137 } 138 139 barnull140 private suspend fun bar() { 141 yield() 142 expect(2) 143 } 144 } 145