From 25b2ec8366997ae584565601acd3610ed764d53c Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Wed, 10 Apr 2019 18:32:07 +0700 Subject: [PATCH] Test runner: Report stacktraces for failed tests in TC messages --- runtime/src/main/kotlin/kotlin/Throwable.kt | 35 ++++++++++++------- .../native/internal/test/TeamCityLogger.kt | 6 ++-- .../kotlin/native/internal/test/TestSuite.kt | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/runtime/src/main/kotlin/kotlin/Throwable.kt b/runtime/src/main/kotlin/kotlin/Throwable.kt index fc2830cf2d3..f4d6ed68520 100644 --- a/runtime/src/main/kotlin/kotlin/Throwable.kt +++ b/runtime/src/main/kotlin/kotlin/Throwable.kt @@ -33,24 +33,33 @@ public open class Throwable(open val message: String?, open val cause: Throwable public fun getStackTrace(): Array = stackTraceStrings - public fun printStackTrace() { - println(this.toString()) + public fun printStackTrace(): Unit = dumpStackTrace { println(it) } - for (element in stackTraceStrings) { - println(" at $element") + internal fun dumpStackTrace(): String = buildString { + dumpStackTrace { appendln(it) } + } + + private inline fun writeStackTraceElements(throwable: Throwable, writeln: (String) -> Unit) { + for (element in throwable.stackTraceStrings) { + writeln(" at $element") } - - this.cause?.printEnclosedStackTrace(this) } - @Suppress("UNUSED_PARAMETER") - private fun printEnclosedStackTrace(enclosing: Throwable) { - // TODO: should skip common stack frames - print("Caused by: ") - this.printStackTrace() + private inline fun dumpStackTrace(crossinline writeln: (String) -> Unit) { + writeln(this@Throwable.toString()) + + writeStackTraceElements(this, writeln) + + var cause = this.cause + while (cause != null) { + // TODO: should skip common stack frames + writeln("Caused by: $cause") + writeStackTraceElements(cause, writeln) + cause = cause.cause + } } - override public fun toString(): String { + public override fun toString(): String { val kClass = this::class val s = kClass.qualifiedName ?: kClass.simpleName ?: "Throwable" return if (message != null) s + ": " + message.toString() else s @@ -61,4 +70,4 @@ public open class Throwable(open val message: String?, open val cause: Throwable private external fun getCurrentStackTrace(): NativePtrArray @SymbolName("Kotlin_getStackTraceStrings") -private external fun getStackTraceStrings(stackTrace: NativePtrArray): Array \ No newline at end of file +private external fun getStackTraceStrings(stackTrace: NativePtrArray): Array diff --git a/runtime/src/main/kotlin/kotlin/native/internal/test/TeamCityLogger.kt b/runtime/src/main/kotlin/kotlin/native/internal/test/TeamCityLogger.kt index f23d90fcaed..984a0eca6f2 100644 --- a/runtime/src/main/kotlin/kotlin/native/internal/test/TeamCityLogger.kt +++ b/runtime/src/main/kotlin/kotlin/native/internal/test/TeamCityLogger.kt @@ -51,9 +51,9 @@ internal class TeamCityLogger : BaseTestLogger() { override fun pass(testCase: TestCase, timeMillis: Long) = finish(testCase, timeMillis) override fun fail(testCase: TestCase, e: Throwable, timeMillis: Long) { - // TODO: Add 'details=...' command with the stack trace (need to implement stacktrace dumping as a string) - e.printStackTrace() - report("testFailed name='${testCase.tcName}' message='${e.message?.escapeForTC()}'") + val stackTrace = e.dumpStackTrace().escapeForTC() + val message = e.message?.escapeForTC() + report("testFailed name='${testCase.tcName}' message='$message' details='$stackTrace'") finish(testCase, timeMillis) } diff --git a/runtime/src/main/kotlin/kotlin/native/internal/test/TestSuite.kt b/runtime/src/main/kotlin/kotlin/native/internal/test/TestSuite.kt index 9934828e64c..5a9fe5dfc85 100644 --- a/runtime/src/main/kotlin/kotlin/native/internal/test/TestSuite.kt +++ b/runtime/src/main/kotlin/kotlin/native/internal/test/TestSuite.kt @@ -89,7 +89,7 @@ public abstract class BaseClassSuite(name: String, ignored: } } - // These two methods are overrided in test suite classes generated by the compiler. + // These two methods are overridden in test suite classes generated by the compiler. abstract fun createInstance(): INSTANCE open fun getCompanion(): COMPANION = throw NotImplementedError("Test class has no companion object")