1 /* 2 * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") 5 package kotlinx.coroutines.debug 6 7 import com.google.gson.* 8 import kotlinx.coroutines.* 9 import kotlinx.coroutines.debug.internal.* 10 import org.junit.Test 11 import kotlin.coroutines.* 12 import kotlin.test.* 13 14 @ExperimentalStdlibApi 15 class DumpCoroutineInfoAsJsonAndReferencesTest : DebugTestBase() { 16 private data class CoroutineInfoFromJson( 17 val name: String?, 18 val id: Long?, 19 val dispatcher: String?, 20 val sequenceNumber: Long?, 21 val state: String? 22 ) 23 24 @Test testDumpOfUnnamedCoroutinenull25 fun testDumpOfUnnamedCoroutine() = 26 runTestWithNamedDeferred(name = null) 27 28 @Test 29 fun testDumpOfNamedCoroutine() = 30 runTestWithNamedDeferred("Name") 31 32 @Test 33 fun testDumpOfNamedCoroutineWithSpecialCharacters() = 34 runTestWithNamedDeferred("Name with\n \"special\" characters\\/\t\b") 35 36 @Test 37 fun testDumpWithNoCoroutines() { 38 val dumpResult = DebugProbesImpl.dumpCoroutinesInfoAsJsonAndReferences() 39 assertEquals(dumpResult.size, 4) 40 assertIsEmptyArray(dumpResult[1]) 41 assertIsEmptyArray(dumpResult[2]) 42 assertIsEmptyArray(dumpResult[3]) 43 } 44 assertIsEmptyArraynull45 private fun assertIsEmptyArray(obj: Any) = 46 assertTrue(obj is Array<*> && obj.isEmpty()) 47 48 private fun runTestWithNamedDeferred(name: String?) = runTest { 49 val context = if (name == null) EmptyCoroutineContext else CoroutineName(name) 50 val deferred = async(context) { 51 suspendingMethod() 52 assertTrue(true) 53 } 54 yield() 55 verifyDump() 56 deferred.cancelAndJoin() 57 } 58 suspendingMethodnull59 private suspend fun suspendingMethod() { 60 delay(Long.MAX_VALUE) 61 } 62 verifyDumpnull63 private fun verifyDump() { 64 val dumpResult = DebugProbesImpl.dumpCoroutinesInfoAsJsonAndReferences() 65 66 assertEquals(dumpResult.size, 4) 67 68 val coroutinesInfoAsJsonString = dumpResult[0] 69 val lastObservedThreads = dumpResult[1] 70 val lastObservedFrames = dumpResult[2] 71 val coroutinesInfo = dumpResult[3] 72 73 assertTrue(coroutinesInfoAsJsonString is String) 74 assertTrue(lastObservedThreads is Array<*>) 75 assertTrue(lastObservedFrames is Array<*>) 76 assertTrue(coroutinesInfo is Array<*>) 77 78 val coroutinesInfoFromJson = Gson().fromJson(coroutinesInfoAsJsonString, Array<CoroutineInfoFromJson>::class.java) 79 80 val size = coroutinesInfo.size 81 assertTrue(size != 0) 82 assertEquals(size, coroutinesInfoFromJson.size) 83 assertEquals(size, lastObservedFrames.size) 84 assertEquals(size, lastObservedThreads.size) 85 86 for (i in 0 until size) { 87 val info = coroutinesInfo[i] 88 val infoFromJson = coroutinesInfoFromJson[i] 89 assertTrue(info is DebugCoroutineInfo) 90 assertEquals(info.lastObservedThread, lastObservedThreads[i]) 91 assertEquals(info.lastObservedFrame, lastObservedFrames[i]) 92 assertEquals(info.sequenceNumber, infoFromJson.sequenceNumber) 93 assertEquals(info.state, infoFromJson.state) 94 val context = info.context 95 assertEquals(context[CoroutineName.Key]?.name, infoFromJson.name) 96 assertEquals(context[CoroutineId.Key]?.id, infoFromJson.id) 97 assertEquals(context[CoroutineDispatcher.Key]?.toString(), infoFromJson.dispatcher) 98 } 99 } 100 } 101