aboutsummaryrefslogtreecommitdiff
path: root/src/org/junit/internal/runners
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/junit/internal/runners')
-rw-r--r--src/org/junit/internal/runners/ClassRoadie.java79
-rw-r--r--src/org/junit/internal/runners/ErrorReportingRunner.java60
-rw-r--r--src/org/junit/internal/runners/FailedBefore.java14
-rw-r--r--src/org/junit/internal/runners/InitializationError.java30
-rw-r--r--src/org/junit/internal/runners/JUnit38ClassRunner.java158
-rw-r--r--src/org/junit/internal/runners/JUnit4ClassRunner.java145
-rw-r--r--src/org/junit/internal/runners/MethodRoadie.java157
-rw-r--r--src/org/junit/internal/runners/MethodValidator.java91
-rw-r--r--src/org/junit/internal/runners/SuiteMethod.java40
-rw-r--r--src/org/junit/internal/runners/TestClass.java102
-rw-r--r--src/org/junit/internal/runners/TestMethod.java69
-rw-r--r--src/org/junit/internal/runners/model/EachTestNotifier.java51
-rw-r--r--src/org/junit/internal/runners/model/MultipleFailureException.java12
-rw-r--r--src/org/junit/internal/runners/model/ReflectiveCallable.java22
-rw-r--r--src/org/junit/internal/runners/package-info.java6
-rw-r--r--src/org/junit/internal/runners/rules/RuleFieldValidator.java92
-rw-r--r--src/org/junit/internal/runners/statements/ExpectException.java38
-rw-r--r--src/org/junit/internal/runners/statements/Fail.java17
-rw-r--r--src/org/junit/internal/runners/statements/FailOnTimeout.java71
-rw-r--r--src/org/junit/internal/runners/statements/InvokeMethod.java22
-rw-r--r--src/org/junit/internal/runners/statements/RunAfters.java43
-rw-r--r--src/org/junit/internal/runners/statements/RunBefores.java30
22 files changed, 1349 insertions, 0 deletions
diff --git a/src/org/junit/internal/runners/ClassRoadie.java b/src/org/junit/internal/runners/ClassRoadie.java
new file mode 100644
index 0000000..1f77d37
--- /dev/null
+++ b/src/org/junit/internal/runners/ClassRoadie.java
@@ -0,0 +1,79 @@
+package org.junit.internal.runners;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+/**
+ * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
+ * removed in the next 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;
+ }
+
+ protected void runUnprotected() {
+ fRunnable.run();
+ };
+
+ protected void addFailure(Throwable targetException) {
+ fNotifier.fireTestFailure(new Failure(fDescription, targetException));
+ }
+
+ public void runProtected() {
+ try {
+ runBefores();
+ runUnprotected();
+ } catch (FailedBefore e) {
+ } finally {
+ runAfters();
+ }
+ }
+
+ private void runBefores() throws FailedBefore {
+ try {
+ 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();
+ }
+ }
+
+ 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
diff --git a/src/org/junit/internal/runners/ErrorReportingRunner.java b/src/org/junit/internal/runners/ErrorReportingRunner.java
new file mode 100644
index 0000000..200b6f0
--- /dev/null
+++ b/src/org/junit/internal/runners/ErrorReportingRunner.java
@@ -0,0 +1,60 @@
+package org.junit.internal.runners;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.Failure;
+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
diff --git a/src/org/junit/internal/runners/FailedBefore.java b/src/org/junit/internal/runners/FailedBefore.java
new file mode 100644
index 0000000..29dcba4
--- /dev/null
+++ b/src/org/junit/internal/runners/FailedBefore.java
@@ -0,0 +1,14 @@
+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
+ * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
+ */
+@Deprecated
+class FailedBefore extends Exception {
+ private static final long serialVersionUID= 1L;
+} \ No newline at end of file
diff --git a/src/org/junit/internal/runners/InitializationError.java b/src/org/junit/internal/runners/InitializationError.java
new file mode 100644
index 0000000..5715ec5
--- /dev/null
+++ b/src/org/junit/internal/runners/InitializationError.java
@@ -0,0 +1,30 @@
+package org.junit.internal.runners;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Deprecated
+/**
+ * Use the published version: {@link org.junit.runners.InitializationError}
+ * This may disappear as soon as 1 April 2009
+ */
+public class InitializationError extends Exception {
+ private static final long serialVersionUID= 1L;
+ private final List<Throwable> fErrors;
+
+ public InitializationError(List<Throwable> errors) {
+ fErrors= errors;
+ }
+
+ public InitializationError(Throwable... errors) {
+ this(Arrays.asList(errors));
+ }
+
+ public InitializationError(String string) {
+ this(new Exception(string));
+ }
+
+ public List<Throwable> getCauses() {
+ return fErrors;
+ }
+}
diff --git a/src/org/junit/internal/runners/JUnit38ClassRunner.java b/src/org/junit/internal/runners/JUnit38ClassRunner.java
new file mode 100644
index 0000000..0028d0c
--- /dev/null
+++ b/src/org/junit/internal/runners/JUnit38ClassRunner.java
@@ -0,0 +1,158 @@
+package org.junit.internal.runners;
+
+import junit.extensions.TestDecorator;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import org.junit.runner.Describable;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.manipulation.Sortable;
+import org.junit.runner.manipulation.Sorter;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+
+public class 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;
+ }
+}
diff --git a/src/org/junit/internal/runners/JUnit4ClassRunner.java b/src/org/junit/internal/runners/JUnit4ClassRunner.java
new file mode 100644
index 0000000..d732880
--- /dev/null
+++ b/src/org/junit/internal/runners/JUnit4ClassRunner.java
@@ -0,0 +1,145 @@
+package org.junit.internal.runners;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.manipulation.Sortable;
+import org.junit.runner.manipulation.Sorter;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+/**
+ * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
+ * removed in the next 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;
+ }
+} \ No newline at end of file
diff --git a/src/org/junit/internal/runners/MethodRoadie.java b/src/org/junit/internal/runners/MethodRoadie.java
new file mode 100644
index 0000000..4407821
--- /dev/null
+++ b/src/org/junit/internal/runners/MethodRoadie.java
@@ -0,0 +1,157 @@
+package org.junit.internal.runners;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.internal.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+/**
+ * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
+ * removed in the next 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;
+
+ public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) {
+ fTest= test;
+ fNotifier= notifier;
+ fDescription= description;
+ fTestMethod= 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);
+ }
+ }
+
+ 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();
+ }
+ });
+ }
+
+ 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();
+ }
+ }
+
+ private void runAfters() {
+ List<Method> afters= fTestMethod.getAfters();
+ for (Method after : afters)
+ try {
+ after.invoke(fTest);
+ } catch (InvocationTargetException e) {
+ addFailure(e.getTargetException());
+ } catch (Throwable e) {
+ addFailure(e); // Untested, but seems impossible
+ }
+ }
+
+ protected void addFailure(Throwable e) {
+ fNotifier.fireTestFailure(new Failure(fDescription, e));
+ }
+}
+
diff --git a/src/org/junit/internal/runners/MethodValidator.java b/src/org/junit/internal/runners/MethodValidator.java
new file mode 100644
index 0000000..cadc93f
--- /dev/null
+++ b/src/org/junit/internal/runners/MethodValidator.java
@@ -0,0 +1,91 @@
+package org.junit.internal.runners;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+/**
+ * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
+ * removed in the next release. Please use
+ * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
+ */
+@Deprecated
+public class MethodValidator {
+
+ private final List<Throwable> fErrors= new ArrayList<Throwable>();
+
+ private TestClass fTestClass;
+
+ public MethodValidator(TestClass testClass) {
+ fTestClass = testClass;
+ }
+
+ public void validateInstanceMethods() {
+ validateTestMethods(After.class, false);
+ validateTestMethods(Before.class, false);
+ validateTestMethods(Test.class, false);
+
+ List<Method> methods= fTestClass.getAnnotatedMethods(Test.class);
+ if (methods.size() == 0)
+ fErrors.add(new Exception("No runnable methods"));
+ }
+
+ public void validateStaticMethods() {
+ 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);
+ }
+
+ public void validateNoArgConstructor() {
+ try {
+ fTestClass.getConstructor();
+ } catch (Exception e) {
+ fErrors.add(new Exception("Test class should have public zero-argument constructor", e));
+ }
+ }
+
+ private void validateTestMethods(Class<? extends Annotation> annotation,
+ boolean isStatic) {
+ List<Method> methods= 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() + "() "
+ + state + " be static"));
+ }
+ if (!Modifier.isPublic(each.getDeclaringClass().getModifiers()))
+ fErrors.add(new Exception("Class " + each.getDeclaringClass().getName()
+ + " should be public"));
+ if (!Modifier.isPublic(each.getModifiers()))
+ fErrors.add(new Exception("Method " + each.getName()
+ + " should be public"));
+ if (each.getReturnType() != Void.TYPE)
+ fErrors.add(new Exception("Method " + each.getName()
+ + " should be void"));
+ if (each.getParameterTypes().length != 0)
+ fErrors.add(new Exception("Method " + each.getName()
+ + " should have no parameters"));
+ }
+ }
+}
diff --git a/src/org/junit/internal/runners/SuiteMethod.java b/src/org/junit/internal/runners/SuiteMethod.java
new file mode 100644
index 0000000..4e8bebc
--- /dev/null
+++ b/src/org/junit/internal/runners/SuiteMethod.java
@@ -0,0 +1,40 @@
+package org.junit.internal.runners;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import junit.framework.Test;
+
+/** Runner for use with JUnit 3.8.x-style AllTests classes
+ * (those that only implement a static <code>suite()</code>
+ * method). For example:
+ * <pre>
+ * &#064;RunWith(AllTests.class)
+ * public class ProductTests {
+ * public static junit.framework.Test suite() {
+ * ...
+ * }
+ * }
+ * </pre>
+ */
+public class SuiteMethod extends JUnit38ClassRunner {
+ 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;
+ }
+}
diff --git a/src/org/junit/internal/runners/TestClass.java b/src/org/junit/internal/runners/TestClass.java
new file mode 100644
index 0000000..1ca2b9d
--- /dev/null
+++ b/src/org/junit/internal/runners/TestClass.java
@@ -0,0 +1,102 @@
+package org.junit.internal.runners;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+/**
+ * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
+ * removed in the next 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();
+ }
+
+}
diff --git a/src/org/junit/internal/runners/TestMethod.java b/src/org/junit/internal/runners/TestMethod.java
new file mode 100644
index 0000000..a06213c
--- /dev/null
+++ b/src/org/junit/internal/runners/TestMethod.java
@@ -0,0 +1,69 @@
+package org.junit.internal.runners;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.Test.None;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+/**
+ * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
+ * removed in the next release. Please use
+ * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
+ */
+@Deprecated
+public class TestMethod {
+ private final Method fMethod;
+ private TestClass fTestClass;
+
+ public TestMethod(Method method, TestClass testClass) {
+ fMethod= method;
+ fTestClass= testClass;
+ }
+
+ public boolean isIgnored() {
+ return fMethod.getAnnotation(Ignore.class) != null;
+ }
+
+ public long getTimeout() {
+ Test annotation= fMethod.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();
+ }
+
+ boolean isUnexpected(Throwable exception) {
+ return ! getExpectedException().isAssignableFrom(exception.getClass());
+ }
+
+ boolean expectsException() {
+ return getExpectedException() != null;
+ }
+
+ List<Method> getBefores() {
+ return fTestClass.getAnnotatedMethods(Before.class);
+ }
+
+ List<Method> getAfters() {
+ return fTestClass.getAnnotatedMethods(After.class);
+ }
+
+ public void invoke(Object test) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+ fMethod.invoke(test);
+ }
+
+}
diff --git a/src/org/junit/internal/runners/model/EachTestNotifier.java b/src/org/junit/internal/runners/model/EachTestNotifier.java
new file mode 100644
index 0000000..a7d534c
--- /dev/null
+++ b/src/org/junit/internal/runners/model/EachTestNotifier.java
@@ -0,0 +1,51 @@
+/**
+ *
+ */
+package org.junit.internal.runners.model;
+
+import org.junit.internal.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+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);
+ }
+} \ No newline at end of file
diff --git a/src/org/junit/internal/runners/model/MultipleFailureException.java b/src/org/junit/internal/runners/model/MultipleFailureException.java
new file mode 100644
index 0000000..3316806
--- /dev/null
+++ b/src/org/junit/internal/runners/model/MultipleFailureException.java
@@ -0,0 +1,12 @@
+package org.junit.internal.runners.model;
+
+import java.util.List;
+
+@Deprecated
+public class MultipleFailureException extends org.junit.runners.model.MultipleFailureException {
+ private static final long serialVersionUID= 1L;
+
+ public MultipleFailureException(List<Throwable> errors) {
+ super(errors);
+ }
+}
diff --git a/src/org/junit/internal/runners/model/ReflectiveCallable.java b/src/org/junit/internal/runners/model/ReflectiveCallable.java
new file mode 100644
index 0000000..9150d90
--- /dev/null
+++ b/src/org/junit/internal/runners/model/ReflectiveCallable.java
@@ -0,0 +1,22 @@
+/**
+ *
+ */
+package org.junit.internal.runners.model;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * When invoked, throws the exception from the reflected method, rather than
+ * wrapping it in an InvocationTargetException.
+ */
+public abstract class ReflectiveCallable {
+ public Object run() throws Throwable {
+ try {
+ return runReflectiveCall();
+ } catch (InvocationTargetException e) {
+ throw e.getTargetException();
+ }
+ }
+
+ protected abstract Object runReflectiveCall() throws Throwable;
+} \ No newline at end of file
diff --git a/src/org/junit/internal/runners/package-info.java b/src/org/junit/internal/runners/package-info.java
new file mode 100644
index 0000000..5ab7e7b
--- /dev/null
+++ b/src/org/junit/internal/runners/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Provides implementations of {@link org.junit.runner.Runner}
+ *
+ * @since 4.0
+ */
+package org.junit.internal.runners; \ No newline at end of file
diff --git a/src/org/junit/internal/runners/rules/RuleFieldValidator.java b/src/org/junit/internal/runners/rules/RuleFieldValidator.java
new file mode 100644
index 0000000..e7df8bf
--- /dev/null
+++ b/src/org/junit/internal/runners/rules/RuleFieldValidator.java
@@ -0,0 +1,92 @@
+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/org/junit/internal/runners/statements/ExpectException.java b/src/org/junit/internal/runners/statements/ExpectException.java
new file mode 100644
index 0000000..ddfef07
--- /dev/null
+++ b/src/org/junit/internal/runners/statements/ExpectException.java
@@ -0,0 +1,38 @@
+/**
+ *
+ */
+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());
+ }
+} \ No newline at end of file
diff --git a/src/org/junit/internal/runners/statements/Fail.java b/src/org/junit/internal/runners/statements/Fail.java
new file mode 100644
index 0000000..e7d0d5c
--- /dev/null
+++ b/src/org/junit/internal/runners/statements/Fail.java
@@ -0,0 +1,17 @@
+package org.junit.internal.runners.statements;
+
+import org.junit.runners.model.Statement;
+
+
+public class Fail extends Statement {
+ private final Throwable fError;
+
+ public Fail(Throwable e) {
+ fError= e;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ throw fError;
+ }
+}
diff --git a/src/org/junit/internal/runners/statements/FailOnTimeout.java b/src/org/junit/internal/runners/statements/FailOnTimeout.java
new file mode 100644
index 0000000..bff7c72
--- /dev/null
+++ b/src/org/junit/internal/runners/statements/FailOnTimeout.java
@@ -0,0 +1,71 @@
+/**
+ *
+ */
+package org.junit.internal.runners.statements;
+
+import org.junit.runners.model.Statement;
+
+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
diff --git a/src/org/junit/internal/runners/statements/InvokeMethod.java b/src/org/junit/internal/runners/statements/InvokeMethod.java
new file mode 100644
index 0000000..e2e81e1
--- /dev/null
+++ b/src/org/junit/internal/runners/statements/InvokeMethod.java
@@ -0,0 +1,22 @@
+/**
+ *
+ */
+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);
+ }
+} \ No newline at end of file
diff --git a/src/org/junit/internal/runners/statements/RunAfters.java b/src/org/junit/internal/runners/statements/RunAfters.java
new file mode 100644
index 0000000..475ec72
--- /dev/null
+++ b/src/org/junit/internal/runners/statements/RunAfters.java
@@ -0,0 +1,43 @@
+/**
+ *
+ */
+package org.junit.internal.runners.statements;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.MultipleFailureException;
+import org.junit.runners.model.Statement;
+
+public class RunAfters extends Statement {
+ private final Statement fNext;
+
+ private final Object fTarget;
+
+ private final List<FrameworkMethod> fAfters;
+
+ public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) {
+ fNext= next;
+ fAfters= afters;
+ fTarget= target;
+ }
+
+ @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);
+ }
+} \ No newline at end of file
diff --git a/src/org/junit/internal/runners/statements/RunBefores.java b/src/org/junit/internal/runners/statements/RunBefores.java
new file mode 100644
index 0000000..66a34e1
--- /dev/null
+++ b/src/org/junit/internal/runners/statements/RunBefores.java
@@ -0,0 +1,30 @@
+/**
+ *
+ */
+package org.junit.internal.runners.statements;
+
+import java.util.List;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+public class RunBefores extends Statement {
+ private final Statement fNext;
+
+ private final Object fTarget;
+
+ private final List<FrameworkMethod> fBefores;
+
+ public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) {
+ fNext= next;
+ fBefores= befores;
+ fTarget= target;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ for (FrameworkMethod before : fBefores)
+ before.invokeExplosively(fTarget);
+ fNext.evaluate();
+ }
+} \ No newline at end of file