Rework test runner to avoid mutable objects. (#1702)
This commit is contained in:
+4
-4
@@ -75,7 +75,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
val functionDescriptor = irCall.descriptor
|
val functionDescriptor = irCall.descriptor
|
||||||
if (!functionDescriptor.needsInlining) return irCall // This call does not need inlining.
|
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.
|
if (functionDeclaration == null) { // We failed to get the declaration.
|
||||||
val message = "Inliner failed to obtain function declaration: " +
|
val message = "Inliner failed to obtain function declaration: " +
|
||||||
functionDescriptor.fqNameSafe.toString()
|
functionDescriptor.fqNameSafe.toString()
|
||||||
@@ -83,9 +83,9 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
return irCall
|
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.
|
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<DeclarationDescriptor,
|
|||||||
private fun inlineFunction(callee: IrCall, // Call to be substituted.
|
private fun inlineFunction(callee: IrCall, // Call to be substituted.
|
||||||
caller: IrFunction): IrReturnableBlockImpl { // Function to substitute.
|
caller: IrFunction): IrReturnableBlockImpl { // Function to substitute.
|
||||||
|
|
||||||
val copyFunctionDeclaration = copyIrElement.copy( // Create copy of original function.
|
val copyFunctionDeclaration = copyIrElement.copy( // Create copy of original function.
|
||||||
irElement = caller, // Descriptors declared inside the function will be copied.
|
irElement = caller, // Descriptors declared inside the function will be copied.
|
||||||
typeSubstitutor = createTypeSubstitutor(callee) // Type parameters will be substituted with type arguments.
|
typeSubstitutor = createTypeSubstitutor(callee) // Type parameters will be substituted with type arguments.
|
||||||
) as IrFunction
|
) as IrFunction
|
||||||
|
|||||||
+4
-2
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.ir.declarations.*
|
|||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.*
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
|
||||||
@@ -405,9 +406,10 @@ internal class TestProcessor (val context: KonanBackendContext) {
|
|||||||
endOffset = UNDEFINED_OFFSET,
|
endOffset = UNDEFINED_OFFSET,
|
||||||
symbol = symbols.symbolTable.referenceConstructor(superConstructor),
|
symbol = symbols.symbolTable.referenceConstructor(superConstructor),
|
||||||
descriptor = superConstructor,
|
descriptor = superConstructor,
|
||||||
typeArguments = mapOf(superConstructor.typeParameters[0] to testClassType,
|
typeArgumentsCount = 2
|
||||||
superConstructor.typeParameters[1] to testCompanionType)
|
|
||||||
).apply {
|
).apply {
|
||||||
|
copyTypeArgumentsFrom(mapOf(superConstructor.typeParameters[0] to testClassType,
|
||||||
|
superConstructor.typeParameters[1] to testCompanionType))
|
||||||
putValueArgument(0, IrConstImpl.string(
|
putValueArgument(0, IrConstImpl.string(
|
||||||
UNDEFINED_OFFSET,
|
UNDEFINED_OFFSET,
|
||||||
UNDEFINED_OFFSET,
|
UNDEFINED_OFFSET,
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ fun test() {
|
|||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
println("Custom main")
|
println("Custom main")
|
||||||
TestRunner.run(args)
|
testLauncherEntryPoint(args)
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,16 @@ package konan.test
|
|||||||
|
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
fun main(args:Array<String>) {
|
private val _generatedSuites = mutableListOf<TestSuite>()
|
||||||
val exitCode = TestRunner.run(args)
|
|
||||||
exitProcess(exitCode)
|
internal fun registerSuite(suite: TestSuite): Unit {
|
||||||
|
_generatedSuites.add(suite)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testLauncherEntryPoint(args: Array<String>): Int {
|
||||||
|
return TestRunner(_generatedSuites, args).run()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
exitProcess(testLauncherEntryPoint(args))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,22 +21,55 @@ import kotlin.system.getTimeMillis
|
|||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
import kotlin.text.StringBuilder
|
import kotlin.text.StringBuilder
|
||||||
|
|
||||||
object TestRunner {
|
class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||||
|
private val filters = mutableListOf<(TestCase) -> Boolean>()
|
||||||
private val suites_ = mutableListOf<TestSuite>()
|
private val listeners = mutableSetOf<TestListener>()
|
||||||
val suites: Collection<TestSuite>
|
private var logger: TestLogger = GTestLogger()
|
||||||
get() = suites_
|
private var runTests = true
|
||||||
|
|
||||||
var logger: TestLogger = GTestLogger()
|
|
||||||
var iterations = 1
|
var iterations = 1
|
||||||
|
private set
|
||||||
val listeners = mutableSetOf<TestListener>()
|
|
||||||
val filters = mutableListOf<(TestCase) -> Boolean>()
|
|
||||||
|
|
||||||
var exitCode = 0
|
var exitCode = 0
|
||||||
private set
|
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
|
private val TestCase.matchFilters: Boolean
|
||||||
get() = filters.map { it(this) }.all { it }
|
get() = filters.map { it(this) }.all { it }
|
||||||
@@ -50,10 +83,6 @@ object TestRunner {
|
|||||||
|
|
||||||
private fun filterSuites(): Collection<TestSuite> = suites.map { FilteredSuite(it) }
|
private fun filterSuites(): Collection<TestSuite> = suites.map { FilteredSuite(it) }
|
||||||
|
|
||||||
fun register(suite: TestSuite) = suites_.add(suite)
|
|
||||||
fun register(suites: Iterable<TestSuite>) = suites_.addAll(suites)
|
|
||||||
fun register(vararg suites: TestSuite) = suites_.addAll(suites)
|
|
||||||
|
|
||||||
// TODO: Support short aliases.
|
// TODO: Support short aliases.
|
||||||
// TODO: Support several test iterations.
|
// 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.
|
* --ktest_logger=GTEST|TEAMCITY|SIMPLE|SILENT - Use the specified output format. The default one is GTEST.
|
||||||
*/
|
*/
|
||||||
fun useArgs(args: Array<String>): Boolean {
|
|
||||||
try {
|
|
||||||
return parseArgs(args)
|
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
logger.log("Error: ${e.message}")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseArgs(args: Array<String>): 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) =
|
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 ->
|
private fun String.toGTestPatterns() = splitToSequence(':').map { pattern ->
|
||||||
val result = StringBuilder()
|
val result = StringBuilder()
|
||||||
@@ -137,8 +121,8 @@ object TestRunner {
|
|||||||
pattern.forEachIndexed { index, c ->
|
pattern.forEachIndexed { index, c ->
|
||||||
if (c == '*' || c == '?') {
|
if (c == '*' || c == '?') {
|
||||||
result.append(pattern.substringEscaped(prevIndex until index))
|
result.append(pattern.substringEscaped(prevIndex until index))
|
||||||
prevIndex = index+1
|
prevIndex = index + 1
|
||||||
result.append( if (c == '*') ".*" else ".")
|
result.append(if (c == '*') ".*" else ".")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.append(pattern.substringEscaped(prevIndex until pattern.length))
|
result.append(pattern.substringEscaped(prevIndex until pattern.length))
|
||||||
@@ -159,7 +143,7 @@ object TestRunner {
|
|||||||
|
|
||||||
this.filters.add { testCase ->
|
this.filters.add { testCase ->
|
||||||
positivePatterns.any { testCase.prettyName.matches(it) } &&
|
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) {
|
private fun setLoggerFromArg(logger: String) {
|
||||||
when(logger.toUpperCase()) {
|
when (logger.toUpperCase()) {
|
||||||
"GTEST" -> this.logger = GTestLogger()
|
"GTEST" -> this.logger = GTestLogger()
|
||||||
"TEAMCITY" -> this.logger = TeamCityLogger()
|
"TEAMCITY" -> this.logger = TeamCityLogger()
|
||||||
"SIMPLE" -> this.logger = SimpleTestLogger()
|
"SIMPLE" -> this.logger = SimpleTestLogger()
|
||||||
@@ -249,15 +233,9 @@ object TestRunner {
|
|||||||
sendToListeners { finishIteration(this@TestRunner, iteration, iterationTime) }
|
sendToListeners { finishIteration(this@TestRunner, iteration, iterationTime) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run(args: Array<String>): Int {
|
|
||||||
return if (useArgs(args)) {
|
|
||||||
run()
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun run(): Int {
|
fun run(): Int {
|
||||||
|
if (!runTests)
|
||||||
|
return 0
|
||||||
sendToListeners { startTesting(this@TestRunner) }
|
sendToListeners { startTesting(this@TestRunner) }
|
||||||
val totalTime = measureTimeMillis {
|
val totalTime = measureTimeMillis {
|
||||||
var i = 1
|
var i = 1
|
||||||
@@ -268,5 +246,6 @@ object TestRunner {
|
|||||||
}
|
}
|
||||||
sendToListeners { finishTesting(this@TestRunner, totalTime) }
|
sendToListeners { finishTesting(this@TestRunner, totalTime) }
|
||||||
return exitCode
|
return exitCode
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String, o
|
|||||||
abstract fun createTestCase(name: String, testFunction: F, ignored: Boolean): BasicTestCase<F>
|
abstract fun createTestCase(name: String, testFunction: F, ignored: Boolean): BasicTestCase<F>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
TestRunner.register(this)
|
registerSuite(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val size: Int
|
override val size: Int
|
||||||
|
|||||||
Reference in New Issue
Block a user