[Test] Compute target backend in test generator automatically using reflection

This commit is contained in:
Dmitriy Novozhilov
2020-12-25 12:14:03 +03:00
committed by TeamCityServer
parent 9378d1ff31
commit 3c2079c926
7 changed files with 100 additions and 10 deletions
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.test.runners
import org.jetbrains.kotlin.test.TargetBackend
/**
* If your test runner has specific target backend then you can add this interface
* to your runner and after that you should not declare targetBackend
* parameter in DSL in addition to defining it in test runner
*
* Please make sure that all abstract runners which are used in test generator
* have `open` modality, not `abstract`. This is required because test generator
* instantiates runner to get value of targetBackend and generate tests properly
*/
interface RunnerWithTargetBackendForTestGeneratorMarker {
val targetBackend: TargetBackend
}
@@ -20,7 +20,7 @@ fun generateTestGroupSuiteWithJUnit5(
dryRun: Boolean = false,
init: TestGroupSuite.() -> Unit
) {
val suite = testGroupSuite(init)
val suite = TestGroupSuite(ReflectionBasedTargetBackendComputer).apply(init)
for (testGroup in suite.testGroups) {
for (testClass in testGroup.testClasses) {
val (changed, testSourceFilePath) = NewTestGeneratorImpl.generateAndSave(testClass, dryRun)
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.test.generators
import org.jetbrains.kotlin.generators.model.DefaultTargetBackendComputer
import org.jetbrains.kotlin.generators.model.TargetBackendComputer
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.runners.RunnerWithTargetBackendForTestGeneratorMarker
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.isSubclassOf
object ReflectionBasedTargetBackendComputer : TargetBackendComputer {
private val runnerMarkerKClass = RunnerWithTargetBackendForTestGeneratorMarker::class
private const val TARGET_BACKEND_PROPERTY_NAME = "targetBackend"
override fun compute(definedTargetBackend: TargetBackend?, testKClass: KClass<*>): TargetBackend {
if (!testKClass.isSubclassOf(runnerMarkerKClass)) return DefaultTargetBackendComputer.compute(definedTargetBackend, testKClass)
require(definedTargetBackend == null) {
"""
Test ${testKClass.simpleName} is inheritor of ${runnerMarkerKClass.simpleName} which means that
target you should not specify targetBackend in test generation DSL, because it will be
read from test runner class itself
""".trimIndent()
}
require(testKClass.isOpen) {
"""
Test runner which inherits from ${runnerMarkerKClass.simpleName} and used as base class
for real test should have `open` modality
""".trimIndent()
}
val instance = testKClass.createInstance() as RunnerWithTargetBackendForTestGeneratorMarker
val kProperty = runnerMarkerKClass.declaredMemberProperties.single { it.name == TARGET_BACKEND_PROPERTY_NAME }
return kProperty.get(instance) as TargetBackend
}
}
@@ -10,8 +10,8 @@ dependencies {
testImplementation(platform("org.junit:junit-bom:5.7.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation(projectTests(":compiler:tests-common"))
testImplementation(projectTests(":compiler:test-infrastructure"))
testImplementation(projectTests(":compiler:tests-common-new"))
testImplementation(projectTests(":compiler:tests-common"))
testImplementation(projectTests(":compiler"))
testImplementation(projectTests(":compiler:fir:raw-fir:psi2fir"))
testImplementation(projectTests(":compiler:fir:raw-fir:light-tree2fir"))
@@ -11,14 +11,15 @@ import org.jetbrains.kotlin.test.TargetBackend
import java.io.File
import java.util.*
import java.util.regex.Pattern
import kotlin.reflect.KClass
fun testGroupSuite(
init: TestGroupSuite.() -> Unit
): TestGroupSuite {
return TestGroupSuite().apply(init)
return TestGroupSuite(DefaultTargetBackendComputer).apply(init)
}
class TestGroupSuite {
class TestGroupSuite(val targetBackendComputer: TargetBackendComputer) {
private val _testGroups = mutableListOf<TestGroup>()
val testGroups: List<TestGroup>
get() = _testGroups
@@ -35,6 +36,7 @@ class TestGroupSuite {
testDataRoot,
testRunnerMethodName,
additionalRunnerArguments,
targetBackendComputer = targetBackendComputer
).apply(init)
}
}
@@ -45,6 +47,7 @@ class TestGroup(
val testRunnerMethodName: String,
val additionalRunnerArguments: List<String> = emptyList(),
val annotations: List<AnnotationModel> = emptyList(),
val targetBackendComputer: TargetBackendComputer
) {
private val _testClasses: MutableList<TestClass> = mutableListOf()
val testClasses: List<TestClass>
@@ -56,24 +59,28 @@ class TestGroup(
annotations: List<AnnotationModel> = emptyList(),
noinline init: TestClass.() -> Unit
) {
testClass(T::class.java.name, suiteTestClassName, useJunit4, annotations, init)
val testKClass = T::class
testClass(testKClass, testKClass.java.name, suiteTestClassName, useJunit4, annotations, init)
}
fun testClass(
testKClass: KClass<*>,
baseTestClassName: String,
suiteTestClassName: String = getDefaultSuiteTestClassName(baseTestClassName.substringAfterLast('.')),
useJunit4: Boolean,
annotations: List<AnnotationModel> = emptyList(),
init: TestClass.() -> Unit
) {
_testClasses += TestClass(baseTestClassName, suiteTestClassName, useJunit4, annotations).apply(init)
_testClasses += TestClass(testKClass, baseTestClassName, suiteTestClassName, useJunit4, annotations, targetBackendComputer).apply(init)
}
inner class TestClass(
val testKClass: KClass<*>,
val baseTestClassName: String,
val suiteTestClassName: String,
val useJunit4: Boolean,
val annotations: List<AnnotationModel>
val annotations: List<AnnotationModel>,
val targetBackendComputer: TargetBackendComputer
) {
val baseDir: String
get() = this@TestGroup.testsRoot
@@ -92,7 +99,7 @@ class TestGroup(
testClassName: String? = null, // specific name for generated test class
// which backend will be used in test. Specifying value may affect some test with
// directives TARGET_BACKEND/DONT_TARGET_EXACT_BACKEND won't be generated
targetBackend: TargetBackend = TargetBackend.ANY,
targetBackend: TargetBackend? = null,
excludeDirs: List<String> = listOf(),
filenameStartsLowerCase: Boolean? = null, // assert that file is properly named
skipIgnored: Boolean = false, // pretty meaningless flag, affects only few test names in one test runner
@@ -102,18 +109,20 @@ class TestGroup(
val compiledPattern = Pattern.compile(pattern)
val compiledExcludedPattern = excludedPattern?.let { Pattern.compile(it) }
val className = testClassName ?: TestGeneratorUtil.fileNameToJavaIdentifier(rootFile)
val realTargetBackend = targetBackendComputer.compute(targetBackend, testKClass)
testModels.add(
if (singleClass) {
if (excludeDirs.isNotEmpty()) error("excludeDirs is unsupported for SingleClassTestModel yet")
SingleClassTestModel(
rootFile, compiledPattern, compiledExcludedPattern, filenameStartsLowerCase, testMethod, className,
targetBackend, skipIgnored, testRunnerMethodName, additionalRunnerArguments, annotations
realTargetBackend, skipIgnored, testRunnerMethodName, additionalRunnerArguments, annotations
)
} else {
SimpleTestClassModel(
rootFile, recursive, excludeParentDirs,
compiledPattern, compiledExcludedPattern, filenameStartsLowerCase, testMethod, className,
targetBackend, excludeDirs, skipIgnored, testRunnerMethodName, additionalRunnerArguments, deep, annotations
realTargetBackend, excludeDirs, skipIgnored, testRunnerMethodName, additionalRunnerArguments, deep, annotations
)
}
)
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.generators.model
import org.jetbrains.kotlin.test.TargetBackend
import kotlin.reflect.KClass
fun interface TargetBackendComputer {
fun compute(definedTargetBackend: TargetBackend?, testKClass: KClass<*>): TargetBackend
}
object DefaultTargetBackendComputer : TargetBackendComputer {
override fun compute(definedTargetBackend: TargetBackend?, testKClass: KClass<*>): TargetBackend {
return definedTargetBackend ?: TargetBackend.ANY
}
}
@@ -7,6 +7,7 @@ plugins {
val depenencyProjects = arrayOf(
":generators",
":compiler",
":compiler:test-infrastructure",
":compiler:tests-common-new",
":compiler:tests-for-compiler-generator",
":js:js.tests",