unit-tests: Create a simple test runner implementation
This commit is contained in:
committed by
ilmat192
parent
77195e8e90
commit
f26e5b5f02
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.cli.common.CLICompiler
|
|||||||
import org.jetbrains.kotlin.cli.common.CLITool
|
import org.jetbrains.kotlin.cli.common.CLITool
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
@@ -133,6 +134,8 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
|
|||||||
|
|
||||||
put(ENABLE_ASSERTIONS, arguments.enableAssertions)
|
put(ENABLE_ASSERTIONS, arguments.enableAssertions)
|
||||||
|
|
||||||
|
put(GENERATE_TEST_RUNNER, arguments.generateTestRunner)
|
||||||
|
|
||||||
// We need to download dependencies only if we use them ( = there are files to compile).
|
// We need to download dependencies only if we use them ( = there are files to compile).
|
||||||
put(CHECK_DEPENDENCIES, if (configuration.kotlinSourceRoots.isNotEmpty()) {
|
put(CHECK_DEPENDENCIES, if (configuration.kotlinSourceRoots.isNotEmpty()) {
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -24,11 +24,14 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
|
|||||||
// Prepend them with a single dash.
|
// Prepend them with a single dash.
|
||||||
// Keep the list lexically sorted.
|
// Keep the list lexically sorted.
|
||||||
|
|
||||||
|
@Argument(value = "-enable_assertions", shortName = "-ea", description = "Enable runtime assertions in generated code")
|
||||||
|
var enableAssertions: Boolean = false
|
||||||
|
|
||||||
@Argument(value = "-g", description = "Enable emitting debug information")
|
@Argument(value = "-g", description = "Enable emitting debug information")
|
||||||
var debug: Boolean = false
|
var debug: Boolean = false
|
||||||
|
|
||||||
@Argument(value = "-enable_assertions", shortName = "-ea", description = "Enable runtime assertions in generated code")
|
@Argument(value = "-generate_test_runner", shortName = "-tr", description = "Produce a runner for unit tests")
|
||||||
var enableAssertions: Boolean = false
|
var generateTestRunner: Boolean = false
|
||||||
|
|
||||||
@Argument(value = "-includeBinary", shortName = "-ib", valueDescription = "<path>", description = "Pack external binary within the klib")
|
@Argument(value = "-includeBinary", shortName = "-ib", valueDescription = "<path>", description = "Pack external binary within the klib")
|
||||||
var includeBinaries: Array<String>? = null
|
var includeBinaries: Array<String>? = null
|
||||||
|
|||||||
+2
@@ -37,6 +37,8 @@ class KonanConfigKeys {
|
|||||||
= CompilerConfigurationKey.create("enable backend phases")
|
= CompilerConfigurationKey.create("enable backend phases")
|
||||||
val ENTRY: CompilerConfigurationKey<String?>
|
val ENTRY: CompilerConfigurationKey<String?>
|
||||||
= CompilerConfigurationKey.create("fully qualified main() name")
|
= CompilerConfigurationKey.create("fully qualified main() name")
|
||||||
|
val GENERATE_TEST_RUNNER: CompilerConfigurationKey<Boolean>
|
||||||
|
= CompilerConfigurationKey.create("generate test runner")
|
||||||
val INCLUDED_BINARY_FILES: CompilerConfigurationKey<List<String>>
|
val INCLUDED_BINARY_FILES: CompilerConfigurationKey<List<String>>
|
||||||
= CompilerConfigurationKey.create("included binary file paths")
|
= CompilerConfigurationKey.create("included binary file paths")
|
||||||
val LIBRARY_FILES: CompilerConfigurationKey<List<String>>
|
val LIBRARY_FILES: CompilerConfigurationKey<List<String>>
|
||||||
|
|||||||
+4
@@ -40,6 +40,10 @@ internal class KonanLower(val context: Context) {
|
|||||||
fun lowerModule(irModule: IrModuleFragment) {
|
fun lowerModule(irModule: IrModuleFragment) {
|
||||||
val phaser = PhaseManager(context)
|
val phaser = PhaseManager(context)
|
||||||
|
|
||||||
|
//phaser.phase(KonanPhase.TEST_PROCESSOR) {
|
||||||
|
// TestProcessor(context).process(irModule)
|
||||||
|
//}
|
||||||
|
|
||||||
phaser.phase(KonanPhase.LOWER_SPECIAL_CALLS) {
|
phaser.phase(KonanPhase.LOWER_SPECIAL_CALLS) {
|
||||||
irModule.files.forEach(SpecialCallsLowering(context)::lower)
|
irModule.files.forEach(SpecialCallsLowering(context)::lower)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -28,6 +28,7 @@ enum class KonanPhase(val description: String,
|
|||||||
/* */ SERIALIZER("Serialize descriptor tree and inline IR bodies"),
|
/* */ SERIALIZER("Serialize descriptor tree and inline IR bodies"),
|
||||||
/* */ BACKEND("All backend"),
|
/* */ BACKEND("All backend"),
|
||||||
/* ... */ LOWER("IR Lowering"),
|
/* ... */ LOWER("IR Lowering"),
|
||||||
|
/* ... ... */ TEST_PROCESSOR("Unit test processor"), // TODO: It is not a lowering. Move into a separate phase before lowering?
|
||||||
/* ... ... */ LOWER_SPECIAL_CALLS("Special calls processing before inlining"),
|
/* ... ... */ LOWER_SPECIAL_CALLS("Special calls processing before inlining"),
|
||||||
/* ... ... */ LOWER_INLINE_CONSTRUCTORS("Inline constructors transformation", LOWER_SPECIAL_CALLS),
|
/* ... ... */ LOWER_INLINE_CONSTRUCTORS("Inline constructors transformation", LOWER_SPECIAL_CALLS),
|
||||||
/* ... ... */ LOWER_INLINE("Functions inlining", LOWER_INLINE_CONSTRUCTORS, LOWER_SPECIAL_CALLS),
|
/* ... ... */ LOWER_INLINE("Functions inlining", LOWER_INLINE_CONSTRUCTORS, LOWER_SPECIAL_CALLS),
|
||||||
|
|||||||
+15
-1
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.backend.konan.reportCompilationError
|
|||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||||
|
import org.jetbrains.kotlin.backend.konan.report
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
@@ -36,7 +38,18 @@ internal fun findMainEntryPoint(context: Context): FunctionDescriptor? {
|
|||||||
val config = context.config.configuration
|
val config = context.config.configuration
|
||||||
if (config.get(KonanConfigKeys.PRODUCE) != CompilerOutputKind.PROGRAM) return null
|
if (config.get(KonanConfigKeys.PRODUCE) != CompilerOutputKind.PROGRAM) return null
|
||||||
|
|
||||||
val entryPoint = FqName(config.get(KonanConfigKeys.ENTRY) ?: defaultEntryName)
|
val userEntryName = config.get(KonanConfigKeys.ENTRY)
|
||||||
|
val entryPoint: FqName
|
||||||
|
if (config.getBoolean(KonanConfigKeys.GENERATE_TEST_RUNNER)) {
|
||||||
|
entryPoint = FqName(testEntryName)
|
||||||
|
if (userEntryName != null) {
|
||||||
|
config.report(CompilerMessageSeverity.WARNING,
|
||||||
|
"Custom entry point is ignored if test runner is generated"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entryPoint = FqName(userEntryName ?: defaultEntryName)
|
||||||
|
}
|
||||||
|
|
||||||
val entryName = entryPoint.shortName()
|
val entryName = entryPoint.shortName()
|
||||||
val packageName = entryPoint.parent()
|
val packageName = entryPoint.parent()
|
||||||
@@ -57,6 +70,7 @@ internal fun findMainEntryPoint(context: Context): FunctionDescriptor? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val defaultEntryName = "main"
|
private val defaultEntryName = "main"
|
||||||
|
private val testEntryName = "konan.test.main"
|
||||||
|
|
||||||
private val defaultEntryPackage = FqName.ROOT
|
private val defaultEntryPackage = FqName.ROOT
|
||||||
|
|
||||||
|
|||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.IrElementVisitorVoidWithContext
|
||||||
|
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.contributedMethods
|
||||||
|
import org.jetbrains.kotlin.backend.konan.llvm.symbolName
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
internal class TestProcessor (val context: KonanBackendContext): FileLoweringPass {
|
||||||
|
|
||||||
|
// TODO: Replace with symbols
|
||||||
|
companion object {
|
||||||
|
val fqNameTestAnnotation = FqName("kotlin.test.Test")
|
||||||
|
val fqNameBeforeAnnotation = FqName("kotlin.test.Before")
|
||||||
|
val fqNameAfterAnnotation = FqName("kotlin.test.After")
|
||||||
|
val fqNameBeforeClassAnnotation = FqName("kotlin.test.BeforeClass")
|
||||||
|
val fqNameAfterClassAnnotation = FqName("kotlin.test.AfterClass")
|
||||||
|
val fqNameIgnoreAnnotation = FqName("kotlin.test.Ignore")
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class TestClass(val clazz: IrClassSymbol) {
|
||||||
|
val testFunctions = mutableListOf<IrFunctionSymbol>()
|
||||||
|
val beforeFunctions = mutableListOf<IrFunctionSymbol>()
|
||||||
|
val afterFunctions = mutableListOf<IrFunctionSymbol>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class TopLevelTestSuite() {
|
||||||
|
val testFunctions = mutableListOf<IrFunctionSymbol>()
|
||||||
|
val beforeFunctions = mutableListOf<IrFunctionSymbol>()
|
||||||
|
val afterFunctions = mutableListOf<IrFunctionSymbol>()
|
||||||
|
val beforeClassFunctions = mutableListOf<IrFunctionSymbol>()
|
||||||
|
val afterClassFunctions = mutableListOf<IrFunctionSymbol>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class AnnotationCollector : IrElementVisitorVoid {
|
||||||
|
val testClasses = mutableMapOf<IrClassSymbol, TestClass>()
|
||||||
|
val topLevelTestSuite = TopLevelTestSuite()
|
||||||
|
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// override fun visitClass(declaration: IrClass) {
|
||||||
|
// val doNotprocess = declaration.descriptor.staticScope.contributedMethods.none { it.testFunction() }
|
||||||
|
// if (!doNotprocess) {
|
||||||
|
// testClasses.add(declaration.symbol)
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// declaration.acceptChildrenVoid(object:IrElementVisitorVoid{
|
||||||
|
// override fun visitElement(element: IrElement) {
|
||||||
|
// element.acceptChildrenVoid(this)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun visitFunction(declaration: IrFunction) {
|
||||||
|
// processFunction(declaration.descriptor)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
val descriptor = declaration.descriptor
|
||||||
|
val symbol = declaration.symbol
|
||||||
|
|
||||||
|
// TODO: Make it smarter
|
||||||
|
//if (descriptor.test()) testFunctions.add(symbol)
|
||||||
|
//if (descriptor.before()) beforeFunctions.add(symbol)
|
||||||
|
//if (descriptor.after()) afterFunctions.add(symbol)
|
||||||
|
//if (descriptor.beforeClass()) beforeClassFunctions.add(symbol)
|
||||||
|
//if (descriptor.afterClass()) afterClassFunctions.add(symbol)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
irFile.acceptChildrenVoid(AnnotationCollector())
|
||||||
|
/**
|
||||||
|
* TODO: for all test classes generate registration...
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FunctionDescriptor.isTestFunction() =
|
||||||
|
annotations.any { annotation ->
|
||||||
|
hasTestAnnotation(annotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun hasTestAnnotation(annotation: AnnotationDescriptor): Boolean {
|
||||||
|
return annotation.fqName == fqNameTestAnnotation ||
|
||||||
|
annotation.fqName == fqNameBeforeAnnotation ||
|
||||||
|
annotation.fqName == fqNameAfterAnnotation ||
|
||||||
|
annotation.fqName == fqNameBeforeClassAnnotation ||
|
||||||
|
annotation.fqName == fqNameAfterClassAnnotation
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FunctionDescriptor.isAfter() = testAnnotationFqName(fqNameAfterAnnotation)
|
||||||
|
fun FunctionDescriptor.isBefore() = testAnnotationFqName(fqNameBeforeAnnotation)
|
||||||
|
fun FunctionDescriptor.isAfterClass() = testAnnotationFqName(fqNameAfterClassAnnotation)
|
||||||
|
fun FunctionDescriptor.isBeforeClass() = testAnnotationFqName(fqNameBeforeClassAnnotation)
|
||||||
|
fun FunctionDescriptor.isTest() = testAnnotationFqName(fqNameTestAnnotation)
|
||||||
|
|
||||||
|
fun FunctionDescriptor.testAnnotationFqName(fqName:FqName) = annotations.any { it.fqName == fqNameTestAnnotation }
|
||||||
|
|
||||||
|
}
|
||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.contributedMethods
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
internal class UnitProcessor (val context: KonanBackendContext):FileLoweringPass {
|
||||||
|
val fqNameTestAnnotation = FqName("org.junit.Test")
|
||||||
|
val fqNameBeforeAnnotation = FqName("org.junit.Before")
|
||||||
|
val fqNameAfterAnnotation = FqName("org.junit.After")
|
||||||
|
val fqNameBeforeClassAnnotation = FqName("org.junit.BeforeClass")
|
||||||
|
val fqNameAfterClassAnnotation = FqName("org.junit.AfterClass")
|
||||||
|
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
val testFunction = mutableListOf<FunctionDescriptor>()
|
||||||
|
val beforeFunction = mutableListOf<FunctionDescriptor>()
|
||||||
|
val afterFunction = mutableListOf<FunctionDescriptor>()
|
||||||
|
val beforeClassFunction = mutableListOf<FunctionDescriptor>()
|
||||||
|
val afterClassFunction = mutableListOf<FunctionDescriptor>()
|
||||||
|
val testClasses = mutableListOf<ClassDescriptor>()
|
||||||
|
irFile.acceptChildrenVoid(object: IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
val classDescriptor = declaration.descriptor
|
||||||
|
val doNotprocess = declaration.descriptor.staticScope.contributedMethods.none{it.junitFunction()}
|
||||||
|
if (!doNotprocess) {
|
||||||
|
testClasses.add(declaration.descriptor)
|
||||||
|
declaration.acceptChildrenVoid(object:IrElementVisitorVoid{
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
processFunction(declaration.descriptor)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
processFunction(declaration.descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processFunction(descriptor: FunctionDescriptor) {
|
||||||
|
when {
|
||||||
|
descriptor.test() -> testFunction.add(descriptor)
|
||||||
|
descriptor.before() -> beforeFunction.add(descriptor)
|
||||||
|
descriptor.after() -> afterFunction.add(descriptor)
|
||||||
|
descriptor.beforeClass() -> beforeClassFunction.add(descriptor)
|
||||||
|
descriptor.afterClass() -> afterClassFunction.add(descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* TODO: for all test classes generate registration...
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FunctionDescriptor.junitFunction() =
|
||||||
|
annotations.any { annotation ->
|
||||||
|
hasJunitAnnotation(annotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun hasJunitAnnotation(annotation: AnnotationDescriptor): Boolean {
|
||||||
|
return annotation.fqName == fqNameTestAnnotation &&
|
||||||
|
annotation.fqName == fqNameBeforeAnnotation &&
|
||||||
|
annotation.fqName == fqNameAfterAnnotation &&
|
||||||
|
annotation.fqName == fqNameBeforeClassAnnotation &&
|
||||||
|
annotation.fqName == fqNameAfterClassAnnotation
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FunctionDescriptor.after() = testAnnotationFqName(fqNameAfterAnnotation)
|
||||||
|
fun FunctionDescriptor.before() = testAnnotationFqName(fqNameBeforeAnnotation)
|
||||||
|
fun FunctionDescriptor.afterClass() = testAnnotationFqName(fqNameAfterClassAnnotation)
|
||||||
|
fun FunctionDescriptor.beforeClass() = testAnnotationFqName(fqNameBeforeClassAnnotation)
|
||||||
|
fun FunctionDescriptor.test() = testAnnotationFqName(fqNameTestAnnotation)
|
||||||
|
|
||||||
|
fun FunctionDescriptor.testAnnotationFqName(fqName:FqName) = annotations.any { it.fqName == fqNameTestAnnotation }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1950,6 +1950,11 @@ task deserialized_members(type: LinkKonanTest) {
|
|||||||
source = "serialization/deserialize_members.kt"
|
source = "serialization/deserialize_members.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task testing_smoke(type: RunKonanTest) {
|
||||||
|
source = "testing/smoke.kt"
|
||||||
|
flags = [] // TODO: Add flag to generate test-runner
|
||||||
|
}
|
||||||
|
|
||||||
// Just check that the driver is able to produce runnable binaries.
|
// Just check that the driver is able to produce runnable binaries.
|
||||||
task driver0(type: RunDriverKonanTest) {
|
task driver0(type: RunDriverKonanTest) {
|
||||||
goldValue = "Hello, world!\n"
|
goldValue = "Hello, world!\n"
|
||||||
@@ -2121,3 +2126,4 @@ if (isMac()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import org.junit.*
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun simple() {}
|
||||||
|
|
||||||
|
class SimpleTestA{
|
||||||
|
companion object {
|
||||||
|
@BeforeClass
|
||||||
|
fun beforeClass() {
|
||||||
|
println("before class SimpleTestA")
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
fun afterClass() {
|
||||||
|
println("after class SimpleTestA")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Before
|
||||||
|
fun before() {
|
||||||
|
println("SimpleTestA::before")
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun after() {
|
||||||
|
println("SimpleTestA::after")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun a() {
|
||||||
|
println("SimpleTestA::a")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun b() {
|
||||||
|
println("SimpleTestA::b")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleTestB{
|
||||||
|
companion object {
|
||||||
|
@BeforeClass
|
||||||
|
fun beforeClass() {
|
||||||
|
println("before class SimpleTestB")
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
fun afterClass() {
|
||||||
|
println("after class SimpleTestB")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun before() {
|
||||||
|
println("SimpleTestB::before")
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun after() {
|
||||||
|
println("SimpleTestB::after")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun a() {
|
||||||
|
println("SimpleTestB::a")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun b() {
|
||||||
|
println("SimpleTestB::b")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---- This should be generated...
|
||||||
|
private val test = {
|
||||||
|
val objA = SimpleTestA()
|
||||||
|
|
||||||
|
val objB = SimpleTestB()
|
||||||
|
|
||||||
|
TestRunner.register(listOf(
|
||||||
|
TestSuite(
|
||||||
|
beforeClass = { SimpleTestA.beforeClass() },
|
||||||
|
afterClass = { SimpleTestA.afterClass() },
|
||||||
|
before = { objA.before() },
|
||||||
|
after = { objA.after() },
|
||||||
|
funcs = arrayOf({ objA.a() }, { objA.b() })),
|
||||||
|
TestSuite(
|
||||||
|
beforeClass = { SimpleTestB.beforeClass() },
|
||||||
|
afterClass = { SimpleTestB.afterClass() },
|
||||||
|
before = { objB.before() },
|
||||||
|
after = { objB.after() },
|
||||||
|
funcs = arrayOf({ objB.a() }, { objB.b() }))
|
||||||
|
|
||||||
|
))
|
||||||
|
}()
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class A {
|
||||||
|
@Test
|
||||||
|
fun test() {
|
||||||
|
println("A.test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test() {
|
||||||
|
println("test")
|
||||||
|
}
|
||||||
|
|
||||||
@@ -258,6 +258,10 @@ targetList.each { target ->
|
|||||||
rename("${target}Start.bc", 'start.bc')
|
rename("${target}Start.bc", 'start.bc')
|
||||||
into("klib/stdlib/targets/$target/native")
|
into("klib/stdlib/targets/$target/native")
|
||||||
}
|
}
|
||||||
|
from(project(':runtime').file("build/${target}Test.bc")) {
|
||||||
|
rename("${target}Test.bc", 'test.bc')
|
||||||
|
into("klib/stdlib/targets/$target/native")
|
||||||
|
}
|
||||||
if (target == 'wasm32') {
|
if (target == 'wasm32') {
|
||||||
from(project(':runtime').file('src/launcher/js')) {
|
from(project(':runtime').file('src/launcher/js')) {
|
||||||
into('klib/stdlib/targets/wasm32/included')
|
into('klib/stdlib/targets/wasm32/included')
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package konan.test
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun test() { println("test") }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTestSuites(): Collection<TestSuite> {
|
||||||
|
return listOf(
|
||||||
|
object: TestClass<A>("A") {
|
||||||
|
override fun createInstance() = A()
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerTestCase(BasicTestCase("test", A::test))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args:Array<String>) {
|
||||||
|
TestRunner(getTestSuites()).run()
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package konan.test
|
||||||
|
|
||||||
|
interface TestListener {
|
||||||
|
fun pass(testCase: TestCase)
|
||||||
|
fun fail(testCase: TestCase, e: AssertionError)
|
||||||
|
fun error(testCase: TestCase, e: Throwable)
|
||||||
|
fun ignore(testCase: TestCase)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package konan.test
|
||||||
|
|
||||||
|
class TestRunner(suites: Collection<TestSuite> = emptyList()) {
|
||||||
|
|
||||||
|
object SimpleTestListener: TestListener {
|
||||||
|
override fun pass(testCase: TestCase) = println("Pass: $testCase")
|
||||||
|
override fun fail(testCase: TestCase, e: AssertionError) = println("Fail: $testCase (${e.message})")
|
||||||
|
override fun error(testCase: TestCase, e: Throwable) = println("Error: $testCase (${e.message})")
|
||||||
|
override fun ignore(testCase: TestCase) = println("Ignore: $testCase")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _suites = mutableListOf<TestSuite>().apply { addAll(suites) }
|
||||||
|
val suites: Collection<TestSuite> get() = _suites
|
||||||
|
|
||||||
|
fun register(suite: TestSuite) = _suites.add(suite)
|
||||||
|
fun register(suites: Iterable<TestSuite>) = _suites.addAll(suites)
|
||||||
|
fun register(vararg suites: TestSuite) = _suites.addAll(suites)
|
||||||
|
|
||||||
|
fun run() {
|
||||||
|
suites.forEach {
|
||||||
|
it.run(SimpleTestListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package konan.test
|
||||||
|
|
||||||
|
interface TestCase {
|
||||||
|
val name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestSuite {
|
||||||
|
val name: String
|
||||||
|
val testCases: Map<String, TestCase>
|
||||||
|
fun run(listener: TestListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class FunctionKind {
|
||||||
|
BEFORE,
|
||||||
|
AFTER,
|
||||||
|
BEFORE_CLASS,
|
||||||
|
AFTER_CLASS
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String): TestSuite {
|
||||||
|
override fun toString(): String = name
|
||||||
|
|
||||||
|
inner class BasicTestCase(override val name: String, val testFunction: F): TestCase {
|
||||||
|
override fun toString(): String = name
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _testCases = mutableMapOf<String, BasicTestCase>()
|
||||||
|
override val testCases: Map<String, BasicTestCase>
|
||||||
|
get() = _testCases
|
||||||
|
|
||||||
|
private val specialFunctions = mutableMapOf<FunctionKind, MutableSet<F>>()
|
||||||
|
private fun Map<FunctionKind, Set<F>>.getFunctions(type: FunctionKind) =
|
||||||
|
specialFunctions.getOrPut(type) { mutableSetOf() }
|
||||||
|
|
||||||
|
val before: Collection<F> get() = specialFunctions.getFunctions(FunctionKind.BEFORE)
|
||||||
|
val after: Collection<F> get() = specialFunctions.getFunctions(FunctionKind.AFTER)
|
||||||
|
|
||||||
|
// TODO: Must be in companions. Support it.
|
||||||
|
val beforeClass: Collection<F> get() = specialFunctions.getFunctions(FunctionKind.BEFORE_CLASS)
|
||||||
|
val afterClass: Collection<F> get() = specialFunctions.getFunctions(FunctionKind.AFTER_CLASS)
|
||||||
|
|
||||||
|
protected fun registerTestCase(testCase: BasicTestCase) = _testCases.put(testCase.name, testCase)
|
||||||
|
protected fun registerTestCases(testCases: Collection<BasicTestCase>) = testCases.forEach { registerTestCase(it) }
|
||||||
|
protected fun registerTestCases(vararg testCases: BasicTestCase) = registerTestCases(testCases.toList())
|
||||||
|
|
||||||
|
protected fun registerFunction(type: FunctionKind, function: F) =
|
||||||
|
specialFunctions.getFunctions(type).add(function)
|
||||||
|
|
||||||
|
protected abstract fun doBeforeClass()
|
||||||
|
protected abstract fun doAfterClass()
|
||||||
|
|
||||||
|
protected abstract fun doTest(testCase: BasicTestCase)
|
||||||
|
|
||||||
|
override fun run(listener: TestListener) {
|
||||||
|
doBeforeClass()
|
||||||
|
testCases.values.forEach {
|
||||||
|
try {
|
||||||
|
doTest(it)
|
||||||
|
// TODO: merge catches?
|
||||||
|
} catch (e: AssertionError) {
|
||||||
|
listener.fail(it, e)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
listener.error(it, e)
|
||||||
|
}
|
||||||
|
listener.pass(it)
|
||||||
|
}
|
||||||
|
doAfterClass()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class TestClass<T>(name: String): AbstractTestSuite<T.() -> Unit>(name) {
|
||||||
|
|
||||||
|
abstract fun createInstance(): T
|
||||||
|
|
||||||
|
// TODO: What about companions?
|
||||||
|
override fun doBeforeClass() {} // = beforeClass.forEach { createInstance().it() }
|
||||||
|
override fun doAfterClass() {} // = afterClass.forEach { createInstance().it() }
|
||||||
|
|
||||||
|
override fun doTest(testCase: BasicTestCase) {
|
||||||
|
val instance = createInstance()
|
||||||
|
val testFunction = testCase.testFunction
|
||||||
|
before.forEach { instance.it() }
|
||||||
|
instance.testFunction()
|
||||||
|
after.forEach { instance.it() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TopLevelTestSuite(name: String): AbstractTestSuite<() -> Unit>(name) {
|
||||||
|
override fun doBeforeClass() = beforeClass.forEach { it() }
|
||||||
|
override fun doAfterClass() = afterClass.forEach { it() }
|
||||||
|
|
||||||
|
override fun doTest(testCase: BasicTestCase) {
|
||||||
|
before.forEach { it() }
|
||||||
|
testCase.testFunction()
|
||||||
|
after.forEach { it() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package kotlin.test
|
||||||
|
|
||||||
|
// TODO: Move test code in a separate library.
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
annotation class Test
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
annotation class BeforeClass
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
annotation class AfterClass
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
annotation class Before
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
annotation class After
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||||
|
annotation class Ignore
|
||||||
@@ -28,3 +28,4 @@ include ':backend.native:tests'
|
|||||||
include ':shared'
|
include ':shared'
|
||||||
include ':tools:kotlin-native-gradle-plugin'
|
include ':tools:kotlin-native-gradle-plugin'
|
||||||
include ':utilities'
|
include ':utilities'
|
||||||
|
include ':ext:junit'
|
||||||
Reference in New Issue
Block a user