blob: cab4ed86b360551f8099e803ec6a5d3cbd0b40bf [file] [log] [blame]
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.debug
import java.io.*
import kotlin.test.*
public fun String.trimStackTrace(): String =
trimIndent()
.replace(Regex(":[0-9]+"), "")
.replace(Regex("#[0-9]+"), "")
.applyBackspace()
public fun String.applyBackspace(): String {
val array = toCharArray()
val stack = CharArray(array.size)
var stackSize = -1
for (c in array) {
if (c != '\b') {
stack[++stackSize] = c
} else {
--stackSize
}
}
return String(stack, 0, stackSize + 1)
}
public fun verifyStackTrace(e: Throwable, traces: List<String>) {
val stacktrace = toStackTrace(e)
traces.forEach {
val expectedLines = it.trimStackTrace().split("\n")
for (i in 0 until expectedLines.size) {
traces.forEach {
assertTrue(
stacktrace.trimStackTrace().contains(it.trimStackTrace()),
"\nExpected trace element:\n$it\n\nActual stacktrace:\n$stacktrace"
)
}
}
}
val causes = stacktrace.count("Caused by")
assertNotEquals(0, causes)
assertEquals(causes, traces.map { it.count("Caused by") }.sum())
}
public fun toStackTrace(t: Throwable): String {
val sw = StringWriter()
t.printStackTrace(PrintWriter(sw))
return sw.toString()
}
public fun String.count(substring: String): Int = split(substring).size - 1
public fun verifyDump(vararg traces: String, ignoredCoroutine: String? = null) {
val baos = ByteArrayOutputStream()
DebugProbes.dumpCoroutines(PrintStream(baos))
val trace = baos.toString().split("\n\n")
if (traces.isEmpty()) {
assertEquals(1, trace.size)
assertTrue(trace[0].startsWith("Coroutines dump"))
return
}
// Drop "Coroutine dump" line
trace.withIndex().drop(1).forEach { (index, value) ->
if (ignoredCoroutine != null && value.contains(ignoredCoroutine)) {
return@forEach
}
val expected = traces[index - 1].applyBackspace().split("\n\t(Coroutine creation stacktrace)\n", limit = 2)
val actual = value.applyBackspace().split("\n\t(Coroutine creation stacktrace)\n", limit = 2)
assertEquals(expected.size, actual.size)
expected.withIndex().forEach { (index, trace) ->
val actualTrace = actual[index].trimStackTrace().sanitizeAddresses()
val expectedTrace = trace.trimStackTrace().sanitizeAddresses()
val actualLines = actualTrace.split("\n")
val expectedLines = expectedTrace.split("\n")
for (i in 0 until expectedLines.size) {
assertEquals(expectedLines[i], actualLines[i])
}
}
}
}
public fun String.trimPackage() = replace("kotlinx.coroutines.debug.", "")
public fun verifyPartialDump(createdCoroutinesCount: Int, vararg frames: String) {
val baos = ByteArrayOutputStream()
DebugProbes.dumpCoroutines(PrintStream(baos))
val dump = baos.toString()
val trace = dump.split("\n\n")
val matches = frames.all { frame ->
trace.any { tr -> tr.contains(frame) }
}
assertEquals(createdCoroutinesCount, DebugProbes.dumpCoroutinesInfo().size)
assertTrue(matches)
}
private fun String.sanitizeAddresses(): String {
val index = indexOf("coroutine\"")
val next = indexOf(',', index)
if (index == -1 || next == -1) return this
return substring(0, index) + substring(next, length)
}