aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/junit/internal/runners
diff options
context:
space:
mode:
authorPaul Duffin <paulduffin@google.com>2016-12-14 11:49:43 +0000
committerPaul Duffin <paulduffin@google.com>2016-12-20 15:52:52 +0000
commitaeb93fc33cae3aadbb9b46083350ad2dc9aea645 (patch)
treeb316db7dee11d1aeee3510562e036fd41705b8b5 /src/main/java/org/junit/internal/runners
parent26401927b83770db45f00706ccc589955644c6c2 (diff)
downloadjunit-aeb93fc33cae3aadbb9b46083350ad2dc9aea645.tar.gz
Upgrade to JUnit 4.12
The license has changed from Common Public License v1.0 to Eclipse Public License v1.0. This will not compile as it is because it is intended to be built against Hamcrest 1.3 or later but it is being built against Hamcrest 1.1. A follow on patch will fix the compilation errors so that it builds against Hamcrest 1.1. That allows Hamcrest to be upgraded separately. The patch can be reverted once Hamcrest has been upgraded. There are also some Android specific issues that will also be fixed in follow on patches. Bug: 33613916 Test: make checkbuild Change-Id: Ic2c983a030399e3ace1a14927cb143fbd8307b4f
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