From f3fd6579a0bb60992b5b0580cebffa59fd8d5d1e Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Wed, 20 Sep 2017 15:03:26 +0300 Subject: [PATCH] unit-tests: Add gtest-like output --- .../src/main/kotlin/konan/test/Launcher.kt | 12 ++-- .../src/main/kotlin/konan/test/Listeners.kt | 67 +++++++++++++++++++ .../main/kotlin/konan/test/TestListener.kt | 32 +++++++-- .../src/main/kotlin/konan/test/TestRunner.kt | 20 +++--- .../src/main/kotlin/konan/test/TestSuite.kt | 12 +++- 5 files changed, 120 insertions(+), 23 deletions(-) create mode 100644 runtime/src/main/kotlin/konan/test/Listeners.kt diff --git a/runtime/src/main/kotlin/konan/test/Launcher.kt b/runtime/src/main/kotlin/konan/test/Launcher.kt index 256af47bc80..ec8b18bf799 100644 --- a/runtime/src/main/kotlin/konan/test/Launcher.kt +++ b/runtime/src/main/kotlin/konan/test/Launcher.kt @@ -8,15 +8,15 @@ class SimpleTestListener: TestListener { private set override fun startTesting(runner: TestRunner) = println("Starting testing") - override fun endTesting(runner: TestRunner) = println("Testing finished") + override fun endTesting(runner: TestRunner, timeMillis: Long) = println("Testing finished") override fun startSuite(suite: TestSuite) = println("Starting test suite: $suite") - override fun endSuite(suite: TestSuite) = println("Test suite finished: $suite") + override fun endSuite(suite: TestSuite, timeMillis: Long) = println("Test suite finished: $suite") override fun ignoreSuite(suite: TestSuite) = println("Test suite ignored: $suite") override fun start(testCase: TestCase) = println("Starting test case: $testCase") - override fun pass(testCase: TestCase) = println("Passed: $testCase") - override fun fail(testCase: TestCase, e: Throwable) { + override fun pass(testCase: TestCase, timeMillis: Long) = println("Passed: $testCase") + override fun fail(testCase: TestCase, e: Throwable, timeMillis: Long) { println("Failed: $testCase. Exception:") e.printStackTrace() hasFails = true @@ -25,7 +25,7 @@ class SimpleTestListener: TestListener { } fun main(args:Array) { - val listener = SimpleTestListener() + val listener = GTestListener() TestRunner.run(listener) - exitProcess( if (listener.hasFails) -1 else 0 ) + exitProcess( if (listener.hasFailedTests) -1 else 0 ) } diff --git a/runtime/src/main/kotlin/konan/test/Listeners.kt b/runtime/src/main/kotlin/konan/test/Listeners.kt new file mode 100644 index 00000000000..0b9cbc65f9c --- /dev/null +++ b/runtime/src/main/kotlin/konan/test/Listeners.kt @@ -0,0 +1,67 @@ +package konan.test + +val TestRunner.totalTests + get() = suites.map { it.size }.reduce { sum, size -> sum + size } + +val TestRunner.totalSuites + get() = suites.size + +class GTestListener: TestListener, AbstractTestStatistics() { + + private val TestCase.prettyName get() = "${suite.name}.$name" + private val failedTests_ = mutableListOf() + + private var totalSuites = 0 + + override fun startTesting(runner: TestRunner) { + println("[==========] Running ${runner.totalTests} tests from ${runner.totalSuites} test case.") + println("[----------] Global test environment set-up.") // TODO: just emulation + } + + private fun printResults(timeMillis: Long) { + println("[----------] Global test environment tear-down") + println("[==========] $total tests from $totalSuites test cases ran. ($timeMillis ms total)") + println("[ PASSED ] $passed tests.") + if (hasFailedTests) { + println("[ FAILED ] $failed tests, listed below:") + failedTests.forEach { + println("[ FAILED ] ${it.prettyName}") + } + println("\n$failed FAILED TESTS") + } + if (ignored != 0) { + println("YOU HAVE $ignored DISABLED TEST(S)") + } + } + + override fun endTesting(runner: TestRunner, timeMillis: Long) = printResults(timeMillis) + + override fun startSuite(suite: TestSuite) = println("[----------] ${suite.size} tests from ${suite.name}") + override fun endSuite(suite: TestSuite, timeMillis: Long) { + totalSuites++ + println("[----------] ${suite.size} tests from ${suite.name} ($timeMillis ms total)\n") + } + + override fun ignoreSuite(suite: TestSuite) {} + + override fun start(testCase: TestCase) = println("[ RUN ] ${testCase.prettyName}") + + override fun pass(testCase: TestCase, timeMillis: Long) { + registerPass() + println("[ OK ] ${testCase.prettyName} ($timeMillis ms)") + } + + override fun fail(testCase: TestCase, e: Throwable, timeMillis: Long) { + registerFail() + failedTests_.add(testCase) + e.printStackTrace() + println("[ FAILED ] ${testCase.prettyName} ($timeMillis ms)") + } + + override fun ignore(testCase: TestCase) = registerIgnore() + + override val failedTests: Collection + get() = failedTests_ + override val hasFailedTests: Boolean + get() = failed != 0 +} diff --git a/runtime/src/main/kotlin/konan/test/TestListener.kt b/runtime/src/main/kotlin/konan/test/TestListener.kt index 277c1e09bba..9e1873fa9d3 100644 --- a/runtime/src/main/kotlin/konan/test/TestListener.kt +++ b/runtime/src/main/kotlin/konan/test/TestListener.kt @@ -1,17 +1,37 @@ package konan.test -import kotlin.AssertionError - interface TestListener { fun startTesting(runner: TestRunner) - fun endTesting(runner: TestRunner) + fun endTesting(runner: TestRunner, timeMillis: Long) fun startSuite(suite: TestSuite) - fun endSuite(suite: TestSuite) + fun endSuite(suite: TestSuite, timeMillis: Long) fun ignoreSuite(suite: TestSuite) fun start(testCase: TestCase) - fun pass(testCase: TestCase) - fun fail(testCase: TestCase, e: Throwable) + fun pass(testCase: TestCase, timeMillis: Long) + fun fail(testCase: TestCase, e: Throwable, timeMillis: Long) fun ignore(testCase: TestCase) +} + +interface TestStatistics { + val total: Int + val passed: Int + val failed: Int + val ignored: Int + + // TODO: Do we need such properties for other cases? + val failedTests: Collection + val hasFailedTests: Boolean +} + +abstract class AbstractTestStatistics: TestStatistics { + override var total: Int = 0; protected set + override var passed: Int = 0; protected set + override var failed: Int = 0; protected set + override var ignored: Int = 0; protected set + + protected fun registerPass() { total++; passed++ } + protected fun registerFail() { total++; failed++ } + protected fun registerIgnore() { total++; ignored++ } } \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/test/TestRunner.kt b/runtime/src/main/kotlin/konan/test/TestRunner.kt index 2d4f816cea5..262ddcce47e 100644 --- a/runtime/src/main/kotlin/konan/test/TestRunner.kt +++ b/runtime/src/main/kotlin/konan/test/TestRunner.kt @@ -1,5 +1,7 @@ package konan.test +import kotlin.system.measureTimeMillis + object TestRunner { private val _suites = mutableListOf() @@ -11,15 +13,17 @@ object TestRunner { fun run(listener: TestListener) = with(listener) { startTesting(this@TestRunner) - suites.forEach { - if (it.ignored) { - ignoreSuite(it) - } else { - startSuite(it) - it.run(listener) - endSuite(it) + val totalTime = measureTimeMillis { + suites.forEach { + if (it.ignored) { + ignoreSuite(it) + } else { + startSuite(it) + val time = measureTimeMillis { it.run(listener) } + endSuite(it, time) + } } } - endTesting(this@TestRunner) + endTesting(this@TestRunner, totalTime) } } diff --git a/runtime/src/main/kotlin/konan/test/TestSuite.kt b/runtime/src/main/kotlin/konan/test/TestSuite.kt index 6622122a79f..febe1c3fda8 100644 --- a/runtime/src/main/kotlin/konan/test/TestSuite.kt +++ b/runtime/src/main/kotlin/konan/test/TestSuite.kt @@ -1,6 +1,8 @@ package konan.test import kotlin.IllegalArgumentException +import kotlin.system.getTimeMillis +import kotlin.system.measureTimeMillis interface TestCase { val name: String @@ -12,6 +14,7 @@ interface TestSuite { val name: String val ignored: Boolean val testCases: Map + val size : Int fun run(listener: TestListener) } @@ -57,21 +60,24 @@ abstract class AbstractTestSuite>(override val name: String, o TestRunner.register(this) } + override val size: Int + get() = testCases.size + override fun run(listener: TestListener) { doBeforeClass() testCases.values.forEach { if (it.ignored) { listener.ignore(it) } else { + val startTime = getTimeMillis() try { listener.start(it) doTest(it) - listener.pass(it) + listener.pass(it, getTimeMillis() - startTime) } catch (e: Throwable) { - listener.fail(it, e) + listener.fail(it, e, getTimeMillis() - startTime) } } - } doAfterClass() }