Allow to add additional annotations to generated classes

This commit is contained in:
Nikolay Krasko
2019-10-18 19:44:44 +03:00
parent b885b848f4
commit 0c5dc507e9
7 changed files with 105 additions and 10 deletions
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2019 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
import org.jetbrains.kotlin.test.MuteExtraSuffix
import org.jetbrains.kotlin.utils.Printer
class AnnotationModel(
val annotation: Class<out Annotation>,
val arguments: List<Any>
) {
fun generate(p: Printer) {
val argumentsString = arguments.joinToString(separator = ",") { "\"$it\""}
p.print("@${annotation.simpleName}($argumentsString)")
}
}
fun muteExtraSuffix(suffix: String) = AnnotationModel(MuteExtraSuffix::class.java, arguments = listOf(suffix))
@@ -61,4 +61,16 @@ public class DelegatingTestClassModel implements TestClassModel {
public String getDataString() { public String getDataString() {
return delegate.getDataString(); return delegate.getDataString();
} }
@NotNull
@Override
public Collection<AnnotationModel> getAnnotations() {
return delegate.getAnnotations();
}
@NotNull
@Override
public Collection<Class<?>> getImports() {
return delegate.getImports();
}
} }
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.utils.Printer;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class SimpleTestClassModel implements TestClassModel { public class SimpleTestClassModel implements TestClassModel {
private static final Comparator<TestEntityModel> BY_NAME = Comparator.comparing(TestEntityModel::getName); private static final Comparator<TestEntityModel> BY_NAME = Comparator.comparing(TestEntityModel::getName);
@@ -43,6 +44,9 @@ public class SimpleTestClassModel implements TestClassModel {
@Nullable @Nullable
private Collection<MethodModel> testMethods; private Collection<MethodModel> testMethods;
@NotNull
private final Collection<AnnotationModel> annotations;
private final boolean skipIgnored; private final boolean skipIgnored;
private final String testRunnerMethodName; private final String testRunnerMethodName;
private final List<String> additionalRunnerArguments; private final List<String> additionalRunnerArguments;
@@ -60,7 +64,8 @@ public class SimpleTestClassModel implements TestClassModel {
boolean skipIgnored, boolean skipIgnored,
String testRunnerMethodName, String testRunnerMethodName,
List<String> additionalRunnerArguments, List<String> additionalRunnerArguments,
Integer deep Integer deep,
@NotNull Collection<AnnotationModel> annotations
) { ) {
this.rootFile = rootFile; this.rootFile = rootFile;
this.recursive = recursive; this.recursive = recursive;
@@ -75,6 +80,7 @@ public class SimpleTestClassModel implements TestClassModel {
this.testRunnerMethodName = testRunnerMethodName; this.testRunnerMethodName = testRunnerMethodName;
this.additionalRunnerArguments = additionalRunnerArguments; this.additionalRunnerArguments = additionalRunnerArguments;
this.deep = deep; this.deep = deep;
this.annotations = annotations;
} }
@NotNull @NotNull
@@ -94,8 +100,9 @@ public class SimpleTestClassModel implements TestClassModel {
children.add(new SimpleTestClassModel( children.add(new SimpleTestClassModel(
file, true, excludeParentDirs, filenamePattern, checkFilenameStartsLowerCase, file, true, excludeParentDirs, filenamePattern, checkFilenameStartsLowerCase,
doTestMethodName, innerTestClassName, targetBackend, excludesStripOneDirectory(file.getName()), doTestMethodName, innerTestClassName, targetBackend, excludesStripOneDirectory(file.getName()),
skipIgnored, testRunnerMethodName, additionalRunnerArguments, deep != null ? deep - 1 : null) skipIgnored, testRunnerMethodName, additionalRunnerArguments, deep != null ? deep - 1 : null, annotations)
); );
} }
} }
} }
@@ -223,6 +230,18 @@ public class SimpleTestClassModel implements TestClassModel {
return testClassName; return testClassName;
} }
@NotNull
@Override
public Collection<AnnotationModel> getAnnotations() {
return annotations;
}
@NotNull
@Override
public Collection<Class<?>> getImports() {
return annotations.stream().map(AnnotationModel::getAnnotation).collect(Collectors.toSet());
}
private class TestAllFilesPresentMethodModel implements TestMethodModel { private class TestAllFilesPresentMethodModel implements TestMethodModel {
@NotNull @NotNull
@Override @Override
@@ -31,6 +31,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class SingleClassTestModel implements TestClassModel { public class SingleClassTestModel implements TestClassModel {
@NotNull @NotNull
@@ -52,6 +53,9 @@ public class SingleClassTestModel implements TestClassModel {
private final String testRunnerMethodName; private final String testRunnerMethodName;
private final List<String> additionalRunnerArguments; private final List<String> additionalRunnerArguments;
@NotNull
private final List<AnnotationModel> annotations;
public SingleClassTestModel( public SingleClassTestModel(
@NotNull File rootFile, @NotNull File rootFile,
@NotNull Pattern filenamePattern, @NotNull Pattern filenamePattern,
@@ -61,7 +65,8 @@ public class SingleClassTestModel implements TestClassModel {
@NotNull TargetBackend targetBackend, @NotNull TargetBackend targetBackend,
boolean skipIgnored, boolean skipIgnored,
String testRunnerMethodName, String testRunnerMethodName,
List<String> additionalRunnerArguments List<String> additionalRunnerArguments,
@NotNull List<AnnotationModel> annotations
) { ) {
this.rootFile = rootFile; this.rootFile = rootFile;
this.filenamePattern = filenamePattern; this.filenamePattern = filenamePattern;
@@ -72,6 +77,7 @@ public class SingleClassTestModel implements TestClassModel {
this.skipIgnored = skipIgnored; this.skipIgnored = skipIgnored;
this.testRunnerMethodName = testRunnerMethodName; this.testRunnerMethodName = testRunnerMethodName;
this.additionalRunnerArguments = additionalRunnerArguments; this.additionalRunnerArguments = additionalRunnerArguments;
this.annotations = annotations;
} }
@NotNull @NotNull
@@ -134,6 +140,18 @@ public class SingleClassTestModel implements TestClassModel {
return testClassName; return testClassName;
} }
@NotNull
@Override
public Collection<AnnotationModel> getAnnotations() {
return annotations;
}
@NotNull
@Override
public Collection<Class<?>> getImports() {
return annotations.stream().map(AnnotationModel::getAnnotation).collect(Collectors.toSet());
}
private class TestAllFilesPresentMethodModel implements TestMethodModel { private class TestAllFilesPresentMethodModel implements TestMethodModel {
@NotNull @NotNull
@Override @Override
@@ -15,32 +15,35 @@ class TestGroup(
private val testsRoot: String, private val testsRoot: String,
val testDataRoot: String, val testDataRoot: String,
val testRunnerMethodName: String, val testRunnerMethodName: String,
val additionalRunnerArguments: List<String> = emptyList() val additionalRunnerArguments: List<String> = emptyList(),
val annotations: List<AnnotationModel> = emptyList()
) { ) {
inline fun <reified T : TestCase> testClass( inline fun <reified T : TestCase> testClass(
suiteTestClassName: String = getDefaultSuiteTestClassName(T::class.java.simpleName), suiteTestClassName: String = getDefaultSuiteTestClassName(T::class.java.simpleName),
useJunit4: Boolean = false, useJunit4: Boolean = false,
annotations: List<AnnotationModel> = emptyList(),
noinline init: TestClass.() -> Unit noinline init: TestClass.() -> Unit
) { ) {
testClass(T::class.java.name, suiteTestClassName, useJunit4, init) testClass(T::class.java.name, suiteTestClassName, useJunit4, annotations, init)
} }
fun testClass( fun testClass(
baseTestClassName: String, baseTestClassName: String,
suiteTestClassName: String = getDefaultSuiteTestClassName(baseTestClassName.substringAfterLast('.')), suiteTestClassName: String = getDefaultSuiteTestClassName(baseTestClassName.substringAfterLast('.')),
useJunit4: Boolean, useJunit4: Boolean,
annotations: List<AnnotationModel> = emptyList(),
init: TestClass.() -> Unit init: TestClass.() -> Unit
) { ) {
TestGenerator( TestGenerator(
testsRoot, testsRoot,
suiteTestClassName, suiteTestClassName,
baseTestClassName, baseTestClassName,
TestClass().apply(init).testModels, TestClass(annotations).apply(init).testModels,
useJunit4 useJunit4
).generateAndSave() ).generateAndSave()
} }
inner class TestClass { inner class TestClass(val annotations: List<AnnotationModel>) {
val testModels = ArrayList<TestClassModel>() val testModels = ArrayList<TestClassModel>()
fun model( fun model(
@@ -66,13 +69,13 @@ class TestGroup(
if (excludeDirs.isNotEmpty()) error("excludeDirs is unsupported for SingleClassTestModel yet") if (excludeDirs.isNotEmpty()) error("excludeDirs is unsupported for SingleClassTestModel yet")
SingleClassTestModel( SingleClassTestModel(
rootFile, compiledPattern, filenameStartsLowerCase, testMethod, className, targetBackend, rootFile, compiledPattern, filenameStartsLowerCase, testMethod, className, targetBackend,
skipIgnored, testRunnerMethodName, additionalRunnerArguments skipIgnored, testRunnerMethodName, additionalRunnerArguments, annotations
) )
} else { } else {
SimpleTestClassModel( SimpleTestClassModel(
rootFile, recursive, excludeParentDirs, rootFile, recursive, excludeParentDirs,
compiledPattern, filenameStartsLowerCase, testMethod, className, compiledPattern, filenameStartsLowerCase, testMethod, className,
targetBackend, excludeDirs, skipIgnored, testRunnerMethodName, additionalRunnerArguments, deep targetBackend, excludeDirs, skipIgnored, testRunnerMethodName, additionalRunnerArguments, deep, annotations
) )
} }
) )
@@ -25,7 +25,6 @@ class TestGenerator(
testClassModels: Collection<TestClassModel>, testClassModels: Collection<TestClassModel>,
useJunit4: Boolean useJunit4: Boolean
) { ) {
private val baseTestClassPackage: String private val baseTestClassPackage: String
private val suiteClassPackage: String private val suiteClassPackage: String
private val suiteClassName: String private val suiteClassName: String
@@ -73,6 +72,11 @@ class TestGenerator(
if (suiteClassPackage != baseTestClassPackage) { if (suiteClassPackage != baseTestClassPackage) {
p.println("import $baseTestClassPackage.$baseTestClassName;") p.println("import $baseTestClassPackage.$baseTestClassName;")
} }
for (clazz in testClassModels.flatMap { classModel -> classModel.imports }.toSet()) {
p.println("import ${clazz.name};")
}
p.println("import " + TestMetadata::class.java.canonicalName + ";") p.println("import " + TestMetadata::class.java.canonicalName + ";")
p.println("import " + RunWith::class.java.canonicalName + ";") p.println("import " + RunWith::class.java.canonicalName + ";")
if (useJunit4) { if (useJunit4) {
@@ -112,6 +116,12 @@ class TestGenerator(
override val dataPathRoot: String? override val dataPathRoot: String?
get() = null get() = null
override val annotations: Collection<AnnotationModel>
get() = emptyList()
override val imports: Collection<Class<*>>
get() = emptyList()
} }
} }
@@ -126,6 +136,8 @@ class TestGenerator(
generateMetadata(p, testClassModel) generateMetadata(p, testClassModel)
generateTestDataPath(p, testClassModel) generateTestDataPath(p, testClassModel)
generateParameterAnnotations(p, testClassModel)
p.println("@RunWith(", if (useJunit4) JUNIT4_RUNNER.simpleName else RUNNER.simpleName, ".class)") p.println("@RunWith(", if (useJunit4) JUNIT4_RUNNER.simpleName else RUNNER.simpleName, ".class)")
p.println("public " + staticModifier + "class ", testClassModel.name, " extends ", baseTestClassName, " {") p.println("public " + staticModifier + "class ", testClassModel.name, " extends ", baseTestClassName, " {")
@@ -201,6 +213,13 @@ class TestGenerator(
} }
} }
private fun generateParameterAnnotations(p: Printer, testClassModel: TestClassModel) {
for (annotationModel in testClassModel.annotations) {
annotationModel.generate(p);
p.println()
}
}
private fun generateSuppressAllWarnings(p: Printer) { private fun generateSuppressAllWarnings(p: Printer) {
p.println("@SuppressWarnings(\"all\")") p.println("@SuppressWarnings(\"all\")")
} }
@@ -24,10 +24,12 @@ interface TestEntityModel {
} }
interface TestClassModel : TestEntityModel { interface TestClassModel : TestEntityModel {
val imports: Collection<Class<*>>
val innerTestClasses: Collection<TestClassModel> val innerTestClasses: Collection<TestClassModel>
val methods: Collection<MethodModel> val methods: Collection<MethodModel>
val isEmpty: Boolean val isEmpty: Boolean
val dataPathRoot: String? val dataPathRoot: String?
val annotations: Collection<AnnotationModel>
} }
interface MethodModel : TestEntityModel { interface MethodModel : TestEntityModel {