Vsevolod Tolstopyatov | c7239ac | 2018-12-10 11:41:00 +0300 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
| 3 | */ |
| 4 | |
| 5 | package kotlinx.coroutines.debug |
| 6 | |
| 7 | import kotlinx.coroutines.* |
| 8 | import kotlinx.coroutines.channels.* |
| 9 | import org.junit.* |
| 10 | import org.junit.Test |
Steve Elliott | ca095be | 2022-07-25 14:26:10 +0000 | [diff] [blame] | 11 | import java.io.* |
Vsevolod Tolstopyatov | c7239ac | 2018-12-10 11:41:00 +0300 | [diff] [blame] | 12 | import kotlin.coroutines.* |
| 13 | import kotlin.test.* |
| 14 | |
Vsevolod Tolstopyatov | 115a139 | 2018-12-18 12:47:50 +0300 | [diff] [blame] | 15 | class ToStringTest : TestBase() { |
Vsevolod Tolstopyatov | c7239ac | 2018-12-10 11:41:00 +0300 | [diff] [blame] | 16 | |
| 17 | @Before |
| 18 | fun setUp() { |
| 19 | before() |
| 20 | DebugProbes.sanitizeStackTraces = false |
| 21 | DebugProbes.install() |
| 22 | } |
| 23 | |
| 24 | @After |
| 25 | fun tearDown() { |
| 26 | try { |
| 27 | DebugProbes.uninstall() |
| 28 | } finally { |
| 29 | onCompletion() |
| 30 | } |
| 31 | } |
| 32 | |
Vsevolod Tolstopyatov | 8d506b3 | 2019-02-20 18:31:02 +0300 | [diff] [blame] | 33 | |
| 34 | private suspend fun CoroutineScope.launchNestedScopes(): Job { |
| 35 | return launch { |
| 36 | expect(1) |
| 37 | coroutineScope { |
| 38 | expect(2) |
| 39 | launchDelayed() |
| 40 | |
| 41 | supervisorScope { |
| 42 | expect(3) |
| 43 | launchDelayed() |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | private fun CoroutineScope.launchDelayed(): Job { |
| 50 | return launch { |
| 51 | delay(Long.MAX_VALUE) |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | @Test |
| 56 | fun testPrintHierarchyWithScopes() = runBlocking { |
| 57 | val tab = '\t' |
| 58 | val expectedString = """ |
| 59 | "coroutine":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchNestedScopes$2$1.invokeSuspend(ToStringTest.kt) |
| 60 | $tab"coroutine":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchDelayed$1.invokeSuspend(ToStringTest.kt) |
| 61 | $tab"coroutine":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchDelayed$1.invokeSuspend(ToStringTest.kt) |
| 62 | """.trimIndent() |
| 63 | |
| 64 | val job = launchNestedScopes() |
| 65 | try { |
| 66 | repeat(5) { yield() } |
| 67 | val expected = expectedString.trimStackTrace().trimPackage() |
| 68 | expect(4) |
| 69 | assertEquals(expected, DebugProbes.jobToString(job).trimEnd().trimStackTrace().trimPackage()) |
| 70 | assertEquals(expected, DebugProbes.scopeToString(CoroutineScope(job)).trimEnd().trimStackTrace().trimPackage()) |
| 71 | } finally { |
| 72 | finish(5) |
| 73 | job.cancelAndJoin() |
| 74 | } |
| 75 | } |
| 76 | |
Vsevolod Tolstopyatov | c7239ac | 2018-12-10 11:41:00 +0300 | [diff] [blame] | 77 | @Test |
Vsevolod Tolstopyatov | 8e2428c | 2018-12-10 19:12:08 +0300 | [diff] [blame] | 78 | fun testCompletingHierarchy() = runBlocking { |
| 79 | val tab = '\t' |
| 80 | val expectedString = """ |
| 81 | "coroutine#2":StandaloneCoroutine{Completing} |
Vsevolod Tolstopyatov | 115a139 | 2018-12-18 12:47:50 +0300 | [diff] [blame] | 82 | $tab"foo#3":DeferredCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}1.invokeSuspend(ToStringTest.kt:30) |
| 83 | $tab"coroutine#4":ActorCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}2${'$'}1.invokeSuspend(ToStringTest.kt:40) |
| 84 | $tab$tab"coroutine#5":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}2${'$'}job$1.invokeSuspend(ToStringTest.kt:37) |
Vsevolod Tolstopyatov | 8e2428c | 2018-12-10 19:12:08 +0300 | [diff] [blame] | 85 | """.trimIndent() |
| 86 | |
| 87 | checkHierarchy(isCompleting = true, expectedString = expectedString) |
| 88 | } |
| 89 | |
| 90 | @Test |
| 91 | fun testActiveHierarchy() = runBlocking { |
| 92 | val tab = '\t' |
| 93 | val expectedString = """ |
Vsevolod Tolstopyatov | 115a139 | 2018-12-18 12:47:50 +0300 | [diff] [blame] | 94 | "coroutine#2":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1.invokeSuspend(ToStringTest.kt:94) |
| 95 | $tab"foo#3":DeferredCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}1.invokeSuspend(ToStringTest.kt:30) |
| 96 | $tab"coroutine#4":ActorCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}2${'$'}1.invokeSuspend(ToStringTest.kt:40) |
| 97 | $tab$tab"coroutine#5":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}2${'$'}job$1.invokeSuspend(ToStringTest.kt:37) |
Vsevolod Tolstopyatov | 8e2428c | 2018-12-10 19:12:08 +0300 | [diff] [blame] | 98 | """.trimIndent() |
| 99 | checkHierarchy(isCompleting = false, expectedString = expectedString) |
| 100 | } |
| 101 | |
| 102 | private suspend fun CoroutineScope.checkHierarchy(isCompleting: Boolean, expectedString: String) { |
| 103 | val root = launchHierarchy(isCompleting) |
| 104 | repeat(4) { yield() } |
Vsevolod Tolstopyatov | 115a139 | 2018-12-18 12:47:50 +0300 | [diff] [blame] | 105 | val expected = expectedString.trimStackTrace().trimPackage() |
Vsevolod Tolstopyatov | 8e2428c | 2018-12-10 19:12:08 +0300 | [diff] [blame] | 106 | expect(6) |
Vsevolod Tolstopyatov | 115a139 | 2018-12-18 12:47:50 +0300 | [diff] [blame] | 107 | assertEquals(expected, DebugProbes.jobToString(root).trimEnd().trimStackTrace().trimPackage()) |
| 108 | assertEquals(expected, DebugProbes.scopeToString(CoroutineScope(root)).trimEnd().trimStackTrace().trimPackage()) |
Steve Elliott | ca095be | 2022-07-25 14:26:10 +0000 | [diff] [blame] | 109 | assertEquals(expected, printToString { DebugProbes.printScope(CoroutineScope(root), it) }.trimEnd().trimStackTrace().trimPackage()) |
| 110 | assertEquals(expected, printToString { DebugProbes.printJob(root, it) }.trimEnd().trimStackTrace().trimPackage()) |
Vsevolod Tolstopyatov | 115a139 | 2018-12-18 12:47:50 +0300 | [diff] [blame] | 111 | |
Vsevolod Tolstopyatov | 8d506b3 | 2019-02-20 18:31:02 +0300 | [diff] [blame] | 112 | root.cancelAndJoin() |
Vsevolod Tolstopyatov | 8e2428c | 2018-12-10 19:12:08 +0300 | [diff] [blame] | 113 | finish(7) |
| 114 | } |
| 115 | |
| 116 | private fun CoroutineScope.launchHierarchy(isCompleting: Boolean): Job { |
| 117 | return launch { |
Vsevolod Tolstopyatov | c7239ac | 2018-12-10 11:41:00 +0300 | [diff] [blame] | 118 | expect(1) |
| 119 | async(CoroutineName("foo")) { |
| 120 | expect(2) |
| 121 | delay(Long.MAX_VALUE) |
| 122 | } |
| 123 | |
| 124 | actor<Int> { |
| 125 | expect(3) |
| 126 | val job = launch { |
| 127 | expect(4) |
| 128 | delay(Long.MAX_VALUE) |
| 129 | } |
| 130 | |
| 131 | withContext(wrapperDispatcher(coroutineContext)) { |
| 132 | expect(5) |
| 133 | job.join() |
| 134 | } |
| 135 | } |
Vsevolod Tolstopyatov | 8e2428c | 2018-12-10 19:12:08 +0300 | [diff] [blame] | 136 | |
| 137 | if (!isCompleting) { |
| 138 | delay(Long.MAX_VALUE) |
| 139 | } |
Vsevolod Tolstopyatov | c7239ac | 2018-12-10 11:41:00 +0300 | [diff] [blame] | 140 | } |
Vsevolod Tolstopyatov | c7239ac | 2018-12-10 11:41:00 +0300 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | private fun wrapperDispatcher(context: CoroutineContext): CoroutineContext { |
| 144 | val dispatcher = context[ContinuationInterceptor] as CoroutineDispatcher |
| 145 | return object : CoroutineDispatcher() { |
| 146 | override fun dispatch(context: CoroutineContext, block: Runnable) { |
| 147 | dispatcher.dispatch(context, block) |
| 148 | } |
| 149 | } |
| 150 | } |
Steve Elliott | ca095be | 2022-07-25 14:26:10 +0000 | [diff] [blame] | 151 | |
| 152 | private inline fun printToString(block: (PrintStream) -> Unit): String { |
| 153 | val baos = ByteArrayOutputStream() |
| 154 | val ps = PrintStream(baos) |
| 155 | block(ps) |
| 156 | ps.close() |
| 157 | return baos.toString() |
| 158 | } |
Vsevolod Tolstopyatov | c7239ac | 2018-12-10 11:41:00 +0300 | [diff] [blame] | 159 | } |