diff options
Diffstat (limited to 'src/main/java/org/junit/internal/runners')
23 files changed, 1482 insertions, 1089 deletions
diff --git a/src/main/java/org/junit/internal/runners/ClassRoadie.java b/src/main/java/org/junit/internal/runners/ClassRoadie.java index 1f77d37..df1b453 100644 --- a/src/main/java/org/junit/internal/runners/ClassRoadie.java +++ b/src/main/java/org/junit/internal/runners/ClassRoadie.java @@ -4,6 +4,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; +import org.junit.internal.AssumptionViolatedException; import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; @@ -11,69 +12,70 @@ import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated -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; - } +public class ClassRoadie { + private RunNotifier notifier; + private TestClass testClass; + private Description description; + private final Runnable runnable; - protected void runUnprotected() { - fRunnable.run(); - }; + public ClassRoadie(RunNotifier notifier, TestClass testClass, + Description description, Runnable runnable) { + this.notifier = notifier; + this.testClass = testClass; + this.description = description; + this.runnable = runnable; + } - protected void addFailure(Throwable targetException) { - fNotifier.fireTestFailure(new Failure(fDescription, targetException)); - } + protected void runUnprotected() { + runnable.run(); + } - public void runProtected() { - try { - runBefores(); - runUnprotected(); - } catch (FailedBefore e) { - } finally { - runAfters(); - } - } + protected void addFailure(Throwable targetException) { + notifier.fireTestFailure(new Failure(description, targetException)); + } - private void runBefores() throws FailedBefore { - try { - try { - List<Method> befores= fTestClass.getBefores(); - for (Method before : befores) - before.invoke(null); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } - } catch (org.junit.internal.AssumptionViolatedException e) { - throw new FailedBefore(); - } catch (Throwable e) { - addFailure(e); - throw new FailedBefore(); - } - } + public void runProtected() { + try { + runBefores(); + runUnprotected(); + } catch (FailedBefore e) { + } finally { + runAfters(); + } + } - 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 - } - } -}
\ No newline at end of file + private void runBefores() throws FailedBefore { + try { + try { + List<Method> befores = testClass.getBefores(); + for (Method before : befores) { + before.invoke(null); + } + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } catch (AssumptionViolatedException e) { + throw new FailedBefore(); + } catch (Throwable e) { + addFailure(e); + throw new FailedBefore(); + } + } + + private void runAfters() { + List<Method> afters = testClass.getAfters(); + for (Method after : afters) { + try { + after.invoke(null); + } catch (InvocationTargetException e) { + addFailure(e.getTargetException()); + } catch (Throwable e) { + addFailure(e); // Untested, but seems impossible + } + } + } +} diff --git a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java index 200b6f0..1d32beb 100644 --- a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java +++ b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java @@ -11,50 +11,58 @@ import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.InitializationError; public class ErrorReportingRunner extends Runner { - private final List<Throwable> fCauses; - - private final Class<?> fTestClass; - - public ErrorReportingRunner(Class<?> testClass, Throwable cause) { - fTestClass= testClass; - fCauses= getCauses(cause); - } - - @Override - public Description getDescription() { - Description description= Description.createSuiteDescription(fTestClass); - for (Throwable each : fCauses) - description.addChild(describeCause(each)); - return description; - } - - @Override - public void run(RunNotifier notifier) { - for (Throwable each : fCauses) - runCause(each, notifier); - } - - @SuppressWarnings("deprecation") - private List<Throwable> getCauses(Throwable cause) { - if (cause instanceof InvocationTargetException) - return getCauses(cause.getCause()); - if (cause instanceof InitializationError) - return ((InitializationError) cause).getCauses(); - if (cause instanceof org.junit.internal.runners.InitializationError) - return ((org.junit.internal.runners.InitializationError) cause) - .getCauses(); - return Arrays.asList(cause); - } - - private Description describeCause(Throwable child) { - return Description.createTestDescription(fTestClass, - "initializationError"); - } - - private void runCause(Throwable child, RunNotifier notifier) { - Description description= describeCause(child); - notifier.fireTestStarted(description); - notifier.fireTestFailure(new Failure(description, child)); - notifier.fireTestFinished(description); - } -}
\ No newline at end of file + private final List<Throwable> causes; + + private final Class<?> testClass; + + public ErrorReportingRunner(Class<?> testClass, Throwable cause) { + if (testClass == null) { + throw new NullPointerException("Test class cannot be null"); + } + this.testClass = testClass; + causes = getCauses(cause); + } + + @Override + public Description getDescription() { + Description description = Description.createSuiteDescription(testClass); + for (Throwable each : causes) { + description.addChild(describeCause(each)); + } + return description; + } + + @Override + public void run(RunNotifier notifier) { + for (Throwable each : causes) { + runCause(each, notifier); + } + } + + @SuppressWarnings("deprecation") + private List<Throwable> getCauses(Throwable cause) { + if (cause instanceof InvocationTargetException) { + return getCauses(cause.getCause()); + } + if (cause instanceof InitializationError) { + return ((InitializationError) cause).getCauses(); + } + if (cause instanceof org.junit.internal.runners.InitializationError) { + return ((org.junit.internal.runners.InitializationError) cause) + .getCauses(); + } + return Arrays.asList(cause); + } + + private Description describeCause(Throwable child) { + return Description.createTestDescription(testClass, + "initializationError"); + } + + private void runCause(Throwable child, RunNotifier notifier) { + Description description = describeCause(child); + notifier.fireTestStarted(description); + notifier.fireTestFailure(new Failure(description, child)); + notifier.fireTestFinished(description); + } +} diff --git a/src/main/java/org/junit/internal/runners/FailedBefore.java b/src/main/java/org/junit/internal/runners/FailedBefore.java index 29dcba4..1036cb6 100644 --- a/src/main/java/org/junit/internal/runners/FailedBefore.java +++ b/src/main/java/org/junit/internal/runners/FailedBefore.java @@ -2,13 +2,12 @@ package org.junit.internal.runners; import org.junit.runners.BlockJUnit4ClassRunner; - /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated class FailedBefore extends Exception { - private static final long serialVersionUID= 1L; + private static final long serialVersionUID = 1L; }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/InitializationError.java b/src/main/java/org/junit/internal/runners/InitializationError.java index 5715ec5..52065ec 100644 --- a/src/main/java/org/junit/internal/runners/InitializationError.java +++ b/src/main/java/org/junit/internal/runners/InitializationError.java @@ -3,28 +3,35 @@ package org.junit.internal.runners; import java.util.Arrays; import java.util.List; -@Deprecated /** - * Use the published version: {@link org.junit.runners.InitializationError} + * Use the published version: + * {@link org.junit.runners.model.InitializationError} * This may disappear as soon as 1 April 2009 */ +@Deprecated public class InitializationError extends Exception { - private static final long serialVersionUID= 1L; - private final List<Throwable> fErrors; + private static final long serialVersionUID = 1L; + + /* + * We have to use the f prefix until the next major release to ensure + * serialization compatibility. + * See https://github.com/junit-team/junit/issues/976 + */ + private final List<Throwable> fErrors; + + public InitializationError(List<Throwable> errors) { + this.fErrors = errors; + } - public InitializationError(List<Throwable> errors) { - fErrors= errors; - } + public InitializationError(Throwable... errors) { + this(Arrays.asList(errors)); + } - public InitializationError(Throwable... errors) { - this(Arrays.asList(errors)); - } - - public InitializationError(String string) { - this(new Exception(string)); - } + public InitializationError(String string) { + this(new Exception(string)); + } - public List<Throwable> getCauses() { - return fErrors; - } + public List<Throwable> getCauses() { + return fErrors; + } } diff --git a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java index 0028d0c..631fcf2 100644 --- a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java +++ b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java @@ -17,142 +17,164 @@ import org.junit.runner.manipulation.Sortable; import org.junit.runner.manipulation.Sorter; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; public class JUnit38ClassRunner extends Runner implements Filterable, Sortable { - private 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 Describable) { - Describable facade= (Describable) test; - return facade.getDescription(); - } - return Description.createTestDescription(getEffectiveClass(test), getName(test)); - } - - private Class<? extends Test> getEffectiveClass(Test test) { - return test.getClass(); - } - - 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; - - public JUnit38ClassRunner(Class<?> klass) { - this(new TestSuite(klass.asSubclass(TestCase.class))); - } - - public JUnit38ClassRunner(Test test) { - super(); - setTest(test); - } - - @Override - public void run(RunNotifier notifier) { - TestResult result= new TestResult(); - result.addListener(createAdaptingListener(notifier)); - getTest().run(result); - } - - public TestListener createAdaptingListener(final RunNotifier notifier) { - return new OldTestClassAdaptingListener(notifier); - } - - @Override - public Description getDescription() { - return makeDescription(getTest()); - } - - private static 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 ? createSuiteDescription(ts) : ts.getName(); - Description description= Description.createSuiteDescription(name); - int n= ts.testCount(); - for (int i= 0; i < n; i++) { - Description made= makeDescription(ts.testAt(i)); - description.addChild(made); - } - return description; - } else if (test instanceof Describable) { - Describable adapter= (Describable) 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()); - } - } - - private static String createSuiteDescription(TestSuite ts) { - int count= ts.countTestCases(); - String example = count == 0 ? "" : String.format(" [example: %s]", ts.testAt(0)); - return String.format("TestSuite with %s tests%s", count, example); - } - - public void filter(Filter filter) throws NoTestsRemainException { - if (getTest() instanceof Filterable) { - Filterable adapter= (Filterable) getTest(); - adapter.filter(filter); - } else if (getTest() instanceof TestSuite) { - TestSuite suite= (TestSuite) getTest(); - TestSuite filtered= new TestSuite(suite.getName()); - int n= suite.testCount(); - for (int i= 0; i < n; i++) { - Test test= suite.testAt(i); - if (filter.shouldRun(makeDescription(test))) - filtered.addTest(test); - } - setTest(filtered); - } - } - - public void sort(Sorter sorter) { - if (getTest() instanceof Sortable) { - Sortable adapter= (Sortable) getTest(); - adapter.sort(sorter); - } - } - - private void setTest(Test test) { - fTest = test; - } - - private Test getTest() { - return fTest; - } + private static final class OldTestClassAdaptingListener implements + TestListener { + private final RunNotifier notifier; + + private OldTestClassAdaptingListener(RunNotifier notifier) { + this.notifier = notifier; + } + + public void endTest(Test test) { + notifier.fireTestFinished(asDescription(test)); + } + + public void startTest(Test test) { + notifier.fireTestStarted(asDescription(test)); + } + + // Implement junit.framework.TestListener + public void addError(Test test, Throwable e) { + Failure failure = new Failure(asDescription(test), e); + notifier.fireTestFailure(failure); + } + + private Description asDescription(Test test) { + if (test instanceof Describable) { + Describable facade = (Describable) test; + return facade.getDescription(); + } + return Description.createTestDescription(getEffectiveClass(test), getName(test)); + } + + private Class<? extends Test> getEffectiveClass(Test test) { + return test.getClass(); + } + + 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 volatile Test test; + + public JUnit38ClassRunner(Class<?> klass) { + this(new TestSuite(klass.asSubclass(TestCase.class))); + } + + public JUnit38ClassRunner(Test test) { + super(); + setTest(test); + } + + @Override + public void run(RunNotifier notifier) { + TestResult result = new TestResult(); + result.addListener(createAdaptingListener(notifier)); + getTest().run(result); + } + + public TestListener createAdaptingListener(final RunNotifier notifier) { + return new OldTestClassAdaptingListener(notifier); + } + + @Override + public Description getDescription() { + return makeDescription(getTest()); + } + + private static Description makeDescription(Test test) { + if (test instanceof TestCase) { + TestCase tc = (TestCase) test; + return Description.createTestDescription(tc.getClass(), tc.getName(), + getAnnotations(tc)); + } else if (test instanceof TestSuite) { + TestSuite ts = (TestSuite) test; + String name = ts.getName() == null ? createSuiteDescription(ts) : ts.getName(); + Description description = Description.createSuiteDescription(name); + int n = ts.testCount(); + for (int i = 0; i < n; i++) { + Description made = makeDescription(ts.testAt(i)); + description.addChild(made); + } + return description; + } else if (test instanceof Describable) { + Describable adapter = (Describable) 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()); + } + } + + /** + * Get the annotations associated with given TestCase. + * @param test the TestCase. + */ + private static Annotation[] getAnnotations(TestCase test) { + try { + Method m = test.getClass().getMethod(test.getName()); + return m.getDeclaredAnnotations(); + } catch (SecurityException e) { + } catch (NoSuchMethodException e) { + } + return new Annotation[0]; + } + + private static String createSuiteDescription(TestSuite ts) { + int count = ts.countTestCases(); + String example = count == 0 ? "" : String.format(" [example: %s]", ts.testAt(0)); + return String.format("TestSuite with %s tests%s", count, example); + } + + public void filter(Filter filter) throws NoTestsRemainException { + if (getTest() instanceof Filterable) { + Filterable adapter = (Filterable) getTest(); + adapter.filter(filter); + } else if (getTest() instanceof TestSuite) { + TestSuite suite = (TestSuite) getTest(); + TestSuite filtered = new TestSuite(suite.getName()); + int n = suite.testCount(); + for (int i = 0; i < n; i++) { + Test test = suite.testAt(i); + if (filter.shouldRun(makeDescription(test))) { + filtered.addTest(test); + } + } + setTest(filtered); + if (filtered.testCount() == 0) { + throw new NoTestsRemainException(); + } + } + } + + public void sort(Sorter sorter) { + if (getTest() instanceof Sortable) { + Sortable adapter = (Sortable) getTest(); + adapter.sort(sorter); + } + } + + private void setTest(Test test) { + this.test = test; + } + + private Test getTest() { + return test; + } } diff --git a/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java b/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java index d732880..69a23c4 100644 --- a/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java +++ b/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java @@ -21,125 +21,127 @@ import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. - * - * This may disappear as soon as 1 April 2009 */ @Deprecated 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= getTestMethods(); - validate(); - } - - protected List<Method> getTestMethods() { - return fTestClass.getTestMethods(); - } - - 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(), classAnnotations()); - List<Method> testMethods= fTestMethods; - for (Method method : testMethods) - spec.addChild(methodDescription(method)); - return spec; - } - - protected Annotation[] classAnnotations() { - return fTestClass.getJavaClass().getAnnotations(); - } - - protected String getName() { - return getTestClass().getName(); - } - - protected Object createTest() throws Exception { - return getTestClass().getConstructor().newInstance(); - } - - protected void invokeTestMethod(Method method, RunNotifier notifier) { - Description description= methodDescription(method); - Object test; - try { - test= createTest(); - } catch (InvocationTargetException e) { - testAborted(notifier, description, e.getCause()); - return; - } catch (Exception e) { - testAborted(notifier, description, e); - return; - } - TestMethod testMethod= wrapMethod(method); - new MethodRoadie(test, testMethod, notifier, description).run(); - } - - private void testAborted(RunNotifier notifier, Description description, - Throwable e) { - notifier.fireTestStarted(description); - notifier.fireTestFailure(new Failure(description, e)); - notifier.fireTestFinished(description); - } - - protected TestMethod wrapMethod(Method method) { - return new TestMethod(method, fTestClass); - } - - protected String testName(Method method) { - return method.getName(); - } - - protected Description methodDescription(Method method) { - return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method)); - } - - protected Annotation[] testAnnotations(Method method) { - return method.getAnnotations(); - } - - 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; - } + private final List<Method> testMethods; + private TestClass testClass; + + public JUnit4ClassRunner(Class<?> klass) throws InitializationError { + testClass = new TestClass(klass); + testMethods = getTestMethods(); + validate(); + } + + protected List<Method> getTestMethods() { + return testClass.getTestMethods(); + } + + protected void validate() throws InitializationError { + MethodValidator methodValidator = new MethodValidator(testClass); + methodValidator.validateMethodsForDefaultRunner(); + methodValidator.assertValid(); + } + + @Override + public void run(final RunNotifier notifier) { + new ClassRoadie(notifier, testClass, getDescription(), new Runnable() { + public void run() { + runMethods(notifier); + } + }).runProtected(); + } + + protected void runMethods(final RunNotifier notifier) { + for (Method method : testMethods) { + invokeTestMethod(method, notifier); + } + } + + @Override + public Description getDescription() { + Description spec = Description.createSuiteDescription(getName(), classAnnotations()); + List<Method> testMethods = this.testMethods; + for (Method method : testMethods) { + spec.addChild(methodDescription(method)); + } + return spec; + } + + protected Annotation[] classAnnotations() { + return testClass.getJavaClass().getAnnotations(); + } + + protected String getName() { + return getTestClass().getName(); + } + + protected Object createTest() throws Exception { + return getTestClass().getConstructor().newInstance(); + } + + protected void invokeTestMethod(Method method, RunNotifier notifier) { + Description description = methodDescription(method); + Object test; + try { + test = createTest(); + } catch (InvocationTargetException e) { + testAborted(notifier, description, e.getCause()); + return; + } catch (Exception e) { + testAborted(notifier, description, e); + return; + } + TestMethod testMethod = wrapMethod(method); + new MethodRoadie(test, testMethod, notifier, description).run(); + } + + private void testAborted(RunNotifier notifier, Description description, + Throwable e) { + notifier.fireTestStarted(description); + notifier.fireTestFailure(new Failure(description, e)); + notifier.fireTestFinished(description); + } + + protected TestMethod wrapMethod(Method method) { + return new TestMethod(method, testClass); + } + + protected String testName(Method method) { + return method.getName(); + } + + protected Description methodDescription(Method method) { + return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method)); + } + + protected Annotation[] testAnnotations(Method method) { + return method.getAnnotations(); + } + + public void filter(Filter filter) throws NoTestsRemainException { + for (Iterator<Method> iter = testMethods.iterator(); iter.hasNext(); ) { + Method method = iter.next(); + if (!filter.shouldRun(methodDescription(method))) { + iter.remove(); + } + } + if (testMethods.isEmpty()) { + throw new NoTestsRemainException(); + } + } + + public void sort(final Sorter sorter) { + Collections.sort(testMethods, new Comparator<Method>() { + public int compare(Method o1, Method o2) { + return sorter.compare(methodDescription(o1), methodDescription(o2)); + } + }); + } + + protected TestClass getTestClass() { + return testClass; + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/MethodRoadie.java b/src/main/java/org/junit/internal/runners/MethodRoadie.java index 4407821..01a476b 100644 --- a/src/main/java/org/junit/internal/runners/MethodRoadie.java +++ b/src/main/java/org/junit/internal/runners/MethodRoadie.java @@ -15,143 +15,149 @@ import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.TestTimedOutException; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class MethodRoadie { - private final Object fTest; - private final RunNotifier fNotifier; - private final Description fDescription; - private TestMethod fTestMethod; + private final Object test; + private final RunNotifier notifier; + private final Description description; + private TestMethod testMethod; - public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) { - fTest= test; - fNotifier= notifier; - fDescription= description; - fTestMethod= method; - } + public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) { + this.test = test; + this.notifier = notifier; + this.description = description; + testMethod = method; + } - 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); - } - } + public void run() { + if (testMethod.isIgnored()) { + notifier.fireTestIgnored(description); + return; + } + notifier.fireTestStarted(description); + try { + long timeout = testMethod.getTimeout(); + if (timeout > 0) { + runWithTimeout(timeout); + } else { + runTest(); + } + } finally { + notifier.fireTestFinished(description); + } + } - private void runWithTimeout(final long timeout) { - runBeforesThenTestThenAfters(new Runnable() { - - public void run() { - ExecutorService service= Executors.newSingleThreadExecutor(); - Callable<Object> callable= new Callable<Object>() { - public Object call() throws Exception { - runTestMethod(); - 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() { - runBeforesThenTestThenAfters(new Runnable() { - public void run() { - runTestMethod(); - } - }); - } + private void runWithTimeout(final long timeout) { + runBeforesThenTestThenAfters(new Runnable() { - public void runBeforesThenTestThenAfters(Runnable test) { - try { - runBefores(); - test.run(); - } catch (FailedBefore e) { - } catch (Exception e) { - throw new RuntimeException("test should never throw an exception to this level"); - } finally { - runAfters(); - } - } - - protected void runTestMethod() { - try { - fTestMethod.invoke(fTest); - if (fTestMethod.expectsException()) - addFailure(new AssertionError("Expected exception: " + fTestMethod.getExpectedException().getName())); - } catch (InvocationTargetException e) { - Throwable actual= e.getTargetException(); - if (actual instanceof AssumptionViolatedException) - return; - else 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 { - try { - List<Method> befores= fTestMethod.getBefores(); - for (Method before : befores) - before.invoke(fTest); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } - } catch (AssumptionViolatedException e) { - throw new FailedBefore(); - } catch (Throwable e) { - addFailure(e); - throw new FailedBefore(); - } - } + public void run() { + ExecutorService service = Executors.newSingleThreadExecutor(); + Callable<Object> callable = new Callable<Object>() { + public Object call() throws Exception { + runTestMethod(); + 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 TestTimedOutException(timeout, TimeUnit.MILLISECONDS)); + } catch (Exception e) { + addFailure(e); + } + } + }); + } - 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 - } - } + public void runTest() { + runBeforesThenTestThenAfters(new Runnable() { + public void run() { + runTestMethod(); + } + }); + } - protected void addFailure(Throwable e) { - fNotifier.fireTestFailure(new Failure(fDescription, e)); - } + public void runBeforesThenTestThenAfters(Runnable test) { + try { + runBefores(); + test.run(); + } catch (FailedBefore e) { + } catch (Exception e) { + throw new RuntimeException("test should never throw an exception to this level"); + } finally { + runAfters(); + } + } + + protected void runTestMethod() { + try { + testMethod.invoke(test); + if (testMethod.expectsException()) { + addFailure(new AssertionError("Expected exception: " + testMethod.getExpectedException().getName())); + } + } catch (InvocationTargetException e) { + Throwable actual = e.getTargetException(); + if (actual instanceof AssumptionViolatedException) { + return; + } else if (!testMethod.expectsException()) { + addFailure(actual); + } else if (testMethod.isUnexpected(actual)) { + String message = "Unexpected exception, expected<" + testMethod.getExpectedException().getName() + "> but was<" + + actual.getClass().getName() + ">"; + addFailure(new Exception(message, actual)); + } + } catch (Throwable e) { + addFailure(e); + } + } + + private void runBefores() throws FailedBefore { + try { + try { + List<Method> befores = testMethod.getBefores(); + for (Method before : befores) { + before.invoke(test); + } + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } catch (AssumptionViolatedException e) { + throw new FailedBefore(); + } catch (Throwable e) { + addFailure(e); + throw new FailedBefore(); + } + } + + private void runAfters() { + List<Method> afters = testMethod.getAfters(); + for (Method after : afters) { + try { + after.invoke(test); + } catch (InvocationTargetException e) { + addFailure(e.getTargetException()); + } catch (Throwable e) { + addFailure(e); // Untested, but seems impossible + } + } + } + + protected void addFailure(Throwable e) { + notifier.fireTestFailure(new Failure(description, e)); + } } diff --git a/src/main/java/org/junit/internal/runners/MethodValidator.java b/src/main/java/org/junit/internal/runners/MethodValidator.java index cadc93f..ba9c9d1 100644 --- a/src/main/java/org/junit/internal/runners/MethodValidator.java +++ b/src/main/java/org/junit/internal/runners/MethodValidator.java @@ -15,77 +15,83 @@ import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class MethodValidator { - private final List<Throwable> fErrors= new ArrayList<Throwable>(); + private final List<Throwable> errors = new ArrayList<Throwable>(); - private TestClass fTestClass; + private TestClass testClass; - public MethodValidator(TestClass testClass) { - fTestClass = testClass; - } + public MethodValidator(TestClass testClass) { + this.testClass = 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 validateInstanceMethods() { + validateTestMethods(After.class, false); + validateTestMethods(Before.class, false); + validateTestMethods(Test.class, false); - public void validateStaticMethods() { - validateTestMethods(BeforeClass.class, true); - validateTestMethods(AfterClass.class, true); - } - - public List<Throwable> validateMethodsForDefaultRunner() { - validateNoArgConstructor(); - validateStaticMethods(); - validateInstanceMethods(); - return fErrors; - } - - public void assertValid() throws InitializationError { - if (!fErrors.isEmpty()) - throw new InitializationError(fErrors); - } + List<Method> methods = testClass.getAnnotatedMethods(Test.class); + if (methods.size() == 0) { + errors.add(new Exception("No runnable methods")); + } + } - public void validateNoArgConstructor() { - try { - fTestClass.getConstructor(); - } catch (Exception e) { - fErrors.add(new Exception("Test class should have public zero-argument constructor", e)); - } - } + public void validateStaticMethods() { + validateTestMethods(BeforeClass.class, true); + validateTestMethods(AfterClass.class, true); + } - private void validateTestMethods(Class<? extends Annotation> annotation, - boolean isStatic) { - List<Method> methods= fTestClass.getAnnotatedMethods(annotation); - - for (Method each : methods) { - if (Modifier.isStatic(each.getModifiers()) != isStatic) { - String state= isStatic ? "should" : "should not"; - fErrors.add(new Exception("Method " + each.getName() + "() " + public List<Throwable> validateMethodsForDefaultRunner() { + validateNoArgConstructor(); + validateStaticMethods(); + validateInstanceMethods(); + return errors; + } + + public void assertValid() throws InitializationError { + if (!errors.isEmpty()) { + throw new InitializationError(errors); + } + } + + public void validateNoArgConstructor() { + try { + testClass.getConstructor(); + } catch (Exception e) { + errors.add(new Exception("Test class should have public zero-argument constructor", e)); + } + } + + private void validateTestMethods(Class<? extends Annotation> annotation, + boolean isStatic) { + List<Method> methods = testClass.getAnnotatedMethods(annotation); + + for (Method each : methods) { + if (Modifier.isStatic(each.getModifiers()) != isStatic) { + String state = isStatic ? "should" : "should not"; + errors.add(new Exception("Method " + each.getName() + "() " + state + " be static")); - } - if (!Modifier.isPublic(each.getDeclaringClass().getModifiers())) - fErrors.add(new Exception("Class " + each.getDeclaringClass().getName() + } + if (!Modifier.isPublic(each.getDeclaringClass().getModifiers())) { + errors.add(new Exception("Class " + each.getDeclaringClass().getName() + " should be public")); - if (!Modifier.isPublic(each.getModifiers())) - fErrors.add(new Exception("Method " + each.getName() + } + if (!Modifier.isPublic(each.getModifiers())) { + errors.add(new Exception("Method " + each.getName() + " should be public")); - if (each.getReturnType() != Void.TYPE) - fErrors.add(new Exception("Method " + each.getName() + } + if (each.getReturnType() != Void.TYPE) { + errors.add(new Exception("Method " + each.getName() + " should be void")); - if (each.getParameterTypes().length != 0) - fErrors.add(new Exception("Method " + each.getName() + } + if (each.getParameterTypes().length != 0) { + errors.add(new Exception("Method " + each.getName() + " should have no parameters")); - } - } + } + } + } } diff --git a/src/main/java/org/junit/internal/runners/SuiteMethod.java b/src/main/java/org/junit/internal/runners/SuiteMethod.java index 4e8bebc..e336983 100644 --- a/src/main/java/org/junit/internal/runners/SuiteMethod.java +++ b/src/main/java/org/junit/internal/runners/SuiteMethod.java @@ -6,7 +6,8 @@ import java.lang.reflect.Modifier; import junit.framework.Test; -/** Runner for use with JUnit 3.8.x-style AllTests classes +/** + * Runner for use with JUnit 3.8.x-style AllTests classes * (those that only implement a static <code>suite()</code> * method). For example: * <pre> @@ -19,22 +20,22 @@ import junit.framework.Test; * </pre> */ public class SuiteMethod extends JUnit38ClassRunner { - public SuiteMethod(Class<?> klass) throws Throwable { - super(testFromSuiteMethod(klass)); - } + public SuiteMethod(Class<?> klass) throws Throwable { + super(testFromSuiteMethod(klass)); + } - public static Test testFromSuiteMethod(Class<?> klass) throws Throwable { - Method suiteMethod= null; - Test suite= null; - try { - suiteMethod= klass.getMethod("suite"); - if (! Modifier.isStatic(suiteMethod.getModifiers())) { - throw new Exception(klass.getName() + ".suite() must be static"); - } - suite= (Test) suiteMethod.invoke(null); // static method - } catch (InvocationTargetException e) { - throw e.getCause(); - } - return suite; - } + public static Test testFromSuiteMethod(Class<?> klass) throws Throwable { + Method suiteMethod = null; + Test suite = null; + try { + suiteMethod = klass.getMethod("suite"); + if (!Modifier.isStatic(suiteMethod.getModifiers())) { + throw new Exception(klass.getName() + ".suite() must be static"); + } + suite = (Test) suiteMethod.invoke(null); // static method + } catch (InvocationTargetException e) { + throw e.getCause(); + } + return suite; + } } diff --git a/src/main/java/org/junit/internal/runners/TestClass.java b/src/main/java/org/junit/internal/runners/TestClass.java index 1ca2b9d..1abaeea 100644 --- a/src/main/java/org/junit/internal/runners/TestClass.java +++ b/src/main/java/org/junit/internal/runners/TestClass.java @@ -11,92 +11,99 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.internal.MethodSorter; import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class TestClass { - private final Class<?> fClass; - - public TestClass(Class<?> klass) { - fClass= klass; - } - - public List<Method> getTestMethods() { - return getAnnotatedMethods(Test.class); - } - - List<Method> getBefores() { - return getAnnotatedMethods(BeforeClass.class); - } - - 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(); - } + private final Class<?> klass; + + public TestClass(Class<?> klass) { + this.klass = klass; + } + + public List<Method> getTestMethods() { + return getAnnotatedMethods(Test.class); + } + + List<Method> getBefores() { + return getAnnotatedMethods(BeforeClass.class); + } + + 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(klass)) { + Method[] methods = MethodSorter.getDeclaredMethods(eachClass); + 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 klass.getConstructor(); + } + + public Class<?> getJavaClass() { + return klass; + } + + public String getName() { + return klass.getName(); + } } diff --git a/src/main/java/org/junit/internal/runners/TestMethod.java b/src/main/java/org/junit/internal/runners/TestMethod.java index a06213c..821e193 100644 --- a/src/main/java/org/junit/internal/runners/TestMethod.java +++ b/src/main/java/org/junit/internal/runners/TestMethod.java @@ -13,57 +13,59 @@ import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class TestMethod { - private final Method fMethod; - private TestClass fTestClass; + private final Method method; + private TestClass testClass; - public TestMethod(Method method, TestClass testClass) { - fMethod= method; - fTestClass= testClass; - } + public TestMethod(Method method, TestClass testClass) { + this.method = method; + this.testClass = testClass; + } - public boolean isIgnored() { - return fMethod.getAnnotation(Ignore.class) != null; - } + public boolean isIgnored() { + return method.getAnnotation(Ignore.class) != null; + } - public long getTimeout() { - Test annotation= fMethod.getAnnotation(Test.class); - if (annotation == null) - return 0; - long timeout= annotation.timeout(); - return timeout; - } + public long getTimeout() { + Test annotation = method.getAnnotation(Test.class); + if (annotation == null) { + return 0; + } + long timeout = annotation.timeout(); + return timeout; + } - protected Class<? extends Throwable> getExpectedException() { - Test annotation= fMethod.getAnnotation(Test.class); - if (annotation == null || annotation.expected() == None.class) - return null; - else - return annotation.expected(); - } + protected Class<? extends Throwable> getExpectedException() { + Test annotation = method.getAnnotation(Test.class); + if (annotation == null || annotation.expected() == None.class) { + return null; + } else { + return annotation.expected(); + } + } - boolean isUnexpected(Throwable exception) { - return ! getExpectedException().isAssignableFrom(exception.getClass()); - } + boolean isUnexpected(Throwable exception) { + return !getExpectedException().isAssignableFrom(exception.getClass()); + } - boolean expectsException() { - return getExpectedException() != null; - } + boolean expectsException() { + return getExpectedException() != null; + } - List<Method> getBefores() { - return fTestClass.getAnnotatedMethods(Before.class); - } + List<Method> getBefores() { + return testClass.getAnnotatedMethods(Before.class); + } - List<Method> getAfters() { - return fTestClass.getAnnotatedMethods(After.class); - } + List<Method> getAfters() { + return testClass.getAnnotatedMethods(After.class); + } - public void invoke(Object test) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { - fMethod.invoke(test); - } + public void invoke(Object test) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + method.invoke(test); + } } diff --git a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java index a7d534c..e094809 100644 --- a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java +++ b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.runners.model; import org.junit.internal.AssumptionViolatedException; @@ -10,42 +7,42 @@ import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.MultipleFailureException; public class EachTestNotifier { - private final RunNotifier fNotifier; - - private final Description fDescription; - - public EachTestNotifier(RunNotifier notifier, Description description) { - fNotifier= notifier; - fDescription= description; - } - - public void addFailure(Throwable targetException) { - if (targetException instanceof MultipleFailureException) { - addMultipleFailureException((MultipleFailureException) targetException); - } else { - fNotifier - .fireTestFailure(new Failure(fDescription, targetException)); - } - } - - private void addMultipleFailureException(MultipleFailureException mfe) { - for (Throwable each : mfe.getFailures()) - addFailure(each); - } - - public void addFailedAssumption(AssumptionViolatedException e) { - fNotifier.fireTestAssumptionFailed(new Failure(fDescription, e)); - } - - public void fireTestFinished() { - fNotifier.fireTestFinished(fDescription); - } - - public void fireTestStarted() { - fNotifier.fireTestStarted(fDescription); - } - - public void fireTestIgnored() { - fNotifier.fireTestIgnored(fDescription); - } + private final RunNotifier notifier; + + private final Description description; + + public EachTestNotifier(RunNotifier notifier, Description description) { + this.notifier = notifier; + this.description = description; + } + + public void addFailure(Throwable targetException) { + if (targetException instanceof MultipleFailureException) { + addMultipleFailureException((MultipleFailureException) targetException); + } else { + notifier.fireTestFailure(new Failure(description, targetException)); + } + } + + private void addMultipleFailureException(MultipleFailureException mfe) { + for (Throwable each : mfe.getFailures()) { + addFailure(each); + } + } + + public void addFailedAssumption(AssumptionViolatedException e) { + notifier.fireTestAssumptionFailed(new Failure(description, e)); + } + + public void fireTestFinished() { + notifier.fireTestFinished(description); + } + + public void fireTestStarted() { + notifier.fireTestStarted(description); + } + + public void fireTestIgnored() { + notifier.fireTestIgnored(description); + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java b/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java index 3316806..054f042 100644 --- a/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java +++ b/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java @@ -4,9 +4,9 @@ import java.util.List; @Deprecated public class MultipleFailureException extends org.junit.runners.model.MultipleFailureException { - private static final long serialVersionUID= 1L; + private static final long serialVersionUID = 1L; - public MultipleFailureException(List<Throwable> errors) { - super(errors); - } + public MultipleFailureException(List<Throwable> errors) { + super(errors); + } } diff --git a/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java b/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java index 9150d90..79d5c05 100644 --- a/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java +++ b/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.runners.model; import java.lang.reflect.InvocationTargetException; @@ -10,13 +7,13 @@ import java.lang.reflect.InvocationTargetException; * wrapping it in an InvocationTargetException. */ public abstract class ReflectiveCallable { - public Object run() throws Throwable { - try { - return runReflectiveCall(); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } - } + public Object run() throws Throwable { + try { + return runReflectiveCall(); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } - protected abstract Object runReflectiveCall() throws Throwable; + protected abstract Object runReflectiveCall() throws Throwable; }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java b/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java deleted file mode 100644 index e7df8bf..0000000 --- a/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.junit.internal.runners.rules; - -import java.lang.annotation.Annotation; -import java.util.List; - -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.rules.TestRule; -import org.junit.runners.model.FrameworkField; -import org.junit.runners.model.TestClass; - -/** - * A RuleFieldValidator validates the rule fields of a - * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the - * {@code TestClass} are written to a list of errors. - * - * There are two slightly different validators. The {@link #CLASS_RULE_VALIDATOR} - * validates fields with a {@link ClassRule} annotation and the - * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation. - */ -public enum RuleFieldValidator { - /** - * Validates fields with a {@link ClassRule} annotation. - */ - CLASS_RULE_VALIDATOR(ClassRule.class, true), - /** - * Validates fields with a {@link Rule} annotation. - */ - RULE_VALIDATOR(Rule.class, false); - - private final Class<? extends Annotation> fAnnotation; - - private final boolean fOnlyStaticFields; - - private RuleFieldValidator(Class<? extends Annotation> annotation, - boolean onlyStaticFields) { - this.fAnnotation= annotation; - this.fOnlyStaticFields= onlyStaticFields; - } - - /** - * Validate the {@link org.junit.runners.model.TestClass} and adds reasons - * for rejecting the class to a list of errors. - * @param target the {@code TestClass} to validate. - * @param errors the list of errors. - */ - public void validate(TestClass target, List<Throwable> errors) { - List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation); - for (FrameworkField each : fields) - validateField(each, errors); - } - - private void validateField(FrameworkField field, List<Throwable> errors) { - optionallyValidateStatic(field, errors); - validatePublic(field, errors); - validateTestRuleOrMethodRule(field, errors); - } - - private void optionallyValidateStatic(FrameworkField field, - List<Throwable> errors) { - if (fOnlyStaticFields && !field.isStatic()) - addError(errors, field, "must be static."); - } - - private void validatePublic(FrameworkField field, List<Throwable> errors) { - if (!field.isPublic()) - addError(errors, field, "must be public."); - } - - private void validateTestRuleOrMethodRule(FrameworkField field, - List<Throwable> errors) { - if (!isMethodRule(field) && !isTestRule(field)) - addError(errors, field, "must implement MethodRule or TestRule."); - } - - private boolean isTestRule(FrameworkField target) { - return TestRule.class.isAssignableFrom(target.getType()); - } - - @SuppressWarnings("deprecation") - private boolean isMethodRule(FrameworkField target) { - return org.junit.rules.MethodRule.class.isAssignableFrom(target - .getType()); - } - - private void addError(List<Throwable> errors, FrameworkField field, - String suffix) { - String message= "The @" + fAnnotation.getSimpleName() + " '" - + field.getName() + "' " + suffix; - errors.add(new Exception(message)); - } -} diff --git a/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java b/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java index 23f921b..36de4f1 100644 --- a/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java +++ b/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java @@ -1,91 +1,279 @@ package org.junit.internal.runners.rules; -import java.lang.annotation.Annotation; -import java.util.List; import org.junit.ClassRule; import org.junit.Rule; +import org.junit.rules.MethodRule; import org.junit.rules.TestRule; -import org.junit.runners.model.FrameworkField; +import org.junit.runners.model.FrameworkMember; import org.junit.runners.model.TestClass; +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + /** - * A RuleFieldValidator validates the rule fields of a - * {@link TestClass}. All reasons for rejecting the + * A RuleMemberValidator validates the rule fields/methods of a + * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the * {@code TestClass} are written to a list of errors. * - * There are two slightly different validators. The {@link #CLASS_RULE_VALIDATOR} + * <p>There are four slightly different validators. The {@link #CLASS_RULE_VALIDATOR} * validates fields with a {@link ClassRule} annotation and the - * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation. + * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation.</p> + * + * <p>The {@link #CLASS_RULE_METHOD_VALIDATOR} + * validates methods with a {@link ClassRule} annotation and the + * {@link #RULE_METHOD_VALIDATOR} validates methods with a {@link Rule} annotation.</p> */ -public enum RuleMemberValidator { - /** - * Validates fields with a {@link ClassRule} annotation. - */ - CLASS_RULE_VALIDATOR(ClassRule.class, true), - /** - * Validates fields with a {@link Rule} annotation. - */ - RULE_VALIDATOR(Rule.class, false); - - private final Class<? extends Annotation> fAnnotation; - - private final boolean fOnlyStaticFields; - - private RuleMemberValidator(Class<? extends Annotation> annotation, - boolean onlyStaticFields) { - this.fAnnotation= annotation; - this.fOnlyStaticFields= onlyStaticFields; - } - - /** - * Validate the {@link TestClass} and adds reasons - * for rejecting the class to a list of errors. - * @param target the {@code TestClass} to validate. - * @param errors the list of errors. - */ - public void validate(TestClass target, List<Throwable> errors) { - List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation); - for (FrameworkField each : fields) - validateField(each, errors); - } - - private void validateField(FrameworkField field, List<Throwable> errors) { - optionallyValidateStatic(field, errors); - validatePublic(field, errors); - validateTestRuleOrMethodRule(field, errors); - } - - private void optionallyValidateStatic(FrameworkField field, - List<Throwable> errors) { - if (fOnlyStaticFields && !field.isStatic()) - addError(errors, field, "must be static."); - } - - private void validatePublic(FrameworkField field, List<Throwable> errors) { - if (!field.isPublic()) - addError(errors, field, "must be public."); - } - - private void validateTestRuleOrMethodRule(FrameworkField field, - List<Throwable> errors) { - if (!isMethodRule(field) && !isTestRule(field)) - addError(errors, field, "must implement MethodRule or TestRule."); - } - - private boolean isTestRule(FrameworkField target) { - return TestRule.class.isAssignableFrom(target.getType()); - } - - @SuppressWarnings("deprecation") - private boolean isMethodRule(FrameworkField target) { - return org.junit.rules.MethodRule.class.isAssignableFrom(target - .getType()); - } - - private void addError(List<Throwable> errors, FrameworkField field, - String suffix) { - String message= "The @" + fAnnotation.getSimpleName() + " '" - + field.getName() + "' " + suffix; - errors.add(new Exception(message)); - } +public class RuleMemberValidator { + /** + * Validates fields with a {@link ClassRule} annotation. + */ + public static final RuleMemberValidator CLASS_RULE_VALIDATOR = + classRuleValidatorBuilder() + .withValidator(new DeclaringClassMustBePublic()) + .withValidator(new MemberMustBeStatic()) + .withValidator(new MemberMustBePublic()) + .withValidator(new FieldMustBeATestRule()) + .build(); + /** + * Validates fields with a {@link Rule} annotation. + */ + public static final RuleMemberValidator RULE_VALIDATOR = + testRuleValidatorBuilder() + .withValidator(new MemberMustBeNonStaticOrAlsoClassRule()) + .withValidator(new MemberMustBePublic()) + .withValidator(new FieldMustBeARule()) + .build(); + /** + * Validates methods with a {@link ClassRule} annotation. + */ + public static final RuleMemberValidator CLASS_RULE_METHOD_VALIDATOR = + classRuleValidatorBuilder() + .forMethods() + .withValidator(new DeclaringClassMustBePublic()) + .withValidator(new MemberMustBeStatic()) + .withValidator(new MemberMustBePublic()) + .withValidator(new MethodMustBeATestRule()) + .build(); + + /** + * Validates methods with a {@link Rule} annotation. + */ + public static final RuleMemberValidator RULE_METHOD_VALIDATOR = + testRuleValidatorBuilder() + .forMethods() + .withValidator(new MemberMustBeNonStaticOrAlsoClassRule()) + .withValidator(new MemberMustBePublic()) + .withValidator(new MethodMustBeARule()) + .build(); + + private final Class<? extends Annotation> annotation; + private final boolean methods; + private final List<RuleValidator> validatorStrategies; + + RuleMemberValidator(Builder builder) { + this.annotation = builder.annotation; + this.methods = builder.methods; + this.validatorStrategies = builder.validators; + } + + /** + * Validate the {@link org.junit.runners.model.TestClass} and adds reasons + * for rejecting the class to a list of errors. + * + * @param target the {@code TestClass} to validate. + * @param errors the list of errors. + */ + public void validate(TestClass target, List<Throwable> errors) { + List<? extends FrameworkMember<?>> members = methods ? target.getAnnotatedMethods(annotation) + : target.getAnnotatedFields(annotation); + + for (FrameworkMember<?> each : members) { + validateMember(each, errors); + } + } + + private void validateMember(FrameworkMember<?> member, List<Throwable> errors) { + for (RuleValidator strategy : validatorStrategies) { + strategy.validate(member, annotation, errors); + } + } + + private static Builder classRuleValidatorBuilder() { + return new Builder(ClassRule.class); + } + + private static Builder testRuleValidatorBuilder() { + return new Builder(Rule.class); + } + + private static class Builder { + private final Class<? extends Annotation> annotation; + private boolean methods; + private final List<RuleValidator> validators; + + private Builder(Class<? extends Annotation> annotation) { + this.annotation = annotation; + this.methods = false; + this.validators = new ArrayList<RuleValidator>(); + } + + Builder forMethods() { + methods = true; + return this; + } + + Builder withValidator(RuleValidator validator) { + validators.add(validator); + return this; + } + + RuleMemberValidator build() { + return new RuleMemberValidator(this); + } + } + + private static boolean isRuleType(FrameworkMember<?> member) { + return isMethodRule(member) || isTestRule(member); + } + + private static boolean isTestRule(FrameworkMember<?> member) { + return TestRule.class.isAssignableFrom(member.getType()); + } + + private static boolean isMethodRule(FrameworkMember<?> member) { + return MethodRule.class.isAssignableFrom(member.getType()); + } + + /** + * Encapsulates a single piece of validation logic, used to determine if {@link org.junit.Rule} and + * {@link org.junit.ClassRule} annotations have been used correctly + */ + interface RuleValidator { + /** + * Examine the given member and add any violations of the strategy's validation logic to the given list of errors + * @param member The member (field or member) to examine + * @param annotation The type of rule annotation on the member + * @param errors The list of errors to add validation violations to + */ + void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors); + } + + /** + * Requires the validated member to be non-static + */ + private static final class MemberMustBeNonStaticOrAlsoClassRule implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + boolean isMethodRuleMember = isMethodRule(member); + boolean isClassRuleAnnotated = (member.getAnnotation(ClassRule.class) != null); + + // We disallow: + // - static MethodRule members + // - static @Rule annotated members + // - UNLESS they're also @ClassRule annotated + // Note that MethodRule cannot be annotated with @ClassRule + if (member.isStatic() && (isMethodRuleMember || !isClassRuleAnnotated)) { + String message; + if (isMethodRule(member)) { + message = "must not be static."; + } else { + message = "must not be static or it must be annotated with @ClassRule."; + } + errors.add(new ValidationError(member, annotation, message)); + } + } + } + + /** + * Requires the member to be static + */ + private static final class MemberMustBeStatic implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!member.isStatic()) { + errors.add(new ValidationError(member, annotation, + "must be static.")); + } + } + } + + /** + * Requires the member's declaring class to be public + */ + private static final class DeclaringClassMustBePublic implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isDeclaringClassPublic(member)) { + errors.add(new ValidationError(member, annotation, + "must be declared in a public class.")); + } + } + + private boolean isDeclaringClassPublic(FrameworkMember<?> member) { + return Modifier.isPublic(member.getDeclaringClass().getModifiers()); + } + } + + /** + * Requires the member to be public + */ + private static final class MemberMustBePublic implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!member.isPublic()) { + errors.add(new ValidationError(member, annotation, + "must be public.")); + } + } + } + + /** + * Requires the member is a field implementing {@link org.junit.rules.MethodRule} or {@link org.junit.rules.TestRule} + */ + private static final class FieldMustBeARule implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isRuleType(member)) { + errors.add(new ValidationError(member, annotation, + "must implement MethodRule or TestRule.")); + } + } + } + + /** + * Require the member to return an implementation of {@link org.junit.rules.MethodRule} or + * {@link org.junit.rules.TestRule} + */ + private static final class MethodMustBeARule implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isRuleType(member)) { + errors.add(new ValidationError(member, annotation, + "must return an implementation of MethodRule or TestRule.")); + } + } + } + + /** + * Require the member to return an implementation of {@link org.junit.rules.TestRule} + */ + private static final class MethodMustBeATestRule implements RuleValidator { + public void validate(FrameworkMember<?> member, + Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isTestRule(member)) { + errors.add(new ValidationError(member, annotation, + "must return an implementation of TestRule.")); + } + } + } + + /** + * Requires the member is a field implementing {@link org.junit.rules.TestRule} + */ + private static final class FieldMustBeATestRule implements RuleValidator { + + public void validate(FrameworkMember<?> member, + Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isTestRule(member)) { + errors.add(new ValidationError(member, annotation, + "must implement TestRule.")); + } + } + } } diff --git a/src/main/java/org/junit/internal/runners/rules/ValidationError.java b/src/main/java/org/junit/internal/runners/rules/ValidationError.java new file mode 100644 index 0000000..d1af8ae --- /dev/null +++ b/src/main/java/org/junit/internal/runners/rules/ValidationError.java @@ -0,0 +1,11 @@ +package org.junit.internal.runners.rules; + +import org.junit.runners.model.FrameworkMember; + +import java.lang.annotation.Annotation; + +class ValidationError extends Exception { + public ValidationError(FrameworkMember<?> member, Class<? extends Annotation> annotation, String suffix) { + super(String.format("The @%s '%s' %s", annotation.getSimpleName(), member.getName(), suffix)); + } +} diff --git a/src/main/java/org/junit/internal/runners/statements/ExpectException.java b/src/main/java/org/junit/internal/runners/statements/ExpectException.java index ddfef07..d0636bd 100644 --- a/src/main/java/org/junit/internal/runners/statements/ExpectException.java +++ b/src/main/java/org/junit/internal/runners/statements/ExpectException.java @@ -1,38 +1,36 @@ -/** - * - */ package org.junit.internal.runners.statements; import org.junit.internal.AssumptionViolatedException; import org.junit.runners.model.Statement; public class ExpectException extends Statement { - private Statement fNext; - private final Class<? extends Throwable> fExpected; - - public ExpectException(Statement next, Class<? extends Throwable> expected) { - fNext= next; - fExpected= expected; - } - - @Override - public void evaluate() throws Exception { - boolean complete = false; - try { - fNext.evaluate(); - complete = true; - } catch (AssumptionViolatedException e) { - throw e; - } catch (Throwable e) { - if (!fExpected.isAssignableFrom(e.getClass())) { - String message= "Unexpected exception, expected<" - + fExpected.getName() + "> but was<" - + e.getClass().getName() + ">"; - throw new Exception(message, e); - } - } - if (complete) - throw new AssertionError("Expected exception: " - + fExpected.getName()); - } + private final Statement next; + private final Class<? extends Throwable> expected; + + public ExpectException(Statement next, Class<? extends Throwable> expected) { + this.next = next; + this.expected = expected; + } + + @Override + public void evaluate() throws Exception { + boolean complete = false; + try { + next.evaluate(); + complete = true; + } catch (AssumptionViolatedException e) { + throw e; + } catch (Throwable e) { + if (!expected.isAssignableFrom(e.getClass())) { + String message = "Unexpected exception, expected<" + + expected.getName() + "> but was<" + + e.getClass().getName() + ">"; + throw new Exception(message, e); + } + } + if (complete) { + throw new AssertionError("Expected exception: " + + expected.getName()); + } + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/statements/Fail.java b/src/main/java/org/junit/internal/runners/statements/Fail.java index e7d0d5c..e55875c 100644 --- a/src/main/java/org/junit/internal/runners/statements/Fail.java +++ b/src/main/java/org/junit/internal/runners/statements/Fail.java @@ -2,16 +2,15 @@ package org.junit.internal.runners.statements; import org.junit.runners.model.Statement; - public class Fail extends Statement { - private final Throwable fError; + private final Throwable error; - public Fail(Throwable e) { - fError= e; - } + public Fail(Throwable e) { + error = e; + } - @Override - public void evaluate() throws Throwable { - throw fError; - } + @Override + public void evaluate() throws Throwable { + throw error; + } } diff --git a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java index bff7c72..7f4f0d5 100644 --- a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java +++ b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java @@ -1,71 +1,311 @@ -/** - * - */ package org.junit.internal.runners.statements; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.runners.model.MultipleFailureException; import org.junit.runners.model.Statement; +import org.junit.runners.model.TestTimedOutException; public class FailOnTimeout extends Statement { - private final Statement fOriginalStatement; - - private final long fTimeout; - - public FailOnTimeout(Statement originalStatement, long timeout) { - fOriginalStatement= originalStatement; - fTimeout= timeout; - } - - @Override - public void evaluate() throws Throwable { - StatementThread thread= evaluateStatement(); - if (!thread.fFinished) - throwExceptionForUnfinishedThread(thread); - } - - private StatementThread evaluateStatement() throws InterruptedException { - StatementThread thread= new StatementThread(fOriginalStatement); - thread.start(); - thread.join(fTimeout); - thread.interrupt(); - return thread; - } - - private void throwExceptionForUnfinishedThread(StatementThread thread) - throws Throwable { - if (thread.fExceptionThrownByOriginalStatement != null) - throw thread.fExceptionThrownByOriginalStatement; - else - throwTimeoutException(thread); - } - - private void throwTimeoutException(StatementThread thread) throws Exception { - Exception exception= new Exception(String.format( - "test timed out after %d milliseconds", fTimeout)); - exception.setStackTrace(thread.getStackTrace()); - throw exception; - } - - private static class StatementThread extends Thread { - private final Statement fStatement; - - private boolean fFinished= false; - - private Throwable fExceptionThrownByOriginalStatement= null; - - public StatementThread(Statement statement) { - fStatement= statement; - } - - @Override - public void run() { - try { - fStatement.evaluate(); - fFinished= true; - } catch (InterruptedException e) { - //don't log the InterruptedException - } catch (Throwable e) { - fExceptionThrownByOriginalStatement= e; - } - } - } -}
\ No newline at end of file + private final Statement originalStatement; + private final TimeUnit timeUnit; + private final long timeout; + private final boolean lookForStuckThread; + private volatile ThreadGroup threadGroup = null; + + /** + * Returns a new builder for building an instance. + * + * @since 4.12 + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Creates an instance wrapping the given statement with the given timeout in milliseconds. + * + * @param statement the statement to wrap + * @param timeoutMillis the timeout in milliseconds + * @deprecated use {@link #builder()} instead. + */ + @Deprecated + public FailOnTimeout(Statement statement, long timeoutMillis) { + this(builder().withTimeout(timeoutMillis, TimeUnit.MILLISECONDS), statement); + } + + private FailOnTimeout(Builder builder, Statement statement) { + originalStatement = statement; + timeout = builder.timeout; + timeUnit = builder.unit; + lookForStuckThread = builder.lookForStuckThread; + } + + /** + * Builder for {@link FailOnTimeout}. + * + * @since 4.12 + */ + public static class Builder { + private boolean lookForStuckThread = false; + private long timeout = 0; + private TimeUnit unit = TimeUnit.SECONDS; + + private Builder() { + } + + /** + * Specifies the time to wait before timing out the test. + * + * <p>If this is not called, or is called with a {@code timeout} of + * {@code 0}, the returned {@code Statement} will wait forever for the + * test to complete, however the test will still launch from a separate + * thread. This can be useful for disabling timeouts in environments + * where they are dynamically set based on some property. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the {@code timeout} argument + * @return {@code this} for method chaining. + */ + public Builder withTimeout(long timeout, TimeUnit unit) { + if (timeout < 0) { + throw new IllegalArgumentException("timeout must be non-negative"); + } + if (unit == null) { + throw new NullPointerException("TimeUnit cannot be null"); + } + this.timeout = timeout; + this.unit = unit; + return this; + } + + /** + * Specifies whether to look for a stuck thread. If a timeout occurs and this + * feature is enabled, the test will look for a thread that appears to be stuck + * and dump its backtrace. This feature is experimental. Behavior may change + * after the 4.12 release in response to feedback. + * + * @param enable {@code true} to enable the feature + * @return {@code this} for method chaining. + */ + public Builder withLookingForStuckThread(boolean enable) { + this.lookForStuckThread = enable; + return this; + } + + /** + * Builds a {@link FailOnTimeout} instance using the values in this builder, + * wrapping the given statement. + * + * @param statement + */ + public FailOnTimeout build(Statement statement) { + if (statement == null) { + throw new NullPointerException("statement cannot be null"); + } + return new FailOnTimeout(this, statement); + } + } + + @Override + public void evaluate() throws Throwable { + CallableStatement callable = new CallableStatement(); + FutureTask<Throwable> task = new FutureTask<Throwable>(callable); + threadGroup = new ThreadGroup("FailOnTimeoutGroup"); + Thread thread = new Thread(threadGroup, task, "Time-limited test"); + thread.setDaemon(true); + thread.start(); + callable.awaitStarted(); + Throwable throwable = getResult(task, thread); + if (throwable != null) { + throw throwable; + } + } + + /** + * Wait for the test task, returning the exception thrown by the test if the + * test failed, an exception indicating a timeout if the test timed out, or + * {@code null} if the test passed. + */ + private Throwable getResult(FutureTask<Throwable> task, Thread thread) { + try { + if (timeout > 0) { + return task.get(timeout, timeUnit); + } else { + return task.get(); + } + } catch (InterruptedException e) { + return e; // caller will re-throw; no need to call Thread.interrupt() + } catch (ExecutionException e) { + // test failed; have caller re-throw the exception thrown by the test + return e.getCause(); + } catch (TimeoutException e) { + return createTimeoutException(thread); + } + } + + private Exception createTimeoutException(Thread thread) { + StackTraceElement[] stackTrace = thread.getStackTrace(); + final Thread stuckThread = lookForStuckThread ? getStuckThread(thread) : null; + Exception currThreadException = new TestTimedOutException(timeout, timeUnit); + if (stackTrace != null) { + currThreadException.setStackTrace(stackTrace); + thread.interrupt(); + } + if (stuckThread != null) { + Exception stuckThreadException = + new Exception ("Appears to be stuck in thread " + + stuckThread.getName()); + stuckThreadException.setStackTrace(getStackTrace(stuckThread)); + return new MultipleFailureException( + Arrays.<Throwable>asList(currThreadException, stuckThreadException)); + } else { + return currThreadException; + } + } + + /** + * Retrieves the stack trace for a given thread. + * @param thread The thread whose stack is to be retrieved. + * @return The stack trace; returns a zero-length array if the thread has + * terminated or the stack cannot be retrieved for some other reason. + */ + private StackTraceElement[] getStackTrace(Thread thread) { + try { + return thread.getStackTrace(); + } catch (SecurityException e) { + return new StackTraceElement[0]; + } + } + + /** + * Determines whether the test appears to be stuck in some thread other than + * the "main thread" (the one created to run the test). This feature is experimental. + * Behavior may change after the 4.12 release in response to feedback. + * @param mainThread The main thread created by {@code evaluate()} + * @return The thread which appears to be causing the problem, if different from + * {@code mainThread}, or {@code null} if the main thread appears to be the + * problem or if the thread cannot be determined. The return value is never equal + * to {@code mainThread}. + */ + private Thread getStuckThread(Thread mainThread) { + if (threadGroup == null) { + return null; + } + Thread[] threadsInGroup = getThreadArray(threadGroup); + if (threadsInGroup == null) { + return null; + } + + // Now that we have all the threads in the test's thread group: Assume that + // any thread we're "stuck" in is RUNNABLE. Look for all RUNNABLE threads. + // If just one, we return that (unless it equals threadMain). If there's more + // than one, pick the one that's using the most CPU time, if this feature is + // supported. + Thread stuckThread = null; + long maxCpuTime = 0; + for (Thread thread : threadsInGroup) { + if (thread.getState() == Thread.State.RUNNABLE) { + long threadCpuTime = cpuTime(thread); + if (stuckThread == null || threadCpuTime > maxCpuTime) { + stuckThread = thread; + maxCpuTime = threadCpuTime; + } + } + } + return (stuckThread == mainThread) ? null : stuckThread; + } + + /** + * Returns all active threads belonging to a thread group. + * @param group The thread group. + * @return The active threads in the thread group. The result should be a + * complete list of the active threads at some point in time. Returns {@code null} + * if this cannot be determined, e.g. because new threads are being created at an + * extremely fast rate. + */ + private Thread[] getThreadArray(ThreadGroup group) { + final int count = group.activeCount(); // this is just an estimate + int enumSize = Math.max(count * 2, 100); + int enumCount; + Thread[] threads; + int loopCount = 0; + while (true) { + threads = new Thread[enumSize]; + enumCount = group.enumerate(threads); + if (enumCount < enumSize) { + break; + } + // if there are too many threads to fit into the array, enumerate's result + // is >= the array's length; therefore we can't trust that it returned all + // the threads. Try again. + enumSize += 100; + if (++loopCount >= 5) { + return null; + } + // threads are proliferating too fast for us. Bail before we get into + // trouble. + } + return copyThreads(threads, enumCount); + } + + /** + * Returns an array of the first {@code count} Threads in {@code threads}. + * (Use instead of Arrays.copyOf to maintain compatibility with Java 1.5.) + * @param threads The source array. + * @param count The maximum length of the result array. + * @return The first {@count} (at most) elements of {@code threads}. + */ + private Thread[] copyThreads(Thread[] threads, int count) { + int length = Math.min(count, threads.length); + Thread[] result = new Thread[length]; + for (int i = 0; i < length; i++) { + result[i] = threads[i]; + } + return result; + } + + /** + * Returns the CPU time used by a thread, if possible. + * @param thr The thread to query. + * @return The CPU time used by {@code thr}, or 0 if it cannot be determined. + */ + private long cpuTime (Thread thr) { + ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); + if (mxBean.isThreadCpuTimeSupported()) { + try { + return mxBean.getThreadCpuTime(thr.getId()); + } catch (UnsupportedOperationException e) { + } + } + return 0; + } + + private class CallableStatement implements Callable<Throwable> { + private final CountDownLatch startLatch = new CountDownLatch(1); + + public Throwable call() throws Exception { + try { + startLatch.countDown(); + originalStatement.evaluate(); + } catch (Exception e) { + throw e; + } catch (Throwable e) { + return e; + } + return null; + } + + public void awaitStarted() throws InterruptedException { + startLatch.await(); + } + } +} diff --git a/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java b/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java index e2e81e1..68c0545 100644 --- a/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java +++ b/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java @@ -1,22 +1,19 @@ -/** - * - */ package org.junit.internal.runners.statements; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; public class InvokeMethod extends Statement { - private final FrameworkMethod fTestMethod; - private Object fTarget; - - public InvokeMethod(FrameworkMethod testMethod, Object target) { - fTestMethod= testMethod; - fTarget= target; - } - - @Override - public void evaluate() throws Throwable { - fTestMethod.invokeExplosively(fTarget); - } + private final FrameworkMethod testMethod; + private final Object target; + + public InvokeMethod(FrameworkMethod testMethod, Object target) { + this.testMethod = testMethod; + this.target = target; + } + + @Override + public void evaluate() throws Throwable { + testMethod.invokeExplosively(target); + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/statements/RunAfters.java b/src/main/java/org/junit/internal/runners/statements/RunAfters.java index 475ec72..7512a7d 100644 --- a/src/main/java/org/junit/internal/runners/statements/RunAfters.java +++ b/src/main/java/org/junit/internal/runners/statements/RunAfters.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.runners.statements; import java.util.ArrayList; @@ -11,33 +8,34 @@ import org.junit.runners.model.MultipleFailureException; import org.junit.runners.model.Statement; public class RunAfters extends Statement { - private final Statement fNext; + private final Statement next; - private final Object fTarget; + private final Object target; - private final List<FrameworkMethod> fAfters; - - public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) { - fNext= next; - fAfters= afters; - fTarget= target; - } + private final List<FrameworkMethod> afters; - @Override - public void evaluate() throws Throwable { - List<Throwable> errors = new ArrayList<Throwable>(); - try { - fNext.evaluate(); - } catch (Throwable e) { - errors.add(e); - } finally { - for (FrameworkMethod each : fAfters) - try { - each.invokeExplosively(fTarget); - } catch (Throwable e) { - errors.add(e); - } - } - MultipleFailureException.assertEmpty(errors); - } + public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) { + this.next = next; + this.afters = afters; + this.target = target; + } + + @Override + public void evaluate() throws Throwable { + List<Throwable> errors = new ArrayList<Throwable>(); + try { + next.evaluate(); + } catch (Throwable e) { + errors.add(e); + } finally { + for (FrameworkMethod each : afters) { + try { + each.invokeExplosively(target); + } catch (Throwable e) { + errors.add(e); + } + } + } + MultipleFailureException.assertEmpty(errors); + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/statements/RunBefores.java b/src/main/java/org/junit/internal/runners/statements/RunBefores.java index 66a34e1..238fbe7 100644 --- a/src/main/java/org/junit/internal/runners/statements/RunBefores.java +++ b/src/main/java/org/junit/internal/runners/statements/RunBefores.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.runners.statements; import java.util.List; @@ -9,22 +6,23 @@ import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; public class RunBefores extends Statement { - private final Statement fNext; + private final Statement next; - private final Object fTarget; + private final Object target; - private final List<FrameworkMethod> fBefores; + private final List<FrameworkMethod> befores; - public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) { - fNext= next; - fBefores= befores; - fTarget= target; - } + public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) { + this.next = next; + this.befores = befores; + this.target = target; + } - @Override - public void evaluate() throws Throwable { - for (FrameworkMethod before : fBefores) - before.invokeExplosively(fTarget); - fNext.evaluate(); - } + @Override + public void evaluate() throws Throwable { + for (FrameworkMethod before : befores) { + before.invokeExplosively(target); + } + next.evaluate(); + } }
\ No newline at end of file |