blob: 3994bb4967e71cfd2739963789a369c86a000d33 [file] [log] [blame]
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<String>) {
oneMoreNestedMethod(deferred, traces)
assertTrue(true) // Prevent tail-call optimization
}
private suspend fun oneMoreNestedMethod(deferred: Deferred<*>, traces: List<String>) {
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)
}
}