diff --git a/compiler/tests/org/jetbrains/jet/JUnit3RunnerWithInners.java b/compiler/tests/org/jetbrains/jet/JUnit3RunnerWithInners.java new file mode 100644 index 00000000000..1b1d6cecc32 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/JUnit3RunnerWithInners.java @@ -0,0 +1,176 @@ +/* + * Copyright 2010-2014 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; + +import com.google.common.collect.Lists; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; +import kotlin.Function1; +import kotlin.KotlinPackage; +import org.junit.internal.MethodSorter; +import org.junit.internal.runners.JUnit38ClassRunner; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; + +/** + * This runner runs class with all inners test classes, but monitors situation when those classes are planned to be executed + * with IDEA package test runner. + */ +public class JUnit3RunnerWithInners extends Runner { + private static final Set requestedRunners = new HashSet(); + + private Runner delegateRunner; + private final Class klass; + private Test test; + + private static class FakeEmptyClassTest implements Test { + private final Class klass; + + public FakeEmptyClassTest(Class klass) { + this.klass = klass; + } + + @Override + public int countTestCases() { + return 0; + } + + @Override + public void run(TestResult result) { + result.startTest(this); + result.endTest(this); + } + + public Class getTestClass() { + return klass; + } + + @Override + public String toString() { + return "Empty class with inners"; + } + } + + public JUnit3RunnerWithInners(Class klass) { + this.klass = klass; + requestedRunners.add(klass); + } + + @Override + public void run(RunNotifier notifier) { + getDelegateRunner().run(notifier); + } + + @Override + public Description getDescription() { + if (getCollectedTests() instanceof FakeEmptyClassTest) { + return Description.EMPTY; + } + + return getDelegateRunner().getDescription(); + } + + private Test getCollectedTests() { + if (test == null) { + List innerClasses = collectDeclaredClasses(klass, false); + Set unprocessedInnerClasses = unprocessedClasses(innerClasses); + + if (unprocessedInnerClasses.isEmpty()) { + if (!innerClasses.isEmpty() && !hasTestMethods(klass)) { + test = new FakeEmptyClassTest(klass); + } + else { + test = new TestSuite(klass.asSubclass(TestCase.class)); + } + } + else { + List classes = Lists.newArrayList(); + classes.add(klass); + classes.addAll(unprocessedInnerClasses); + + List filtered = KotlinPackage.filter(classes, new Function1() { + @Override + public Boolean invoke(Class aClass) { + boolean hasInnerClasses = aClass.getDeclaredClasses().length > 0; + return !hasInnerClasses || hasTestMethods(aClass); + } + }); + + + test = new TestSuite(filtered.toArray(new Class[filtered.size()])); + } + } + + return test; + } + + private static Set unprocessedClasses(Collection classes) { + Set result = new LinkedHashSet(); + for (Class aClass : classes) { + if (!requestedRunners.contains(aClass)) { + result.add(aClass); + } + } + + return result; + } + + private Runner getDelegateRunner() { + if (delegateRunner == null) { + delegateRunner = new JUnit38ClassRunner(getCollectedTests()); + } + + return delegateRunner; + } + + private static List collectDeclaredClasses(Class klass, boolean withItself) { + List result = new ArrayList(); + if (withItself) { + result.add(klass); + } + + for (Class aClass : klass.getDeclaredClasses()) { + result.addAll(collectDeclaredClasses(aClass, true)); + } + + return result; + } + + private static boolean hasTestMethods(Class klass) { + for (Class currentClass = klass; Test.class.isAssignableFrom(currentClass); currentClass = currentClass.getSuperclass()) { + for (Method each : MethodSorter.getDeclaredMethods(currentClass)) { + if (isTestMethod(each)) return true; + } + } + + return false; + } + + private static boolean isTestMethod(Method method) { + return method.getParameterTypes().length == 0 && + method.getName().startsWith("test") && + method.getReturnType().equals(Void.TYPE) && + Modifier.isPublic(method.getModifiers()); + } +} \ No newline at end of file diff --git a/generators/src/org/jetbrains/jet/generators/tests/generator/TestGenerator.java b/generators/src/org/jetbrains/jet/generators/tests/generator/TestGenerator.java index 4d659046310..7a61f67d51c 100644 --- a/generators/src/org/jetbrains/jet/generators/tests/generator/TestGenerator.java +++ b/generators/src/org/jetbrains/jet/generators/tests/generator/TestGenerator.java @@ -22,6 +22,7 @@ import com.intellij.util.containers.ContainerUtil; import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.JUnit3RunnerWithInners; import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.di.GeneratorsFileUtil; import org.jetbrains.jet.utils.Printer; @@ -36,6 +37,7 @@ public class TestGenerator { public static final String NAVIGATION_METADATA = "navigationMetadata"; private static final Set GENERATED_FILES = ContainerUtil.newHashSet(); + private static final Class RUNNER = JUnit3RunnerWithInners.class; private final String suiteClassPackage; private final String suiteClassName; @@ -72,9 +74,12 @@ public class TestGenerator { p.println("import com.intellij.testFramework.TestDataPath;"); p.println("import junit.framework.Test;"); p.println("import junit.framework.TestSuite;"); + p.println("import org.junit.runner.RunWith;"); p.println("import org.jetbrains.jet.JetTestUtils;"); p.println("import org.jetbrains.jet.test.InnerTestClasses;"); p.println("import org.jetbrains.jet.test.TestMetadata;"); + p.println("import ", RUNNER.getCanonicalName(), ";"); + p.println(); p.println("import java.io.File;"); p.println("import java.util.regex.Pattern;"); @@ -134,11 +139,11 @@ public class TestGenerator { private void generateTestClass(Printer p, TestClassModel testClassModel, boolean isStatic) { String staticModifier = isStatic ? "static " : ""; + generateMetadata(p, testClassModel); - generateTestDataPath(p, testClassModel); - generateInnerClassesAnnotation(p, testClassModel); + p.println("@RunWith(", RUNNER.getName(), ".class)"); p.println("public " + staticModifier + "class ", testClassModel.getName(), " extends ", baseTestClassName, " {"); p.pushIndent(); @@ -159,36 +164,6 @@ public class TestGenerator { p.println(); } - if (!innerTestClasses.isEmpty()) { - generateSuiteMethod(p, testClassModel, isStatic); - } - - p.popIndent(); - p.println("}"); - } - - private static void generateSuiteMethod(Printer p, TestClassModel testClassModel, boolean innerClass) { - String name = innerClass ? "innerSuite" : "suite"; - p.println("public static Test " + name + "() {"); - p.pushIndent(); - - p.println("TestSuite suite = new TestSuite(\"", testClassModel.getName(), "\");"); - if (!testClassModel.getTestMethods().isEmpty()) { - p.println("suite.addTestSuite(", testClassModel.getName(), ".class);"); - } - for (TestClassModel innerTestClass : testClassModel.getInnerTestClasses()) { - if (innerTestClass.isEmpty()) { - continue; - } - if (innerTestClass.getInnerTestClasses().isEmpty()) { - p.println("suite.addTestSuite(", innerTestClass.getName(), ".class);"); - } - else { - p.println("suite.addTest(", innerTestClass.getName(), ".innerSuite());"); - } - } - p.println("return suite;"); - p.popIndent(); p.println("}"); }