From 7074976f7f9a76ce9fa59e968ca17f518b0ae5ff Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Tue, 19 Jun 2018 14:31:31 +0300 Subject: [PATCH] Rework test runner to avoid mutable objects. (#1702) --- .../backend/konan/lower/FunctionInlining.kt | 8 +- .../backend/konan/lower/TestProcessor.kt | 6 +- backend.native/tests/testing/custom_main.kt | 2 +- .../src/main/kotlin/konan/test/Launcher.kt | 15 ++- .../src/main/kotlin/konan/test/TestRunner.kt | 127 ++++++++---------- .../src/main/kotlin/konan/test/TestSuite.kt | 2 +- 6 files changed, 75 insertions(+), 85 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index 3693e4e757d..dd85de0d9cc 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -75,7 +75,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW val functionDescriptor = irCall.descriptor if (!functionDescriptor.needsInlining) return irCall // This call does not need inlining. - val functionDeclaration = getFunctionDeclaration(functionDescriptor) // Get declaration of the function to be inlined. + val functionDeclaration = getFunctionDeclaration(functionDescriptor) // Get declaration of the function to be inlined. if (functionDeclaration == null) { // We failed to get the declaration. val message = "Inliner failed to obtain function declaration: " + functionDescriptor.fqNameSafe.toString() @@ -83,9 +83,9 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW return irCall } - functionDeclaration.transformChildrenVoid(this) // Process recursive inline. + functionDeclaration.transformChildrenVoid(this) // Process recursive inline. val inliner = Inliner(globalSubstituteMap, functionDeclaration, currentScope!!, context) // Create inliner for this scope. - return inliner.inline(irCall ) // Return newly created IrInlineBody instead of IrCall. + return inliner.inline(irCall) // Return newly created IrInlineBody instead of IrCall. } //-------------------------------------------------------------------------// @@ -134,7 +134,7 @@ private class Inliner(val globalSubstituteMap: MutableMap) { println("Custom main") - TestRunner.run(args) + testLauncherEntryPoint(args) } \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/test/Launcher.kt b/runtime/src/main/kotlin/konan/test/Launcher.kt index 4f439ba2036..72ac953df39 100644 --- a/runtime/src/main/kotlin/konan/test/Launcher.kt +++ b/runtime/src/main/kotlin/konan/test/Launcher.kt @@ -18,7 +18,16 @@ package konan.test import kotlin.system.exitProcess -fun main(args:Array) { - val exitCode = TestRunner.run(args) - exitProcess(exitCode) +private val _generatedSuites = mutableListOf() + +internal fun registerSuite(suite: TestSuite): Unit { + _generatedSuites.add(suite) +} + +fun testLauncherEntryPoint(args: Array): Int { + return TestRunner(_generatedSuites, args).run() +} + +fun main(args: Array) { + exitProcess(testLauncherEntryPoint(args)) } diff --git a/runtime/src/main/kotlin/konan/test/TestRunner.kt b/runtime/src/main/kotlin/konan/test/TestRunner.kt index 13a15c3a11a..cf4e175e2d8 100644 --- a/runtime/src/main/kotlin/konan/test/TestRunner.kt +++ b/runtime/src/main/kotlin/konan/test/TestRunner.kt @@ -21,22 +21,55 @@ import kotlin.system.getTimeMillis import kotlin.system.measureTimeMillis import kotlin.text.StringBuilder -object TestRunner { - - private val suites_ = mutableListOf() - val suites: Collection - get() = suites_ - - var logger: TestLogger = GTestLogger() +class TestRunner(val suites: List, args: Array) { + private val filters = mutableListOf<(TestCase) -> Boolean>() + private val listeners = mutableSetOf() + private var logger: TestLogger = GTestLogger() + private var runTests = true var iterations = 1 - - val listeners = mutableSetOf() - val filters = mutableListOf<(TestCase) -> Boolean>() - + private set var exitCode = 0 private set - class FilteredSuite(val innerSuite: TestSuite): TestSuite by innerSuite { + init { + args.filter { + it.startsWith("--gtest_") || it.startsWith("--ktest_") || it == "--help" || it == "-h" + }.forEach { + val arg = it.split('=') + when (arg.size) { + 1 -> when (arg[0]) { + "--gtest_list_tests", + "--ktest_list_tests" -> { + logger.logTestList(this, filterSuites()); runTests = false + } + "-h", + "--help" -> { + logger.log(help); runTests = false + } + else -> throw IllegalArgumentException("Unknown option: $it\n$help") + } + 2 -> { + val key = arg[0] + val value = arg[1] + when (key) { + "--ktest_logger" -> setLoggerFromArg(value) + "--gtest_filter", + "--ktest_filter" -> setGTestFilterFromArg(value) + "--ktest_regex_filter" -> setRegexFilterFromArg(value, true) + "--ktest_negative_regex_filter" -> setRegexFilterFromArg(value, false) + "--ktest_repeat", + "--gtest_repeat" -> iterations = value.toIntOrNull() ?: throw IllegalArgumentException("Cannot parse number: $value") + else -> if (key.startsWith("--ktest_")) { + throw IllegalArgumentException("Unknown option: $it\n$help") + } + } + } + else -> throw IllegalArgumentException("Unknown option: $it\n$help") + } + } + } + + inner class FilteredSuite(val innerSuite: TestSuite) : TestSuite by innerSuite { private val TestCase.matchFilters: Boolean get() = filters.map { it(this) }.all { it } @@ -50,10 +83,6 @@ object TestRunner { private fun filterSuites(): Collection = suites.map { FilteredSuite(it) } - fun register(suite: TestSuite) = suites_.add(suite) - fun register(suites: Iterable) = suites_.addAll(suites) - fun register(vararg suites: TestSuite) = suites_.addAll(suites) - // TODO: Support short aliases. // TODO: Support several test iterations. /** @@ -82,54 +111,9 @@ object TestRunner { * * --ktest_logger=GTEST|TEAMCITY|SIMPLE|SILENT - Use the specified output format. The default one is GTEST. */ - fun useArgs(args: Array): Boolean { - try { - return parseArgs(args) - } catch (e: IllegalArgumentException) { - logger.log("Error: ${e.message}") - return false - } - } - - private fun parseArgs(args: Array): Boolean { - var result = true - args.filter { - it.startsWith("--gtest_") || it.startsWith("--ktest_") || it == "--help" || it == "-h" - }.forEach { - val arg = it.split('=') - when (arg.size) { - 1 -> when (arg[0]) { - "--gtest_list_tests", - "--ktest_list_tests" -> { logger.logTestList(this, filterSuites()); result = false } - "-h", - "--help" -> { logger.log(help); result = false } - else -> throw IllegalArgumentException("Unknown option: $it\n$help") - } - 2 -> { - val key = arg[0] - val value = arg[1] - when (key) { - "--ktest_logger" -> setLoggerFromArg(value) - "--gtest_filter", - "--ktest_filter" -> setGTestFilterFromArg(value) - "--ktest_regex_filter" -> setRegexFilterFromArg(value, true) - "--ktest_negative_regex_filter" -> setRegexFilterFromArg(value, false) - "--ktest_repeat", - "--gtest_repeat" -> iterations = value.toIntOrNull() ?: - throw IllegalArgumentException("Cannot parse number: $value") - else -> if (key.startsWith("--ktest_")) { - throw IllegalArgumentException("Unknown option: $it\n$help") - } - } - } - else -> throw IllegalArgumentException("Unknown option: $it\n$help") - } - } - return result - } private fun String.substringEscaped(range: IntRange) = - this.substring(range).let { if(it.isNotEmpty()) Regex.escape(it) else "" } + this.substring(range).let { if (it.isNotEmpty()) Regex.escape(it) else "" } private fun String.toGTestPatterns() = splitToSequence(':').map { pattern -> val result = StringBuilder() @@ -137,8 +121,8 @@ object TestRunner { pattern.forEachIndexed { index, c -> if (c == '*' || c == '?') { result.append(pattern.substringEscaped(prevIndex until index)) - prevIndex = index+1 - result.append( if (c == '*') ".*" else ".") + prevIndex = index + 1 + result.append(if (c == '*') ".*" else ".") } } result.append(pattern.substringEscaped(prevIndex until pattern.length)) @@ -159,7 +143,7 @@ object TestRunner { this.filters.add { testCase -> positivePatterns.any { testCase.prettyName.matches(it) } && - negativePatterns.none { testCase.prettyName.matches(it) } + negativePatterns.none { testCase.prettyName.matches(it) } } } @@ -174,7 +158,7 @@ object TestRunner { } private fun setLoggerFromArg(logger: String) { - when(logger.toUpperCase()) { + when (logger.toUpperCase()) { "GTEST" -> this.logger = GTestLogger() "TEAMCITY" -> this.logger = TeamCityLogger() "SIMPLE" -> this.logger = SimpleTestLogger() @@ -249,15 +233,9 @@ object TestRunner { sendToListeners { finishIteration(this@TestRunner, iteration, iterationTime) } } - fun run(args: Array): Int { - return if (useArgs(args)) { - run() - } else { - 0 - } - } - fun run(): Int { + if (!runTests) + return 0 sendToListeners { startTesting(this@TestRunner) } val totalTime = measureTimeMillis { var i = 1 @@ -268,5 +246,6 @@ object TestRunner { } sendToListeners { finishTesting(this@TestRunner, totalTime) } return exitCode + } } diff --git a/runtime/src/main/kotlin/konan/test/TestSuite.kt b/runtime/src/main/kotlin/konan/test/TestSuite.kt index a4bcd750094..e89ac6086c1 100644 --- a/runtime/src/main/kotlin/konan/test/TestSuite.kt +++ b/runtime/src/main/kotlin/konan/test/TestSuite.kt @@ -73,7 +73,7 @@ abstract class AbstractTestSuite>(override val name: String, o abstract fun createTestCase(name: String, testFunction: F, ignored: Boolean): BasicTestCase init { - TestRunner.register(this) + registerSuite(this) } override val size: Int