| /* |
| * Copyright 2000-2009 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 com.intellij.junit4; |
| |
| import com.intellij.junit3.TestRunnerUtil; |
| import org.junit.Ignore; |
| import org.junit.internal.AssumptionViolatedException; |
| import org.junit.internal.requests.ClassRequest; |
| import org.junit.internal.runners.model.EachTestNotifier; |
| import org.junit.runner.Description; |
| import org.junit.runner.Request; |
| import org.junit.runner.RunWith; |
| import org.junit.runner.Runner; |
| import org.junit.runner.manipulation.Filter; |
| import org.junit.runner.notification.RunNotifier; |
| import org.junit.runners.BlockJUnit4ClassRunner; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.model.FrameworkMethod; |
| |
| import java.io.BufferedReader; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.text.MessageFormat; |
| import java.util.*; |
| |
| public class JUnit4TestRunnerUtil { |
| /** |
| * @noinspection HardCodedStringLiteral |
| */ |
| private static final ResourceBundle ourBundle = ResourceBundle.getBundle("RuntimeBundle"); |
| |
| public static Request buildRequest(String[] suiteClassNames, String name, boolean notForked) { |
| if (suiteClassNames.length == 0) { |
| return null; |
| } |
| Vector result = new Vector(); |
| for (int i = 0; i < suiteClassNames.length; i++) { |
| String suiteClassName = suiteClassNames[i]; |
| if (suiteClassName.charAt(0) == '@') { |
| // all tests in the package specified |
| try { |
| final Map classMethods = new HashMap(); |
| BufferedReader reader = new BufferedReader(new FileReader(suiteClassName.substring(1))); |
| try { |
| final String packageName = reader.readLine(); |
| if (packageName == null) return null; |
| |
| final String categoryName = reader.readLine(); |
| final Class category = categoryName != null && categoryName.length() > 0 ? loadTestClass(categoryName) : null; |
| String line; |
| |
| while ((line = reader.readLine()) != null) { |
| String className = line; |
| final int idx = line.indexOf(','); |
| if (idx != -1) { |
| className = line.substring(0, idx); |
| Set methodNames = (Set)classMethods.get(className); |
| if (methodNames == null) { |
| methodNames = new HashSet(); |
| classMethods.put(className, methodNames); |
| } |
| methodNames.add(line.substring(idx + 1)); |
| |
| } |
| appendTestClass(result, className); |
| } |
| String suiteName = packageName.length() == 0 ? "<default package>": packageName; |
| Class[] classes = getArrayOfClasses(result); |
| if (classes.length == 0) { |
| System.out.println(TestRunnerUtil.testsFoundInPackageMesage(0, suiteName)); |
| return null; |
| } |
| Request allClasses; |
| try { |
| Class.forName("org.junit.runner.Computer"); |
| allClasses = JUnit46ClassesRequestBuilder.getClassesRequest(suiteName, classes, classMethods, category); |
| } |
| catch (ClassNotFoundException e) { |
| allClasses = getClassRequestsUsing44API(suiteName, classes); |
| } |
| catch (NoSuchMethodError e) { |
| allClasses = getClassRequestsUsing44API(suiteName, classes); |
| } |
| |
| return classMethods.isEmpty() ? allClasses : allClasses.filterWith(new Filter() { |
| public boolean shouldRun(Description description) { |
| if (description.isTest()) { |
| final Set methods = (Set)classMethods.get(JUnit4ReflectionUtil.getClassName(description)); |
| return methods == null || methods.contains(JUnit4ReflectionUtil.getMethodName(description)); |
| } |
| return true; |
| } |
| |
| public String describe() { |
| return "Tests"; |
| } |
| }); |
| } |
| finally { |
| reader.close(); |
| } |
| } |
| catch (IOException e) { |
| e.printStackTrace(); |
| System.exit(1); |
| } |
| } |
| else { |
| int index = suiteClassName.indexOf(','); |
| if (index != -1) { |
| final Class clazz = loadTestClass(suiteClassName.substring(0, index)); |
| final String methodName = suiteClassName.substring(index + 1); |
| final RunWith clazzAnnotation = (RunWith)clazz.getAnnotation(RunWith.class); |
| final Description testMethodDescription = Description.createTestDescription(clazz, methodName); |
| if (clazzAnnotation == null) { //do not override external runners |
| try { |
| final Method method = clazz.getMethod(methodName, null); |
| if (method != null && notForked && (method.getAnnotation(Ignore.class) != null || clazz.getAnnotation(Ignore.class) != null)) { //override ignored case only |
| final Request classRequest = createIgnoreIgnoredClassRequest(clazz, true); |
| final Filter ignoredTestFilter = Filter.matchMethodDescription(testMethodDescription); |
| return classRequest.filterWith(new Filter() { |
| public boolean shouldRun(Description description) { |
| return ignoredTestFilter.shouldRun(description); |
| } |
| |
| public String describe() { |
| return "Ignored " + methodName; |
| } |
| }); |
| } |
| } |
| catch (Exception ignored) { |
| //return simple method runner |
| } |
| } else { |
| final Request request = getParameterizedRequest(name, clazz, methodName, clazzAnnotation); |
| if (request != null) { |
| return request; |
| } |
| } |
| try { |
| if (clazz.getMethod("suite", new Class[0]) != null && !methodName.equals("suite")) { |
| return Request.classWithoutSuiteMethod(clazz).filterWith(testMethodDescription); |
| } |
| } |
| catch (Throwable e) { |
| //ignore |
| } |
| |
| final Filter methodFilter; |
| try { |
| methodFilter = Filter.matchMethodDescription(testMethodDescription); |
| } |
| catch (NoSuchMethodError e) { |
| return Request.method(clazz, methodName); |
| } |
| return Request.aClass(clazz).filterWith(new Filter() { |
| public boolean shouldRun(Description description) { |
| if (description.isTest() && description.getDisplayName().startsWith("warning(junit.framework.TestSuite$")) { |
| return true; |
| } |
| |
| return methodFilter.shouldRun(description); |
| } |
| |
| public String describe() { |
| return methodFilter.describe(); |
| } |
| }); |
| } else if (name != null && suiteClassNames.length == 1) { |
| final Class clazz = loadTestClass(suiteClassName); |
| if (clazz != null) { |
| final RunWith clazzAnnotation = (RunWith)clazz.getAnnotation(RunWith.class); |
| final Request request = getParameterizedRequest(name, clazz, null, clazzAnnotation); |
| if (request != null) { |
| return request; |
| } |
| } |
| } |
| appendTestClass(result, suiteClassName); |
| } |
| } |
| |
| if (result.size() == 1) { |
| final Class clazz = (Class)result.get(0); |
| try { |
| if (clazz.getAnnotation(Ignore.class) != null) { //override ignored case only |
| return createIgnoreIgnoredClassRequest(clazz, false); |
| } |
| } |
| catch (ClassNotFoundException e) { |
| //return simple class runner |
| } |
| return Request.aClass(clazz); |
| } |
| return Request.classes(getArrayOfClasses(result)); |
| } |
| |
| private static Request getParameterizedRequest(String name, Class clazz, String methodName, RunWith clazzAnnotation) { |
| if (clazzAnnotation == null) return null; |
| |
| final Class runnerClass = clazzAnnotation.value(); |
| if (Parameterized.class.isAssignableFrom(runnerClass)) { |
| try { |
| Class.forName("org.junit.runners.BlockJUnit4ClassRunner"); //ignore for junit4.4 and < |
| return Request.runner(new SelectedParameterizedRunner(clazz, name, methodName, runnerClass)); |
| } |
| catch (Throwable throwable) { |
| //return simple method runner |
| } |
| } |
| return null; |
| } |
| |
| private static Request createIgnoreIgnoredClassRequest(final Class clazz, final boolean recursively) throws ClassNotFoundException { |
| Class.forName("org.junit.runners.BlockJUnit4ClassRunner"); //ignore IgnoreIgnored for junit4.4 and < |
| return new ClassRequest(clazz) { |
| public Runner getRunner() { |
| try { |
| return new IgnoreIgnoredTestJUnit4ClassRunner(clazz, recursively); |
| } |
| catch (Exception ignored) { |
| //return super runner |
| } |
| return super.getRunner(); |
| } |
| }; |
| } |
| |
| private static Request getClassRequestsUsing44API(String suiteName, Class[] classes) { |
| Request allClasses; |
| try { |
| Class.forName("org.junit.internal.requests.ClassesRequest"); |
| allClasses = JUnit4ClassesRequestBuilder.getClassesRequest(suiteName, classes); |
| } |
| catch (ClassNotFoundException e1) { |
| allClasses = JUnit45ClassesRequestBuilder.getClassesRequest(suiteName, classes); |
| } |
| return allClasses; |
| } |
| |
| private static void appendTestClass(Vector result, String className) { |
| final Class aClass = loadTestClass(className); |
| if (!result.contains(aClass)) { //do not append classes twice: rerun failed tests from one test suite |
| result.addElement(aClass); |
| } |
| } |
| |
| private static Class[] getArrayOfClasses(Vector result) { |
| Class[] classes = new Class[result.size()]; |
| for (int i = 0; i < result.size(); i++) { |
| classes[i] = (Class)result.get(i); |
| } |
| return classes; |
| } |
| |
| private static Class loadTestClass(String suiteClassName) { |
| try { |
| return Class.forName(suiteClassName, false, JUnit4TestRunnerUtil.class.getClassLoader()); |
| } |
| catch (ClassNotFoundException e) { |
| String clazz = e.getMessage(); |
| if (clazz == null) { |
| clazz = suiteClassName; |
| } |
| System.err.print(MessageFormat.format(ourBundle.getString("junit.class.not.found"), new Object[]{clazz})); |
| System.exit(1); |
| } |
| catch (Exception e) { |
| System.err.println(MessageFormat.format(ourBundle.getString("junit.cannot.instantiate.tests"), new Object[]{e.toString()})); |
| System.exit(1); |
| } |
| return null; |
| } |
| |
| public static String testsFoundInPackageMesage(int testCount, String name) { |
| return MessageFormat.format(ourBundle.getString("tests.found.in.package"), new Object[]{new Integer(testCount), name}); |
| } |
| |
| |
| private static class IgnoreIgnoredTestJUnit4ClassRunner extends BlockJUnit4ClassRunner { |
| private final boolean myRecursively; |
| |
| public IgnoreIgnoredTestJUnit4ClassRunner(Class clazz, boolean recursively) throws Exception { |
| super(clazz); |
| myRecursively = recursively; |
| } |
| |
| protected void runChild(FrameworkMethod method, RunNotifier notifier) { |
| if (!myRecursively){ |
| super.runChild(method, notifier); |
| return; |
| } |
| final Description description = describeChild(method); |
| final EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description); |
| eachNotifier.fireTestStarted(); |
| try { |
| methodBlock(method).evaluate(); |
| } |
| catch (AssumptionViolatedException e) { |
| eachNotifier.addFailedAssumption(e); |
| } |
| catch (Throwable e) { |
| eachNotifier.addFailure(e); |
| } |
| finally { |
| eachNotifier.fireTestFinished(); |
| } |
| } |
| } |
| |
| private static class SelectedParameterizedRunner extends Parameterized { |
| private final String myName; |
| private final String myMethodName; |
| private Parameterized myRunnerClass; |
| |
| public SelectedParameterizedRunner(Class clazz, String name, String methodName, Class runnerClass) throws Throwable { |
| super(clazz); |
| myName = name; |
| myMethodName = methodName; |
| myRunnerClass = (Parameterized)runnerClass.getConstructor(new Class[] {Class.class}).newInstance(new Object[]{clazz}); |
| } |
| |
| protected List getChildren() { |
| List children; |
| try { |
| Method getChildren = Parameterized.class.getDeclaredMethod("getChildren", new Class[0]); |
| getChildren.setAccessible(true); |
| children = (List)getChildren.invoke(myRunnerClass, new Object[0]); |
| } |
| catch (Throwable e) { |
| children = super.getChildren(); |
| } |
| |
| //filter by params |
| if (myName != null) { |
| for (Iterator iterator = children.iterator(); iterator.hasNext(); ) { |
| Object child = iterator.next(); |
| try { |
| Class aClass = child.getClass(); |
| Field f; |
| try { |
| f = aClass.getDeclaredField("fName"); |
| } |
| catch (NoSuchFieldException e) { |
| try { |
| f = aClass.getDeclaredField("name"); |
| } |
| catch (NoSuchFieldException e1) { |
| continue; |
| } |
| } |
| f.setAccessible(true); |
| String fName = (String)f.get(child); |
| if (!myName.equals(fName)) { |
| iterator.remove(); |
| } |
| } |
| catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| if (children.isEmpty()) { |
| System.err.println("No tests were found by passed name: " + myName); |
| System.exit(1); |
| } |
| } |
| |
| //filter only selected method |
| if (myMethodName != null) { |
| for (int i = 0; i < children.size(); i++) { |
| try { |
| final BlockJUnit4ClassRunner child = (BlockJUnit4ClassRunner)children.get(i); |
| final Method getChildrenMethod = BlockJUnit4ClassRunner.class.getDeclaredMethod("getChildren", new Class[0]); |
| getChildrenMethod.setAccessible(true); |
| final List list = (List)getChildrenMethod.invoke(child, new Object[0]); |
| for (Iterator iterator = list.iterator(); iterator.hasNext(); ) { |
| final FrameworkMethod description = (FrameworkMethod)iterator.next(); |
| if (!description.getName().equals(myMethodName)) { |
| iterator.remove(); |
| } |
| } |
| } |
| catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| return children; |
| } |
| } |
| } |