| import kotlinx.coroutines.testing.* |
| import kotlinx.coroutines.* |
| import kotlinx.coroutines.debug.* |
| import kotlinx.coroutines.debug.internal.* |
| import org.junit.* |
| |
| /** |
| * This is fast but fragile version of [DebugLeaksStressTest] that check reachability of a captured object |
| * in [DebugProbesImpl] via [FieldWalker]. |
| */ |
| @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") |
| class DebugLeaksTest : DebugTestBase() { |
| private class Captured |
| |
| @Test |
| fun testIteratorLeak() { |
| val captured = Captured() |
| iterator { yield(captured) } |
| assertNoCapturedReference() |
| } |
| |
| @Test |
| fun testLazyGlobalCoroutineLeak() { |
| val captured = Captured() |
| GlobalScope.launch(start = CoroutineStart.LAZY) { println(captured) } |
| assertNoCapturedReference() |
| } |
| |
| @Test |
| fun testLazyCancelledChildCoroutineLeak() = runTest { |
| val captured = Captured() |
| coroutineScope { |
| val child = launch(start = CoroutineStart.LAZY) { println(captured) } |
| child.cancel() |
| } |
| assertNoCapturedReference() |
| } |
| |
| @Test |
| fun testAbandonedGlobalCoroutineLeak() { |
| val captured = Captured() |
| GlobalScope.launch { |
| suspendForever() |
| println(captured) |
| } |
| assertNoCapturedReference() |
| } |
| |
| private suspend fun suspendForever() = suspendCancellableCoroutine<Unit> { } |
| |
| private fun assertNoCapturedReference() { |
| FieldWalker.assertReachableCount(0, DebugProbesImpl, rootStatics = true) { it is Captured } |
| } |
| } |