aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/junit/internal/runners
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/junit/internal/runners')
-rw-r--r--src/main/java/org/junit/internal/runners/ClassRoadie.java118
-rw-r--r--src/main/java/org/junit/internal/runners/ErrorReportingRunner.java102
-rw-r--r--src/main/java/org/junit/internal/runners/FailedBefore.java5
-rw-r--r--src/main/java/org/junit/internal/runners/InitializationError.java41
-rw-r--r--src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java294
-rw-r--r--src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java236
-rw-r--r--src/main/java/org/junit/internal/runners/MethodRoadie.java256
-rw-r--r--src/main/java/org/junit/internal/runners/MethodValidator.java120
-rw-r--r--src/main/java/org/junit/internal/runners/SuiteMethod.java37
-rw-r--r--src/main/java/org/junit/internal/runners/TestClass.java165
-rw-r--r--src/main/java/org/junit/internal/runners/TestMethod.java80
-rw-r--r--src/main/java/org/junit/internal/runners/model/EachTestNotifier.java79
-rw-r--r--src/main/java/org/junit/internal/runners/model/MultipleFailureException.java8
-rw-r--r--src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java19
-rw-r--r--src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java92
-rw-r--r--src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java344
-rw-r--r--src/main/java/org/junit/internal/runners/rules/ValidationError.java11
-rw-r--r--src/main/java/org/junit/internal/runners/statements/ExpectException.java60
-rw-r--r--src/main/java/org/junit/internal/runners/statements/Fail.java17
-rw-r--r--src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java372
-rw-r--r--src/main/java/org/junit/internal/runners/statements/InvokeMethod.java27
-rw-r--r--src/main/java/org/junit/internal/runners/statements/RunAfters.java56
-rw-r--r--src/main/java/org/junit/internal/runners/statements/RunBefores.java32
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