From 3c2079c926ed04ec060738ce90db25dead4a2c11 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 25 Dec 2020 12:14:03 +0300 Subject: [PATCH] [Test] Compute target backend in test generator automatically using reflection --- ...WithTargetBackendForTestGeneratorMarker.kt | 21 ++++++++++ .../test/generators/NewTestGenerationDSL.kt | 2 +- .../ReflectionBasedTargetBackendComputer.kt | 40 +++++++++++++++++++ .../build.gradle.kts | 2 +- .../kotlin/generators/TestGenerationDSL.kt | 25 ++++++++---- .../generators/model/TargetBackendComputer.kt | 19 +++++++++ .../pill/generate-all-tests/build.gradle.kts | 1 + 7 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/runners/RunnerWithTargetBackendForTestGeneratorMarker.kt create mode 100644 compiler/tests-common-new/tests/org/jetbrains/kotlin/test/generators/ReflectionBasedTargetBackendComputer.kt create mode 100644 generators/test-generator/tests/org/jetbrains/kotlin/generators/model/TargetBackendComputer.kt diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/runners/RunnerWithTargetBackendForTestGeneratorMarker.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/runners/RunnerWithTargetBackendForTestGeneratorMarker.kt new file mode 100644 index 00000000000..3625ee56e60 --- /dev/null +++ b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/runners/RunnerWithTargetBackendForTestGeneratorMarker.kt @@ -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 +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/generators/NewTestGenerationDSL.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/generators/NewTestGenerationDSL.kt index 4a8222e28c0..15854a4a2db 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/generators/NewTestGenerationDSL.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/generators/NewTestGenerationDSL.kt @@ -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) diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/generators/ReflectionBasedTargetBackendComputer.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/generators/ReflectionBasedTargetBackendComputer.kt new file mode 100644 index 00000000000..c54e3feb240 --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/generators/ReflectionBasedTargetBackendComputer.kt @@ -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 + } +} diff --git a/compiler/tests-for-compiler-generator/build.gradle.kts b/compiler/tests-for-compiler-generator/build.gradle.kts index 7decb6567f0..2ea78ef228c 100644 --- a/compiler/tests-for-compiler-generator/build.gradle.kts +++ b/compiler/tests-for-compiler-generator/build.gradle.kts @@ -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")) diff --git a/generators/test-generator/tests/org/jetbrains/kotlin/generators/TestGenerationDSL.kt b/generators/test-generator/tests/org/jetbrains/kotlin/generators/TestGenerationDSL.kt index 263ec402de0..7c8cbb4ebc1 100644 --- a/generators/test-generator/tests/org/jetbrains/kotlin/generators/TestGenerationDSL.kt +++ b/generators/test-generator/tests/org/jetbrains/kotlin/generators/TestGenerationDSL.kt @@ -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() val testGroups: List 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 = emptyList(), val annotations: List = emptyList(), + val targetBackendComputer: TargetBackendComputer ) { private val _testClasses: MutableList = mutableListOf() val testClasses: List @@ -56,24 +59,28 @@ class TestGroup( annotations: List = 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 = 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 + val annotations: List, + 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 = 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 ) } ) diff --git a/generators/test-generator/tests/org/jetbrains/kotlin/generators/model/TargetBackendComputer.kt b/generators/test-generator/tests/org/jetbrains/kotlin/generators/model/TargetBackendComputer.kt new file mode 100644 index 00000000000..ea0c954aba7 --- /dev/null +++ b/generators/test-generator/tests/org/jetbrains/kotlin/generators/model/TargetBackendComputer.kt @@ -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 + } +} diff --git a/plugins/pill/generate-all-tests/build.gradle.kts b/plugins/pill/generate-all-tests/build.gradle.kts index 5869152cd70..554bf890b8b 100644 --- a/plugins/pill/generate-all-tests/build.gradle.kts +++ b/plugins/pill/generate-all-tests/build.gradle.kts @@ -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",