[TEST-GEN] Create abstract TestGenerator and move current generator logic to TestGeneratorImpl

This commit is contained in:
Dmitriy Novozhilov
2020-11-09 11:30:37 +03:00
parent d45fb4dfd8
commit c51ea6b142
4 changed files with 114 additions and 75 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.generators.tests.generator
import junit.framework.TestCase
import org.jetbrains.kotlin.generators.tests.generator.InconsistencyChecker.Companion.hasDryRunArg
import org.jetbrains.kotlin.generators.tests.generator.InconsistencyChecker.Companion.inconsistencyChecker
import org.jetbrains.kotlin.generators.tests.generator.generators.TestGenerator
import org.jetbrains.kotlin.generators.tests.generator.generators.impl.*
import org.jetbrains.kotlin.test.TargetBackend
import java.io.File
@@ -38,23 +39,16 @@ class TestGroup(
annotations: List<AnnotationModel> = emptyList(),
init: TestClass.() -> Unit
) {
val testGenerator = TestGenerator(
val generationData = TestGenerator.GenerationData(
testsRoot,
suiteTestClassName,
baseTestClassName,
TestClass(annotations).apply(init).testModels,
useJunit4,
methodGenerators = listOf(
CoroutinesTestMethodGenerator,
RunTestMethodGenerator,
RunTestMethodWithPackageReplacementGenerator,
SimpleTestClassModelTestAllFilesPresentMethodGenerator,
SimpleTestMethodGenerator,
SingleClassTestModelAllFilesPresentedMethodGenerator
)
)
if (testGenerator.generateAndSave(dryRun)) {
inconsistencyChecker(dryRun).add(testGenerator.testSourceFilePath)
val (changed, testSourceFilePath) = TestGeneratorImpl.generateAndSave(generationData, dryRun)
if (changed) {
inconsistencyChecker(dryRun).add(testSourceFilePath)
}
}
@@ -69,7 +63,7 @@ class TestGroup(
pattern: String = if (extension == null) """^([^\.]+)$""" else "^(.+)\\.$extension\$",
excludedPattern: String? = null,
testMethod: String = "doTest",
singleClass: Boolean = false, // if true then tests from subdirectories will be flattern to single class
singleClass: Boolean = false, // if true then tests from subdirectories will be flatten to single class
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
@@ -17,6 +17,6 @@ abstract class MethodGenerator<in T : MethodModel> {
abstract val kind: MethodModel.Kind
abstract fun generateSignature(method: @UnsafeVariance T, p: Printer)
abstract fun generateBody(method: @UnsafeVariance T, p: Printer)
abstract fun generateSignature(method: T, p: Printer)
abstract fun generateBody(method: T, p: Printer)
}
@@ -0,0 +1,29 @@
/*
* 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.tests.generator.generators
import org.jetbrains.kotlin.generators.tests.generator.MethodModel
import org.jetbrains.kotlin.generators.tests.generator.TestClassModel
abstract class TestGenerator(
methodGenerators: List<MethodGenerator<*>>
) {
protected val methodGenerators: Map<MethodModel.Kind, MethodGenerator<*>> =
methodGenerators.associateBy { it.kind }.withDefault { error("Generator for method with kind $it not found") }
abstract fun generateAndSave(data: GenerationData, dryRun: Boolean): GenerationResult
data class GenerationData(
val baseDir: String,
val suiteTestClassFqName: String,
val baseTestClassFqName: String,
val testClassModels: Collection<TestClassModel>,
val useJunit4: Boolean
)
data class GenerationResult(val newFileGenerated: Boolean, val testSourceFilePath: String)
}
@@ -1,11 +1,13 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.tests.generator
package org.jetbrains.kotlin.generators.tests.generator.generators.impl
import org.jetbrains.kotlin.generators.tests.generator.*
import org.jetbrains.kotlin.generators.tests.generator.generators.MethodGenerator
import org.jetbrains.kotlin.generators.tests.generator.generators.TestGenerator
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners
import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -18,45 +20,81 @@ import java.io.File
import java.io.IOException
import java.util.*
class TestGenerator(
private val METHOD_GENERATORS = listOf(
RunTestMethodGenerator,
SimpleTestClassModelTestAllFilesPresentMethodGenerator,
SimpleTestMethodGenerator,
SingleClassTestModelAllFilesPresentedMethodGenerator
)
object TestGeneratorImpl : TestGenerator(METHOD_GENERATORS) {
override fun generateAndSave(data: GenerationData, dryRun: Boolean): GenerationResult {
val (baseDir, suiteTestClassFqName, baseTestClassFqName, testClassModels, useJunit4) = data
val generatorInstance = TestGeneratorImplInstance(
baseDir,
suiteTestClassFqName,
baseTestClassFqName,
testClassModels,
useJunit4,
methodGenerators
)
return generatorInstance.generateAndSave(dryRun)
}
}
private class TestGeneratorImplInstance(
baseDir: String,
suiteTestClassFqName: String,
baseTestClassFqName: String,
testClassModels: Collection<TestClassModel>,
useJunit4: Boolean,
methodGenerators: List<MethodGenerator<*>>
private val testClassModels: Collection<TestClassModel>,
private val useJunit4: Boolean,
private val methodGenerators: Map<MethodModel.Kind, MethodGenerator<*>>
) {
private val methodGenerators: Map<MethodModel.Kind, MethodGenerator<*>> =
methodGenerators.associateBy { it.kind }.withDefault { error("Generator for method with kind $it not found") }
companion object {
private val GENERATED_FILES = HashSet<String>()
private val RUNNER = JUnit3RunnerWithInners::class.java
private val JUNIT4_RUNNER = BlockJUnit4ClassRunner::class.java
private val baseTestClassPackage: String
private val suiteClassPackage: String
private val suiteClassName: String
private val baseTestClassName: String
private val testClassModels: Collection<TestClassModel>
private val useJunit4: Boolean
internal val testSourceFilePath: String
private fun generateMetadata(p: Printer, testDataSource: TestEntityModel) {
val dataString = testDataSource.dataString
if (dataString != null) {
p.println("@TestMetadata(\"", dataString, "\")")
}
}
init {
this.baseTestClassPackage = baseTestClassFqName.substringBeforeLast('.', "")
this.baseTestClassName = baseTestClassFqName.substringAfterLast('.', baseTestClassFqName)
this.suiteClassPackage = suiteTestClassFqName.substringBeforeLast('.', baseTestClassPackage)
this.suiteClassName = suiteTestClassFqName.substringAfterLast('.', suiteTestClassFqName)
this.testClassModels = ArrayList(testClassModels)
this.useJunit4 = useJunit4
private fun generateTestDataPath(p: Printer, testClassModel: TestClassModel) {
val dataPathRoot = testClassModel.dataPathRoot
if (dataPathRoot != null) {
p.println("@TestDataPath(\"", dataPathRoot, "\")")
}
}
this.testSourceFilePath = baseDir + "/" + this.suiteClassPackage.replace(".", "/") + "/" + this.suiteClassName + ".java"
private fun generateParameterAnnotations(p: Printer, testClassModel: TestClassModel) {
for (annotationModel in testClassModel.annotations) {
annotationModel.generate(p)
p.println()
}
}
if (!GENERATED_FILES.add(testSourceFilePath)) {
throw IllegalArgumentException("Same test file already generated in current session: " + testSourceFilePath)
private fun generateSuppressAllWarnings(p: Printer) {
p.println("@SuppressWarnings(\"all\")")
}
}
private val baseTestClassPackage: String = baseTestClassFqName.substringBeforeLast('.', "")
private val baseTestClassName: String = baseTestClassFqName.substringAfterLast('.', baseTestClassFqName)
private val suiteClassPackage: String = suiteTestClassFqName.substringBeforeLast('.', baseTestClassPackage)
private val suiteClassName: String = suiteTestClassFqName.substringAfterLast('.', suiteTestClassFqName)
private val testSourceFilePath: String = baseDir + "/" + this.suiteClassPackage.replace(".", "/") + "/" + this.suiteClassName + ".java"
init {
if (!GENERATED_FILES.add(testSourceFilePath)) {
throw IllegalArgumentException("Same test file already generated in current session: $testSourceFilePath")
}
}
/**
* @return true if a new file is generated
*/
@Throws(IOException::class)
fun generateAndSave(dryRun: Boolean): Boolean {
fun generateAndSave(dryRun: Boolean): TestGenerator.GenerationResult {
val generatedCode = generate()
val testSourceFile = File(testSourceFilePath)
@@ -65,7 +103,7 @@ class TestGenerator(
if (!dryRun) {
GeneratorsFileUtil.writeFileIfContentChanged(testSourceFile, generatedCode, false)
}
return changed
return TestGenerator.GenerationResult(changed, testSourceFilePath)
}
private fun generate(): String {
@@ -201,47 +239,25 @@ class TestGenerator(
val generator = methodGenerators.getValue(methodModel.kind)
generateMetadata(p, methodModel)
generator.generateSignature(methodModel, p)
generator.hackyGenerateSignature(methodModel, p)
p.printWithNoIndent(" {")
p.println()
p.pushIndent()
generator.generateBody(methodModel, p)
generator.hackyGenerateBody(methodModel, p)
p.popIndent()
p.println("}")
}
companion object {
private val GENERATED_FILES = HashSet<String>()
private val RUNNER = JUnit3RunnerWithInners::class.java
private val JUNIT4_RUNNER = BlockJUnit4ClassRunner::class.java
private fun <T : MethodModel> MethodGenerator<T>.hackyGenerateBody(method: MethodModel, p: Printer) {
@Suppress("UNCHECKED_CAST")
generateBody(method as T, p)
}
private fun generateMetadata(p: Printer, testDataSource: TestEntityModel) {
val dataString = testDataSource.dataString
if (dataString != null) {
p.println("@TestMetadata(\"", dataString, "\")")
}
}
private fun generateTestDataPath(p: Printer, testClassModel: TestClassModel) {
val dataPathRoot = testClassModel.dataPathRoot
if (dataPathRoot != null) {
p.println("@TestDataPath(\"", dataPathRoot, "\")")
}
}
private fun generateParameterAnnotations(p: Printer, testClassModel: TestClassModel) {
for (annotationModel in testClassModel.annotations) {
annotationModel.generate(p);
p.println()
}
}
private fun generateSuppressAllWarnings(p: Printer) {
p.println("@SuppressWarnings(\"all\")")
}
private fun <T : MethodModel> MethodGenerator<T>.hackyGenerateSignature(method: MethodModel, p: Printer) {
@Suppress("UNCHECKED_CAST")
generateSignature(method as T, p)
}
}