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
11 import kotlin.coroutines.*
12 import kotlin.test.*
13 
14 class ToStringTest : TestBase() {
15 
16     @Before
setUpnull17     fun setUp() {
18         before()
19         DebugProbes.sanitizeStackTraces = false
20         DebugProbes.install()
21     }
22 
23     @After
tearDownnull24     fun tearDown() {
25         try {
26             DebugProbes.uninstall()
27         } finally {
28             onCompletion()
29         }
30     }
31 
32 
launchNestedScopesnull33     private suspend fun CoroutineScope.launchNestedScopes(): Job {
34         return launch {
35             expect(1)
36             coroutineScope {
37                 expect(2)
38                 launchDelayed()
39 
40                 supervisorScope {
41                     expect(3)
42                     launchDelayed()
43                 }
44             }
45         }
46     }
47 
launchDelayednull48     private fun CoroutineScope.launchDelayed(): Job {
49         return launch {
50             delay(Long.MAX_VALUE)
51         }
52     }
53 
54     @Test
<lambda>null55     fun testPrintHierarchyWithScopes() = runBlocking {
56         val tab = '\t'
57         val expectedString = """
58           "coroutine":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchNestedScopes$2$1.invokeSuspend(ToStringTest.kt)
59           $tab"coroutine":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchDelayed$1.invokeSuspend(ToStringTest.kt)
60           $tab"coroutine":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchDelayed$1.invokeSuspend(ToStringTest.kt)
61             """.trimIndent()
62 
63         val job = launchNestedScopes()
64         try {
65             repeat(5) { yield() }
66             val expected = expectedString.trimStackTrace().trimPackage()
67             expect(4)
68             assertEquals(expected, DebugProbes.jobToString(job).trimEnd().trimStackTrace().trimPackage())
69             assertEquals(expected, DebugProbes.scopeToString(CoroutineScope(job)).trimEnd().trimStackTrace().trimPackage())
70         } finally {
71             finish(5)
72             job.cancelAndJoin()
73         }
74     }
75 
76     @Test
<lambda>null77     fun testCompletingHierarchy() = runBlocking {
78         val tab = '\t'
79         val expectedString = """
80             "coroutine#2":StandaloneCoroutine{Completing}
81             $tab"foo#3":DeferredCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}1.invokeSuspend(ToStringTest.kt:30)
82             $tab"coroutine#4":ActorCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}2${'$'}1.invokeSuspend(ToStringTest.kt:40)
83             $tab$tab"coroutine#5":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}2${'$'}job$1.invokeSuspend(ToStringTest.kt:37)
84             """.trimIndent()
85 
86         checkHierarchy(isCompleting = true, expectedString = expectedString)
87     }
88 
89     @Test
<lambda>null90     fun testActiveHierarchy() = runBlocking {
91         val tab = '\t'
92         val expectedString = """
93             "coroutine#2":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1.invokeSuspend(ToStringTest.kt:94)
94             $tab"foo#3":DeferredCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}1.invokeSuspend(ToStringTest.kt:30)
95             $tab"coroutine#4":ActorCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}2${'$'}1.invokeSuspend(ToStringTest.kt:40)
96             $tab$tab"coroutine#5":StandaloneCoroutine{Active}, continuation is SUSPENDED at line ToStringTest${'$'}launchHierarchy${'$'}1${'$'}2${'$'}job$1.invokeSuspend(ToStringTest.kt:37)
97             """.trimIndent()
98         checkHierarchy(isCompleting = false, expectedString = expectedString)
99     }
100 
checkHierarchynull101     private suspend fun CoroutineScope.checkHierarchy(isCompleting: Boolean, expectedString: String) {
102         val root = launchHierarchy(isCompleting)
103         repeat(4) { yield() }
104         val expected = expectedString.trimStackTrace().trimPackage()
105         expect(6)
106         assertEquals(expected, DebugProbes.jobToString(root).trimEnd().trimStackTrace().trimPackage())
107         assertEquals(expected, DebugProbes.scopeToString(CoroutineScope(root)).trimEnd().trimStackTrace().trimPackage())
108 
109         root.cancelAndJoin()
110         finish(7)
111     }
112 
CoroutineScopenull113     private fun CoroutineScope.launchHierarchy(isCompleting: Boolean): Job {
114         return launch {
115             expect(1)
116             async(CoroutineName("foo")) {
117                 expect(2)
118                 delay(Long.MAX_VALUE)
119             }
120 
121             actor<Int> {
122                 expect(3)
123                 val job = launch {
124                     expect(4)
125                     delay(Long.MAX_VALUE)
126                 }
127 
128                 withContext(wrapperDispatcher(coroutineContext)) {
129                     expect(5)
130                     job.join()
131                 }
132             }
133 
134             if (!isCompleting) {
135                 delay(Long.MAX_VALUE)
136             }
137         }
138     }
139 
wrapperDispatchernull140     private fun wrapperDispatcher(context: CoroutineContext): CoroutineContext {
141         val dispatcher = context[ContinuationInterceptor] as CoroutineDispatcher
142         return object : CoroutineDispatcher() {
143             override fun dispatch(context: CoroutineContext, block: Runnable) {
144                 dispatcher.dispatch(context, block)
145             }
146         }
147     }
148 }
149