• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 java.io.*
12 import kotlin.coroutines.*
13 import kotlin.test.*
14 
15 class ToStringTest : TestBase() {
16 
17     @Before
setUpnull18     fun setUp() {
19         before()
20         DebugProbes.sanitizeStackTraces = false
21         DebugProbes.install()
22     }
23 
24     @After
tearDownnull25     fun tearDown() {
26         try {
27             DebugProbes.uninstall()
28         } finally {
29             onCompletion()
30         }
31     }
32 
33 
launchNestedScopesnull34     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 
launchDelayednull49     private fun CoroutineScope.launchDelayed(): Job {
50         return launch {
51             delay(Long.MAX_VALUE)
52         }
53     }
54 
55     @Test
<lambda>null56     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 
77     @Test
<lambda>null78     fun testCompletingHierarchy() = runBlocking {
79         val tab = '\t'
80         val expectedString = """
81             "coroutine#2":StandaloneCoroutine{Completing}
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)
85             """.trimIndent()
86 
87         checkHierarchy(isCompleting = true, expectedString = expectedString)
88     }
89 
90     @Test
<lambda>null91     fun testActiveHierarchy() = runBlocking {
92         val tab = '\t'
93         val expectedString = """
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)
98             """.trimIndent()
99         checkHierarchy(isCompleting = false, expectedString = expectedString)
100     }
101 
checkHierarchynull102     private suspend fun CoroutineScope.checkHierarchy(isCompleting: Boolean, expectedString: String) {
103         val root = launchHierarchy(isCompleting)
104         repeat(4) { yield() }
105         val expected = expectedString.trimStackTrace().trimPackage()
106         expect(6)
107         assertEquals(expected, DebugProbes.jobToString(root).trimEnd().trimStackTrace().trimPackage())
108         assertEquals(expected, DebugProbes.scopeToString(CoroutineScope(root)).trimEnd().trimStackTrace().trimPackage())
109         assertEquals(expected, printToString { DebugProbes.printScope(CoroutineScope(root), it) }.trimEnd().trimStackTrace().trimPackage())
110         assertEquals(expected, printToString { DebugProbes.printJob(root, it) }.trimEnd().trimStackTrace().trimPackage())
111 
112         root.cancelAndJoin()
113         finish(7)
114     }
115 
CoroutineScopenull116     private fun CoroutineScope.launchHierarchy(isCompleting: Boolean): Job {
117         return launch {
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             }
136 
137             if (!isCompleting) {
138                 delay(Long.MAX_VALUE)
139             }
140         }
141     }
142 
wrapperDispatchernull143     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     }
151 
printToStringnull152     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     }
159 }
160