/* * 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 ? "": 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 { Field f; try { f = child.getClass().getDeclaredField("fName"); } catch (NoSuchFieldException e) { 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; } } }