Test Class/Method models introduced

- no unnecessary nesting when there is only one test class in a suite
- escaping for method names
- more regular model structure
This commit is contained in:
Andrey Breslav
2012-07-09 20:34:48 +04:00
parent 8b2ad52ce6
commit af4e5d4e42
11 changed files with 345 additions and 202 deletions
@@ -52,6 +52,7 @@ import org.junit.Assert;
import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Matcher;
@@ -274,6 +275,10 @@ public class JetTestUtils {
return StringUtil.convertLineSeparators(text);
}
public static String getFilePath(File file) {
return file.getPath().replaceAll("\\\\", "/");
}
public interface TestFileFactory<F> {
F create(String fileName, String text);
}
@@ -357,4 +362,37 @@ public class JetTestUtils {
}
}
public static void allTestsPresent(
@NotNull Class<?> testCaseClass,
@NotNull String generatorClassFqName,
@NotNull File testDataDir,
@NotNull String extension,
boolean recursive
) {
Set<String> methodNames = new HashSet<String>();
for (Method method : testCaseClass.getDeclaredMethods()) {
boolean isTestMethod = method.getName().startsWith("test");
if (isTestMethod) {
methodNames.add(method.getName().toLowerCase() + "." + extension);
}
}
for (File file : testDataDir.listFiles()) {
if (file.isDirectory()) {
if (recursive) {
allTestsPresent(testCaseClass, generatorClassFqName, file, extension, recursive);
}
}
else {
String name = file.getName();
if (name.endsWith("." + extension) && !methodNames.contains("test" + name.toLowerCase())) {
String generatorClassSimpleName = generatorClassFqName.substring(generatorClassFqName.lastIndexOf(".") + 1);
junit.framework.Assert.fail("Test data file missing from the generated test class: " +
file +
"\nPlease re-run the generator: " + generatorClassFqName +
"(" + generatorClassSimpleName + ".java:1)");
}
}
}
}
}
@@ -16,7 +16,7 @@
package org.jetbrains.jet.jvm.compiler;
import org.jetbrains.jet.test.generator.TestDataSource;
import org.jetbrains.jet.test.generator.SimpleTestClassModel;
import org.jetbrains.jet.test.generator.TestGenerator;
import java.io.File;
@@ -38,7 +38,7 @@ public class ReadJavaBinaryClassTestGenerator {
aPackage,
"AbstractReadJavaBinaryClassTest",
Arrays.asList(
new TestDataSource(new File("compiler/testData/readJavaBinaryClass"), true, TestGenerator.filterFilesByExtension(extension), "doTest")
new SimpleTestClassModel(new File("compiler/testData/readJavaBinaryClass"), true, extension, "doTest")
),
ReadJavaBinaryClassTestGenerator.class.getName(),
TestGenerator.TargetTestFrameworks.JUNIT_3
@@ -16,7 +16,7 @@
package org.jetbrains.jet.lang.resolve.lazy;
import org.jetbrains.jet.test.generator.TestDataSource;
import org.jetbrains.jet.test.generator.SimpleTestClassModel;
import org.jetbrains.jet.test.generator.TestGenerator;
import java.io.File;
@@ -45,13 +45,13 @@ public class LazyResolveTestGenerator {
TARGET_PACKAGE,
"AbstractLazyResolveDescriptorRendererTest",
Arrays.asList(
new TestDataSource(new File("compiler/testData/renderer"),
new SimpleTestClassModel(new File("compiler/testData/renderer"),
true,
TestGenerator.filterFilesByExtension(TEST_DATA_FILE_EXTENSION),
TEST_DATA_FILE_EXTENSION,
"doTest"),
new TestDataSource(new File("compiler/testData/lazyResolve/descriptorRenderer"),
new SimpleTestClassModel(new File("compiler/testData/lazyResolve/descriptorRenderer"),
true,
TestGenerator.filterFilesByExtension(TEST_DATA_FILE_EXTENSION),
TEST_DATA_FILE_EXTENSION,
"doTest")
),
LazyResolveTestGenerator.class.getSimpleName()
@@ -67,17 +67,17 @@ public class LazyResolveTestGenerator {
TARGET_PACKAGE,
"AbstractLazyResolveNamespaceComparingTest",
Arrays.asList(
new TestDataSource(new File("compiler/testData/readKotlinBinaryClass"),
new SimpleTestClassModel(new File("compiler/testData/readKotlinBinaryClass"),
true,
TestGenerator.filterFilesByExtension(TEST_DATA_FILE_EXTENSION),
TEST_DATA_FILE_EXTENSION,
"doTestSinglePackage"),
new TestDataSource(new File("compiler/testData/readJavaBinaryClass"),
new SimpleTestClassModel(new File("compiler/testData/readJavaBinaryClass"),
true,
TestGenerator.filterFilesByExtension(TEST_DATA_FILE_EXTENSION),
TEST_DATA_FILE_EXTENSION,
"doTestSinglePackage"),
new TestDataSource(new File("compiler/testData/lazyResolve/namespaceComparator"),
new SimpleTestClassModel(new File("compiler/testData/lazyResolve/namespaceComparator"),
true,
TestGenerator.filterFilesByExtension(TEST_DATA_FILE_EXTENSION),
TEST_DATA_FILE_EXTENSION,
"doTest")
),
LazyResolveTestGenerator.class.getSimpleName()
@@ -16,28 +16,30 @@
package org.jetbrains.jet.test.generator;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import java.io.File;
import java.util.Collection;
/**
* @author abreslav
*/
public class TestDataFile {
private final File file;
private final String doTestMethodName;
* @author abreslav
*/
public class DelegatingTestClassModel implements TestClassModel {
private final TestClassModel delegate;
public TestDataFile(File file, String doTestMethodName) {
this.file = file;
this.doTestMethodName = doTestMethodName;
public DelegatingTestClassModel(TestClassModel delegate) {
this.delegate = delegate;
}
public String getTestCall() {
return doTestMethodName + "(\"" + Printer.getFilePath(file) + "\");";
@Override
public String getName() {
return delegate.getName();
}
public String getTestMethodName() {
return "test" + FileUtil.getNameWithoutExtension(StringUtil.capitalize(file.getName()));
@Override
public Collection<TestClassModel> getInnerTestClasses() {
return delegate.getInnerTestClasses();
}
@Override
public Collection<TestMethodModel> getTestMethods() {
return delegate.getTestMethods();
}
}
@@ -18,8 +18,6 @@ package org.jetbrains.jet.test.generator;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author abreslav
*/
@@ -59,8 +57,4 @@ public class Printer {
indent = indent.substring(INDENTATION_UNIT.length());
}
public static String getFilePath(File file) {
return file.getPath().replaceAll("\\\\", "/");
}
}
@@ -0,0 +1,104 @@
/*
* Copyright 2010-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.test.generator;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.JetTestUtils;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author abreslav
*/
public class SimpleTestClassModel implements TestClassModel {
private final File rootFile;
private final boolean recursive;
private final String extension;
private final String doTestMethodName;
private final String testClassName;
public SimpleTestClassModel(@NotNull File rootFile, boolean recursive, @NotNull String extension, @NotNull String doTestMethodName) {
this.rootFile = rootFile;
this.recursive = recursive;
this.extension = extension;
this.doTestMethodName = doTestMethodName;
this.testClassName = StringUtil.capitalize(rootFile.getName());
}
@Override
public Collection<TestClassModel> getInnerTestClasses() {
if (!rootFile.isDirectory() || !recursive) {
return Collections.emptyList();
}
List<TestClassModel> children = Lists.newArrayList();
File[] files = rootFile.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
children.add(new SimpleTestClassModel(file, recursive, extension, doTestMethodName));
}
}
}
return children;
}
@Override
public Collection<TestMethodModel> getTestMethods() {
if (!rootFile.isDirectory()) {
return Collections.<TestMethodModel>singletonList(new SimpleTestMethodModel(rootFile, rootFile, doTestMethodName));
}
List<TestMethodModel> result = Lists.newArrayList();
result.add(new TestMethodModel() {
@Override
public String getName() {
return "testAllFilesPresentIn" + StringUtil.capitalize(rootFile.getName());
}
@Override
public void generateBody(@NotNull Printer p, @NotNull String generatorClassFqName) {
p.println("JetTestUtils.allTestsPresent(" +
"this.getClass(), " +
"\"", generatorClassFqName, "\", " +
"new File(\"", JetTestUtils.getFilePath(rootFile) + "\"), \"",
extension,
"\", ", recursive,
");");
}
});
File[] listFiles = rootFile.listFiles();
if (listFiles != null) {
for (File file : listFiles) {
if (!file.isDirectory() && file.getName().endsWith("." + extension)) {
result.add(new SimpleTestMethodModel(rootFile, file, doTestMethodName));
}
}
}
return result;
}
@Override
public String getName() {
return testClassName;
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2010-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.test.generator;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.JetTestUtils;
import java.io.File;
/**
* @author abreslav
*/
public class SimpleTestMethodModel implements TestMethodModel {
private final File rootDir;
private final File file;
private final String doTestMethodName;
public SimpleTestMethodModel(File rootDir, File file, String doTestMethodName) {
this.rootDir = rootDir;
this.file = file;
this.doTestMethodName = doTestMethodName;
}
@Override
public void generateBody(@NotNull Printer p, @NotNull String generatorClassFqName) {
p.println(doTestMethodName, "(\"", JetTestUtils.getFilePath(file), "\");");
}
@Override
public String getName() {
String fileName = FileUtil.getNameWithoutExtension(file.getName());
String unescapedName;
if (rootDir.equals(file.getParentFile())) {
unescapedName = fileName;
}
else {
String relativePath = FileUtil.getRelativePath(rootDir, file.getParentFile());
unescapedName = relativePath + "-" + StringUtil.capitalize(fileName);
}
return "test" + escapeForMethodName(StringUtil.capitalize(unescapedName));
}
private static String escapeForMethodName(String fileName) {
// A file name may contain characters (like ".") that can't be a part of method name
StringBuilder result = new StringBuilder();
for (int i = 0; i < fileName.length(); i++) {
char c = fileName.charAt(i);
if (Character.isJavaIdentifierPart(c)) {
result.append(c);
}
else {
result.append("_");
}
}
return result.toString();
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.test.generator;
import java.util.Collection;
/**
* @author abreslav
*/
public interface TestClassModel {
String getName();
Collection<TestClassModel> getInnerTestClasses();
Collection<TestMethodModel> getTestMethods();
}
@@ -1,87 +0,0 @@
/*
* Copyright 2010-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.test.generator;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileFilter;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author abreslav
*/
public class TestDataSource {
private final File rootFile;
private final boolean recursive;
private final FileFilter filter;
private final String doTestMethodName;
private final String testClassName;
public TestDataSource(@NotNull File rootFile, boolean recursive, @NotNull FileFilter filter, String doTestMethodName) {
this.rootFile = rootFile;
this.recursive = recursive;
this.filter = filter;
this.doTestMethodName = doTestMethodName;
this.testClassName = StringUtil.capitalize(rootFile.getName());
}
public Collection<TestDataFile> getFiles() {
if (!rootFile.isDirectory()) {
return Collections.singletonList(new TestDataFile(rootFile, doTestMethodName));
}
List<File> files = Lists.newArrayList();
collectFiles(rootFile, files, recursive);
return Collections2.transform(files, new Function<File, TestDataFile>() {
@Override
public TestDataFile apply(File file) {
return new TestDataFile(file, doTestMethodName);
}
});
}
private void collectFiles(File current, List<File> result, boolean recursive) {
for (File file : current.listFiles(filter)) {
if (file.isDirectory()) {
if (recursive) {
collectFiles(file, result, recursive);
}
}
else {
result.add(file);
}
}
}
public void getAllTestsPresentCheck(@NotNull Printer p) {
p.println("allTestsPresent(" + getTestClassName() + ".class, new File(\"" + Printer.getFilePath(rootFile) + "\"), " + recursive + ");");
}
public String getAllTestsPresentMethodName() {
return "testAllFilesPresentIn" + StringUtil.capitalize(rootFile.getName());
}
public String getTestClassName() {
return testClassName;
}
}
@@ -16,12 +16,10 @@
package org.jetbrains.jet.test.generator;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
@@ -64,8 +62,8 @@ public class TestGenerator {
p.pushIndent();
p.println("TestSuite suite = new TestSuite();");
for (TestDataSource testDataSource : testGenerator.testDataSources) {
p.println("suite.addTestSuite(", testDataSource.getTestClassName(), ".class);");
for (TestClassModel testDataSource : testGenerator.testClassModels) {
p.println("suite.addTestSuite(", testDataSource.getName(), ".class);");
}
p.println("return suite;");
@@ -99,9 +97,9 @@ public class TestGenerator {
p.println("@RunWith(Suite.class)");
p.println("@Suite.SuiteClasses({");
p.pushIndent();
for (Iterator<TestDataSource> iterator = testGenerator.testDataSources.iterator(); iterator.hasNext(); ) {
TestDataSource testDataSource = iterator.next();
p.print(testGenerator.suiteClassName, ".", testDataSource.getTestClassName(), ".class");
for (Iterator<? extends TestClassModel> iterator = testGenerator.testClassModels.iterator(); iterator.hasNext(); ) {
TestClassModel testClassModel = iterator.next();
p.print(testGenerator.suiteClassName, ".", testClassModel.getName(), ".class");
if (iterator.hasNext()) {
p.printWithNoIndent(",");
}
@@ -128,22 +126,13 @@ public class TestGenerator {
}
}
public static FileFilter filterFilesByExtension(@NotNull final String extension) {
return new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith("." + extension);
}
};
}
private final String baseDir;
private final String testDataFileExtension;
private final String suiteClassPackage;
private final String suiteClassName;
private final String baseTestClassPackage;
private final String baseTestClassName;
private final Collection<TestDataSource> testDataSources;
private final Collection<? extends TestClassModel> testClassModels;
private final String generatorName;
private final TargetTestFramework targetTestFramework;
@@ -154,10 +143,10 @@ public class TestGenerator {
@NotNull String suiteClassName,
@NotNull String baseTestClassPackage,
@NotNull String baseTestClassName,
@NotNull Collection<TestDataSource> testDataSources,
@NotNull Collection<? extends TestClassModel> testClassModels,
@NotNull String generatorName
) {
this(baseDir, testDataFileExtension, suiteClassPackage, suiteClassName, baseTestClassPackage, baseTestClassName, testDataSources,
) {
this(baseDir, testDataFileExtension, suiteClassPackage, suiteClassName, baseTestClassPackage, baseTestClassName, testClassModels,
generatorName, TargetTestFrameworks.JUNIT_4);
}
@@ -168,7 +157,7 @@ public class TestGenerator {
@NotNull String suiteClassName,
@NotNull String baseTestClassPackage,
@NotNull String baseTestClassName,
@NotNull Collection<TestDataSource> testDataSources,
@NotNull Collection<? extends TestClassModel> testClassModels,
@NotNull String generatorName,
@NotNull TargetTestFramework targetTestFramework
) {
@@ -178,7 +167,7 @@ public class TestGenerator {
this.suiteClassName = suiteClassName;
this.baseTestClassPackage = baseTestClassPackage;
this.baseTestClassName = baseTestClassName;
this.testDataSources = testDataSources;
this.testClassModels = testClassModels;
this.generatorName = generatorName;
this.targetTestFramework = targetTestFramework;
}
@@ -200,6 +189,7 @@ public class TestGenerator {
p.println("import java.lang.reflect.Method;");
p.println("import java.util.HashSet;");
p.println("import java.util.Set;");
p.println("import org.jetbrains.jet.JetTestUtils;");
p.println();
p.println("import ", baseTestClassPackage, ".", baseTestClassName, ";");
@@ -207,18 +197,29 @@ public class TestGenerator {
p.println("/* This class is generated by ", generatorName, ". DO NOT MODIFY MANUALLY */");
targetTestFramework.generateSuiteClassAnnotations(this, p);
p.println("public class ", suiteClassName, " {");
p.pushIndent();
for (TestDataSource testDataSource : testDataSources) {
generateTestClass(p, testDataSource);
p.println();
if (testClassModels.size() == 1) {
TestClassModel theOnlyTestClass = testClassModels.iterator().next();
generateTestClass(p, new DelegatingTestClassModel(theOnlyTestClass) {
@Override
public String getName() {
return suiteClassName;
}
}, false);
}
else {
p.println("public class ", suiteClassName, " {");
p.pushIndent();
targetTestFramework.generateExtraSuiteClassMethods(this, p);
for (TestClassModel testDataSource : testClassModels) {
generateTestClass(p, testDataSource, true);
p.println();
}
p.popIndent();
p.println("}");
targetTestFramework.generateExtraSuiteClassMethods(this, p);
p.popIndent();
p.println("}");
}
String testSourceFilePath = baseDir + "/" + suiteClassPackage.replace(".", "/") + "/" + suiteClassName + ".java";
File testSourceFile = new File(testSourceFilePath);
@@ -226,68 +227,26 @@ public class TestGenerator {
System.out.println("Output written to file:\n" + testSourceFile.getAbsolutePath());
}
private void generateTestClass(Printer p, TestDataSource testDataSource) {
p.println("public static class ", testDataSource.getTestClassName(), " extends ", baseTestClassName, " {");
private void generateTestClass(Printer p, TestClassModel testDataSource, boolean isStatic) {
String staticModifier = isStatic ? "static " : "";
p.println("public " + staticModifier + "class ", testDataSource.getName(), " extends ", baseTestClassName, " {");
p.pushIndent();
Collection<TestDataFile> files = Lists.newArrayList();
files.addAll(testDataSource.getFiles());
Collection<TestMethodModel> testMethods = testDataSource.getTestMethods();
targetTestFramework.generateTestMethodAnnotations(this, p);
p.println("public void " + testDataSource.getAllTestsPresentMethodName() + "() throws Exception {");
p.pushIndent();
testDataSource.getAllTestsPresentCheck(p);
p.popIndent();
p.println("}");
p.println();
for (TestDataFile file : files) {
for (TestMethodModel testMethodModel : testMethods) {
targetTestFramework.generateTestMethodAnnotations(this, p);
p.println("public void ", file.getTestMethodName(), "() throws Exception {");
p.println("public void ", testMethodModel.getName(), "() throws Exception {");
p.pushIndent();
p.println(file.getTestCall());
testMethodModel.generateBody(p, generatorName);
p.popIndent();
p.println("}");
p.println();
}
generateAllTestsPresent(p);
p.popIndent();
p.println("}");
}
private void generateAllTestsPresent(Printer p) {
String isTestMethod = targetTestFramework.getIsTestMethodCondition("method");
String[] methodText = new String[] {
"public static void allTestsPresent(Class<?> clazz, File testDataDir, boolean recursive) {",
" Set<String> methodNames = new HashSet<String>();",
" for (Method method : clazz.getDeclaredMethods()) {",
" if (" + isTestMethod + ") {",
" methodNames.add(method.getName().toLowerCase() + \"." + testDataFileExtension + "\");",
" }",
" }",
" for (File file : testDataDir.listFiles()) {",
" if (file.isDirectory()) {",
" if (recursive) {",
" allTestsPresent(clazz, file, recursive);",
" }",
" }",
" else {",
" String name = file.getName();",
" if (name.endsWith(\"." + testDataFileExtension + "\") && !methodNames.contains(\"test\" + name.toLowerCase())) {",
" Assert.fail(\"Test data file missing from the generated test class: \" + file + \"\\nPlease re-run the generator: " + generatorName + "\");",
" }",
" }",
" }",
"}"};
for (String s : methodText) {
p.println(s);
}
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.test.generator;
import org.jetbrains.annotations.NotNull;
/**
* @author abreslav
*/
public interface TestMethodModel {
String getName();
void generateBody(@NotNull Printer p, @NotNull String generatorClassFqName);
}