Files
kotlin-fork/runtime/src/main/kotlin/konan/test/TestSuite.kt
T
2017-09-25 11:31:22 +03:00

146 lines
5.5 KiB
Kotlin

package konan.test
import kotlin.AssertionError
interface TestCase {
val name: String
val suite: TestSuite
}
interface TestSuite {
val name: String
val testCases: Map<String, TestCase>
fun run(listener: TestListener)
}
enum class TestFunctionKind {
BEFORE,
AFTER,
BEFORE_CLASS,
AFTER_CLASS
}
abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String): TestSuite {
override fun toString(): String = name
// TODO: Make inner and remove the type param when the bug is fixed.
class BasicTestCase<F: Function<Unit>>(
override val name: String,
override val suite: AbstractTestSuite<F>,
val testFunction: F
) : TestCase {
override fun toString(): String = "$suite.$name"
}
private val _testCases = mutableMapOf<String, BasicTestCase<F>>()
override val testCases: Map<String, BasicTestCase<F>>
get() = _testCases
private fun registerTestCase(testCase: BasicTestCase<F>) = _testCases.put(testCase.name, testCase)
fun registerTestCase(name: String, testFunction: F) = registerTestCase(createTestCase(name, testFunction))
fun createTestCase(name: String, testFunction: F) = BasicTestCase(name, this, testFunction)
protected abstract fun doBeforeClass()
protected abstract fun doAfterClass()
protected abstract fun doTest(testCase: BasicTestCase<F>)
init {
TestRunner.register(this)
}
override fun run(listener: TestListener) {
doBeforeClass()
testCases.values.forEach {
try {
doTest(it)
listener.pass(it)
// TODO: merge catches?
} catch (e: AssertionError) {
listener.fail(it, e)
} catch (e: Throwable) {
listener.error(it, e)
}
}
doAfterClass()
}
}
abstract class BaseClassSuite<INSTANCE, COMPANION>(name: String): AbstractTestSuite<INSTANCE.() -> Unit>(name) {
// These two methods are overrided in a test suite class generated by the compiler.
abstract fun createInstance(): INSTANCE
open fun getCompanion(): COMPANION = throw NotImplementedError("Test class has no companion object")
companion object {
val INSTANCE_KINDS = listOf(TestFunctionKind.BEFORE, TestFunctionKind.AFTER)
val COMPANION_KINDS = listOf(TestFunctionKind.BEFORE_CLASS, TestFunctionKind.AFTER_CLASS)
}
private val instanceFunctions = mutableMapOf<TestFunctionKind, MutableSet<INSTANCE.() -> Unit>>()
private fun getInstanceFunctions(kind: TestFunctionKind): MutableCollection<INSTANCE.() -> Unit> {
check(kind in INSTANCE_KINDS)
return instanceFunctions.getOrPut(kind) { mutableSetOf() }
}
private val companionFunction = mutableMapOf<TestFunctionKind, MutableSet<COMPANION.() -> Unit>>()
private fun getCompanionFunctions(kind: TestFunctionKind): MutableCollection<COMPANION.() -> Unit> {
check(kind in COMPANION_KINDS)
return companionFunction.getOrPut(kind) { mutableSetOf() }
}
val before: Collection<INSTANCE.() -> Unit> get() = getInstanceFunctions(TestFunctionKind.BEFORE)
val after: Collection<INSTANCE.() -> Unit> get() = getInstanceFunctions(TestFunctionKind.AFTER)
val beforeClass: Collection<COMPANION.() -> Unit> get() = getCompanionFunctions(TestFunctionKind.BEFORE_CLASS)
val afterClass: Collection<COMPANION.() -> Unit> get() = getCompanionFunctions(TestFunctionKind.AFTER_CLASS)
@Suppress("UNCHECKED_CAST")
fun registerFunction(kind: TestFunctionKind, function: Function1<*, Unit>) =
when (kind) {
in INSTANCE_KINDS -> getInstanceFunctions(kind).add(function as INSTANCE.() -> Unit)
in COMPANION_KINDS -> getCompanionFunctions(kind).add(function as COMPANION.() -> Unit)
else -> throw IllegalArgumentException("Unknown function kind: $kind")
}
override fun doBeforeClass() = beforeClass.forEach { getCompanion().it() }
override fun doAfterClass() = afterClass.forEach { getCompanion().it() }
override fun doTest(testCase: BasicTestCase<INSTANCE.() -> Unit>) {
val instance = createInstance()
val testFunction = testCase.testFunction
try {
before.forEach { instance.it() }
instance.testFunction()
} finally {
after.forEach { instance.it() }
}
}
}
private typealias TopLevelFun = () -> Unit
class TopLevelSuite(name: String): AbstractTestSuite<TopLevelFun>(name) {
private val specialFunctions = mutableMapOf<TestFunctionKind, MutableSet<TopLevelFun>>()
private fun getFunctions(type: TestFunctionKind) = specialFunctions.getOrPut(type) { mutableSetOf() }
val before: Collection<TopLevelFun> get() = getFunctions(TestFunctionKind.BEFORE)
val after: Collection<TopLevelFun> get() = getFunctions(TestFunctionKind.AFTER)
val beforeClass: Collection<TopLevelFun> get() = getFunctions(TestFunctionKind.BEFORE_CLASS)
val afterClass: Collection<TopLevelFun> get() = getFunctions(TestFunctionKind.AFTER_CLASS)
fun registerFunction(kind: TestFunctionKind, function: TopLevelFun) = getFunctions(kind).add(function)
override fun doBeforeClass() = beforeClass.forEach { it() }
override fun doAfterClass() = afterClass.forEach { it() }
override fun doTest(testCase: BasicTestCase<() -> Unit>) =
try {
before.forEach { it() }
testCase.testFunction()
} finally {
after.forEach { it() }
}
}