[TEST-GEN] Extract logic of generating test methods into separate abstraction MethodGenerator

This commit is contained in:
Dmitriy Novozhilov
2020-11-09 11:08:57 +03:00
parent 2acbe96f15
commit d45fb4dfd8
13 changed files with 318 additions and 128 deletions
@@ -6,42 +6,30 @@
package org.jetbrains.kotlin.generators.tests.generator
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.utils.Printer
class RunTestMethodModel(
private val targetBackend: TargetBackend,
private val testMethodName: String,
private val testRunnerMethodName: String,
private val additionalRunnerArguments: List<String> = emptyList()
val targetBackend: TargetBackend,
val testMethodName: String,
val testRunnerMethodName: String,
val additionalRunnerArguments: List<String> = emptyList()
) : MethodModel {
object Kind : MethodModel.Kind()
override val kind: MethodModel.Kind
get() = Kind
override val name = METHOD_NAME
override val dataString: String? = null
override fun generateSignature(p: Printer) {
p.print("private void $name(String testDataFilePath) throws Exception")
}
override fun generateBody(p: Printer) {
if (!isWithTargetBackend()) {
p.println("KotlinTestUtils.$testRunnerMethodName(this::$testMethodName, this, testDataFilePath);")
} else {
val className = TargetBackend::class.java.simpleName
val additionalArguments = if (additionalRunnerArguments.isNotEmpty())
additionalRunnerArguments.joinToString(separator = ", ", prefix = ", ")
else ""
p.println("KotlinTestUtils.$testRunnerMethodName(this::$testMethodName, $className.$targetBackend, testDataFilePath$additionalArguments);")
}
}
override fun imports(): Collection<Class<*>> {
return super.imports() + if (isWithTargetBackend()) setOf(TargetBackend::class.java) else emptySet()
}
private fun isWithTargetBackend(): Boolean {
fun isWithTargetBackend(): Boolean {
return !(targetBackend == TargetBackend.ANY && additionalRunnerArguments.isEmpty() && testRunnerMethodName == METHOD_NAME)
}
companion object {
const val METHOD_NAME = "runTest"
}
}
}
@@ -5,25 +5,23 @@
package org.jetbrains.kotlin.generators.tests.generator
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.generators.tests.generator.TestGeneratorUtil.fileNameToJavaIdentifier
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.utils.Printer
import java.io.File
import java.util.*
import java.util.regex.Pattern
class SimpleTestClassModel(
private val rootFile: File,
private val recursive: Boolean,
val rootFile: File,
val recursive: Boolean,
private val excludeParentDirs: Boolean,
private val filenamePattern: Pattern,
private val excludePattern: Pattern?,
val filenamePattern: Pattern,
val excludePattern: Pattern?,
private val checkFilenameStartsLowerCase: Boolean?,
private val doTestMethodName: String,
private val testClassName: String,
private val targetBackend: TargetBackend,
val targetBackend: TargetBackend,
excludeDirs: Collection<String>,
private val skipIgnored: Boolean,
private val testRunnerMethodName: String,
@@ -34,7 +32,7 @@ class SimpleTestClassModel(
override val name: String
get() = testClassName
private val excludeDirs: Set<String> = excludeDirs.toSet()
val excludeDirs: Set<String> = excludeDirs.toSet()
override val innerTestClasses: Collection<TestClassModel> by lazy {
if (!rootFile.isDirectory || !recursive || deep != null && deep < 1) {
@@ -127,43 +125,20 @@ class SimpleTestClassModel(
override val dataPathRoot: String
get() = "\$PROJECT_ROOT"
private inner class TestAllFilesPresentMethodModel : TestMethodModel() {
object TestAllFilesPresentMethodKind : MethodModel.Kind()
inner class TestAllFilesPresentMethodModel : MethodModel {
override val kind: MethodModel.Kind
get() = TestAllFilesPresentMethodKind
override val name: String
get() = "testAllFilesPresentIn$testClassName"
override val dataString: String?
get() = null
override fun generateBody(p: Printer) {
val exclude = StringBuilder()
for (dir in excludeDirs) {
exclude.append(", \"")
exclude.append(StringUtil.escapeStringCharacters(dir))
exclude.append("\"")
}
val excludedArgument = if (excludePattern != null) {
String.format("Pattern.compile(\"%s\")", StringUtil.escapeStringCharacters(excludePattern.pattern()))
} else {
null
}
val assertTestsPresentStr = if (targetBackend === TargetBackend.ANY) {
String.format(
"KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File(\"%s\"), Pattern.compile(\"%s\"), %s, %s%s);",
KotlinTestUtils.getFilePath(rootFile),
StringUtil.escapeStringCharacters(filenamePattern.pattern()),
excludedArgument,
recursive,
exclude
)
} else {
String.format(
"KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File(\"%s\"), Pattern.compile(\"%s\"), %s, %s.%s, %s%s);",
KotlinTestUtils.getFilePath(rootFile), StringUtil.escapeStringCharacters(filenamePattern.pattern()),
excludedArgument, TargetBackend::class.java.simpleName, targetBackend.toString(), recursive, exclude
)
}
p.println(assertTestsPresentStr)
}
val classModel: SimpleTestClassModel
get() = this@SimpleTestClassModel
override fun shouldBeGenerated(): Boolean {
return true
@@ -15,16 +15,16 @@ import java.util.regex.Pattern
open class SimpleTestMethodModel(
private val rootDir: File,
protected val file: File,
val file: File,
private val filenamePattern: Pattern,
checkFilenameStartsLowerCase: Boolean?,
protected val targetBackend: TargetBackend,
private val skipIgnored: Boolean
) : TestMethodModel() {
override fun generateBody(p: Printer) {
val filePath = KotlinTestUtils.getFilePath(file) + if (file.isDirectory) "/" else ""
p.println(RunTestMethodModel.METHOD_NAME, "(\"", filePath, "\");")
}
) : MethodModel {
object Kind : MethodModel.Kind()
override val kind: MethodModel.Kind
get() = Kind
override val dataString: String
get() {
@@ -16,22 +16,20 @@
package org.jetbrains.kotlin.generators.tests.generator
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.utils.Printer
import java.io.File
import java.util.*
import java.util.regex.Pattern
class SingleClassTestModel(
private val rootFile: File,
private val filenamePattern: Pattern,
private val excludePattern: Pattern?,
val rootFile: File,
val filenamePattern: Pattern,
val excludePattern: Pattern?,
private val checkFilenameStartsLowerCase: Boolean?,
private val doTestMethodName: String,
private val testClassName: String,
private val targetBackend: TargetBackend,
val targetBackend: TargetBackend,
private val skipIgnored: Boolean,
private val testRunnerMethodName: String,
private val additionalRunnerArguments: List<String>,
@@ -56,7 +54,7 @@ class SingleClassTestModel(
override val innerTestClasses: Collection<TestClassModel>
get() = emptyList()
private fun getTestMethodsFromFile(file: File): Collection<TestMethodModel> {
private fun getTestMethodsFromFile(file: File): Collection<MethodModel> {
return listOf(
SimpleTestMethodModel(
rootFile, file, filenamePattern, checkFilenameStartsLowerCase, targetBackend, skipIgnored
@@ -70,36 +68,18 @@ class SingleClassTestModel(
override val dataString: String = KotlinTestUtils.getFilePath(rootFile)
override val dataPathRoot: String = "\$PROJECT_ROOT"
private inner class TestAllFilesPresentMethodModel : TestMethodModel() {
object AllFilesPresentedMethodKind : MethodModel.Kind()
inner class TestAllFilesPresentMethodModel : MethodModel {
override val name: String = "testAllFilesPresentIn$testClassName"
override val dataString: String?
get() = null
override fun generateBody(p: Printer) {
val assertTestsPresentStr: String
val excludedArgument = if (excludePattern != null) {
String.format(
"Pattern.compile(\"%s\")", StringUtil.escapeStringCharacters(
excludePattern.pattern()
)
)
} else {
null
}
assertTestsPresentStr = if (targetBackend !== TargetBackend.ANY) {
String.format(
"KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClassWithExcluded(this.getClass(), new File(\"%s\"), Pattern.compile(\"%s\"), %s, %s.%s);",
KotlinTestUtils.getFilePath(rootFile), StringUtil.escapeStringCharacters(filenamePattern.pattern()),
excludedArgument, TargetBackend::class.java.simpleName, targetBackend.toString()
)
} else {
String.format(
"KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClassWithExcluded(this.getClass(), new File(\"%s\"), Pattern.compile(\"%s\"), %s);",
KotlinTestUtils.getFilePath(rootFile), StringUtil.escapeStringCharacters(filenamePattern.pattern()), excludedArgument
)
}
p.println(assertTestsPresentStr)
}
val classModel: SingleClassTestModel
get() = this@SingleClassTestModel
override val kind: MethodModel.Kind
get() = AllFilesPresentedMethodKind
override fun shouldBeGenerated(): Boolean {
return true
@@ -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.impl.*
import org.jetbrains.kotlin.test.TargetBackend
import java.io.File
import java.util.*
@@ -42,7 +43,15 @@ class TestGroup(
suiteTestClassName,
baseTestClassName,
TestClass(annotations).apply(init).testModels,
useJunit4
useJunit4,
methodGenerators = listOf(
CoroutinesTestMethodGenerator,
RunTestMethodGenerator,
RunTestMethodWithPackageReplacementGenerator,
SimpleTestClassModelTestAllFilesPresentMethodGenerator,
SimpleTestMethodGenerator,
SingleClassTestModelAllFilesPresentedMethodGenerator
)
)
if (testGenerator.generateAndSave(dryRun)) {
inconsistencyChecker(dryRun).add(testGenerator.testSourceFilePath)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.generators.tests.generator
import org.jetbrains.kotlin.generators.tests.generator.generators.MethodGenerator
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners
import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -22,8 +23,12 @@ class TestGenerator(
suiteTestClassFqName: String,
baseTestClassFqName: String,
testClassModels: Collection<TestClassModel>,
useJunit4: Boolean
useJunit4: Boolean,
methodGenerators: List<MethodGenerator<*>>
) {
private val methodGenerators: Map<MethodModel.Kind, MethodGenerator<*>> =
methodGenerators.associateBy { it.kind }.withDefault { error("Generator for method with kind $it not found") }
private val baseTestClassPackage: String
private val suiteClassPackage: String
private val suiteClassName: String
@@ -188,28 +193,31 @@ class TestGenerator(
p.println("}")
}
private fun generateTestMethod(p: Printer, methodModel: MethodModel, useJunit4: Boolean) {
if (useJunit4 && (methodModel !is RunTestMethodModel)) {
p.println("@Test")
}
val generator = methodGenerators.getValue(methodModel.kind)
generateMetadata(p, methodModel)
generator.generateSignature(methodModel, p)
p.printWithNoIndent(" {")
p.println()
p.pushIndent()
generator.generateBody(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 generateTestMethod(p: Printer, methodModel: MethodModel, useJunit4: Boolean) {
if (useJunit4 && (methodModel !is RunTestMethodModel)) {
p.println("@Test")
}
generateMetadata(p, methodModel)
methodModel.generateSignature(p)
p.printWithNoIndent(" {")
p.println()
p.pushIndent()
methodModel.generateBody(p)
p.popIndent()
p.println("}")
}
private fun generateMetadata(p: Printer, testDataSource: TestEntityModel) {
val dataString = testDataSource.dataString
@@ -44,14 +44,9 @@ abstract class TestClassModel : ClassModel {
}
interface MethodModel : TestEntityModel {
abstract class Kind
val kind: Kind
fun shouldBeGenerated(): Boolean = true
fun generateSignature(p: Printer)
fun generateBody(p: Printer)
fun imports(): Collection<Class<*>> = emptyList()
}
abstract class TestMethodModel : MethodModel {
override fun generateSignature(p: Printer) {
p.print("public void $name() throws Exception")
}
}
@@ -0,0 +1,22 @@
/*
* 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.utils.Printer
abstract class MethodGenerator<in T : MethodModel> {
companion object {
fun generateDefaultSignature(method: MethodModel, p: Printer) {
p.print("public void ${method.name}() throws Exception")
}
}
abstract val kind: MethodModel.Kind
abstract fun generateSignature(method: @UnsafeVariance T, p: Printer)
abstract fun generateBody(method: @UnsafeVariance T, p: Printer)
}
@@ -0,0 +1,35 @@
/*
* 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.impl
import org.jetbrains.kotlin.generators.tests.generator.MethodModel
import org.jetbrains.kotlin.generators.tests.generator.RunTestMethodModel
import org.jetbrains.kotlin.generators.tests.generator.generators.MethodGenerator
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.utils.Printer
object RunTestMethodGenerator : MethodGenerator<RunTestMethodModel>() {
override val kind: MethodModel.Kind
get() = RunTestMethodModel.Kind
override fun generateBody(method: RunTestMethodModel, p: Printer) {
with(method) {
if (!isWithTargetBackend()) {
p.println("KotlinTestUtils.${method.testRunnerMethodName}(this::$testMethodName, this, testDataFilePath);")
} else {
val className = TargetBackend::class.java.simpleName
val additionalArguments = if (additionalRunnerArguments.isNotEmpty())
additionalRunnerArguments.joinToString(separator = ", ", prefix = ", ")
else ""
p.println("KotlinTestUtils.$testRunnerMethodName(this::$testMethodName, $className.$targetBackend, testDataFilePath$additionalArguments);")
}
}
}
override fun generateSignature(method: RunTestMethodModel, p: Printer) {
p.print("private void ${method.name}(String testDataFilePath) throws Exception")
}
}
@@ -0,0 +1,56 @@
/*
* 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.impl
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.generators.tests.generator.MethodModel
import org.jetbrains.kotlin.generators.tests.generator.SimpleTestClassModel
import org.jetbrains.kotlin.generators.tests.generator.generators.MethodGenerator
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.addToStdlib.runIf
object SimpleTestClassModelTestAllFilesPresentMethodGenerator : MethodGenerator<SimpleTestClassModel.TestAllFilesPresentMethodModel>() {
override val kind: MethodModel.Kind
get() = SimpleTestClassModel.TestAllFilesPresentMethodKind
override fun generateSignature(method: SimpleTestClassModel.TestAllFilesPresentMethodModel, p: Printer) {
generateDefaultSignature(method, p)
}
override fun generateBody(method: SimpleTestClassModel.TestAllFilesPresentMethodModel, p: Printer) {
with(method) {
val exclude = StringBuilder()
for (dir in classModel.excludeDirs) {
exclude.append(", \"")
exclude.append(StringUtil.escapeStringCharacters(dir))
exclude.append("\"")
}
val excludedArgument = runIf(classModel.excludePattern != null) {
String.format("Pattern.compile(\"%s\")", StringUtil.escapeStringCharacters(classModel.excludePattern!!.pattern()))
}
val assertTestsPresentStr = if (classModel.targetBackend === TargetBackend.ANY) {
String.format(
"KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File(\"%s\"), Pattern.compile(\"%s\"), %s, %s%s);",
KotlinTestUtils.getFilePath(classModel.rootFile),
StringUtil.escapeStringCharacters(classModel.filenamePattern.pattern()),
excludedArgument,
classModel.recursive,
exclude
)
} else {
String.format(
"KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File(\"%s\"), Pattern.compile(\"%s\"), %s, %s.%s, %s%s);",
KotlinTestUtils.getFilePath(classModel.rootFile),
StringUtil.escapeStringCharacters(classModel.filenamePattern.pattern()),
excludedArgument, TargetBackend::class.java.simpleName, classModel.targetBackend.toString(), classModel.recursive, exclude
)
}
p.println(assertTestsPresentStr)
}
}
}
@@ -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.impl
import org.jetbrains.kotlin.generators.tests.generator.MethodModel
import org.jetbrains.kotlin.generators.tests.generator.RunTestMethodModel
import org.jetbrains.kotlin.generators.tests.generator.SimpleTestMethodModel
import org.jetbrains.kotlin.generators.tests.generator.generators.MethodGenerator
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.Printer
object SimpleTestMethodGenerator : MethodGenerator<SimpleTestMethodModel>() {
override val kind: MethodModel.Kind
get() = SimpleTestMethodModel.Kind
override fun generateSignature(method: SimpleTestMethodModel, p: Printer) {
generateDefaultSignature(method, p)
}
override fun generateBody(method: SimpleTestMethodModel, p: Printer) {
with(method) {
val filePath = KotlinTestUtils.getFilePath(file) + if (file.isDirectory) "/" else ""
p.println(RunTestMethodModel.METHOD_NAME, "(\"", filePath, "\");")
}
}
}
@@ -0,0 +1,56 @@
/*
* 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.impl
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.generators.tests.generator.MethodModel
import org.jetbrains.kotlin.generators.tests.generator.SingleClassTestModel
import org.jetbrains.kotlin.generators.tests.generator.generators.MethodGenerator
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.utils.Printer
object SingleClassTestModelAllFilesPresentedMethodGenerator : MethodGenerator<SingleClassTestModel.TestAllFilesPresentMethodModel>() {
override val kind: MethodModel.Kind
get() = SingleClassTestModel.AllFilesPresentedMethodKind
override fun generateSignature(method: SingleClassTestModel.TestAllFilesPresentMethodModel, p: Printer) {
generateDefaultSignature(method, p)
}
override fun generateBody(method: SingleClassTestModel.TestAllFilesPresentMethodModel, p: Printer) {
with(method) {
with(classModel) {
val assertTestsPresentStr: String
val excludedArgument = if (excludePattern != null) {
String.format(
"Pattern.compile(\"%s\")", StringUtil.escapeStringCharacters(
excludePattern.pattern()
)
)
} else {
null
}
assertTestsPresentStr = if (targetBackend !== TargetBackend.ANY) {
String.format(
"KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClassWithExcluded(this.getClass(), new File(\"%s\"), Pattern.compile(\"%s\"), %s, %s.%s);",
KotlinTestUtils.getFilePath(rootFile), StringUtil.escapeStringCharacters(filenamePattern.pattern()),
excludedArgument, TargetBackend::class.java.simpleName, targetBackend.toString()
)
} else {
String.format(
"KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClassWithExcluded(this.getClass(), new File(\"%s\"), Pattern.compile(\"%s\"), %s);",
KotlinTestUtils.getFilePath(rootFile),
StringUtil.escapeStringCharacters(filenamePattern.pattern()),
excludedArgument
)
}
p.println(assertTestsPresentStr)
}
}
}
}
@@ -0,0 +1,37 @@
/*
* 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.util
import org.jetbrains.kotlin.generators.tests.generator.MethodModel
import org.jetbrains.kotlin.generators.tests.generator.SimpleTestMethodModel
import org.jetbrains.kotlin.test.TargetBackend
import java.io.File
import java.util.regex.Pattern
class CoroutinesTestMethodModel(
rootDir: File,
file: File,
filenamePattern: Pattern,
checkFilenameStartsLowerCase: Boolean?,
targetBackend: TargetBackend,
skipIgnored: Boolean,
val isLanguageVersion1_3: Boolean
) : SimpleTestMethodModel(
rootDir,
file,
filenamePattern,
checkFilenameStartsLowerCase,
targetBackend,
skipIgnored
) {
object Kind : MethodModel.Kind()
override val kind: MethodModel.Kind
get() = Kind
override val name: String
get() = super.name + if (isLanguageVersion1_3) "_1_3" else "_1_2"
}