Update of /cvsroot/junit/junit/src/org/junit/internal/runners In directory sc8-pr-cvs6.sourceforge.net:/tmp/cvs-serv19867/src/org/junit/internal/runners Added Files: TestClassRunner.java ErrorReportingRunner.java TextListener.java CompositeRunner.java TestIntrospector.java MethodValidator.java TestClassMethodsRunner.java TestMethodRunner.java OldTestClassRunner.java package-info.java InitializationError.java EmptyDescription.java BeforeAndAfterRunner.java Log Message: Created a separate src folder for sources, and adjusted Eclipse classpath and build file accordingly --- NEW FILE: TestClassRunner.java --- package org.junit.internal.runners; import org.junit.AfterClass; import org.junit.BeforeClass; 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; import org.junit.runner.notification.Failure; public class TestClassRunner extends Runner implements Filterable, Sortable { protected final Runner fEnclosedRunner; private final Class<?> fTestClass; public TestClassRunner(Class<?> klass) throws InitializationError { this(klass, new TestClassMethodsRunner(klass)); } public TestClassRunner(Class<?> klass, Runner runner) throws InitializationError { fTestClass= klass; fEnclosedRunner= runner; MethodValidator methodValidator= new MethodValidator(klass); validate(methodValidator); methodValidator.assertValid(); } // TODO: this is parallel to passed-in runner protected void validate(MethodValidator methodValidator) { methodValidator.validateAllMethods(); } @Override public void run(final RunNotifier notifier) { BeforeAndAfterRunner runner = new BeforeAndAfterRunner(getTestClass(), BeforeClass.class, AfterClass.class, null) { @Override protected void runUnprotected() { fEnclosedRunner.run(notifier); } // TODO: looks very similar to other method of BeforeAfter, now @Override protected void addFailure(Throwable targetException) { notifier.fireTestFailure(new Failure(getDescription(), targetException)); } }; runner.runProtected(); } @Override public Description getDescription() { return fEnclosedRunner.getDescription(); } // TODO: good behavior when createTest fails // TODO: dup? public void filter(Filter filter) throws NoTestsRemainException { filter.apply(fEnclosedRunner); } public void sort(Sorter sorter) { sorter.apply(fEnclosedRunner); } protected Class<?> getTestClass() { return fTestClass; } } --- NEW FILE: ErrorReportingRunner.java --- package org.junit.internal.runners; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.Failure; public class ErrorReportingRunner extends Runner { private final Description fDescription; private final Throwable fCause; public ErrorReportingRunner(Description description, Throwable cause) { fDescription= description; fCause= cause; } @Override public Description getDescription() { return fDescription; } // TODO: this is duplicated in TestClassMethodsRunner @Override public void run(RunNotifier notifier) { notifier.fireTestStarted(fDescription); notifier.fireTestFailure(new Failure(fDescription, fCause)); notifier.fireTestFinished(fDescription); } } --- NEW FILE: TextListener.java --- package org.junit.internal.runners; import java.io.PrintStream; import java.text.NumberFormat; import org.junit.runner.Description; import org.junit.runner.Result; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; public class TextListener extends RunListener { private final PrintStream fWriter; public TextListener() { this(System.out); } public TextListener(PrintStream writer) { this.fWriter= writer; } @Override public void testRunFinished(Result result) { printHeader(result.getRunTime()); printFailures(result); printFooter(result); } @Override public void testStarted(Description description) { fWriter.append('.'); } @Override public void testFailure(Failure failure) { fWriter.append('E'); } @Override public void testIgnored(Description description) { fWriter.append('I'); } /* * Internal methods */ private PrintStream getWriter() { return fWriter; } protected void printHeader(long runTime) { getWriter().println(); getWriter().println("Time: " + elapsedTimeAsString(runTime)); } protected void printFailures(Result result) { if (result.getFailureCount() == 0) return; if (result.getFailureCount() == 1) getWriter().println("There was " + result.getFailureCount() + " failure:"); else getWriter().println("There were " + result.getFailureCount() + " failures:"); int i= 1; for (Failure each : result.getFailures()) printFailure(each, i++); } protected void printFailure(Failure failure, int count) { printFailureHeader(failure, count); printFailureTrace(failure); } protected void printFailureHeader(Failure failure, int count) { getWriter().println(count + ") " + failure.getTestHeader()); } protected void printFailureTrace(Failure failure) { getWriter().print(failure.getTrace()); } protected void printFooter(Result result) { if (result.wasSuccessful()) { getWriter().println(); getWriter().print("OK"); getWriter().println(" (" + result.getRunCount() + " test" + (result.getRunCount() == 1 ? "" : "s") + ")"); } else { getWriter().println(); getWriter().println("FAILURES!!!"); getWriter().println("Tests run: " + result.getRunCount() + ", Failures: " + result.getFailureCount()); } getWriter().println(); } /** * Returns the formatted string of the elapsed time. Duplicated from * BaseTestRunner. Fix it. */ protected String elapsedTimeAsString(long runTime) { return NumberFormat.getInstance().format((double) runTime / 1000); } } --- NEW FILE: CompositeRunner.java --- package org.junit.internal.runners; import java.util.ArrayList; 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 CompositeRunner extends Runner implements Filterable, Sortable { private final List<Runner> fRunners= new ArrayList<Runner>(); private final String fName; public CompositeRunner(String name) { fName= name; } @Override public void run(RunNotifier notifier) { for (Runner each : fRunners) each.run(notifier); } @Override public Description getDescription() { Description spec= Description.createSuiteDescription(fName); for (Runner runner : fRunners) { spec.addChild(runner.getDescription()); } return spec; } public List<Runner> getRunners() { return fRunners; } public void addAll(List<? extends Runner> runners) { fRunners.addAll(runners); } public void add(Runner runner) { fRunners.add(runner); } public void filter(Filter filter) throws NoTestsRemainException { for (Iterator<Runner> iter= fRunners.iterator(); iter.hasNext();) { Runner runner= iter.next(); if (filter.shouldRun(runner.getDescription())) { filter.apply(runner); } else { iter.remove(); } } } protected String getName() { return fName; } public void sort(final Sorter sorter) { Collections.sort(fRunners, new Comparator<Runner>() { public int compare(Runner o1, Runner o2) { return sorter.compare(o1.getDescription(), o2.getDescription()); } }); for (Runner each : fRunners) { sorter.apply(each); } } } --- NEW FILE: TestIntrospector.java --- package org.junit.internal.runners; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.Test.None; public class TestIntrospector { private final Class< ?> fTestClass; public TestIntrospector(Class<?> testClass) { fTestClass= testClass; } public List<Method> getTestMethods(Class<? extends Annotation> annotationClass) { List<Method> results= new ArrayList<Method>(); for (Class<?> eachClass : getSuperClasses(fTestClass)) { 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; } public boolean isIgnored(Method eachMethod) { return eachMethod.getAnnotation(Ignore.class) != null; } 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; } long getTimeout(Method method) { Test annotation= method.getAnnotation(Test.class); long timeout= annotation.timeout(); return timeout; } Class<? extends Throwable> expectedException(Method method) { Test annotation= method.getAnnotation(Test.class); if (annotation.expected() == None.class) return null; else return annotation.expected(); } } --- NEW FILE: MethodValidator.java --- package org.junit.internal.runners; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class MethodValidator { private final TestIntrospector fIntrospector; private final List<Throwable> fErrors= new ArrayList<Throwable>(); private final Class<?> fTestClass; public MethodValidator(Class<?> testClass) { fTestClass= testClass; fIntrospector= new TestIntrospector(testClass); } public void validateInstanceMethods() { validateTestMethods(After.class, false); validateTestMethods(Before.class, false); validateTestMethods(Test.class, false); } public void validateStaticMethods() { validateTestMethods(BeforeClass.class, true); validateTestMethods(AfterClass.class, true); } // TODO Ugly API--one method should do both public List<Throwable> validateAllMethods() { validateNoArgConstructor(); validateStaticMethods(); validateInstanceMethods(); return fErrors; } public void assertValid() throws InitializationError { if (!fErrors.isEmpty()) throw new InitializationError(fErrors); } public void validateNoArgConstructor() { try { fTestClass.getConstructor(); } catch (Exception e) { fErrors.add(new Exception("Test class should have public zero-argument constructor", e)); } } private void validateTestMethods(Class<? extends Annotation> annotation, boolean isStatic) { List<Method> methods= fIntrospector.getTestMethods(annotation); for (Method each : methods) { if (Modifier.isStatic(each.getModifiers()) != isStatic) { String state= isStatic ? "should" : "should not"; fErrors.add(new Exception("Method " + each.getName() + "() " + state + " be static")); } if (!Modifier.isPublic(each.getDeclaringClass().getModifiers())) fErrors.add(new Exception("Class " + each.getDeclaringClass().getName() + " should be public")); if (!Modifier.isPublic(each.getModifiers())) fErrors.add(new Exception("Method " + each.getName() + " should be public")); if (each.getReturnType() != Void.TYPE) fErrors.add(new Exception("Method " + each.getName() + " should be void")); if (each.getParameterTypes().length != 0) fErrors.add(new Exception("Method " + each.getName() + " should have no parameters")); } } } --- NEW FILE: TestClassMethodsRunner.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.Test; 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.Failure; import org.junit.runner.notification.RunNotifier; public class TestClassMethodsRunner extends Runner implements Filterable, Sortable { private final List<Method> fTestMethods; private final Class<?> fTestClass; // This assumes that some containing runner will perform validation of the test methods public TestClassMethodsRunner(Class<?> klass) { fTestClass= klass; fTestMethods= new TestIntrospector(getTestClass()).getTestMethods(Test.class); } @Override public void run(RunNotifier notifier) { if (fTestMethods.isEmpty()) testAborted(notifier, getDescription(), new Exception("No runnable methods")); for (Method method : fTestMethods) invokeTestMethod(method, notifier); } private void testAborted(RunNotifier notifier, Description description, Throwable cause) { // TODO: duped! // TODO: envious notifier.fireTestStarted(description); notifier.fireTestFailure(new Failure(description, cause)); notifier.fireTestFinished(description); } @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) { testAborted(notifier, methodDescription(method), e.getCause()); return; } catch (Exception e) { testAborted(notifier, methodDescription(method), e); return; } createMethodRunner(test, method, notifier).run(); } protected TestMethodRunner createMethodRunner(Object test, Method method, RunNotifier notifier) { return new TestMethodRunner(test, method, notifier, methodDescription(method)); } protected String testName(Method method) { return method.getName(); } protected Description methodDescription(Method method) { return Description.createTestDescription(getTestClass(), 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 Class<?> getTestClass() { return fTestClass; } } --- NEW FILE: TestMethodRunner.java --- package org.junit.internal.runners; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; 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.After; import org.junit.Before; import org.junit.runner.Description; import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.Failure; public class TestMethodRunner extends BeforeAndAfterRunner { private final Object fTest; private final Method fMethod; private final RunNotifier fNotifier; private final TestIntrospector fTestIntrospector; private final Description fDescription; public TestMethodRunner(Object test, Method method, RunNotifier notifier, Description description) { super(test.getClass(), Before.class, After.class, test); fTest= test; fMethod= method; fNotifier= notifier; fTestIntrospector= new TestIntrospector(test.getClass()); fDescription= description; } public void run() { if (fTestIntrospector.isIgnored(fMethod)) { fNotifier.fireTestIgnored(fDescription); return; } fNotifier.fireTestStarted(fDescription); try { long timeout= fTestIntrospector.getTimeout(fMethod); if (timeout > 0) runWithTimeout(timeout); else runMethod(); } finally { fNotifier.fireTestFinished(fDescription); } } private void runWithTimeout(long timeout) { ExecutorService service= Executors.newSingleThreadExecutor(); Callable<Object> callable= new Callable<Object>() { public Object call() throws Exception { runMethod(); return null; } }; Future<Object> result= service.submit(callable); service.shutdown(); try { boolean terminated= service.awaitTermination(timeout, TimeUnit.MILLISECONDS); if (!terminated) service.shutdownNow(); result.get(timeout, 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); } } private void runMethod() { runProtected(); } @Override protected void runUnprotected() { try { executeMethodBody(); if (expectsException()) addFailure(new AssertionError("Expected exception: " + expectedException().getName())); } catch (InvocationTargetException e) { Throwable actual= e.getTargetException(); if (!expectsException()) addFailure(actual); else if (isUnexpected(actual)) { String message= "Unexpected exception, expected<" + expectedException().getName() + "> but was<" + actual.getClass().getName() + ">"; addFailure(new Exception(message, actual)); } } catch (Throwable e) { addFailure(e); } } protected void executeMethodBody() throws IllegalAccessException, InvocationTargetException { fMethod.invoke(fTest); } @Override protected void addFailure(Throwable e) { fNotifier.fireTestFailure(new Failure(fDescription, e)); } private boolean expectsException() { return expectedException() != null; } private Class<? extends Throwable> expectedException() { return fTestIntrospector.expectedException(fMethod); } private boolean isUnexpected(Throwable exception) { return ! expectedException().isAssignableFrom(exception.getClass()); } } --- NEW FILE: OldTestClassRunner.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.RunNotifier; import org.junit.runner.notification.Failure; public class OldTestClassRunner extends Runner { private Test fTest; @SuppressWarnings("unchecked") public OldTestClassRunner(Class<?> klass) { this(new TestSuite(klass.asSubclass(TestCase.class))); } public OldTestClassRunner(Test test) { super(); fTest= test; } @Override public void run(RunNotifier notifier) { TestResult result= new TestResult(); result.addListener(getListener(notifier)); fTest.run(result); } private TestListener getListener(final RunNotifier notifier) { return new TestListener() { public void endTest(Test test) { // TODO: uncovered notifier.fireTestFinished(asDescription(test)); } public void startTest(Test test) { notifier.fireTestStarted(asDescription(test)); } // Implement junit.framework.TestListener //TODO method not covered public void addError(Test test, Throwable t) { Failure failure= new Failure(asDescription(test), t); notifier.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(); } //TODO method not covered public void addFailure(Test test, AssertionFailedError t) { addError(test, t); } }; } @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: package-info.java --- /** * Provides implementations of {@link org.junit.runner.Runner} * * @since 4.0 */ package org.junit.internal.runners; --- NEW FILE: InitializationError.java --- package org.junit.internal.runners; import java.util.Arrays; import java.util.List; public class InitializationError extends Exception { private static final long serialVersionUID= 1L; private final List<Throwable> fErrors; public InitializationError(List<Throwable> errors) { fErrors= errors; } public InitializationError(Throwable... errors) { this(Arrays.asList(errors)); } public InitializationError(String string) { this(new Exception(string)); } public List<Throwable> getCauses() { return fErrors; } } --- NEW FILE: EmptyDescription.java --- package org.junit.internal.runners; import org.junit.runner.Description; public class EmptyDescription extends Description { public EmptyDescription() { super("No Tests"); } @Override public boolean equals(Object obj) { return getClass().equals(obj.getClass()); } @Override public int hashCode() { return 0; } } --- NEW FILE: BeforeAndAfterRunner.java --- package org.junit.internal.runners; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; public abstract class BeforeAndAfterRunner { private static class FailedBefore extends Exception { private static final long serialVersionUID= 1L; } private final Class<? extends Annotation> fBeforeAnnotation; private final Class<? extends Annotation> fAfterAnnotation; private TestIntrospector fTestIntrospector; private Object fTest; public BeforeAndAfterRunner(Class<?> testClass, Class<? extends Annotation> beforeAnnotation, Class<? extends Annotation> afterAnnotation, Object test) { fBeforeAnnotation= beforeAnnotation; fAfterAnnotation= afterAnnotation; fTestIntrospector= new TestIntrospector(testClass); fTest= test; } public void runProtected() { try { runBefores(); runUnprotected(); } catch (FailedBefore e) { } finally { runAfters(); } } protected abstract void runUnprotected(); protected abstract void addFailure(Throwable targetException); // Stop after first failed @Before private void runBefores() throws FailedBefore { try { List<Method> befores= fTestIntrospector.getTestMethods(fBeforeAnnotation); for (Method before : befores) invokeMethod(before); } catch (InvocationTargetException e) { addFailure(e.getTargetException()); throw new FailedBefore(); } catch (Throwable e) { addFailure(e); throw new FailedBefore(); } } // Try to run all @Afters regardless private void runAfters() { List<Method> afters= fTestIntrospector.getTestMethods(fAfterAnnotation); for (Method after : afters) try { invokeMethod(after); } catch (InvocationTargetException e) { addFailure(e.getTargetException()); } catch (Throwable e) { addFailure(e); // Untested, but seems impossible } } private void invokeMethod(Method method) throws Exception { method.invoke(fTest); } } |