[K/N] Test framework implementation refactoring
* Extract argument processor and settings out of TestRunner. This makes it possible to add external runners to the framework. * Add methods and docs to TestCase and TestSuite classes that describe Kotlin test methods.
This commit is contained in:
committed by
Space Team
parent
a5893d4bee
commit
f727934f6b
@@ -16,11 +16,11 @@ internal class GTestLogger : TestLoggerWithStatistics() {
|
||||
private val Collection<TestSuite>.totalNotIgnored: Int
|
||||
get() = filter { !it.ignored }.size
|
||||
|
||||
override fun startIteration(runner: TestRunner, iteration: Int, suites: Collection<TestSuite>) {
|
||||
if (runner.iterations != 1) {
|
||||
override fun startIteration(settings: TestSettings, iteration: Int, suites: Collection<TestSuite>) {
|
||||
if (settings.iterations != 1) {
|
||||
println("\nRepeating all tests (iteration $iteration) . . .\n")
|
||||
}
|
||||
super.startIteration(runner, iteration, suites)
|
||||
super.startIteration(settings, iteration, suites)
|
||||
println("[==========] Running ${suites.totalTestsNotIgnored} tests from ${suites.totalNotIgnored} test cases.")
|
||||
// Just hack to deal with GTest output parsers.
|
||||
println("[----------] Global test environment set-up.")
|
||||
@@ -47,7 +47,7 @@ internal class GTestLogger : TestLoggerWithStatistics() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun finishIteration(runner: TestRunner, iteration: Int, timeMillis: Long) = printResults(timeMillis)
|
||||
override fun finishIteration(settings: TestSettings, iteration: Int, timeMillis: Long) = printResults(timeMillis)
|
||||
|
||||
override fun startSuite(suite: TestSuite) = println("[----------] ${suite.size} tests from ${suite.name}")
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ import kotlin.experimental.ExperimentalNativeApi
|
||||
|
||||
@ExperimentalNativeApi
|
||||
internal interface TestListener {
|
||||
fun startTesting(runner: TestRunner)
|
||||
fun finishTesting(runner: TestRunner, timeMillis: Long)
|
||||
fun startTesting(settings: TestSettings)
|
||||
fun finishTesting(settings: TestSettings, timeMillis: Long)
|
||||
|
||||
fun startIteration(runner: TestRunner, iteration: Int, suites: Collection<TestSuite>)
|
||||
fun finishIteration(runner: TestRunner, iteration: Int, timeMillis: Long)
|
||||
fun startIteration(settings: TestSettings, iteration: Int, suites: Collection<TestSuite>)
|
||||
fun finishIteration(settings: TestSettings, iteration: Int, timeMillis: Long)
|
||||
|
||||
fun startSuite(suite: TestSuite)
|
||||
fun finishSuite(suite: TestSuite, timeMillis: Long)
|
||||
@@ -27,10 +27,10 @@ internal interface TestListener {
|
||||
|
||||
@ExperimentalNativeApi
|
||||
internal open class BaseTestListener: TestListener {
|
||||
override fun startTesting(runner: TestRunner) {}
|
||||
override fun finishTesting(runner: TestRunner, timeMillis: Long) {}
|
||||
override fun startIteration(runner: TestRunner, iteration: Int, suites: Collection<TestSuite>) {}
|
||||
override fun finishIteration(runner: TestRunner, iteration: Int, timeMillis: Long) {}
|
||||
override fun startTesting(settings: TestSettings) {}
|
||||
override fun finishTesting(settings: TestSettings, timeMillis: Long) {}
|
||||
override fun startIteration(settings: TestSettings, iteration: Int, suites: Collection<TestSuite>) {}
|
||||
override fun finishIteration(settings: TestSettings, iteration: Int, timeMillis: Long) {}
|
||||
override fun startSuite(suite: TestSuite) {}
|
||||
override fun finishSuite(suite: TestSuite, timeMillis: Long) {}
|
||||
override fun ignoreSuite(suite: TestSuite) {}
|
||||
|
||||
@@ -9,14 +9,14 @@ import kotlin.experimental.ExperimentalNativeApi
|
||||
|
||||
@ExperimentalNativeApi
|
||||
internal interface TestLogger: TestListener {
|
||||
fun logTestList(runner: TestRunner, suites: Collection<TestSuite>)
|
||||
fun logTestList(suites: Collection<TestSuite>)
|
||||
fun log(message: String)
|
||||
}
|
||||
|
||||
@ExperimentalNativeApi
|
||||
internal open class BaseTestLogger: BaseTestListener(), TestLogger {
|
||||
override fun log(message: String) = println(message)
|
||||
override fun logTestList(runner: TestRunner, suites: Collection<TestSuite>) {
|
||||
override fun logTestList(suites: Collection<TestSuite>) {
|
||||
suites.forEach { suite ->
|
||||
println("${suite.name}.")
|
||||
suite.testCases.values.forEach {
|
||||
@@ -31,8 +31,8 @@ internal open class TestLoggerWithStatistics: BaseTestLogger() {
|
||||
|
||||
protected val statistics = MutableTestStatistics()
|
||||
|
||||
override fun startTesting(runner: TestRunner) = statistics.reset()
|
||||
override fun startIteration(runner: TestRunner, iteration: Int, suites: Collection<TestSuite>) = statistics.reset()
|
||||
override fun startTesting(settings: TestSettings) = statistics.reset()
|
||||
override fun startIteration(settings: TestSettings, iteration: Int, suites: Collection<TestSuite>) = statistics.reset()
|
||||
|
||||
override fun finishSuite(suite: TestSuite, timeMillis: Long) = statistics.registerSuite()
|
||||
override fun ignoreSuite(suite: TestSuite) = statistics.registerIgnore(suite)
|
||||
@@ -43,19 +43,19 @@ internal open class TestLoggerWithStatistics: BaseTestLogger() {
|
||||
|
||||
@ExperimentalNativeApi
|
||||
internal class SilentTestLogger: BaseTestLogger() {
|
||||
override fun logTestList(runner: TestRunner, suites: Collection<TestSuite>) {}
|
||||
override fun logTestList(suites: Collection<TestSuite>) {}
|
||||
override fun log(message: String) {}
|
||||
override fun fail(testCase: TestCase, e: Throwable, timeMillis: Long) = e.printStackTrace()
|
||||
}
|
||||
|
||||
@ExperimentalNativeApi
|
||||
internal class SimpleTestLogger: BaseTestLogger() {
|
||||
override fun startTesting(runner: TestRunner) = println("Starting testing")
|
||||
override fun finishTesting(runner: TestRunner, timeMillis: Long) = println("Testing finished")
|
||||
override fun startTesting(settings: TestSettings) = println("Starting testing")
|
||||
override fun finishTesting(settings: TestSettings, timeMillis: Long) = println("Testing finished")
|
||||
|
||||
override fun startIteration(runner: TestRunner, iteration: Int, suites: Collection<TestSuite>) =
|
||||
override fun startIteration(settings: TestSettings, iteration: Int, suites: Collection<TestSuite>) =
|
||||
println("Starting iteration: $iteration")
|
||||
override fun finishIteration(runner: TestRunner, iteration: Int, timeMillis: Long) =
|
||||
override fun finishIteration(settings: TestSettings, iteration: Int, timeMillis: Long) =
|
||||
println("Iteration finished: $iteration")
|
||||
|
||||
override fun startSuite(suite: TestSuite) = println("Starting test suite: $suite")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* Copyright 2010-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
@@ -11,20 +11,39 @@ import kotlin.text.StringBuilder
|
||||
import kotlin.time.TimeSource
|
||||
import kotlin.time.measureTime
|
||||
|
||||
@OptIn(kotlin.time.ExperimentalTime::class)
|
||||
/**
|
||||
* Test execution settings.
|
||||
*/
|
||||
@ExperimentalNativeApi
|
||||
internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
private val filters = mutableListOf<(TestCase) -> Boolean>()
|
||||
private val listeners = mutableSetOf<TestListener>()
|
||||
private var logger: TestLogger = GTestLogger()
|
||||
private var runTests = true
|
||||
private var useExitCode = true
|
||||
var iterations = 1
|
||||
private set
|
||||
var exitCode = 0
|
||||
private set
|
||||
internal data class TestSettings(
|
||||
val testSuites: List<TestSuite>,
|
||||
val listeners: Set<TestListener>,
|
||||
val logger: TestLogger,
|
||||
val runTests: Boolean,
|
||||
val iterations: Int,
|
||||
val useExitCode: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* Test processor that initializes the [TestSettings] using the command line options passed in [args].
|
||||
*
|
||||
* See the [help] for the description of arguments
|
||||
*/
|
||||
@ExperimentalNativeApi
|
||||
internal class TestProcessor(val suites: List<TestSuite>, val args: Array<String>) {
|
||||
/**
|
||||
* Processes given arguments to set test settings and filter tests to be run
|
||||
*
|
||||
* @see TestSettings
|
||||
*/
|
||||
fun process(): TestSettings {
|
||||
val listeners = mutableSetOf<TestListener>()
|
||||
val filters = mutableListOf<TestFilter>()
|
||||
var logger: TestLogger = GTestLogger()
|
||||
var runTests = true
|
||||
var useExitCode = true
|
||||
var iterations = 1
|
||||
|
||||
init {
|
||||
args.filter {
|
||||
it.startsWith("--gtest_") || it.startsWith("--ktest_") || it == "--help" || it == "-h"
|
||||
}.forEach {
|
||||
@@ -33,11 +52,13 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
1 -> when (arg[0]) {
|
||||
"--gtest_list_tests",
|
||||
"--ktest_list_tests" -> {
|
||||
logger.logTestList(this, filterSuites()); runTests = false
|
||||
logger.logTestList(suites.filterWith(filters))
|
||||
runTests = false
|
||||
}
|
||||
"-h",
|
||||
"--help" -> {
|
||||
logger.log(help); runTests = false
|
||||
logger.log(help)
|
||||
runTests = false
|
||||
}
|
||||
"--ktest_no_exit_code" -> useExitCode = false
|
||||
else -> throw IllegalArgumentException("Unknown option: $it\n$help")
|
||||
@@ -46,70 +67,28 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
val key = arg[0]
|
||||
val value = arg[1]
|
||||
when (key) {
|
||||
"--ktest_logger" -> setLoggerFromArg(value)
|
||||
"--ktest_logger" -> logger = loggerFromArg(value)
|
||||
"--gtest_filter",
|
||||
"--ktest_filter" -> setGTestFilterFromArg(value)
|
||||
"--ktest_regex_filter" -> setRegexFilterFromArg(value, true)
|
||||
"--ktest_negative_regex_filter" -> setRegexFilterFromArg(value, false)
|
||||
"--ktest_gradle_filter" -> setGradleFilterFromArg(value, true)
|
||||
"--ktest_negative_gradle_filter" -> setGradleFilterFromArg(value, false)
|
||||
"--ktest_filter" -> filters.add(gTestFilterFromArg(value))
|
||||
"--ktest_regex_filter" -> filters.add(regexFilterFromArg(value, true))
|
||||
"--ktest_negative_regex_filter" -> filters.add(regexFilterFromArg(value, false))
|
||||
"--ktest_gradle_filter" -> filters.add(gradleFilterFromArg(value, true))
|
||||
"--ktest_negative_gradle_filter" -> filters.add(gradleFilterFromArg(value, false))
|
||||
"--ktest_repeat",
|
||||
"--gtest_repeat" -> iterations = value.toIntOrNull() ?: throw IllegalArgumentException("Cannot parse number: $value")
|
||||
"--gtest_repeat" ->
|
||||
iterations = value.toIntOrNull() ?: throw IllegalArgumentException("Cannot parse number: $value")
|
||||
else -> throw IllegalArgumentException("Unknown option: $it\n$help")
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown option: $it\n$help")
|
||||
}
|
||||
}
|
||||
val testSuites = suites.filterWith(filters).toList()
|
||||
|
||||
return TestSettings(testSuites, listeners, logger, runTests, iterations, useExitCode)
|
||||
}
|
||||
|
||||
inner class FilteredSuite(val innerSuite: TestSuite) : TestSuite by innerSuite {
|
||||
|
||||
private val TestCase.matchFilters: Boolean
|
||||
get() = filters.all { it(this) }
|
||||
|
||||
override val size: Int
|
||||
get() = testCases.size
|
||||
|
||||
override val testCases: Map<String, TestCase> = innerSuite.testCases.filter { it.value.matchFilters }
|
||||
override fun toString() = innerSuite.toString()
|
||||
}
|
||||
|
||||
private fun filterSuites(): Collection<TestSuite> = suites.map { FilteredSuite(it) }
|
||||
|
||||
// TODO: Support short aliases.
|
||||
// TODO: Support several test iterations.
|
||||
/**
|
||||
* Initialize the TestRunner using the command line options passed in [args].
|
||||
* Returns: true if tests may be ran, false otherwise (there are unrecognized options or just help).
|
||||
* The following options are available:
|
||||
*
|
||||
* --gtest_list_tests
|
||||
* --ktest_list_tests - Show all available tests.
|
||||
*
|
||||
* --gtest_filter=POSITIVE_PATTERNS[-NEGATIVE_PATTERNS]
|
||||
* --ktest_filter=POSITIVE_PATTERNS[-NEGATIVE_PATTERNS] - Run only the tests whose name matches one of the
|
||||
* positive patterns but none of the negative patterns.
|
||||
* '?' matches any single character; '*' matches any
|
||||
* substring; ':' separates two patterns.
|
||||
*
|
||||
* --ktest_regex_filter=PATTERN - Run only the tests whose name matches the pattern.
|
||||
* The pattern is a Kotlin regular expression.
|
||||
*
|
||||
* --ktest_negative_regex_filter=PATTERN - Run only the tests whose name doesn't match the pattern.
|
||||
* The pattern is a Kotlin regular expression.
|
||||
*
|
||||
* --gtest_repeat=COUNT
|
||||
* --ktest_repeat=COUNT - Run the tests repeatedly.
|
||||
* Use a negative count to repeat forever.
|
||||
*
|
||||
* --ktest_logger=GTEST|TEAMCITY|SIMPLE|SILENT - Use the specified output format. The default one is GTEST.
|
||||
*
|
||||
* --ktest_no_exit_code - Don't return a non-zero exit code if there are failing tests.
|
||||
*/
|
||||
|
||||
private fun String.substringEscaped(range: IntRange) =
|
||||
this.substring(range).let { if (it.isNotEmpty()) Regex.escape(it) else "" }
|
||||
private fun String.substringEscaped(range: IntRange) = this.substring(range).let { if (it.isNotEmpty()) Regex.escape(it) else "" }
|
||||
|
||||
private fun String.toGTestPatterns() = splitToSequence(':').map { pattern ->
|
||||
val result = StringBuilder()
|
||||
@@ -125,7 +104,9 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
return@map result.toString().toRegex()
|
||||
}.toList()
|
||||
|
||||
private fun setGTestFilterFromArg(filter: String) {
|
||||
// region filters
|
||||
|
||||
private fun gTestFilterFromArg(filter: String): TestFilter {
|
||||
if (filter.isEmpty()) {
|
||||
throw IllegalArgumentException("Empty filter")
|
||||
}
|
||||
@@ -137,23 +118,23 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
val positivePatterns = filters[0].toGTestPatterns()
|
||||
val negativePatterns = filters.getOrNull(1)?.toGTestPatterns() ?: emptyList()
|
||||
|
||||
this.filters.add { testCase ->
|
||||
return { testCase ->
|
||||
positivePatterns.any { testCase.prettyName.matches(it) } &&
|
||||
negativePatterns.none { testCase.prettyName.matches(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun setRegexFilterFromArg(filter: String, positive: Boolean = true) {
|
||||
private fun regexFilterFromArg(filter: String, positive: Boolean = true): TestFilter {
|
||||
if (filter.isEmpty()) {
|
||||
throw IllegalArgumentException("Empty filter")
|
||||
}
|
||||
val pattern = filter.toRegex()
|
||||
filters.add { testCase ->
|
||||
return { testCase ->
|
||||
testCase.prettyName.matches(pattern) == positive
|
||||
}
|
||||
}
|
||||
|
||||
private fun setGradleFilterFromArg(filter: String, positive: Boolean = true) {
|
||||
private fun gradleFilterFromArg(filter: String, positive: Boolean = true): TestFilter {
|
||||
if (filter.isEmpty()) {
|
||||
throw IllegalArgumentException("Empty filter")
|
||||
}
|
||||
@@ -165,25 +146,39 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
fun TestCase.matches(pattern: Regex) =
|
||||
prettyName.matches(pattern) || suite.name.matches(pattern)
|
||||
|
||||
if (positive) {
|
||||
filters.add { testCase ->
|
||||
return if (positive) {
|
||||
{ testCase ->
|
||||
patterns.any { testCase.matches(it) }
|
||||
}
|
||||
} else {
|
||||
filters.add { testCase ->
|
||||
{ testCase ->
|
||||
patterns.none { testCase.matches(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLoggerFromArg(logger: String) {
|
||||
when (logger.uppercase()) {
|
||||
"GTEST" -> this.logger = GTestLogger()
|
||||
"TEAMCITY" -> this.logger = TeamCityLogger()
|
||||
"SIMPLE" -> this.logger = SimpleTestLogger()
|
||||
"SILENT" -> this.logger = SilentTestLogger()
|
||||
else -> throw IllegalArgumentException("Unknown logger type. Available types: GTEST, TEAMCITY, SIMPLE")
|
||||
}
|
||||
inner class FilteredSuite(val innerSuite: TestSuite, val filters: Collection<TestFilter>) : TestSuite by innerSuite {
|
||||
private val TestCase.matchFilters: Boolean
|
||||
get() = filters.all { it(this) }
|
||||
|
||||
override val size: Int
|
||||
get() = testCases.size
|
||||
|
||||
override val testCases: Map<String, TestCase> = innerSuite.testCases.filter { it.value.matchFilters }
|
||||
override fun toString() = innerSuite.toString()
|
||||
}
|
||||
|
||||
private fun Collection<TestSuite>.filterWith(filters: Collection<TestFilter>): Collection<TestSuite> =
|
||||
map { FilteredSuite(it, filters) }
|
||||
|
||||
// endregion
|
||||
|
||||
private fun loggerFromArg(logger: String): TestLogger = when (logger.uppercase()) {
|
||||
"GTEST" -> GTestLogger()
|
||||
"TEAMCITY" -> TeamCityLogger()
|
||||
"SIMPLE" -> SimpleTestLogger()
|
||||
"SILENT" -> SilentTestLogger()
|
||||
else -> throw IllegalArgumentException("Unknown logger type. Available types: GTEST, TEAMCITY, SIMPLE")
|
||||
}
|
||||
|
||||
private val help: String
|
||||
@@ -221,10 +216,27 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
|
|
||||
|--ktest_no_exit_code - Don't return a non-zero exit code if there are failing tests.
|
||||
""".trimMargin()
|
||||
}
|
||||
|
||||
@OptIn(kotlin.experimental.ExperimentalNativeApi::class)
|
||||
private typealias TestFilter = (TestCase) -> Boolean
|
||||
|
||||
/**
|
||||
* Test runner.
|
||||
*
|
||||
* Runs tests for the given [TestSettings].
|
||||
*/
|
||||
@OptIn(kotlin.time.ExperimentalTime::class)
|
||||
@ExperimentalNativeApi
|
||||
internal class TestRunner(val settings: TestSettings) {
|
||||
constructor(suites: List<TestSuite>, args: Array<String>) : this(TestProcessor(suites, args).process())
|
||||
|
||||
var exitCode = 0
|
||||
private set
|
||||
|
||||
private inline fun sendToListeners(event: TestListener.() -> Unit) {
|
||||
logger.event()
|
||||
listeners.forEach(event)
|
||||
settings.logger.event()
|
||||
settings.listeners.forEach(event)
|
||||
}
|
||||
|
||||
private fun TestSuite.run() {
|
||||
@@ -249,7 +261,7 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
sendToListeners { pass(testCase, startTime.elapsedNow().inWholeMilliseconds) }
|
||||
} catch (e: Throwable) {
|
||||
sendToListeners { fail(testCase, e, startTime.elapsedNow().inWholeMilliseconds) }
|
||||
if (useExitCode) {
|
||||
if (settings.useExitCode) {
|
||||
exitCode = 1
|
||||
}
|
||||
}
|
||||
@@ -259,8 +271,8 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
}
|
||||
|
||||
private fun runIteration(iteration: Int) {
|
||||
val suitesFiltered = filterSuites()
|
||||
sendToListeners { startIteration(this@TestRunner, iteration, suitesFiltered) }
|
||||
val suitesFiltered = settings.testSuites
|
||||
sendToListeners { startIteration(settings, iteration, suitesFiltered) }
|
||||
val iterationTime = measureTime {
|
||||
suitesFiltered.forEach {
|
||||
if (it.ignored) {
|
||||
@@ -276,21 +288,21 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|
||||
}
|
||||
}
|
||||
}.inWholeMilliseconds
|
||||
sendToListeners { finishIteration(this@TestRunner, iteration, iterationTime) }
|
||||
sendToListeners { finishIteration(settings, iteration, iterationTime) }
|
||||
}
|
||||
|
||||
fun run(): Int {
|
||||
if (!runTests)
|
||||
if (!settings.runTests)
|
||||
return 0
|
||||
sendToListeners { startTesting(this@TestRunner) }
|
||||
sendToListeners { startTesting(settings) }
|
||||
val totalTime = measureTime {
|
||||
var i = 1
|
||||
while (i <= iterations || iterations < 0) {
|
||||
while (i <= settings.iterations || settings.iterations < 0) {
|
||||
runIteration(i)
|
||||
i++
|
||||
}
|
||||
}.inWholeMilliseconds
|
||||
sendToListeners { finishTesting(this@TestRunner, totalTime) }
|
||||
sendToListeners { finishTesting(settings, totalTime) }
|
||||
return exitCode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,26 +10,106 @@ import kotlin.IllegalArgumentException
|
||||
import kotlin.system.getTimeMillis
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
/**
|
||||
* Represents a test case, that is a Kotlin test method marked with `@Test` annotaion.
|
||||
*
|
||||
* @see kotlin.test.Test
|
||||
*/
|
||||
@ExperimentalNativeApi
|
||||
public interface TestCase {
|
||||
val name: String
|
||||
|
||||
/**
|
||||
* Shows if this test is ignored by the `@Ignore` annotation.
|
||||
*
|
||||
* @see kotlin.test.Ignore
|
||||
*/
|
||||
val ignored: Boolean
|
||||
|
||||
/**
|
||||
* Test Suite the test belongs to.
|
||||
*/
|
||||
val suite: TestSuite
|
||||
|
||||
fun run()
|
||||
/**
|
||||
* Runs all methods that were marked with `@BeforeTest` annotation.
|
||||
*
|
||||
* @see kotlin.test.BeforeTest
|
||||
*/
|
||||
fun doBefore()
|
||||
|
||||
/**
|
||||
* Runs the test method itself
|
||||
*
|
||||
* @see kotlin.test.Test
|
||||
*/
|
||||
fun doRun()
|
||||
|
||||
/**
|
||||
* Runs test with its before and after functions.
|
||||
*/
|
||||
fun run() {
|
||||
try {
|
||||
doBefore()
|
||||
doRun()
|
||||
} finally {
|
||||
doAfter()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all methods that were marked with `@AfterTest` annotation.
|
||||
*
|
||||
* @see kotlin.test.AfterTest
|
||||
*/
|
||||
fun doAfter()
|
||||
}
|
||||
|
||||
@ExperimentalNativeApi
|
||||
internal val TestCase.prettyName get() = "${suite.name}.$name"
|
||||
|
||||
/**
|
||||
* Represents Test suite, a suite that contains test cases.
|
||||
*
|
||||
* Test suite can be either test class containing test cases
|
||||
* or a top level suite for top level methods.
|
||||
*/
|
||||
@ExperimentalNativeApi
|
||||
public interface TestSuite {
|
||||
/**
|
||||
* Test suite name.
|
||||
*/
|
||||
val name: String
|
||||
|
||||
/**
|
||||
* Shows if the suite is ignored with `@Ignore` annotation.
|
||||
*
|
||||
* @see kotlin.test.Ignore
|
||||
*/
|
||||
val ignored: Boolean
|
||||
|
||||
/**
|
||||
* Test cases this test suite contains
|
||||
*/
|
||||
val testCases: Map<String, TestCase>
|
||||
|
||||
/**
|
||||
* Number of test cases
|
||||
*/
|
||||
val size : Int
|
||||
|
||||
/**
|
||||
* Executes all methods marked with `@BeforeClass` annotation
|
||||
*
|
||||
* @see kotlin.test.BeforeClass
|
||||
*/
|
||||
fun doBeforeClass()
|
||||
|
||||
/**
|
||||
* Executes all methods marked with `@AfterClass` annotation
|
||||
*
|
||||
* @see @see kotlin.test.AfterClass
|
||||
*/
|
||||
fun doAfterClass()
|
||||
}
|
||||
|
||||
@@ -85,14 +165,18 @@ public abstract class BaseClassSuite<INSTANCE, COMPANION>(name: String, ignored:
|
||||
ignored: Boolean)
|
||||
: BasicTestCase<INSTANCE.() -> Unit>(name, suite, testFunction, ignored) {
|
||||
|
||||
override fun run() {
|
||||
val instance = suite.createInstance()
|
||||
try {
|
||||
suite.before.forEach { instance.it() }
|
||||
instance.testFunction()
|
||||
} finally {
|
||||
suite.after.forEach { instance.it() }
|
||||
}
|
||||
internal val instance: INSTANCE by lazy { suite.createInstance() }
|
||||
|
||||
override fun doBefore() {
|
||||
suite.before.forEach { instance.it() }
|
||||
}
|
||||
|
||||
override fun doAfter() {
|
||||
suite.after.forEach { instance.it() }
|
||||
}
|
||||
|
||||
override fun doRun() {
|
||||
instance.testFunction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,14 +230,16 @@ public class TopLevelSuite(name: String): AbstractTestSuite<TopLevelFun>(name, f
|
||||
|
||||
class TestCase(name: String, override val suite: TopLevelSuite, testFunction: TopLevelFun, ignored: Boolean)
|
||||
: BasicTestCase<TopLevelFun>(name, suite, testFunction, ignored) {
|
||||
override fun doBefore() {
|
||||
suite.before.forEach { it() }
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
try {
|
||||
suite.before.forEach { it() }
|
||||
testFunction()
|
||||
} finally {
|
||||
suite.after.forEach { it() }
|
||||
}
|
||||
override fun doAfter() {
|
||||
suite.after.forEach { it() }
|
||||
}
|
||||
|
||||
override fun doRun() {
|
||||
testFunction()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user