unit-tests: Top level test support
This commit is contained in:
+34
-19
@@ -22,17 +22,25 @@ import org.jetbrains.kotlin.backend.common.ir.Symbols
|
|||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.ValueType
|
import org.jetbrains.kotlin.backend.konan.ValueType
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getMemberScope
|
import org.jetbrains.kotlin.backend.konan.descriptors.getMemberScope
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.isKFunctionType
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.TestProcessor
|
import org.jetbrains.kotlin.backend.konan.lower.TestProcessor
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
|
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
|
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
|
||||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
||||||
|
import org.jetbrains.kotlin.serialization.KonanIr
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
// This is what Context collects about IR.
|
// This is what Context collects about IR.
|
||||||
@@ -174,29 +182,36 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
|||||||
Name.identifier(className), NoLookupLocation.FROM_BACKEND
|
Name.identifier(className), NoLookupLocation.FROM_BACKEND
|
||||||
) as ClassDescriptor)
|
) as ClassDescriptor)
|
||||||
|
|
||||||
val abstractTestSuite = getKonanTestClass("AbstractTestSuite")
|
private fun getFunction(name: Name, receiverType: KotlinType, predicate: (FunctionDescriptor) -> Boolean) =
|
||||||
val baseClassSuite = getKonanTestClass("BaseClassSuite")
|
symbolTable.referenceFunction(receiverType.memberScope
|
||||||
val baseTopLevelSuite = getKonanTestClass("BaseTopLevelSuite")
|
.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).single(predicate)
|
||||||
|
)
|
||||||
|
|
||||||
|
val baseClassSuite = getKonanTestClass("BaseClassSuite")
|
||||||
|
val topLevelSuite = getKonanTestClass("TopLevelSuite")
|
||||||
val testFunctionKind = getKonanTestClass("TestFunctionKind")
|
val testFunctionKind = getKonanTestClass("TestFunctionKind")
|
||||||
|
|
||||||
// TODO: Optimize / rename
|
val baseClassSuiteConstructor = baseClassSuite.descriptor.constructors.single {
|
||||||
val registerTestCase = symbolTable.referenceFunction(abstractTestSuite.descriptor
|
it.valueParameters.size == 1 && KotlinBuiltIns.isString(it.valueParameters[0].type)
|
||||||
.unsubstitutedMemberScope
|
}
|
||||||
.getContributedFunctions(Name.identifier("registerTestCase"), NoLookupLocation.FROM_BACKEND)
|
|
||||||
.single {
|
|
||||||
it.valueParameters.size == 2 &&
|
|
||||||
KotlinBuiltIns.isString(it.valueParameters[0].type)
|
|
||||||
/* TODO: Check if it.valueParameters[1] has a function type */
|
|
||||||
})
|
|
||||||
|
|
||||||
val registerFunction = symbolTable.referenceFunction(abstractTestSuite.descriptor
|
val topLevelSuiteConstructor = symbolTable.referenceConstructor(topLevelSuite.descriptor.constructors.single {
|
||||||
.unsubstitutedMemberScope
|
it.valueParameters.size == 1 && KotlinBuiltIns.isString(it.valueParameters[0].type)
|
||||||
.getContributedFunctions(Name.identifier("registerFunction"), NoLookupLocation.FROM_BACKEND)
|
})
|
||||||
.single {
|
|
||||||
|
val topLevelSuiteRegisterFunction =
|
||||||
|
getFunction(Name.identifier("registerFunction"), topLevelSuite.descriptor.defaultType) {
|
||||||
it.valueParameters.size == 2 &&
|
it.valueParameters.size == 2 &&
|
||||||
it.valueParameters[0].type == testFunctionKind.descriptor.defaultType
|
it.valueParameters[0].type == testFunctionKind.descriptor.defaultType &&
|
||||||
/* TODO: Check if it.valueParameters[1] has a function type */
|
it.valueParameters[1].type.isFunctionType
|
||||||
})
|
}
|
||||||
|
|
||||||
|
val topLevelSuiteRegisterTestCase =
|
||||||
|
getFunction(Name.identifier("registerTestCase"), topLevelSuite.descriptor.defaultType) {
|
||||||
|
it.valueParameters.size == 2 &&
|
||||||
|
KotlinBuiltIns.isString(it.valueParameters[0].type) &&
|
||||||
|
it.valueParameters[1].type.isFunctionType
|
||||||
|
}
|
||||||
|
|
||||||
private val testFunctionKindCache = mutableMapOf<TestProcessor.FunctionKind, IrEnumEntrySymbol>()
|
private val testFunctionKindCache = mutableMapOf<TestProcessor.FunctionKind, IrEnumEntrySymbol>()
|
||||||
fun getTestFunctionKind(kind: TestProcessor.FunctionKind): IrEnumEntrySymbol = testFunctionKindCache.getOrPut(kind) {
|
fun getTestFunctionKind(kind: TestProcessor.FunctionKind): IrEnumEntrySymbol = testFunctionKindCache.getOrPut(kind) {
|
||||||
|
|||||||
+248
-173
@@ -8,7 +8,10 @@ import org.jetbrains.kotlin.backend.common.lower.SymbolWithIrBuilder
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
|
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||||
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
|
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
||||||
@@ -36,6 +39,8 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
|||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
internal class TestProcessor (val context: KonanBackendContext): FileLoweringPass {
|
internal class TestProcessor (val context: KonanBackendContext): FileLoweringPass {
|
||||||
|
|
||||||
@@ -45,10 +50,64 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
|
|||||||
val symbols = context.ir.symbols
|
val symbols = context.ir.symbols
|
||||||
|
|
||||||
val baseClassSuiteDescriptor = symbols.baseClassSuite.descriptor
|
val baseClassSuiteDescriptor = symbols.baseClassSuite.descriptor
|
||||||
val topLevelTestSuiteDescriptor = symbols.baseTopLevelSuite.descriptor
|
|
||||||
|
|
||||||
|
// region Useful extensions.
|
||||||
|
var testSuiteCnt = 0
|
||||||
|
fun Name.synthesizeSuiteClassName() = identifier.synthesizeSuiteClassName()
|
||||||
|
fun String.synthesizeSuiteClassName() = "${splitToSequence('.')
|
||||||
|
.mapIndexed { i, it -> if (i != 0) it.capitalize() }
|
||||||
|
.joinToString("")}\$test\$${testSuiteCnt++}".synthesizedName
|
||||||
|
|
||||||
|
private val IrFile.fileName get() = name.substringAfterLast(File.separatorChar)
|
||||||
|
private val IrFile.topLevelSuiteName get() = "Tests in file: $fileName"
|
||||||
|
|
||||||
|
private fun <T: IrElement> IrStatementsBuilder<T>.generateFunctionRegistration(
|
||||||
|
receiver: IrValueSymbol,
|
||||||
|
registerTestCase: IrFunctionSymbol,
|
||||||
|
registerFunction: IrFunctionSymbol,
|
||||||
|
functions: Collection<TestFunction>) {
|
||||||
|
functions.forEach {
|
||||||
|
if (it.kind == FunctionKind.TEST) {
|
||||||
|
// Call registerTestCase(name: String, testFunction: () -> Unit) method.
|
||||||
|
+irCall(registerTestCase).apply {
|
||||||
|
dispatchReceiver = irGet(receiver)
|
||||||
|
putValueArgument(0, IrConstImpl.string(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
context.builtIns.stringType,
|
||||||
|
it.function.descriptor.name.identifier)
|
||||||
|
)
|
||||||
|
putValueArgument(1, IrFunctionReferenceImpl(UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
descriptor.valueParameters[1].type,
|
||||||
|
it.function,
|
||||||
|
it.function.descriptor, emptyMap()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Call registerFunction(kind: TestFunctionKind, () -> Unit) method.
|
||||||
|
+irCall(registerFunction).apply {
|
||||||
|
dispatchReceiver = irGet(receiver)
|
||||||
|
val testKindEntry = it.kind.runtimeKind
|
||||||
|
putValueArgument(0, IrGetEnumValueImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
testKindEntry.descriptor.defaultType,
|
||||||
|
testKindEntry)
|
||||||
|
)
|
||||||
|
putValueArgument(1, IrFunctionReferenceImpl(UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
descriptor.valueParameters[1].type,
|
||||||
|
it.function,
|
||||||
|
it.function.descriptor, emptyMap()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Classes for annotation collection.
|
||||||
// TODO: Support ignore
|
// TODO: Support ignore
|
||||||
enum class FunctionKind(annotationNameString: String, runtimeKindString: String) {
|
internal enum class FunctionKind(annotationNameString: String, runtimeKindString: String) {
|
||||||
TEST("kotlin.test.Test", "") {
|
TEST("kotlin.test.Test", "") {
|
||||||
override val runtimeKindName: Name get() = throw NotImplementedError()
|
override val runtimeKindName: Name get() = throw NotImplementedError()
|
||||||
},
|
},
|
||||||
@@ -62,12 +121,12 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
|
|||||||
open val runtimeKindName = Name.identifier(runtimeKindString)
|
open val runtimeKindName = Name.identifier(runtimeKindString)
|
||||||
}
|
}
|
||||||
|
|
||||||
val FunctionKind.runtimeKind: IrEnumEntrySymbol
|
private val FunctionKind.runtimeKind: IrEnumEntrySymbol
|
||||||
get() = symbols.getTestFunctionKind(this)
|
get() = symbols.getTestFunctionKind(this)
|
||||||
|
|
||||||
private data class TestFunction(val function: IrFunctionSymbol, val kind: FunctionKind)
|
private data class TestFunction(val function: IrFunctionSymbol, val kind: FunctionKind)
|
||||||
|
|
||||||
private class TestSuite(val owner: IrClassSymbol) {
|
private class ClassSuite(val ownerClass: IrClassSymbol) {
|
||||||
val functions = mutableListOf<TestFunction>()
|
val functions = mutableListOf<TestFunction>()
|
||||||
|
|
||||||
fun registerFunction(function: IrFunctionSymbol, kinds: Collection<FunctionKind>) = kinds.forEach {
|
fun registerFunction(function: IrFunctionSymbol, kinds: Collection<FunctionKind>) = kinds.forEach {
|
||||||
@@ -76,13 +135,13 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inner class AnnotationCollector : IrElementVisitorVoid {
|
private inner class AnnotationCollector : IrElementVisitorVoid {
|
||||||
val testClasses = mutableMapOf<IrClassSymbol, TestSuite>()
|
val testClasses = mutableMapOf<IrClassSymbol, ClassSuite>()
|
||||||
val topLevelFunctions = mutableListOf<TestFunction>()
|
val topLevelFunctions = mutableListOf<TestFunction>()
|
||||||
|
|
||||||
private fun MutableMap<IrClassSymbol, TestSuite>.getTestSuite(key: IrClassSymbol) =
|
private fun MutableMap<IrClassSymbol, ClassSuite>.getTestSuite(key: IrClassSymbol) =
|
||||||
getOrPut(key) { TestSuite(key) }
|
getOrPut(key) { ClassSuite(key) }
|
||||||
|
|
||||||
private fun MutableMap<IrClassSymbol, TestSuite>.getTestSuite(key: ClassDescriptor) =
|
private fun MutableMap<IrClassSymbol, ClassSuite>.getTestSuite(key: ClassDescriptor) =
|
||||||
getTestSuite(symbols.symbolTable.referenceClass(key))
|
getTestSuite(symbols.symbolTable.referenceClass(key))
|
||||||
|
|
||||||
private fun MutableList<TestFunction>.registerFunction(
|
private fun MutableList<TestFunction>.registerFunction(
|
||||||
@@ -115,40 +174,40 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
|
|||||||
owner is ClassDescriptor && !owner.canContainTests() ->
|
owner is ClassDescriptor && !owner.canContainTests() ->
|
||||||
context.reportCompilationWarning("Class cannot contain unit-tests: ${owner.fqNameSafe}")
|
context.reportCompilationWarning("Class cannot contain unit-tests: ${owner.fqNameSafe}")
|
||||||
else ->
|
else ->
|
||||||
UnsupportedOperationException("Cannot create test function for owner: $owner")
|
UnsupportedOperationException("Cannot create test function $declaration (defined in $owner")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
private fun Name.testClassName() = Name.identifier("$this\$test")
|
//region Symbol and IR builders
|
||||||
|
|
||||||
/** Creates a [SymbolWithIrBuilder] building the BaseClassSuite<T>.createInstance() function. */
|
/** Builds the BaseClassSuite<T>.createInstance() function. */
|
||||||
private fun instanceGetterBuilder(testClass: IrClassSymbol, testSuite: IrClassSymbol) =
|
private inner class InstanceGetterBuilder(val testClass: IrClassSymbol, val testSuite: IrClassSymbol)
|
||||||
object : SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrFunction>() {
|
: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrFunction>() {
|
||||||
|
|
||||||
val getterName = Name.identifier("createInstance")
|
val getterName = Name.identifier("createInstance")
|
||||||
val superFunction = baseClassSuiteDescriptor
|
val superFunction = baseClassSuiteDescriptor
|
||||||
.unsubstitutedMemberScope
|
.unsubstitutedMemberScope
|
||||||
.getContributedFunctions(getterName, NoLookupLocation.FROM_BACKEND)
|
.getContributedFunctions(getterName, NoLookupLocation.FROM_BACKEND)
|
||||||
.single { it.valueParameters.isEmpty() }
|
.single { it.valueParameters.isEmpty() }
|
||||||
|
|
||||||
override fun buildIr() = IrFunctionImpl(
|
override fun buildIr() = IrFunctionImpl(
|
||||||
startOffset = UNDEFINED_OFFSET,
|
startOffset = UNDEFINED_OFFSET,
|
||||||
endOffset = UNDEFINED_OFFSET,
|
endOffset = UNDEFINED_OFFSET,
|
||||||
origin = TEST_SUITE_GENERATED_MEMBER,
|
origin = TEST_SUITE_GENERATED_MEMBER,
|
||||||
symbol = symbol).apply {
|
symbol = symbol).apply {
|
||||||
val builder = context.createIrBuilder(symbol)
|
val builder = context.createIrBuilder(symbol)
|
||||||
createParameterDeclarations()
|
createParameterDeclarations()
|
||||||
body = builder.irBlockBody {
|
body = builder.irBlockBody {
|
||||||
val constructor = testClass.constructors.single { it.descriptor.valueParameters.isEmpty() }
|
val constructor = testClass.constructors.single { it.descriptor.valueParameters.isEmpty() }
|
||||||
+irReturn(irCall(constructor))
|
+irReturn(irCall(constructor))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add comments for parameters.
|
override fun doInitialize() {
|
||||||
override fun doInitialize() {
|
val descriptor = symbol.descriptor as SimpleFunctionDescriptorImpl
|
||||||
val descriptor = symbol.descriptor as SimpleFunctionDescriptorImpl
|
descriptor.initialize(
|
||||||
descriptor.initialize(
|
|
||||||
/* receiverParameterType = */ null,
|
/* receiverParameterType = */ null,
|
||||||
/* dispatchReceiverParameter = */ testSuite.descriptor.thisAsReceiverParameter,
|
/* dispatchReceiverParameter = */ testSuite.descriptor.thisAsReceiverParameter,
|
||||||
/* typeParameters = */ emptyList(),
|
/* typeParameters = */ emptyList(),
|
||||||
@@ -156,182 +215,198 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
|
|||||||
/* returnType = */ testClass.descriptor.defaultType,
|
/* returnType = */ testClass.descriptor.defaultType,
|
||||||
/* modality = */ Modality.FINAL,
|
/* modality = */ Modality.FINAL,
|
||||||
/* visibility = */ Visibilities.PROTECTED
|
/* visibility = */ Visibilities.PROTECTED
|
||||||
).apply {
|
).apply {
|
||||||
overriddenDescriptors += superFunction
|
overriddenDescriptors += superFunction
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun buildSymbol() = IrSimpleFunctionSymbolImpl(
|
override fun buildSymbol() = IrSimpleFunctionSymbolImpl(
|
||||||
SimpleFunctionDescriptorImpl.create(
|
SimpleFunctionDescriptorImpl.create(
|
||||||
/* containingDeclaration = */ testSuite.descriptor,
|
/* containingDeclaration = */ testSuite.descriptor,
|
||||||
/* annotations = */ Annotations.EMPTY,
|
/* annotations = */ Annotations.EMPTY,
|
||||||
/* name = */ getterName,
|
/* name = */ getterName,
|
||||||
/* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED,
|
/* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||||
/* source = */ SourceElement.NO_SOURCE
|
/* source = */ SourceElement.NO_SOURCE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a constructor for a test suite class representing a test class (any class in the original IrFile with
|
||||||
|
* method(s) annotated with @Test). The test suite class is a subclass of ClassTestSuite<T>
|
||||||
|
* where T is the test class.
|
||||||
|
*/
|
||||||
|
private inner class ClassSuiteConstructorBuilder(val testClass: IrClassSymbol,
|
||||||
|
val testSuite: IrClassSymbol,
|
||||||
|
val functions: Collection<TestFunction>)
|
||||||
|
: SymbolWithIrBuilder<IrConstructorSymbol, IrConstructor>() {
|
||||||
|
|
||||||
|
val suiteName = testClass.descriptor.fqNameSafe.toString()
|
||||||
|
|
||||||
|
private fun IrClassSymbol.getFunction(name: String, predicate: (FunctionDescriptor) -> Boolean) =
|
||||||
|
symbols.symbolTable.referenceFunction(descriptor.unsubstitutedMemberScope
|
||||||
|
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||||
|
.single(predicate))
|
||||||
|
|
||||||
|
override fun buildIr() = IrConstructorImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
TEST_SUITE_GENERATED_MEMBER,
|
||||||
|
symbol).apply {
|
||||||
|
createParameterDeclarations()
|
||||||
|
|
||||||
|
val registerTestCase = testSuite.getFunction("registerTestCase") {
|
||||||
|
it.valueParameters.size == 2 &&
|
||||||
|
KotlinBuiltIns.isString(it.valueParameters[0].type) &&
|
||||||
|
it.valueParameters[1].type.isFunctionType
|
||||||
|
}
|
||||||
|
val registerFunction = testSuite.getFunction("registerFunction") {
|
||||||
|
it.valueParameters.size == 2 &&
|
||||||
|
it.valueParameters[0].type == symbols.testFunctionKind.descriptor.defaultType &&
|
||||||
|
it.valueParameters[1].type.isFunctionType
|
||||||
|
}
|
||||||
|
|
||||||
|
body = context.createIrBuilder(symbol).irBlockBody {
|
||||||
|
val superConstructor = symbols.baseClassSuiteConstructor
|
||||||
|
+IrDelegatingConstructorCallImpl(
|
||||||
|
startOffset = UNDEFINED_OFFSET,
|
||||||
|
endOffset = UNDEFINED_OFFSET,
|
||||||
|
symbol = symbols.symbolTable.referenceConstructor(superConstructor),
|
||||||
|
descriptor = superConstructor,
|
||||||
|
typeArguments = mapOf(
|
||||||
|
superConstructor.typeParameters[0] to testClass.descriptor.defaultType
|
||||||
|
)
|
||||||
|
).apply {
|
||||||
|
putValueArgument(0, IrConstImpl.string(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
context.builtIns.stringType,
|
||||||
|
suiteName)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
|
// TODO: What about null here?
|
||||||
|
generateFunctionRegistration(testSuite.owner.thisReceiver!!.symbol,
|
||||||
|
registerTestCase, registerFunction, functions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a [SymbolWithIrBuilder] building a constructor for a class test suite (BaseClassSuite subclass) */
|
override fun doInitialize() {
|
||||||
private fun constructorBuilder(testClass: IrClassSymbol,
|
val descriptor = symbol.descriptor as ClassConstructorDescriptorImpl
|
||||||
testSuite: IrClassSymbol,
|
descriptor.initialize(emptyList(), Visibilities.PUBLIC).apply {
|
||||||
functions: Collection<TestFunction>) =
|
returnType = testSuite.descriptor.defaultType
|
||||||
object : SymbolWithIrBuilder<IrConstructorSymbol, IrConstructor>() {
|
|
||||||
|
|
||||||
val superConstructor = baseClassSuiteDescriptor.constructors.single {
|
|
||||||
it.valueParameters.size == 1 /* && arg[0].type == String */
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun buildIr() = IrConstructorImpl(
|
override fun buildSymbol() = IrConstructorSymbolImpl(
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
TEST_SUITE_GENERATED_MEMBER,
|
|
||||||
symbol).apply {
|
|
||||||
//createParameterDeclarations() TODO: WHat???
|
|
||||||
val builder = context.createIrBuilder(symbol)
|
|
||||||
|
|
||||||
body = builder.irBlockBody {
|
|
||||||
// TODO: What about type args?
|
|
||||||
+IrDelegatingConstructorCallImpl(
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
symbols.symbolTable.referenceConstructor(superConstructor),
|
|
||||||
superConstructor,
|
|
||||||
mapOf(superConstructor.typeParameters[0] to testClass.descriptor.defaultType)).apply {
|
|
||||||
putValueArgument(0, IrConstImpl.string(
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
context.builtIns.stringType,
|
|
||||||
testClass.descriptor.fqNameSafe.toString())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// TODO: Use fake overrides for calls?
|
|
||||||
val thisReceiver = testSuite.owner.thisReceiver!!.symbol // TODO: What about null here?
|
|
||||||
functions.forEach {
|
|
||||||
if (it.kind == FunctionKind.TEST) {
|
|
||||||
+irCall(symbols.registerTestCase).apply {
|
|
||||||
dispatchReceiver = irGet(thisReceiver)
|
|
||||||
putValueArgument(0, IrConstImpl.string(
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
context.builtIns.stringType,
|
|
||||||
it.function.descriptor.name.identifier)
|
|
||||||
)
|
|
||||||
putValueArgument(1, IrFunctionReferenceImpl(UNDEFINED_OFFSET,
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
descriptor.valueParameters[1].type,
|
|
||||||
it.function,
|
|
||||||
it.function.descriptor, emptyMap()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
+irCall(symbols.registerFunction).apply {
|
|
||||||
dispatchReceiver = irGet(thisReceiver)
|
|
||||||
val testKindEntry = it.kind.runtimeKind
|
|
||||||
putValueArgument(0, IrGetEnumValueImpl(
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
testKindEntry.descriptor.defaultType,
|
|
||||||
testKindEntry)
|
|
||||||
)
|
|
||||||
putValueArgument(1, IrFunctionReferenceImpl(UNDEFINED_OFFSET,
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
descriptor.valueParameters[1].type,
|
|
||||||
it.function,
|
|
||||||
it.function.descriptor, emptyMap()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doInitialize() {
|
|
||||||
val descriptor = symbol.descriptor as ClassConstructorDescriptorImpl
|
|
||||||
descriptor.initialize(emptyList(), Visibilities.PUBLIC).apply {
|
|
||||||
returnType = testSuite.descriptor.defaultType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun buildSymbol() = IrConstructorSymbolImpl(
|
|
||||||
ClassConstructorDescriptorImpl.createSynthesized(
|
ClassConstructorDescriptorImpl.createSynthesized(
|
||||||
testSuite.descriptor,
|
testSuite.descriptor,
|
||||||
Annotations.EMPTY,
|
Annotations.EMPTY,
|
||||||
true,
|
true,
|
||||||
SourceElement.NO_SOURCE
|
SourceElement.NO_SOURCE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a test suite class representing a test class (any class in the original IrFile with method(s)
|
||||||
|
* annotated with @Test). The test suite class is a subclass of ClassTestSuite<T> where T is the test class.
|
||||||
|
*/
|
||||||
|
private inner class ClassSuiteBuilder(val testClass: IrClassSymbol,
|
||||||
|
val containingDeclaration: DeclarationDescriptor,
|
||||||
|
val functions: Collection<TestFunction>)
|
||||||
|
: SymbolWithIrBuilder<IrClassSymbol, IrClass>() {
|
||||||
|
|
||||||
|
val suiteClassName = testClass.descriptor.name.synthesizeSuiteClassName()
|
||||||
|
val superType = baseClassSuiteDescriptor.defaultType.replace(listOf(testClass.descriptor.defaultType))
|
||||||
|
|
||||||
|
val constructorBuilder = ClassSuiteConstructorBuilder(testClass, symbol, functions)
|
||||||
|
val instanceGetterBuilder = InstanceGetterBuilder(testClass, symbol)
|
||||||
|
|
||||||
|
|
||||||
|
override fun buildIr() = IrClassImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
TEST_SUITE_CLASS,
|
||||||
|
symbol).apply {
|
||||||
|
createParameterDeclarations()
|
||||||
|
addMember(constructorBuilder.ir)
|
||||||
|
addMember(instanceGetterBuilder.ir)
|
||||||
|
addFakeOverrides()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a [SymbolWithIrBuilder] building a class test suite (BaseClassSuite subclass) */
|
override fun doInitialize() {
|
||||||
private fun classSuiteBuilder(testClass: IrClassSymbol, irFile: IrFile, functions: Collection<TestFunction>) =
|
val descriptor = symbol.descriptor as ClassDescriptorImpl
|
||||||
object: SymbolWithIrBuilder<IrClassSymbol, IrClass>() {
|
val constructorDescriptor = constructorBuilder.symbol.descriptor
|
||||||
|
|
||||||
val constructorBuilder = constructorBuilder(testClass, symbol, functions)
|
val contributedDescriptors = baseClassSuiteDescriptor
|
||||||
val instanceGetterBuilder = instanceGetterBuilder(testClass, symbol)
|
.unsubstitutedMemberScope
|
||||||
|
.getContributedDescriptors()
|
||||||
|
.map {
|
||||||
|
if (it == instanceGetterBuilder.superFunction) {
|
||||||
|
instanceGetterBuilder.symbol.descriptor
|
||||||
|
} else {
|
||||||
|
it.createFakeOverrideDescriptor(symbol.descriptor as ClassDescriptorImpl)
|
||||||
|
}
|
||||||
|
}.filterNotNull()
|
||||||
|
|
||||||
override fun buildIr() = IrClassImpl(
|
descriptor.initialize(
|
||||||
UNDEFINED_OFFSET,
|
SimpleMemberScope(contributedDescriptors),setOf(constructorDescriptor), constructorDescriptor
|
||||||
UNDEFINED_OFFSET,
|
)
|
||||||
TEST_SUITE_CLASS,
|
|
||||||
symbol).apply {
|
|
||||||
createParameterDeclarations()
|
|
||||||
addMember(constructorBuilder.ir)
|
|
||||||
addMember(instanceGetterBuilder.ir)
|
|
||||||
addFakeOverrides()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doInitialize() {
|
constructorBuilder.initialize()
|
||||||
val descriptor = symbol.descriptor as ClassDescriptorImpl
|
instanceGetterBuilder.initialize()
|
||||||
val constructorDescriptor = constructorBuilder.symbol.descriptor
|
}
|
||||||
|
|
||||||
val contributedDescriptors = baseClassSuiteDescriptor.unsubstitutedMemberScope
|
override fun buildSymbol(): org.jetbrains.kotlin.ir.symbols.IrClassSymbol = IrClassSymbolImpl(
|
||||||
.getContributedDescriptors()
|
ClassDescriptorImpl(
|
||||||
.map {
|
/* containingDeclaration = */ containingDeclaration,
|
||||||
if (it == instanceGetterBuilder.superFunction) {
|
/* name = */ suiteClassName,
|
||||||
instanceGetterBuilder.symbol.descriptor
|
/* modality = */ Modality.FINAL,
|
||||||
} else {
|
/* kind = */ ClassKind.CLASS,
|
||||||
it.createFakeOverrideDescriptor(descriptor)
|
/* superTypes = */ listOf(superType),
|
||||||
}
|
/* source = */ SourceElement.NO_SOURCE,
|
||||||
}
|
/* isExternal = */ false
|
||||||
.filterNotNull()
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
descriptor.initialize(SimpleMemberScope(contributedDescriptors), setOf(constructorDescriptor), constructorDescriptor)
|
// region IR generation methods
|
||||||
|
private fun generateClassSuite(irFile: IrFile, testClass: IrClassSymbol, functions: Collection<TestFunction>) =
|
||||||
constructorBuilder.initialize()
|
with(ClassSuiteBuilder(testClass, irFile.packageFragmentDescriptor, functions)) {
|
||||||
instanceGetterBuilder.initialize()
|
initialize()
|
||||||
}
|
irFile.declarations.add(ir)
|
||||||
|
irFile.addTopLevelInitializer(
|
||||||
override fun buildSymbol(): org.jetbrains.kotlin.ir.symbols.IrClassSymbol = IrClassSymbolImpl(
|
IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, ir.symbol.constructors.single())
|
||||||
ClassDescriptorImpl(
|
|
||||||
/* containingDeclaration = */ irFile.packageFragmentDescriptor,
|
|
||||||
/* name = */ testClass.descriptor.name.testClassName(),
|
|
||||||
/* modality = */ Modality.FINAL,
|
|
||||||
/* kind = */ ClassKind.CLASS,
|
|
||||||
/* superTypes = */ listOf(baseClassSuiteDescriptor.defaultType.replace(listOf(testClass.descriptor.defaultType))),
|
|
||||||
/* source = */ SourceElement.NO_SOURCE,
|
|
||||||
/* isExternal = */ false
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateTestSuite(testClass: IrClassSymbol, irFile: IrFile, functions: Collection<TestFunction>) {
|
private fun generateTopLevelSuite(irFile: IrFile, functions: Collection<TestFunction>) {
|
||||||
// Generate class
|
val builder = context.createIrBuilder(irFile.symbol)
|
||||||
val builder = classSuiteBuilder(testClass, irFile, functions)
|
irFile.addTopLevelInitializer(builder.irBlock {
|
||||||
builder.initialize()
|
val constructorCall = irCall(symbols.topLevelSuiteConstructor).apply {
|
||||||
val irTestSuite = builder.ir
|
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||||
irFile.addTopLevelInitializer(context.createIrBuilder(irFile.symbol).irBlock {
|
context.builtIns.stringType, irFile.topLevelSuiteName))
|
||||||
+irTestSuite
|
}
|
||||||
+IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irTestSuite.symbol.constructors.single())
|
val testSuiteVal = irTemporary(constructorCall, "topLevelTestSuite")
|
||||||
|
generateFunctionRegistration(testSuiteVal.symbol,
|
||||||
|
symbols.topLevelSuiteRegisterTestCase,
|
||||||
|
symbols.topLevelSuiteRegisterFunction,
|
||||||
|
functions)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Support tests in objects
|
||||||
|
// TODO: Support beforeTest/afterTest for companions.
|
||||||
private fun createTestSuites(irFile: IrFile, annotationCollector: AnnotationCollector) {
|
private fun createTestSuites(irFile: IrFile, annotationCollector: AnnotationCollector) {
|
||||||
annotationCollector.testClasses.forEach { _, testClass ->
|
annotationCollector.testClasses.forEach { _, testClass ->
|
||||||
generateTestSuite(testClass.owner, irFile, testClass.functions)
|
generateClassSuite(irFile, testClass.ownerClass, testClass.functions)
|
||||||
|
}
|
||||||
|
if (annotationCollector.topLevelFunctions.isNotEmpty()) {
|
||||||
|
generateTopLevelSuite(irFile, annotationCollector.topLevelFunctions)
|
||||||
}
|
}
|
||||||
// TODO: Use another name for a file?
|
|
||||||
// generateTestSuite(irFile.name, irFile, annotationCollector.topLevelFunctions)
|
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
val annotationCollector = AnnotationCollector()
|
val annotationCollector = AnnotationCollector()
|
||||||
|
|||||||
@@ -8,17 +8,8 @@ class A {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//abstract class AA<T> {
|
@Test
|
||||||
// abstract fun a(): T
|
fun test() {
|
||||||
//}
|
println("test")
|
||||||
//
|
}
|
||||||
//
|
|
||||||
//class BB: AA<Int>() {
|
|
||||||
// override fun a() = 5
|
|
||||||
//}
|
|
||||||
|
|
||||||
//@Test
|
|
||||||
//fun test() {
|
|
||||||
// println("test")
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ package konan.test
|
|||||||
import kotlin.AssertionError
|
import kotlin.AssertionError
|
||||||
|
|
||||||
interface TestListener {
|
interface TestListener {
|
||||||
|
fun startTesting(runner: TestRunner)
|
||||||
|
fun endTesting(runner: TestRunner)
|
||||||
|
|
||||||
|
fun startSuite(suite: TestSuite)
|
||||||
|
fun endSuite(suite: TestSuite)
|
||||||
|
|
||||||
fun pass(testCase: TestCase)
|
fun pass(testCase: TestCase)
|
||||||
fun fail(testCase: TestCase, e: AssertionError)
|
fun fail(testCase: TestCase, e: AssertionError)
|
||||||
fun error(testCase: TestCase, e: Throwable)
|
fun error(testCase: TestCase, e: Throwable)
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ import kotlin.AssertionError
|
|||||||
object TestRunner {
|
object TestRunner {
|
||||||
|
|
||||||
object SimpleTestListener: TestListener {
|
object SimpleTestListener: TestListener {
|
||||||
override fun pass(testCase: TestCase) = println("Pass: $testCase")
|
override fun startTesting(runner: TestRunner) = println("Starting testing\n")
|
||||||
override fun fail(testCase: TestCase, e: AssertionError) = println("Fail: $testCase (${e.message})")
|
override fun endTesting(runner: TestRunner) = println("Testing finished\n")
|
||||||
override fun error(testCase: TestCase, e: Throwable) = println("Error: $testCase (${e.message})")
|
|
||||||
override fun ignore(testCase: TestCase) = println("Ignore: $testCase")
|
|
||||||
|
|
||||||
|
override fun startSuite(suite: TestSuite) = println("Starting test suite: $suite\n")
|
||||||
|
override fun endSuite(suite: TestSuite) = println("Test suite finished: $suite\n")
|
||||||
|
|
||||||
|
override fun pass(testCase: TestCase) = println("Pass: $testCase\n")
|
||||||
|
override fun fail(testCase: TestCase, e: AssertionError) = println("Fail: $testCase (Exception: ${e.message})\n")
|
||||||
|
override fun error(testCase: TestCase, e: Throwable) = println("Error: $testCase (Exception: ${e.message})\n")
|
||||||
|
override fun ignore(testCase: TestCase) = println("Ignore: $testCase\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _suites = mutableListOf<TestSuite>()
|
private val _suites = mutableListOf<TestSuite>()
|
||||||
@@ -20,9 +25,13 @@ object TestRunner {
|
|||||||
fun register(vararg suites: TestSuite) = _suites.addAll(suites)
|
fun register(vararg suites: TestSuite) = _suites.addAll(suites)
|
||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
|
SimpleTestListener.startTesting(this)
|
||||||
suites.forEach {
|
suites.forEach {
|
||||||
|
SimpleTestListener.startSuite(it)
|
||||||
it.run(SimpleTestListener)
|
it.run(SimpleTestListener)
|
||||||
|
SimpleTestListener.endSuite(it)
|
||||||
}
|
}
|
||||||
|
SimpleTestListener.endTesting(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import kotlin.AssertionError
|
|||||||
|
|
||||||
interface TestCase {
|
interface TestCase {
|
||||||
val name: String
|
val name: String
|
||||||
|
val suite: TestSuite
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TestSuite {
|
interface TestSuite {
|
||||||
@@ -23,8 +24,12 @@ abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String):
|
|||||||
override fun toString(): String = name
|
override fun toString(): String = name
|
||||||
|
|
||||||
// TODO: Make inner and remove the type param when the bug is fixed.
|
// TODO: Make inner and remove the type param when the bug is fixed.
|
||||||
class BasicTestCase<F: Function<Unit>>(override val name: String, val testFunction: F): TestCase {
|
class BasicTestCase<F: Function<Unit>>(
|
||||||
override fun toString(): String = name
|
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>>()
|
private val _testCases = mutableMapOf<String, BasicTestCase<F>>()
|
||||||
@@ -42,13 +47,11 @@ abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String):
|
|||||||
val beforeClass: Collection<F> get() = specialFunctions.getFunctions(TestFunctionKind.BEFORE_CLASS)
|
val beforeClass: Collection<F> get() = specialFunctions.getFunctions(TestFunctionKind.BEFORE_CLASS)
|
||||||
val afterClass: Collection<F> get() = specialFunctions.getFunctions(TestFunctionKind.AFTER_CLASS)
|
val afterClass: Collection<F> get() = specialFunctions.getFunctions(TestFunctionKind.AFTER_CLASS)
|
||||||
|
|
||||||
fun registerTestCase(name: String, testFunction: F) = registerTestCase(BasicTestCase(name, testFunction))
|
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 fun registerTestCase(testCase: BasicTestCase<F>) = _testCases.put(testCase.name, testCase)
|
fun registerFunction(type: TestFunctionKind, function: F) =
|
||||||
protected fun registerTestCases(testCases: Collection<BasicTestCase<F>>) = testCases.forEach { registerTestCase(it) }
|
|
||||||
protected fun registerTestCases(vararg testCases: BasicTestCase<F>) = registerTestCases(testCases.toList())
|
|
||||||
|
|
||||||
protected fun registerFunction(type: TestFunctionKind, function: F) =
|
|
||||||
specialFunctions.getFunctions(type).add(function)
|
specialFunctions.getFunctions(type).add(function)
|
||||||
|
|
||||||
protected abstract fun doBeforeClass()
|
protected abstract fun doBeforeClass()
|
||||||
@@ -83,26 +86,31 @@ abstract class BaseClassSuite<T>(name: String): AbstractTestSuite<T.() -> Unit>(
|
|||||||
abstract fun createInstance(): T
|
abstract fun createInstance(): T
|
||||||
|
|
||||||
// TODO: What about companions?
|
// TODO: What about companions?
|
||||||
override fun doBeforeClass() {} // = beforeClass.forEach { createInstance().it() }
|
override fun doBeforeClass() {} // = beforeClass.forEach { createInstance().it() }
|
||||||
override fun doAfterClass() {} // = afterClass.forEach { createInstance().it() }
|
override fun doAfterClass() {} // = afterClass.forEach { createInstance().it() }
|
||||||
|
|
||||||
override fun doTest(testCase: BasicTestCase<T.() -> Unit>) {
|
override fun doTest(testCase: BasicTestCase<T.() -> Unit>) {
|
||||||
val instance = createInstance()
|
val instance = createInstance()
|
||||||
val testFunction = testCase.testFunction
|
val testFunction = testCase.testFunction
|
||||||
before.forEach { instance.it() }
|
try {
|
||||||
instance.testFunction()
|
before.forEach { instance.it() }
|
||||||
after.forEach { instance.it() }
|
instance.testFunction()
|
||||||
|
} finally {
|
||||||
|
after.forEach { instance.it() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BaseTopLevelSuite(name: String): AbstractTestSuite<() -> Unit>(name) {
|
class TopLevelSuite(name: String): AbstractTestSuite<() -> Unit>(name) {
|
||||||
|
|
||||||
override fun doBeforeClass() = beforeClass.forEach { it() }
|
override fun doBeforeClass() = beforeClass.forEach { it() }
|
||||||
override fun doAfterClass() = afterClass.forEach { it() }
|
override fun doAfterClass() = afterClass.forEach { it() }
|
||||||
|
|
||||||
override fun doTest(testCase: BasicTestCase<() -> Unit>) {
|
override fun doTest(testCase: BasicTestCase<() -> Unit>) =
|
||||||
before.forEach { it() }
|
try {
|
||||||
testCase.testFunction()
|
before.forEach { it() }
|
||||||
after.forEach { it() }
|
testCase.testFunction()
|
||||||
}
|
} finally {
|
||||||
|
after.forEach { it() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user