Update of /cvsroot/junit/junit/src/org/junit/internal/runners In directory sc8-pr-cvs6.sourceforge.net:/tmp/cvs-serv30152/src/org/junit/internal/runners Modified Files: CompositeRunner.java MethodValidator.java Added Files: TestClass.java MethodRoadie.java JUnit38ClassRunner.java TestMethod.java JUnit4ClassRunner.java FailedBefore.java ClassRoadie.java Removed Files: TestIntrospector.java TextListener.java TestClassRunner.java TestClassMethodsRunner.java OldTestClassRunner.java TestMethodRunner.java BeforeAndAfterRunner.java Log Message: Runner rearchitecting to begin 4.4 development --- NEW FILE: TestClass.java --- package org.junit.internal.runners; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class TestClass { private final Class<?> fClass; public TestClass(Class<?> klass) { fClass= klass; } List<Method> getTestMethods() { return getAnnotatedMethods(Test.class); } public List<Method> getBefores() { return getAnnotatedMethods(BeforeClass.class); } public List<Method> getAfters() { return getAnnotatedMethods(AfterClass.class); } public List<Method> getAnnotatedMethods(Class<? extends Annotation> annotationClass) { List<Method> results= new ArrayList<Method>(); for (Class<?> eachClass : getSuperClasses(fClass)) { Method[] methods= eachClass.getDeclaredMethods(); for (Method eachMethod : methods) { Annotation annotation= eachMethod.getAnnotation(annotationClass); if (annotation != null && ! isShadowed(eachMethod, results)) results.add(eachMethod); } } if (runsTopToBottom(annotationClass)) Collections.reverse(results); return results; } private boolean runsTopToBottom(Class< ? extends Annotation> annotation) { return annotation.equals(Before.class) || annotation.equals(BeforeClass.class); } private boolean isShadowed(Method method, List<Method> results) { for (Method each : results) { if (isShadowed(method, each)) return true; } return false; } private boolean isShadowed(Method current, Method previous) { if (! previous.getName().equals(current.getName())) return false; if (previous.getParameterTypes().length != current.getParameterTypes().length) return false; for (int i= 0; i < previous.getParameterTypes().length; i++) { if (! previous.getParameterTypes()[i].equals(current.getParameterTypes()[i])) return false; } return true; } private List<Class<?>> getSuperClasses(Class< ?> testClass) { ArrayList<Class<?>> results= new ArrayList<Class<?>>(); Class<?> current= testClass; while (current != null) { results.add(current); current= current.getSuperclass(); } return results; } public Constructor<?> getConstructor() throws SecurityException, NoSuchMethodException { return fClass.getConstructor(); } public Class<?> getJavaClass() { return fClass; } public String getName() { return fClass.getName(); } } --- NEW FILE: MethodRoadie.java --- package org.junit.internal.runners; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; public class MethodRoadie { private final Object fTest; private final Method fMethod; private final RunNotifier fNotifier; private final Description fDescription; private TestMethod fTestMethod; public MethodRoadie(Object test, Method method, RunNotifier notifier, Description description, TestClass testClass) { fTest= test; fMethod= method; fNotifier= notifier; fDescription= description; fTestMethod= new TestMethod(method, testClass); } public void run() { if (fTestMethod.isIgnored()) { fNotifier.fireTestIgnored(fDescription); return; } fNotifier.fireTestStarted(fDescription); try { long timeout= fTestMethod.getTimeout(); if (timeout > 0) runWithTimeout(timeout); else runTest(); } finally { fNotifier.fireTestFinished(fDescription); } } private void runWithTimeout(long timeout) { ExecutorService service= Executors.newSingleThreadExecutor(); Callable<Object> callable= new Callable<Object>() { public Object call() throws Exception { runTest(); return null; } }; Future<Object> result= service.submit(callable); service.shutdown(); try { boolean terminated= service.awaitTermination(timeout, TimeUnit.MILLISECONDS); if (!terminated) service.shutdownNow(); result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation } catch (TimeoutException e) { addFailure(new Exception(String.format("test timed out after %d milliseconds", timeout))); } catch (Exception e) { addFailure(e); } } public void runTest() { try { runBefores(); runTestMethod(); } catch (FailedBefore e) { } finally { runAfters(); } } protected void runTestMethod() { try { fMethod.invoke(fTest); if (fTestMethod.expectsException()) addFailure(new AssertionError("Expected exception: " + fTestMethod.getExpectedException().getName())); } catch (InvocationTargetException e) { Throwable actual= e.getTargetException(); if (!fTestMethod.expectsException()) addFailure(actual); else if (fTestMethod.isUnexpected(actual)) { String message= "Unexpected exception, expected<" + fTestMethod.getExpectedException().getName() + "> but was<" + actual.getClass().getName() + ">"; addFailure(new Exception(message, actual)); } } catch (Throwable e) { addFailure(e); } } private void runBefores() throws FailedBefore { try { List<Method> befores= fTestMethod.getBefores(); for (Method before : befores) before.invoke(fTest); } catch (InvocationTargetException e) { addFailure(e.getTargetException()); throw new FailedBefore(); } catch (Throwable e) { addFailure(e); throw new FailedBefore(); } } private void runAfters() { List<Method> afters= fTestMethod.getAfters(); for (Method after : afters) try { after.invoke(fTest); } catch (InvocationTargetException e) { addFailure(e.getTargetException()); } catch (Throwable e) { addFailure(e); // Untested, but seems impossible } } protected void addFailure(Throwable e) { fNotifier.fireTestFailure(new Failure(fDescription, e)); } } --- NEW FILE: JUnit38ClassRunner.java --- package org.junit.internal.runners; import junit.extensions.TestDecorator; import junit.framework.AssertionFailedError; import junit.framework.JUnit4TestAdapter; import junit.framework.JUnit4TestCaseFacade; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestListener; import junit.framework.TestResult; import junit.framework.TestSuite; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; public class JUnit38ClassRunner extends Runner { private static final class OldTestClassAdaptingListener implements TestListener { private final RunNotifier fNotifier; private OldTestClassAdaptingListener(RunNotifier notifier) { fNotifier= notifier; } public void endTest(Test test) { fNotifier.fireTestFinished(asDescription(test)); } public void startTest(Test test) { fNotifier.fireTestStarted(asDescription(test)); } // Implement junit.framework.TestListener public void addError(Test test, Throwable t) { Failure failure= new Failure(asDescription(test), t); fNotifier.fireTestFailure(failure); } private Description asDescription(Test test) { if (test instanceof JUnit4TestCaseFacade) { JUnit4TestCaseFacade facade= (JUnit4TestCaseFacade) test; return facade.getDescription(); } return Description.createTestDescription(test.getClass(), getName(test)); } private String getName(Test test) { if (test instanceof TestCase) return ((TestCase) test).getName(); else return test.toString(); } public void addFailure(Test test, AssertionFailedError t) { addError(test, t); } } private Test fTest; @SuppressWarnings("unchecked") public JUnit38ClassRunner(Class<?> klass) { this(new TestSuite(klass.asSubclass(TestCase.class))); } public JUnit38ClassRunner(Test test) { super(); fTest= test; } @Override public void run(RunNotifier notifier) { TestResult result= new TestResult(); result.addListener(createAdaptingListener(notifier)); fTest.run(result); } public static TestListener createAdaptingListener(final RunNotifier notifier) { return new OldTestClassAdaptingListener(notifier); } @Override public Description getDescription() { return makeDescription(fTest); } private Description makeDescription(Test test) { if (test instanceof TestCase) { TestCase tc= (TestCase) test; return Description.createTestDescription(tc.getClass(), tc.getName()); } else if (test instanceof TestSuite) { TestSuite ts= (TestSuite) test; String name= ts.getName() == null ? "" : ts.getName(); Description description= Description.createSuiteDescription(name); int n= ts.testCount(); for (int i= 0; i < n; i++) description.addChild(makeDescription(ts.testAt(i))); return description; } else if (test instanceof JUnit4TestAdapter) { JUnit4TestAdapter adapter= (JUnit4TestAdapter) test; return adapter.getDescription(); } else if (test instanceof TestDecorator) { TestDecorator decorator= (TestDecorator) test; return makeDescription(decorator.getTest()); } else { // This is the best we can do in this case return Description.createSuiteDescription(test.getClass()); } } } --- NEW FILE: JUnit4ClassRunner.java --- package org.junit.internal.runners; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.Filterable; import org.junit.runner.manipulation.NoTestsRemainException; import org.junit.runner.manipulation.Sortable; import org.junit.runner.manipulation.Sorter; import org.junit.runner.notification.RunNotifier; public class JUnit4ClassRunner extends Runner implements Filterable, Sortable { private final List<Method> fTestMethods; private TestClass fTestClass; public JUnit4ClassRunner(Class<?> klass) throws InitializationError { fTestClass= new TestClass(klass); fTestMethods= fTestClass.getTestMethods(); validate(); } protected void validate() throws InitializationError { MethodValidator methodValidator= new MethodValidator(fTestClass); methodValidator.validateMethodsForDefaultRunner(); methodValidator.assertValid(); } @Override public void run(final RunNotifier notifier) { new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() { public void run() { runMethods(notifier); } }).runProtected(); } protected void runMethods(final RunNotifier notifier) { for (Method method : fTestMethods) invokeTestMethod(method, notifier); } @Override public Description getDescription() { Description spec= Description.createSuiteDescription(getName()); List<Method> testMethods= fTestMethods; for (Method method : testMethods) spec.addChild(methodDescription(method)); return spec; } protected String getName() { return getTestClass().getName(); } protected Object createTest() throws Exception { return getTestClass().getConstructor().newInstance(); } protected void invokeTestMethod(Method method, RunNotifier notifier) { Object test; try { test= createTest(); } catch (InvocationTargetException e) { notifier.testAborted(methodDescription(method), e.getCause()); return; } catch (Exception e) { notifier.testAborted(methodDescription(method), e); return; } createMethodRunner(test, method, notifier).run(); } protected MethodRoadie createMethodRunner(Object test, Method method, RunNotifier notifier) { return new MethodRoadie(test, method, notifier, methodDescription(method), fTestClass); } protected String testName(Method method) { return method.getName(); } protected Description methodDescription(Method method) { return Description.createTestDescription(getTestClass().getJavaClass(), testName(method)); } public void filter(Filter filter) throws NoTestsRemainException { for (Iterator<Method> iter= fTestMethods.iterator(); iter.hasNext();) { Method method= iter.next(); if (!filter.shouldRun(methodDescription(method))) iter.remove(); } if (fTestMethods.isEmpty()) throw new NoTestsRemainException(); } public void sort(final Sorter sorter) { Collections.sort(fTestMethods, new Comparator<Method>() { public int compare(Method o1, Method o2) { return sorter.compare(methodDescription(o1), methodDescription(o2)); } }); } protected TestClass getTestClass() { return fTestClass; } } --- NEW FILE: FailedBefore.java --- package org.junit.internal.runners; class FailedBefore extends Exception { private static final long serialVersionUID= 1L; } --- NEW FILE: ClassRoadie.java --- package org.junit.internal.runners; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; public class ClassRoadie { private RunNotifier fNotifier; private TestClass fTestClass; private Description fDescription; private final Runnable fRunnable; public ClassRoadie(RunNotifier notifier, TestClass testClass, Description description, Runnable runnable) { fNotifier= notifier; fTestClass= testClass; fDescription= description; fRunnable= runnable; } protected void runUnprotected() { fRunnable.run(); }; protected void addFailure(Throwable targetException) { fNotifier.fireTestFailure(new Failure(fDescription, targetException)); } public void runProtected() { try { runBefores(); runUnprotected(); } catch (FailedBefore e) { } finally { runAfters(); } } private void runBefores() throws FailedBefore { try { List<Method> befores= fTestClass.getBefores(); for (Method before : befores) before.invoke(null); } catch (InvocationTargetException e) { addFailure(e.getTargetException()); throw new FailedBefore(); } catch (Throwable e) { addFailure(e); throw new FailedBefore(); } } private void runAfters() { List<Method> afters= fTestClass.getAfters(); for (Method after : afters) try { after.invoke(null); } catch (InvocationTargetException e) { addFailure(e.getTargetException()); } catch (Throwable e) { addFailure(e); // Untested, but seems impossible } } } Index: CompositeRunner.java =================================================================== RCS file: /cvsroot/junit/junit/src/org/junit/internal/runners/CompositeRunner.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- CompositeRunner.java 6 Dec 2006 01:22:48 -0000 1.2 +++ CompositeRunner.java 20 Mar 2007 14:43:50 -0000 1.3 @@ -25,6 +25,10 @@ @Override public void run(RunNotifier notifier) { + runChildren(notifier); + } + + protected void runChildren(RunNotifier notifier) { for (Runner each : fRunners) each.run(notifier); } Index: MethodValidator.java =================================================================== RCS file: /cvsroot/junit/junit/src/org/junit/internal/runners/MethodValidator.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- MethodValidator.java 21 Feb 2007 14:46:51 -0000 1.4 +++ MethodValidator.java 20 Mar 2007 14:43:50 -0000 1.5 @@ -13,21 +13,23 @@ import org.junit.Test; public class MethodValidator { - private final TestIntrospector fIntrospector; private final List<Throwable> fErrors= new ArrayList<Throwable>(); - private final Class<?> fTestClass; + private TestClass fTestClass; - public MethodValidator(Class<?> testClass) { - fTestClass= testClass; - fIntrospector= new TestIntrospector(testClass); + public MethodValidator(TestClass testClass) { + fTestClass = testClass; } public void validateInstanceMethods() { validateTestMethods(After.class, false); validateTestMethods(Before.class, false); validateTestMethods(Test.class, false); + + List<Method> methods= fTestClass.getAnnotatedMethods(Test.class); + if (methods.size() == 0) + fErrors.add(new Exception("No runnable methods")); } public void validateStaticMethods() { @@ -57,7 +59,8 @@ private void validateTestMethods(Class<? extends Annotation> annotation, boolean isStatic) { - List<Method> methods= fIntrospector.getTestMethods(annotation); + List<Method> methods= fTestClass.getAnnotatedMethods(annotation); + for (Method each : methods) { if (Modifier.isStatic(each.getModifiers()) != isStatic) { String state= isStatic ? "should" : "should not"; --- TestIntrospector.java DELETED --- --- TextListener.java DELETED --- --- TestClassRunner.java DELETED --- --- TestClassMethodsRunner.java DELETED --- --- OldTestClassRunner.java DELETED --- --- TestMethodRunner.java DELETED --- --- BeforeAndAfterRunner.java DELETED --- |