aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/junit/runner
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/runner
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/runner')
-rw-r--r--src/main/java/org/junit/runner/Computer.java52
-rw-r--r--src/main/java/org/junit/runner/Describable.java10
-rw-r--r--src/main/java/org/junit/runner/FilterFactories.java82
-rw-r--r--src/main/java/org/junit/runner/FilterFactory.java24
-rw-r--r--src/main/java/org/junit/runner/FilterFactoryParams.java23
-rw-r--r--src/main/java/org/junit/runner/JUnitCommandLineParseResult.java149
-rw-r--r--src/main/java/org/junit/runner/JUnitCore.java311
-rw-r--r--src/main/java/org/junit/runner/Request.java273
-rw-r--r--src/main/java/org/junit/runner/Result.java274
-rw-r--r--src/main/java/org/junit/runner/RunWith.java24
-rw-r--r--src/main/java/org/junit/runner/Runner.java47
-rw-r--r--src/main/java/org/junit/runner/manipulation/Filter.java192
-rw-r--r--src/main/java/org/junit/runner/manipulation/Filterable.java15
-rw-r--r--src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java4
-rw-r--r--src/main/java/org/junit/runner/manipulation/Sortable.java13
-rw-r--r--src/main/java/org/junit/runner/manipulation/Sorter.java64
-rw-r--r--src/main/java/org/junit/runner/notification/Failure.java116
-rw-r--r--src/main/java/org/junit/runner/notification/RunListener.java171
-rw-r--r--src/main/java/org/junit/runner/notification/RunNotifier.java346
-rw-r--r--src/main/java/org/junit/runner/notification/StoppedByUserException.java7
-rw-r--r--src/main/java/org/junit/runner/notification/SynchronizedRunListener.java103
21 files changed, 1446 insertions, 854 deletions
diff --git a/src/main/java/org/junit/runner/Computer.java b/src/main/java/org/junit/runner/Computer.java
index 939f702..8bb4b20 100644
--- a/src/main/java/org/junit/runner/Computer.java
+++ b/src/main/java/org/junit/runner/Computer.java
@@ -8,33 +8,35 @@ import org.junit.runners.model.RunnerBuilder;
* Represents a strategy for computing runners and suites.
* WARNING: this class is very likely to undergo serious changes in version 4.8 and
* beyond.
+ *
+ * @since 4.6
*/
public class Computer {
- /**
- * Returns a new default computer, which runs tests in serial order
- */
- public static Computer serial() {
- return new Computer();
- }
+ /**
+ * Returns a new default computer, which runs tests in serial order
+ */
+ public static Computer serial() {
+ return new Computer();
+ }
- /**
- * Create a suite for {@code classes}, building Runners with {@code builder}.
- * Throws an InitializationError if Runner construction fails
- */
- public Runner getSuite(final RunnerBuilder builder,
- Class<?>[] classes) throws InitializationError {
- return new Suite(new RunnerBuilder() {
- @Override
- public Runner runnerForClass(Class<?> testClass) throws Throwable {
- return getRunner(builder, testClass);
- }
- }, classes);
- }
+ /**
+ * Create a suite for {@code classes}, building Runners with {@code builder}.
+ * Throws an InitializationError if Runner construction fails
+ */
+ public Runner getSuite(final RunnerBuilder builder,
+ Class<?>[] classes) throws InitializationError {
+ return new Suite(new RunnerBuilder() {
+ @Override
+ public Runner runnerForClass(Class<?> testClass) throws Throwable {
+ return getRunner(builder, testClass);
+ }
+ }, classes);
+ }
- /**
- * Create a single-class runner for {@code testClass}, using {@code builder}
- */
- protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
- return builder.runnerForClass(testClass);
- }
+ /**
+ * Create a single-class runner for {@code testClass}, using {@code builder}
+ */
+ protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
+ return builder.runnerForClass(testClass);
+ }
}
diff --git a/src/main/java/org/junit/runner/Describable.java b/src/main/java/org/junit/runner/Describable.java
index e59cc01..1514141 100644
--- a/src/main/java/org/junit/runner/Describable.java
+++ b/src/main/java/org/junit/runner/Describable.java
@@ -3,10 +3,12 @@ package org.junit.runner;
/**
* Represents an object that can describe itself
+ *
+ * @since 4.5
*/
public interface Describable {
- /**
- * @return a {@link Description} showing the tests to be run by the receiver
- */
- public abstract Description getDescription();
+ /**
+ * @return a {@link Description} showing the tests to be run by the receiver
+ */
+ public abstract Description getDescription();
} \ No newline at end of file
diff --git a/src/main/java/org/junit/runner/FilterFactories.java b/src/main/java/org/junit/runner/FilterFactories.java
new file mode 100644
index 0000000..020d394
--- /dev/null
+++ b/src/main/java/org/junit/runner/FilterFactories.java
@@ -0,0 +1,82 @@
+package org.junit.runner;
+
+import org.junit.internal.Classes;
+import org.junit.runner.FilterFactory.FilterNotCreatedException;
+import org.junit.runner.manipulation.Filter;
+
+/**
+ * Utility class whose methods create a {@link FilterFactory}.
+ */
+class FilterFactories {
+ /**
+ * Creates a {@link Filter}.
+ *
+ * A filter specification is of the form "package.of.FilterFactory=args-to-filter-factory" or
+ * "package.of.FilterFactory".
+ *
+ * @param request the request that will be filtered
+ * @param filterSpec the filter specification
+ * @throws org.junit.runner.FilterFactory.FilterNotCreatedException
+ */
+ public static Filter createFilterFromFilterSpec(Request request, String filterSpec)
+ throws FilterFactory.FilterNotCreatedException {
+ Description topLevelDescription = request.getRunner().getDescription();
+ String[] tuple;
+
+ if (filterSpec.contains("=")) {
+ tuple = filterSpec.split("=", 2);
+ } else {
+ tuple = new String[]{ filterSpec, "" };
+ }
+
+ return createFilter(tuple[0], new FilterFactoryParams(topLevelDescription, tuple[1]));
+ }
+
+ /**
+ * Creates a {@link Filter}.
+ *
+ * @param filterFactoryFqcn The fully qualified class name of the {@link FilterFactory}
+ * @param params The arguments to the {@link FilterFactory}
+ */
+ public static Filter createFilter(String filterFactoryFqcn, FilterFactoryParams params)
+ throws FilterFactory.FilterNotCreatedException {
+ FilterFactory filterFactory = createFilterFactory(filterFactoryFqcn);
+
+ return filterFactory.createFilter(params);
+ }
+
+ /**
+ * Creates a {@link Filter}.
+ *
+ * @param filterFactoryClass The class of the {@link FilterFactory}
+ * @param params The arguments to the {@link FilterFactory}
+ *
+ */
+ public static Filter createFilter(Class<? extends FilterFactory> filterFactoryClass, FilterFactoryParams params)
+ throws FilterFactory.FilterNotCreatedException {
+ FilterFactory filterFactory = createFilterFactory(filterFactoryClass);
+
+ return filterFactory.createFilter(params);
+ }
+
+ static FilterFactory createFilterFactory(String filterFactoryFqcn) throws FilterNotCreatedException {
+ Class<? extends FilterFactory> filterFactoryClass;
+
+ try {
+ filterFactoryClass = Classes.getClass(filterFactoryFqcn).asSubclass(FilterFactory.class);
+ } catch (Exception e) {
+ throw new FilterNotCreatedException(e);
+ }
+
+ return createFilterFactory(filterFactoryClass);
+ }
+
+ static FilterFactory createFilterFactory(Class<? extends FilterFactory> filterFactoryClass)
+ throws FilterNotCreatedException {
+ try {
+ return filterFactoryClass.getConstructor().newInstance();
+ } catch (Exception e) {
+ throw new FilterNotCreatedException(e);
+ }
+ }
+}
diff --git a/src/main/java/org/junit/runner/FilterFactory.java b/src/main/java/org/junit/runner/FilterFactory.java
new file mode 100644
index 0000000..57b4eaa
--- /dev/null
+++ b/src/main/java/org/junit/runner/FilterFactory.java
@@ -0,0 +1,24 @@
+package org.junit.runner;
+
+import org.junit.runner.manipulation.Filter;
+
+/**
+ * Extend this class to create a factory that creates {@link Filter}.
+ */
+public interface FilterFactory {
+ /**
+ * Creates a {@link Filter} given a {@link FilterFactoryParams} argument.
+ *
+ * @param params Parameters needed to create the {@link Filter}
+ */
+ Filter createFilter(FilterFactoryParams params) throws FilterNotCreatedException;
+
+ /**
+ * Exception thrown if the {@link Filter} cannot be created.
+ */
+ public static class FilterNotCreatedException extends Exception {
+ public FilterNotCreatedException(Exception exception) {
+ super(exception.getMessage(), exception);
+ }
+ }
+}
diff --git a/src/main/java/org/junit/runner/FilterFactoryParams.java b/src/main/java/org/junit/runner/FilterFactoryParams.java
new file mode 100644
index 0000000..1e74ab9
--- /dev/null
+++ b/src/main/java/org/junit/runner/FilterFactoryParams.java
@@ -0,0 +1,23 @@
+package org.junit.runner;
+
+public final class FilterFactoryParams {
+ private final Description topLevelDescription;
+ private final String args;
+
+ public FilterFactoryParams(Description topLevelDescription, String args) {
+ if (args == null || topLevelDescription == null) {
+ throw new NullPointerException();
+ }
+
+ this.topLevelDescription = topLevelDescription;
+ this.args = args;
+ }
+
+ public String getArgs() {
+ return args;
+ }
+
+ public Description getTopLevelDescription() {
+ return topLevelDescription;
+ }
+}
diff --git a/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
new file mode 100644
index 0000000..434157c
--- /dev/null
+++ b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
@@ -0,0 +1,149 @@
+package org.junit.runner;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.internal.Classes;
+import org.junit.runner.FilterFactory.FilterNotCreatedException;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runners.model.InitializationError;
+
+class JUnitCommandLineParseResult {
+ private final List<String> filterSpecs = new ArrayList<String>();
+ private final List<Class<?>> classes = new ArrayList<Class<?>>();
+ private final List<Throwable> parserErrors = new ArrayList<Throwable>();
+
+ /**
+ * Do not use. Testing purposes only.
+ */
+ JUnitCommandLineParseResult() {}
+
+ /**
+ * Returns filter specs parsed from command line.
+ */
+ public List<String> getFilterSpecs() {
+ return Collections.unmodifiableList(filterSpecs);
+ }
+
+ /**
+ * Returns test classes parsed from command line.
+ */
+ public List<Class<?>> getClasses() {
+ return Collections.unmodifiableList(classes);
+ }
+
+ /**
+ * Parses the arguments.
+ *
+ * @param args Arguments
+ */
+ public static JUnitCommandLineParseResult parse(String[] args) {
+ JUnitCommandLineParseResult result = new JUnitCommandLineParseResult();
+
+ result.parseArgs(args);
+
+ return result;
+ }
+
+ private void parseArgs(String[] args) {
+ parseParameters(parseOptions(args));
+ }
+
+ String[] parseOptions(String... args) {
+ for (int i = 0; i != args.length; ++i) {
+ String arg = args[i];
+
+ if (arg.equals("--")) {
+ return copyArray(args, i + 1, args.length);
+ } else if (arg.startsWith("--")) {
+ if (arg.startsWith("--filter=") || arg.equals("--filter")) {
+ String filterSpec;
+ if (arg.equals("--filter")) {
+ ++i;
+
+ if (i < args.length) {
+ filterSpec = args[i];
+ } else {
+ parserErrors.add(new CommandLineParserError(arg + " value not specified"));
+ break;
+ }
+ } else {
+ filterSpec = arg.substring(arg.indexOf('=') + 1);
+ }
+
+ filterSpecs.add(filterSpec);
+ } else {
+ parserErrors.add(new CommandLineParserError("JUnit knows nothing about the " + arg + " option"));
+ }
+ } else {
+ return copyArray(args, i, args.length);
+ }
+ }
+
+ return new String[]{};
+ }
+
+ private String[] copyArray(String[] args, int from, int to) {
+ ArrayList<String> result = new ArrayList<String>();
+
+ for (int j = from; j != to; ++j) {
+ result.add(args[j]);
+ }
+
+ return result.toArray(new String[result.size()]);
+ }
+
+ void parseParameters(String[] args) {
+ for (String arg : args) {
+ try {
+ classes.add(Classes.getClass(arg));
+ } catch (ClassNotFoundException e) {
+ parserErrors.add(new IllegalArgumentException("Could not find class [" + arg + "]", e));
+ }
+ }
+ }
+
+ private Request errorReport(Throwable cause) {
+ return Request.errorReport(JUnitCommandLineParseResult.class, cause);
+ }
+
+ /**
+ * Creates a {@link Request}.
+ *
+ * @param computer {@link Computer} to be used.
+ */
+ public Request createRequest(Computer computer) {
+ if (parserErrors.isEmpty()) {
+ Request request = Request.classes(
+ computer, classes.toArray(new Class<?>[classes.size()]));
+ return applyFilterSpecs(request);
+ } else {
+ return errorReport(new InitializationError(parserErrors));
+ }
+ }
+
+ private Request applyFilterSpecs(Request request) {
+ try {
+ for (String filterSpec : filterSpecs) {
+ Filter filter = FilterFactories.createFilterFromFilterSpec(
+ request, filterSpec);
+ request = request.filterWith(filter);
+ }
+ return request;
+ } catch (FilterNotCreatedException e) {
+ return errorReport(e);
+ }
+ }
+
+ /**
+ * Exception used if there's a problem parsing the command line.
+ */
+ public static class CommandLineParserError extends Exception {
+ private static final long serialVersionUID= 1L;
+
+ public CommandLineParserError(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/src/main/java/org/junit/runner/JUnitCore.java b/src/main/java/org/junit/runner/JUnitCore.java
index 2fcd3b3..c1479e0 100644
--- a/src/main/java/org/junit/runner/JUnitCore.java
+++ b/src/main/java/org/junit/runner/JUnitCore.java
@@ -1,186 +1,167 @@
package org.junit.runner;
-import java.util.ArrayList;
-import java.util.List;
-
import junit.runner.Version;
import org.junit.internal.JUnitSystem;
import org.junit.internal.RealSystem;
import org.junit.internal.TextListener;
import org.junit.internal.runners.JUnit38ClassRunner;
-import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
/**
- * <code>JUnitCore</code> is a facade for running tests. It supports running JUnit 4 tests,
- * JUnit 3.8.x tests, and mixtures. To run tests from the command line, run
+ * <code>JUnitCore</code> is a facade for running tests. It supports running JUnit 4 tests,
+ * JUnit 3.8.x tests, and mixtures. To run tests from the command line, run
* <code>java org.junit.runner.JUnitCore TestClass1 TestClass2 ...</code>.
- * For one-shot test runs, use the static method {@link #runClasses(Class[])}.
+ * For one-shot test runs, use the static method {@link #runClasses(Class[])}.
* If you want to add special listeners,
* create an instance of {@link org.junit.runner.JUnitCore} first and use it to run the tests.
- *
+ *
* @see org.junit.runner.Result
* @see org.junit.runner.notification.RunListener
* @see org.junit.runner.Request
+ * @since 4.0
*/
public class JUnitCore {
- private RunNotifier fNotifier;
-
- /**
- * Create a new <code>JUnitCore</code> to run tests.
- */
- public JUnitCore() {
- fNotifier= new RunNotifier();
- }
-
- /**
- * Run the tests contained in the classes named in the <code>args</code>.
- * If all tests run successfully, exit with a status of 0. Otherwise exit with a status of 1.
- * Write feedback while tests are running and write
- * stack traces for all failed tests after the tests all complete.
- * @param args names of classes in which to find tests to run
- */
- public static void main(String... args) {
- runMainAndExit(new RealSystem(), args);
- }
-
- /**
- * Do not use. Testing purposes only.
- * @param system
- */
- public static void runMainAndExit(JUnitSystem system, String... args) {
- Result result= new JUnitCore().runMain(system, args);
- system.exit(result.wasSuccessful() ? 0 : 1);
- }
-
- /**
- * Run the tests contained in <code>classes</code>. Write feedback while the tests
- * are running and write stack traces for all failed tests after all tests complete. This is
- * similar to {@link #main(String[])}, but intended to be used programmatically.
- * @param computer Helps construct Runners from classes
- * @param classes Classes in which to find tests
- * @return a {@link Result} describing the details of the test run and the failed tests.
- */
- public static Result runClasses(Computer computer, Class<?>... classes) {
- return new JUnitCore().run(computer, classes);
- }
- /**
- * Run the tests contained in <code>classes</code>. Write feedback while the tests
- * are running and write stack traces for all failed tests after all tests complete. This is
- * similar to {@link #main(String[])}, but intended to be used programmatically.
- * @param classes Classes in which to find tests
- * @return a {@link Result} describing the details of the test run and the failed tests.
- */
- public static Result runClasses(Class<?>... classes) {
- return new JUnitCore().run(defaultComputer(), classes);
- }
-
- /**
- * Do not use. Testing purposes only.
- * @param system
- */
- public Result runMain(JUnitSystem system, String... args) {
- system.out().println("JUnit version " + Version.id());
- List<Class<?>> classes= new ArrayList<Class<?>>();
- List<Failure> missingClasses= new ArrayList<Failure>();
- for (String each : args)
- try {
- classes.add(Class.forName(each));
- } catch (ClassNotFoundException e) {
- system.out().println("Could not find class: " + each);
- Description description= Description.createSuiteDescription(each);
- Failure failure= new Failure(description, e);
- missingClasses.add(failure);
- }
- RunListener listener= new TextListener(system);
- addListener(listener);
- Result result= run(classes.toArray(new Class[0]));
- for (Failure each : missingClasses)
- result.getFailures().add(each);
- return result;
- }
-
- /**
- * @return the version number of this release
- */
- public String getVersion() {
- return Version.id();
- }
-
- /**
- * Run all the tests in <code>classes</code>.
- * @param classes the classes containing tests
- * @return a {@link Result} describing the details of the test run and the failed tests.
- */
- public Result run(Class<?>... classes) {
- return run(Request.classes(defaultComputer(), classes));
- }
-
- /**
- * Run all the tests in <code>classes</code>.
- * @param computer Helps construct Runners from classes
- * @param classes the classes containing tests
- * @return a {@link Result} describing the details of the test run and the failed tests.
- */
- public Result run(Computer computer, Class<?>... classes) {
- return run(Request.classes(computer, classes));
- }
-
- /**
- * Run all the tests contained in <code>request</code>.
- * @param request the request describing tests
- * @return a {@link Result} describing the details of the test run and the failed tests.
- */
- public Result run(Request request) {
- return run(request.getRunner());
- }
-
- /**
- * Run all the tests contained in JUnit 3.8.x <code>test</code>. Here for backward compatibility.
- * @param test the old-style test
- * @return a {@link Result} describing the details of the test run and the failed tests.
- */
- public Result run(junit.framework.Test test) {
- return run(new JUnit38ClassRunner(test));
- }
-
- /**
- * Do not use. Testing purposes only.
- */
- public Result run(Runner runner) {
- Result result= new Result();
- RunListener listener= result.createListener();
- fNotifier.addFirstListener(listener);
- try {
- fNotifier.fireTestRunStarted(runner.getDescription());
- runner.run(fNotifier);
- fNotifier.fireTestRunFinished(result);
- } finally {
- removeListener(listener);
- }
- return result;
- }
-
- /**
- * Add a listener to be notified as the tests run.
- * @param listener the listener to add
- * @see org.junit.runner.notification.RunListener
- */
- public void addListener(RunListener listener) {
- fNotifier.addListener(listener);
- }
-
- /**
- * Remove a listener.
- * @param listener the listener to remove
- */
- public void removeListener(RunListener listener) {
- fNotifier.removeListener(listener);
- }
-
- static Computer defaultComputer() {
- return new Computer();
- }
+ private final RunNotifier notifier = new RunNotifier();
+
+ /**
+ * Run the tests contained in the classes named in the <code>args</code>.
+ * If all tests run successfully, exit with a status of 0. Otherwise exit with a status of 1.
+ * Write feedback while tests are running and write
+ * stack traces for all failed tests after the tests all complete.
+ *
+ * @param args names of classes in which to find tests to run
+ */
+ public static void main(String... args) {
+ Result result = new JUnitCore().runMain(new RealSystem(), args);
+ System.exit(result.wasSuccessful() ? 0 : 1);
+ }
+
+ /**
+ * Run the tests contained in <code>classes</code>. Write feedback while the tests
+ * are running and write stack traces for all failed tests after all tests complete. This is
+ * similar to {@link #main(String[])}, but intended to be used programmatically.
+ *
+ * @param classes Classes in which to find tests
+ * @return a {@link Result} describing the details of the test run and the failed tests.
+ */
+ public static Result runClasses(Class<?>... classes) {
+ return runClasses(defaultComputer(), classes);
+ }
+
+ /**
+ * Run the tests contained in <code>classes</code>. Write feedback while the tests
+ * are running and write stack traces for all failed tests after all tests complete. This is
+ * similar to {@link #main(String[])}, but intended to be used programmatically.
+ *
+ * @param computer Helps construct Runners from classes
+ * @param classes Classes in which to find tests
+ * @return a {@link Result} describing the details of the test run and the failed tests.
+ */
+ public static Result runClasses(Computer computer, Class<?>... classes) {
+ return new JUnitCore().run(computer, classes);
+ }
+
+ /**
+ * @param system
+ * @param args from main()
+ */
+ Result runMain(JUnitSystem system, String... args) {
+ system.out().println("JUnit version " + Version.id());
+
+ JUnitCommandLineParseResult jUnitCommandLineParseResult = JUnitCommandLineParseResult.parse(args);
+
+ RunListener listener = new TextListener(system);
+ addListener(listener);
+
+ return run(jUnitCommandLineParseResult.createRequest(defaultComputer()));
+ }
+
+ /**
+ * @return the version number of this release
+ */
+ public String getVersion() {
+ return Version.id();
+ }
+
+ /**
+ * Run all the tests in <code>classes</code>.
+ *
+ * @param classes the classes containing tests
+ * @return a {@link Result} describing the details of the test run and the failed tests.
+ */
+ public Result run(Class<?>... classes) {
+ return run(defaultComputer(), classes);
+ }
+
+ /**
+ * Run all the tests in <code>classes</code>.
+ *
+ * @param computer Helps construct Runners from classes
+ * @param classes the classes containing tests
+ * @return a {@link Result} describing the details of the test run and the failed tests.
+ */
+ public Result run(Computer computer, Class<?>... classes) {
+ return run(Request.classes(computer, classes));
+ }
+
+ /**
+ * Run all the tests contained in <code>request</code>.
+ *
+ * @param request the request describing tests
+ * @return a {@link Result} describing the details of the test run and the failed tests.
+ */
+ public Result run(Request request) {
+ return run(request.getRunner());
+ }
+
+ /**
+ * Run all the tests contained in JUnit 3.8.x <code>test</code>. Here for backward compatibility.
+ *
+ * @param test the old-style test
+ * @return a {@link Result} describing the details of the test run and the failed tests.
+ */
+ public Result run(junit.framework.Test test) {
+ return run(new JUnit38ClassRunner(test));
+ }
+
+ /**
+ * Do not use. Testing purposes only.
+ */
+ public Result run(Runner runner) {
+ Result result = new Result();
+ RunListener listener = result.createListener();
+ notifier.addFirstListener(listener);
+ try {
+ notifier.fireTestRunStarted(runner.getDescription());
+ runner.run(notifier);
+ notifier.fireTestRunFinished(result);
+ } finally {
+ removeListener(listener);
+ }
+ return result;
+ }
+
+ /**
+ * Add a listener to be notified as the tests run.
+ *
+ * @param listener the listener to add
+ * @see org.junit.runner.notification.RunListener
+ */
+ public void addListener(RunListener listener) {
+ notifier.addListener(listener);
+ }
+
+ /**
+ * Remove a listener.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeListener(RunListener listener) {
+ notifier.removeListener(listener);
+ }
+ static Computer defaultComputer() {
+ return new Computer();
+ }
}
diff --git a/src/main/java/org/junit/runner/Request.java b/src/main/java/org/junit/runner/Request.java
index 310b915..79c0f1e 100644
--- a/src/main/java/org/junit/runner/Request.java
+++ b/src/main/java/org/junit/runner/Request.java
@@ -11,151 +11,160 @@ import org.junit.runner.manipulation.Filter;
import org.junit.runners.model.InitializationError;
/**
- * <p>A <code>Request</code> is an abstract description of tests to be run. Older versions of
+ * A <code>Request</code> is an abstract description of tests to be run. Older versions of
* JUnit did not need such a concept--tests to be run were described either by classes containing
* tests or a tree of {@link org.junit.Test}s. However, we want to support filtering and sorting,
* so we need a more abstract specification than the tests themselves and a richer
- * specification than just the classes.</p>
- *
- * <p>The flow when JUnit runs tests is that a <code>Request</code> specifies some tests to be run ->
- * a {@link org.junit.runner.Runner} is created for each class implied by the <code>Request</code> ->
- * the {@link org.junit.runner.Runner} returns a detailed {@link org.junit.runner.Description}
- * which is a tree structure of the tests to be run.</p>
+ * specification than just the classes.
+ *
+ * <p>The flow when JUnit runs tests is that a <code>Request</code> specifies some tests to be run -&gt;
+ * a {@link org.junit.runner.Runner} is created for each class implied by the <code>Request</code> -&gt;
+ * the {@link org.junit.runner.Runner} returns a detailed {@link org.junit.runner.Description}
+ * which is a tree structure of the tests to be run.
+ *
+ * @since 4.0
*/
public abstract class Request {
- /**
- * Create a <code>Request</code> that, when processed, will run a single test.
- * This is done by filtering out all other tests. This method is used to support rerunning
- * single tests.
- * @param clazz the class of the test
- * @param methodName the name of the test
- * @return a <code>Request</code> that will cause a single test be run
- */
- public static Request method(Class<?> clazz, String methodName) {
- Description method= Description.createTestDescription(clazz, methodName);
- return Request.aClass(clazz).filterWith(method);
- }
+ /**
+ * Create a <code>Request</code> that, when processed, will run a single test.
+ * This is done by filtering out all other tests. This method is used to support rerunning
+ * single tests.
+ *
+ * @param clazz the class of the test
+ * @param methodName the name of the test
+ * @return a <code>Request</code> that will cause a single test be run
+ */
+ public static Request method(Class<?> clazz, String methodName) {
+ Description method = Description.createTestDescription(clazz, methodName);
+ return Request.aClass(clazz).filterWith(method);
+ }
- /**
- * Create a <code>Request</code> that, when processed, will run all the tests
- * in a class. The odd name is necessary because <code>class</code> is a reserved word.
- * @param clazz the class containing the tests
- * @return a <code>Request</code> that will cause all tests in the class to be run
- */
- public static Request aClass(Class<?> clazz) {
- return new ClassRequest(clazz);
- }
+ /**
+ * Create a <code>Request</code> that, when processed, will run all the tests
+ * in a class. The odd name is necessary because <code>class</code> is a reserved word.
+ *
+ * @param clazz the class containing the tests
+ * @return a <code>Request</code> that will cause all tests in the class to be run
+ */
+ public static Request aClass(Class<?> clazz) {
+ return new ClassRequest(clazz);
+ }
- /**
- * Create a <code>Request</code> that, when processed, will run all the tests
- * in a class. If the class has a suite() method, it will be ignored.
- * @param clazz the class containing the tests
- * @return a <code>Request</code> that will cause all tests in the class to be run
- */
- public static Request classWithoutSuiteMethod(Class<?> clazz) {
- return new ClassRequest(clazz, false);
- }
+ /**
+ * Create a <code>Request</code> that, when processed, will run all the tests
+ * in a class. If the class has a suite() method, it will be ignored.
+ *
+ * @param clazz the class containing the tests
+ * @return a <code>Request</code> that will cause all tests in the class to be run
+ */
+ public static Request classWithoutSuiteMethod(Class<?> clazz) {
+ return new ClassRequest(clazz, false);
+ }
- /**
- * Create a <code>Request</code> that, when processed, will run all the tests
- * in a set of classes.
- * @param computer Helps construct Runners from classes
- * @param classes the classes containing the tests
- * @return a <code>Request</code> that will cause all tests in the classes to be run
- */
- public static Request classes(Computer computer, Class<?>... classes) {
- try {
- AllDefaultPossibilitiesBuilder builder= new AllDefaultPossibilitiesBuilder(true);
- Runner suite= computer.getSuite(builder, classes);
- return runner(suite);
- } catch (InitializationError e) {
- throw new RuntimeException(
- "Bug in saff's brain: Suite constructor, called as above, should always complete");
- }
- }
+ /**
+ * Create a <code>Request</code> that, when processed, will run all the tests
+ * in a set of classes.
+ *
+ * @param computer Helps construct Runners from classes
+ * @param classes the classes containing the tests
+ * @return a <code>Request</code> that will cause all tests in the classes to be run
+ */
+ public static Request classes(Computer computer, Class<?>... classes) {
+ try {
+ AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
+ Runner suite = computer.getSuite(builder, classes);
+ return runner(suite);
+ } catch (InitializationError e) {
+ throw new RuntimeException(
+ "Bug in saff's brain: Suite constructor, called as above, should always complete");
+ }
+ }
- /**
- * Create a <code>Request</code> that, when processed, will run all the tests
- * in a set of classes with the default <code>Computer</code>.
- * @param classes the classes containing the tests
- * @return a <code>Request</code> that will cause all tests in the classes to be run
- */
- public static Request classes(Class<?>... classes) {
- return classes(JUnitCore.defaultComputer(), classes);
- }
-
+ /**
+ * Create a <code>Request</code> that, when processed, will run all the tests
+ * in a set of classes with the default <code>Computer</code>.
+ *
+ * @param classes the classes containing the tests
+ * @return a <code>Request</code> that will cause all tests in the classes to be run
+ */
+ public static Request classes(Class<?>... classes) {
+ return classes(JUnitCore.defaultComputer(), classes);
+ }
- /**
- * Not used within JUnit. Clients should simply instantiate ErrorReportingRunner themselves
- */
- @Deprecated
- public static Request errorReport(Class<?> klass, Throwable cause) {
- return runner(new ErrorReportingRunner(klass, cause));
- }
- /**
- * @param runner the runner to return
- * @return a <code>Request</code> that will run the given runner when invoked
- */
- public static Request runner(final Runner runner) {
- return new Request(){
- @Override
- public Runner getRunner() {
- return runner;
- }
- };
- }
+ /**
+ * Creates a {@link Request} that, when processed, will report an error for the given
+ * test class with the given cause.
+ */
+ public static Request errorReport(Class<?> klass, Throwable cause) {
+ return runner(new ErrorReportingRunner(klass, cause));
+ }
- /**
- * Returns a {@link Runner} for this Request
- * @return corresponding {@link Runner} for this Request
- */
- public abstract Runner getRunner();
+ /**
+ * @param runner the runner to return
+ * @return a <code>Request</code> that will run the given runner when invoked
+ */
+ public static Request runner(final Runner runner) {
+ return new Request() {
+ @Override
+ public Runner getRunner() {
+ return runner;
+ }
+ };
+ }
- /**
- * Returns a Request that only contains those tests that should run when
- * <code>filter</code> is applied
- * @param filter The {@link Filter} to apply to this Request
- * @return the filtered Request
- */
- public Request filterWith(Filter filter) {
- return new FilterRequest(this, filter);
- }
+ /**
+ * Returns a {@link Runner} for this Request
+ *
+ * @return corresponding {@link Runner} for this Request
+ */
+ public abstract Runner getRunner();
- /**
- * Returns a Request that only runs contains tests whose {@link Description}
- * equals <code>desiredDescription</code>
- * @param desiredDescription {@link Description} of those tests that should be run
- * @return the filtered Request
- */
- public Request filterWith(final Description desiredDescription) {
- return filterWith(Filter.matchMethodDescription(desiredDescription));
- }
+ /**
+ * Returns a Request that only contains those tests that should run when
+ * <code>filter</code> is applied
+ *
+ * @param filter The {@link Filter} to apply to this Request
+ * @return the filtered Request
+ */
+ public Request filterWith(Filter filter) {
+ return new FilterRequest(this, filter);
+ }
- /**
- * Returns a Request whose Tests can be run in a certain order, defined by
- * <code>comparator</code>
- *
- * For example, here is code to run a test suite in alphabetical order:
- *
- * <pre>
- private static Comparator<Description> forward() {
- return new Comparator<Description>() {
- public int compare(Description o1, Description o2) {
- return o1.getDisplayName().compareTo(o2.getDisplayName());
- }
- };
- }
-
- public static main() {
- new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
- }
- * </pre>
- *
- * @param comparator definition of the order of the tests in this Request
- * @return a Request with ordered Tests
- */
- public Request sortWith(Comparator<Description> comparator) {
- return new SortingRequest(this, comparator);
- }
+ /**
+ * Returns a Request that only runs contains tests whose {@link Description}
+ * equals <code>desiredDescription</code>
+ *
+ * @param desiredDescription {@link Description} of those tests that should be run
+ * @return the filtered Request
+ */
+ public Request filterWith(final Description desiredDescription) {
+ return filterWith(Filter.matchMethodDescription(desiredDescription));
+ }
+
+ /**
+ * Returns a Request whose Tests can be run in a certain order, defined by
+ * <code>comparator</code>
+ * <p>
+ * For example, here is code to run a test suite in alphabetical order:
+ * <pre>
+ * private static Comparator&lt;Description&gt; forward() {
+ * return new Comparator&lt;Description&gt;() {
+ * public int compare(Description o1, Description o2) {
+ * return o1.getDisplayName().compareTo(o2.getDisplayName());
+ * }
+ * };
+ * }
+ *
+ * public static main() {
+ * new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
+ * }
+ * </pre>
+ *
+ * @param comparator definition of the order of the tests in this Request
+ * @return a Request with ordered Tests
+ */
+ public Request sortWith(Comparator<Description> comparator) {
+ return new SortingRequest(this, comparator);
+ }
}
diff --git a/src/main/java/org/junit/runner/Result.java b/src/main/java/org/junit/runner/Result.java
index edfb97c..73ad059 100644
--- a/src/main/java/org/junit/runner/Result.java
+++ b/src/main/java/org/junit/runner/Result.java
@@ -1,106 +1,196 @@
package org.junit.runner;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.ObjectStreamField;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.List;
import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
/**
- * A <code>Result</code> collects and summarizes information from running multiple
- * tests. Since tests are expected to run correctly, successful tests are only noted in
- * the count of tests that ran.
+ * A <code>Result</code> collects and summarizes information from running multiple tests.
+ * All tests are counted -- additional information is collected from tests that fail.
+ *
+ * @since 4.0
*/
public class Result implements Serializable {
- private static final long serialVersionUID = 1L;
- private AtomicInteger fCount = new AtomicInteger();
- private AtomicInteger fIgnoreCount= new AtomicInteger();
- private final List<Failure> fFailures= Collections.synchronizedList(new ArrayList<Failure>());
- private long fRunTime= 0;
- private long fStartTime;
-
- /**
- * @return the number of tests run
- */
- public int getRunCount() {
- return fCount.get();
- }
-
- /**
- * @return the number of tests that failed during the run
- */
- public int getFailureCount() {
- return fFailures.size();
- }
-
- /**
- * @return the number of milliseconds it took to run the entire suite to run
- */
- public long getRunTime() {
- return fRunTime;
- }
-
- /**
- * @return the {@link Failure}s describing tests that failed and the problems they encountered
- */
- public List<Failure> getFailures() {
- return fFailures;
- }
-
- /**
- * @return the number of tests ignored during the run
- */
- public int getIgnoreCount() {
- return fIgnoreCount.get();
- }
-
- /**
- * @return <code>true</code> if all tests succeeded
- */
- public boolean wasSuccessful() {
- return getFailureCount() == 0;
- }
-
- private class Listener extends RunListener {
- @Override
- public void testRunStarted(Description description) throws Exception {
- fStartTime= System.currentTimeMillis();
- }
-
- @Override
- public void testRunFinished(Result result) throws Exception {
- long endTime= System.currentTimeMillis();
- fRunTime+= endTime - fStartTime;
- }
-
- @Override
- public void testFinished(Description description) throws Exception {
- fCount.getAndIncrement();
- }
-
- @Override
- public void testFailure(Failure failure) throws Exception {
- fFailures.add(failure);
- }
-
- @Override
- public void testIgnored(Description description) throws Exception {
- fIgnoreCount.getAndIncrement();
- }
-
- @Override
- public void testAssumptionFailure(Failure failure) {
- // do nothing: same as passing (for 4.5; may change in 4.6)
- }
- }
-
- /**
- * Internal use only.
- */
- public RunListener createListener() {
- return new Listener();
- }
+ private static final long serialVersionUID = 1L;
+ private static final ObjectStreamField[] serialPersistentFields =
+ ObjectStreamClass.lookup(SerializedForm.class).getFields();
+ private final AtomicInteger count;
+ private final AtomicInteger ignoreCount;
+ private final CopyOnWriteArrayList<Failure> failures;
+ private final AtomicLong runTime;
+ private final AtomicLong startTime;
+
+ /** Only set during deserialization process. */
+ private SerializedForm serializedForm;
+
+ public Result() {
+ count = new AtomicInteger();
+ ignoreCount = new AtomicInteger();
+ failures = new CopyOnWriteArrayList<Failure>();
+ runTime = new AtomicLong();
+ startTime = new AtomicLong();
+ }
+
+ private Result(SerializedForm serializedForm) {
+ count = serializedForm.fCount;
+ ignoreCount = serializedForm.fIgnoreCount;
+ failures = new CopyOnWriteArrayList<Failure>(serializedForm.fFailures);
+ runTime = new AtomicLong(serializedForm.fRunTime);
+ startTime = new AtomicLong(serializedForm.fStartTime);
+ }
+
+ /**
+ * @return the number of tests run
+ */
+ public int getRunCount() {
+ return count.get();
+ }
+
+ /**
+ * @return the number of tests that failed during the run
+ */
+ public int getFailureCount() {
+ return failures.size();
+ }
+
+ /**
+ * @return the number of milliseconds it took to run the entire suite to run
+ */
+ public long getRunTime() {
+ return runTime.get();
+ }
+
+ /**
+ * @return the {@link Failure}s describing tests that failed and the problems they encountered
+ */
+ public List<Failure> getFailures() {
+ return failures;
+ }
+
+ /**
+ * @return the number of tests ignored during the run
+ */
+ public int getIgnoreCount() {
+ return ignoreCount.get();
+ }
+
+ /**
+ * @return <code>true</code> if all tests succeeded
+ */
+ public boolean wasSuccessful() {
+ return getFailureCount() == 0;
+ }
+
+ private void writeObject(ObjectOutputStream s) throws IOException {
+ SerializedForm serializedForm = new SerializedForm(this);
+ serializedForm.serialize(s);
+ }
+
+ private void readObject(ObjectInputStream s)
+ throws ClassNotFoundException, IOException {
+ serializedForm = SerializedForm.deserialize(s);
+ }
+
+ private Object readResolve() {
+ return new Result(serializedForm);
+ }
+
+ @RunListener.ThreadSafe
+ private class Listener extends RunListener {
+ @Override
+ public void testRunStarted(Description description) throws Exception {
+ startTime.set(System.currentTimeMillis());
+ }
+
+ @Override
+ public void testRunFinished(Result result) throws Exception {
+ long endTime = System.currentTimeMillis();
+ runTime.addAndGet(endTime - startTime.get());
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ count.getAndIncrement();
+ }
+
+ @Override
+ public void testFailure(Failure failure) throws Exception {
+ failures.add(failure);
+ }
+
+ @Override
+ public void testIgnored(Description description) throws Exception {
+ ignoreCount.getAndIncrement();
+ }
+
+ @Override
+ public void testAssumptionFailure(Failure failure) {
+ // do nothing: same as passing (for 4.5; may change in 4.6)
+ }
+ }
+
+ /**
+ * Internal use only.
+ */
+ public RunListener createListener() {
+ return new Listener();
+ }
+
+ /**
+ * Represents the serialized output of {@code Result}. The fields on this
+ * class match the files that {@code Result} had in JUnit 4.11.
+ */
+ private static class SerializedForm implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private final AtomicInteger fCount;
+ private final AtomicInteger fIgnoreCount;
+ private final List<Failure> fFailures;
+ private final long fRunTime;
+ private final long fStartTime;
+
+ public SerializedForm(Result result) {
+ fCount = result.count;
+ fIgnoreCount = result.ignoreCount;
+ fFailures = Collections.synchronizedList(new ArrayList<Failure>(result.failures));
+ fRunTime = result.runTime.longValue();
+ fStartTime = result.startTime.longValue();
+ }
+
+ @SuppressWarnings("unchecked")
+ private SerializedForm(ObjectInputStream.GetField fields) throws IOException {
+ fCount = (AtomicInteger) fields.get("fCount", null);
+ fIgnoreCount = (AtomicInteger) fields.get("fIgnoreCount", null);
+ fFailures = (List<Failure>) fields.get("fFailures", null);
+ fRunTime = fields.get("fRunTime", 0L);
+ fStartTime = fields.get("fStartTime", 0L);
+ }
+
+ public void serialize(ObjectOutputStream s) throws IOException {
+ ObjectOutputStream.PutField fields = s.putFields();
+ fields.put("fCount", fCount);
+ fields.put("fIgnoreCount", fIgnoreCount);
+ fields.put("fFailures", fFailures);
+ fields.put("fRunTime", fRunTime);
+ fields.put("fStartTime", fStartTime);
+ s.writeFields();
+ }
+
+ public static SerializedForm deserialize(ObjectInputStream s)
+ throws ClassNotFoundException, IOException {
+ ObjectInputStream.GetField fields = s.readFields();
+ return new SerializedForm(fields);
+ }
+ }
}
diff --git a/src/main/java/org/junit/runner/RunWith.java b/src/main/java/org/junit/runner/RunWith.java
index 602edf0..3428ee2 100644
--- a/src/main/java/org/junit/runner/RunWith.java
+++ b/src/main/java/org/junit/runner/RunWith.java
@@ -7,28 +7,30 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * When a class is annotated with <code>&#064;RunWith</code> or extends a class annotated
- * with <code>&#064;RunWith</code>, JUnit will invoke the class it references to run the
- * tests in that class instead of the runner built into JUnit. We added this feature late
- * in development. While it seems powerful we expect the runner API to change as we learn
- * how people really use it. Some of the classes that are currently internal will likely
+ * When a class is annotated with <code>&#064;RunWith</code> or extends a class annotated
+ * with <code>&#064;RunWith</code>, JUnit will invoke the class it references to run the
+ * tests in that class instead of the runner built into JUnit. We added this feature late
+ * in development. While it seems powerful we expect the runner API to change as we learn
+ * how people really use it. Some of the classes that are currently internal will likely
* be refined and become public.
- *
+ *
* For example, suites in JUnit 4 are built using RunWith, and a custom runner named Suite:
- *
+ *
* <pre>
* &#064;RunWith(Suite.class)
* &#064;SuiteClasses({ATest.class, BTest.class, CTest.class})
* public class ABCSuite {
* }
* </pre>
+ *
+ * @since 4.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface RunWith {
- /**
- * @return a Runner class (must have a constructor that takes a single Class to run)
- */
- Class<? extends Runner> value();
+ /**
+ * @return a Runner class (must have a constructor that takes a single Class to run)
+ */
+ Class<? extends Runner> value();
}
diff --git a/src/main/java/org/junit/runner/Runner.java b/src/main/java/org/junit/runner/Runner.java
index 39e424f..d728dd8 100644
--- a/src/main/java/org/junit/runner/Runner.java
+++ b/src/main/java/org/junit/runner/Runner.java
@@ -9,32 +9,35 @@ import org.junit.runner.notification.RunNotifier;
* a custom runner, in addition to implementing the abstract methods here you must
* also provide a constructor that takes as an argument the {@link Class} containing
* the tests.
- * <p/>
- * The default runner implementation guarantees that the instances of the test case
+ *
+ * <p>The default runner implementation guarantees that the instances of the test case
* class will be constructed immediately before running the test and that the runner
- * will retain no reference to the test case instances, generally making them
+ * will retain no reference to the test case instances, generally making them
* available for garbage collection.
- *
+ *
* @see org.junit.runner.Description
* @see org.junit.runner.RunWith
+ * @since 4.0
*/
public abstract class Runner implements Describable {
- /* (non-Javadoc)
- * @see org.junit.runner.Describable#getDescription()
- */
- public abstract Description getDescription();
+ /*
+ * (non-Javadoc)
+ * @see org.junit.runner.Describable#getDescription()
+ */
+ public abstract Description getDescription();
- /**
- * Run the tests for this runner.
- * @param notifier will be notified of events while tests are being run--tests being
- * started, finishing, and failing
- */
- public abstract void run(RunNotifier notifier);
-
- /**
- * @return the number of tests to be run by the receiver
- */
- public int testCount() {
- return getDescription().testCount();
- }
-} \ No newline at end of file
+ /**
+ * Run the tests for this runner.
+ *
+ * @param notifier will be notified of events while tests are being run--tests being
+ * started, finishing, and failing
+ */
+ public abstract void run(RunNotifier notifier);
+
+ /**
+ * @return the number of tests to be run by the receiver
+ */
+ public int testCount() {
+ return getDescription().testCount();
+ }
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Filter.java b/src/main/java/org/junit/runner/manipulation/Filter.java
index c0f31b0..0287351 100644
--- a/src/main/java/org/junit/runner/manipulation/Filter.java
+++ b/src/main/java/org/junit/runner/manipulation/Filter.java
@@ -7,108 +7,116 @@ import org.junit.runner.Request;
* The canonical case of filtering is when you want to run a single test method in a class. Rather
* than introduce runner API just for that one case, JUnit provides a general filtering mechanism.
* If you want to filter the tests to be run, extend <code>Filter</code> and apply an instance of
- * your filter to the {@link org.junit.runner.Request} before running it (see
- * {@link org.junit.runner.JUnitCore#run(Request)}. Alternatively, apply a <code>Filter</code> to
- * a {@link org.junit.runner.Runner} before running tests (for example, in conjunction with
+ * your filter to the {@link org.junit.runner.Request} before running it (see
+ * {@link org.junit.runner.JUnitCore#run(Request)}. Alternatively, apply a <code>Filter</code> to
+ * a {@link org.junit.runner.Runner} before running tests (for example, in conjunction with
* {@link org.junit.runner.RunWith}.
+ *
+ * @since 4.0
*/
public abstract class Filter {
- /**
- * A null <code>Filter</code> that passes all tests through.
- */
- public static Filter ALL= new Filter() {
- @Override
- public boolean shouldRun(Description description) {
- return true;
- }
+ /**
+ * A null <code>Filter</code> that passes all tests through.
+ */
+ public static final Filter ALL = new Filter() {
+ @Override
+ public boolean shouldRun(Description description) {
+ return true;
+ }
- @Override
- public String describe() {
- return "all tests";
- }
-
- @Override
- public void apply(Object child) throws NoTestsRemainException {
- // do nothing
- }
+ @Override
+ public String describe() {
+ return "all tests";
+ }
- @Override
- public Filter intersect(Filter second) {
- return second;
- }
- };
-
- /**
- * Returns a {@code Filter} that only runs the single method described by
- * {@code desiredDescription}
- */
- public static Filter matchMethodDescription(final Description desiredDescription) {
- return new Filter() {
- @Override
- public boolean shouldRun(Description description) {
- if (description.isTest())
- return desiredDescription.equals(description);
-
- // explicitly check if any children want to run
- for (Description each : description.getChildren())
- if (shouldRun(each))
- return true;
- return false;
- }
+ @Override
+ public void apply(Object child) throws NoTestsRemainException {
+ // do nothing
+ }
- @Override
- public String describe() {
- return String.format("Method %s", desiredDescription.getDisplayName());
- }
- };
- }
+ @Override
+ public Filter intersect(Filter second) {
+ return second;
+ }
+ };
+ /**
+ * Returns a {@code Filter} that only runs the single method described by
+ * {@code desiredDescription}
+ */
+ public static Filter matchMethodDescription(final Description desiredDescription) {
+ return new Filter() {
+ @Override
+ public boolean shouldRun(Description description) {
+ if (description.isTest()) {
+ return desiredDescription.equals(description);
+ }
- /**
- * @param description the description of the test to be run
- * @return <code>true</code> if the test should be run
- */
- public abstract boolean shouldRun(Description description);
+ // explicitly check if any children want to run
+ for (Description each : description.getChildren()) {
+ if (shouldRun(each)) {
+ return true;
+ }
+ }
+ return false;
+ }
- /**
- * Returns a textual description of this Filter
- * @return a textual description of this Filter
- */
- public abstract String describe();
+ @Override
+ public String describe() {
+ return String.format("Method %s", desiredDescription.getDisplayName());
+ }
+ };
+ }
- /**
- * Invoke with a {@link org.junit.runner.Runner} to cause all tests it intends to run
- * to first be checked with the filter. Only those that pass the filter will be run.
- * @param child the runner to be filtered by the receiver
- * @throws NoTestsRemainException if the receiver removes all tests
- */
- public void apply(Object child) throws NoTestsRemainException {
- if (!(child instanceof Filterable))
- return;
- Filterable filterable= (Filterable) child;
- filterable.filter(this);
- }
- /**
- * Returns a new Filter that accepts the intersection of the tests accepted
- * by this Filter and {@code second}
- */
- public Filter intersect(final Filter second) {
- if (second == this || second == ALL) {
- return this;
- }
- final Filter first= this;
- return new Filter() {
- @Override
- public boolean shouldRun(Description description) {
- return first.shouldRun(description)
- && second.shouldRun(description);
- }
+ /**
+ * @param description the description of the test to be run
+ * @return <code>true</code> if the test should be run
+ */
+ public abstract boolean shouldRun(Description description);
- @Override
- public String describe() {
- return first.describe() + " and " + second.describe();
- }
- };
- }
+ /**
+ * Returns a textual description of this Filter
+ *
+ * @return a textual description of this Filter
+ */
+ public abstract String describe();
+
+ /**
+ * Invoke with a {@link org.junit.runner.Runner} to cause all tests it intends to run
+ * to first be checked with the filter. Only those that pass the filter will be run.
+ *
+ * @param child the runner to be filtered by the receiver
+ * @throws NoTestsRemainException if the receiver removes all tests
+ */
+ public void apply(Object child) throws NoTestsRemainException {
+ if (!(child instanceof Filterable)) {
+ return;
+ }
+ Filterable filterable = (Filterable) child;
+ filterable.filter(this);
+ }
+
+ /**
+ * Returns a new Filter that accepts the intersection of the tests accepted
+ * by this Filter and {@code second}
+ */
+ public Filter intersect(final Filter second) {
+ if (second == this || second == ALL) {
+ return this;
+ }
+ final Filter first = this;
+ return new Filter() {
+ @Override
+ public boolean shouldRun(Description description) {
+ return first.shouldRun(description)
+ && second.shouldRun(description);
+ }
+
+ @Override
+ public String describe() {
+ return first.describe() + " and " + second.describe();
+ }
+ };
+ }
}
diff --git a/src/main/java/org/junit/runner/manipulation/Filterable.java b/src/main/java/org/junit/runner/manipulation/Filterable.java
index 782c0f7..d605027 100644
--- a/src/main/java/org/junit/runner/manipulation/Filterable.java
+++ b/src/main/java/org/junit/runner/manipulation/Filterable.java
@@ -3,14 +3,17 @@ package org.junit.runner.manipulation;
/**
* Runners that allow filtering should implement this interface. Implement {@link #filter(Filter)}
* to remove tests that don't pass the filter.
+ *
+ * @since 4.0
*/
public interface Filterable {
- /**
- * Remove tests that don't pass the parameter <code>filter</code>.
- * @param filter the {@link Filter} to apply
- * @throws NoTestsRemainException if all tests are filtered out
- */
- void filter(Filter filter) throws NoTestsRemainException;
+ /**
+ * Remove tests that don't pass the parameter <code>filter</code>.
+ *
+ * @param filter the {@link Filter} to apply
+ * @throws NoTestsRemainException if all tests are filtered out
+ */
+ void filter(Filter filter) throws NoTestsRemainException;
}
diff --git a/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java b/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java
index 03fb3bf..21935bd 100644
--- a/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java
+++ b/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java
@@ -2,7 +2,9 @@ package org.junit.runner.manipulation;
/**
* Thrown when a filter removes all tests from a runner.
+ *
+ * @since 4.0
*/
public class NoTestsRemainException extends Exception {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 1L;
}
diff --git a/src/main/java/org/junit/runner/manipulation/Sortable.java b/src/main/java/org/junit/runner/manipulation/Sortable.java
index fec1d0c..9ac864c 100644
--- a/src/main/java/org/junit/runner/manipulation/Sortable.java
+++ b/src/main/java/org/junit/runner/manipulation/Sortable.java
@@ -5,13 +5,16 @@ package org.junit.runner.manipulation;
* failed first, you can reduce the average time to the first test failing. Test sorting should not be used to
* cope with order dependencies between tests. Tests that are isolated from each other are less
* expensive to maintain and can be run individually.
+ *
+ * @since 4.0
*/
public interface Sortable {
- /**
- * Sorts the tests using <code>sorter</code>
- * @param sorter the {@link Sorter} to use for sorting the tests
- */
- public void sort(Sorter sorter);
+ /**
+ * Sorts the tests using <code>sorter</code>
+ *
+ * @param sorter the {@link Sorter} to use for sorting the tests
+ */
+ public void sort(Sorter sorter);
}
diff --git a/src/main/java/org/junit/runner/manipulation/Sorter.java b/src/main/java/org/junit/runner/manipulation/Sorter.java
index 242df14..20192d0 100644
--- a/src/main/java/org/junit/runner/manipulation/Sorter.java
+++ b/src/main/java/org/junit/runner/manipulation/Sorter.java
@@ -7,40 +7,42 @@ import org.junit.runner.Description;
/**
* A <code>Sorter</code> orders tests. In general you will not need
* to use a <code>Sorter</code> directly. Instead, use {@link org.junit.runner.Request#sortWith(Comparator)}.
- *
- *
+ *
+ * @since 4.0
*/
public class Sorter implements Comparator<Description> {
- /**
- * NULL is a <code>Sorter</code> that leaves elements in an undefined order
- */
- public static Sorter NULL= new Sorter(new Comparator<Description>() {
- public int compare(Description o1, Description o2) {
- return 0;
- }});
- private final Comparator<Description> fComparator;
+ /**
+ * NULL is a <code>Sorter</code> that leaves elements in an undefined order
+ */
+ public static final Sorter NULL = new Sorter(new Comparator<Description>() {
+ public int compare(Description o1, Description o2) {
+ return 0;
+ }
+ });
- /**
- * Creates a <code>Sorter</code> that uses <code>comparator</code>
- * to sort tests
- * @param comparator the {@link Comparator} to use when sorting tests
- */
- public Sorter(Comparator<Description> comparator) {
- fComparator= comparator;
- }
+ private final Comparator<Description> comparator;
- /**
- * Sorts the test in <code>runner</code> using <code>comparator</code>
- * @param object
- */
- public void apply(Object object) {
- if (object instanceof Sortable) {
- Sortable sortable = (Sortable) object;
- sortable.sort(this);
- }
- }
+ /**
+ * Creates a <code>Sorter</code> that uses <code>comparator</code>
+ * to sort tests
+ *
+ * @param comparator the {@link Comparator} to use when sorting tests
+ */
+ public Sorter(Comparator<Description> comparator) {
+ this.comparator = comparator;
+ }
- public int compare(Description o1, Description o2) {
- return fComparator.compare(o1, o2);
- }
+ /**
+ * Sorts the test in <code>runner</code> using <code>comparator</code>
+ */
+ public void apply(Object object) {
+ if (object instanceof Sortable) {
+ Sortable sortable = (Sortable) object;
+ sortable.sort(this);
+ }
+ }
+
+ public int compare(Description o1, Description o2) {
+ return comparator.compare(o1, o2);
+ }
}
diff --git a/src/main/java/org/junit/runner/notification/Failure.java b/src/main/java/org/junit/runner/notification/Failure.java
index 501caa5..c03b4c1 100644
--- a/src/main/java/org/junit/runner/notification/Failure.java
+++ b/src/main/java/org/junit/runner/notification/Failure.java
@@ -12,68 +12,76 @@ import org.junit.runner.Description;
* will be of a single test. However, if problems are encountered while constructing the
* test (for example, if a {@link org.junit.BeforeClass} method is not static), it may describe
* something other than a single test.
+ *
+ * @since 4.0
*/
public class Failure implements Serializable {
- private static final long serialVersionUID = 1L;
- private final Description fDescription;
- private final Throwable fThrownException;
+ private static final long serialVersionUID = 1L;
- /**
- * Constructs a <code>Failure</code> with the given description and exception.
- * @param description a {@link org.junit.runner.Description} of the test that failed
- * @param thrownException the exception that was thrown while running the test
- */
- public Failure(Description description, Throwable thrownException) {
- fThrownException = thrownException;
- fDescription= description;
- }
+ /*
+ * 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 Description fDescription;
+ private final Throwable fThrownException;
- /**
- * @return a user-understandable label for the test
- */
- public String getTestHeader() {
- return fDescription.getDisplayName();
- }
+ /**
+ * Constructs a <code>Failure</code> with the given description and exception.
+ *
+ * @param description a {@link org.junit.runner.Description} of the test that failed
+ * @param thrownException the exception that was thrown while running the test
+ */
+ public Failure(Description description, Throwable thrownException) {
+ this.fThrownException = thrownException;
+ this.fDescription = description;
+ }
- /**
- * @return the raw description of the context of the failure.
- */
- public Description getDescription() {
- return fDescription;
- }
+ /**
+ * @return a user-understandable label for the test
+ */
+ public String getTestHeader() {
+ return fDescription.getDisplayName();
+ }
- /**
- * @return the exception thrown
- */
+ /**
+ * @return the raw description of the context of the failure.
+ */
+ public Description getDescription() {
+ return fDescription;
+ }
- public Throwable getException() {
- return fThrownException;
- }
+ /**
+ * @return the exception thrown
+ */
- @Override
- public String toString() {
- StringBuffer buffer= new StringBuffer();
- buffer.append(getTestHeader() + ": "+fThrownException.getMessage());
- return buffer.toString();
- }
+ public Throwable getException() {
+ return fThrownException;
+ }
- /**
- * Convenience method
- * @return the printed form of the exception
- */
- public String getTrace() {
- StringWriter stringWriter= new StringWriter();
- PrintWriter writer= new PrintWriter(stringWriter);
- getException().printStackTrace(writer);
- StringBuffer buffer= stringWriter.getBuffer();
- return buffer.toString();
- }
+ @Override
+ public String toString() {
+ return getTestHeader() + ": " + fThrownException.getMessage();
+ }
- /**
- * Convenience method
- * @return the message of the thrown exception
- */
- public String getMessage() {
- return getException().getMessage();
- }
+ /**
+ * Convenience method
+ *
+ * @return the printed form of the exception
+ */
+ public String getTrace() {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ getException().printStackTrace(writer);
+ return stringWriter.toString();
+ }
+
+ /**
+ * Convenience method
+ *
+ * @return the message of the thrown exception
+ */
+ public String getMessage() {
+ return getException().getMessage();
+ }
}
diff --git a/src/main/java/org/junit/runner/notification/RunListener.java b/src/main/java/org/junit/runner/notification/RunListener.java
index ffe8134..db9d8c1 100644
--- a/src/main/java/org/junit/runner/notification/RunListener.java
+++ b/src/main/java/org/junit/runner/notification/RunListener.java
@@ -1,15 +1,21 @@
package org.junit.runner.notification;
-import org.junit.internal.AssumptionViolatedException;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
import org.junit.runner.Description;
import org.junit.runner.Result;
/**
- * <p>If you need to respond to the events during a test run, extend <code>RunListener</code>
- * and override the appropriate methods. If a listener throws an exception while processing a
- * test event, it will be removed for the remainder of the test run.</p>
- *
- * <p>For example, suppose you have a <code>Cowbell</code>
+ * Register an instance of this class with {@link RunNotifier} to be notified
+ * of events that occur during a test run. All of the methods in this class
+ * are abstract and have no implementation; override one or more methods to
+ * receive events.
+ * <p>
+ * For example, suppose you have a <code>Cowbell</code>
* class that you want to make a noise whenever a test fails. You could write:
* <pre>
* public class RingingListener extends RunListener {
@@ -18,9 +24,8 @@ import org.junit.runner.Result;
* }
* }
* </pre>
- * </p>
- *
- * <p>To invoke your listener, you need to run your tests through <code>JUnitCore</code>.
+ * <p>
+ * To invoke your listener, you need to run your tests through <code>JUnitCore</code>.
* <pre>
* public void main(String... args) {
* JUnitCore core= new JUnitCore();
@@ -28,66 +33,108 @@ import org.junit.runner.Result;
* core.run(MyTestClass.class);
* }
* </pre>
- * </p>
+ * <p>
+ * If a listener throws an exception for a test event, the other listeners will
+ * have their {@link RunListener#testFailure(Failure)} called with a {@code Description}
+ * of {@link Description#TEST_MECHANISM} to indicate the failure.
+ * <p>
+ * By default, JUnit will synchronize calls to your listener. If your listener
+ * is thread-safe and you want to allow JUnit to call your listener from
+ * multiple threads when tests are run in parallel, you can annotate your
+ * test class with {@link RunListener.ThreadSafe}.
+ * <p>
+ * Listener methods will be called from the same thread as is running
+ * the test, unless otherwise indicated by the method Javadoc
+ *
* @see org.junit.runner.JUnitCore
+ * @since 4.0
*/
public class RunListener {
- /**
- * Called before any tests have been run.
- * @param description describes the tests to be run
- */
- public void testRunStarted(Description description) throws Exception {
- }
-
- /**
- * Called when all tests have finished
- * @param result the summary of the test run, including all the tests that failed
- */
- public void testRunFinished(Result result) throws Exception {
- }
-
- /**
- * Called when an atomic test is about to be started.
- * @param description the description of the test that is about to be run
- * (generally a class and method name)
- */
- public void testStarted(Description description) throws Exception {
- }
+ /**
+ * Called before any tests have been run. This may be called on an
+ * arbitrary thread.
+ *
+ * @param description describes the tests to be run
+ */
+ public void testRunStarted(Description description) throws Exception {
+ }
- /**
- * Called when an atomic test has finished, whether the test succeeds or fails.
- * @param description the description of the test that just ran
- */
- public void testFinished(Description description) throws Exception {
- }
+ /**
+ * Called when all tests have finished. This may be called on an
+ * arbitrary thread.
+ *
+ * @param result the summary of the test run, including all the tests that failed
+ */
+ public void testRunFinished(Result result) throws Exception {
+ }
- /**
- * Called when an atomic test fails.
- * @param failure describes the test that failed and the exception that was thrown
- */
- public void testFailure(Failure failure) throws Exception {
- }
+ /**
+ * Called when an atomic test is about to be started.
+ *
+ * @param description the description of the test that is about to be run
+ * (generally a class and method name)
+ */
+ public void testStarted(Description description) throws Exception {
+ }
- /**
- * Called when an atomic test flags that it assumes a condition that is
- * false
- *
- * @param failure
- * describes the test that failed and the
- * {@link AssumptionViolatedException} that was thrown
- */
- public void testAssumptionFailure(Failure failure) {
- }
+ /**
+ * Called when an atomic test has finished, whether the test succeeds or fails.
+ *
+ * @param description the description of the test that just ran
+ */
+ public void testFinished(Description description) throws Exception {
+ }
- /**
- * Called when a test will not be run, generally because a test method is annotated
- * with {@link org.junit.Ignore}.
- *
- * @param description describes the test that will not be run
- */
- public void testIgnored(Description description) throws Exception {
- }
-}
+ /**
+ * Called when an atomic test fails, or when a listener throws an exception.
+ *
+ * <p>In the case of a failure of an atomic test, this method will be called
+ * with the same {@code Description} passed to
+ * {@link #testStarted(Description)}, from the same thread that called
+ * {@link #testStarted(Description)}.
+ *
+ * <p>In the case of a listener throwing an exception, this will be called with
+ * a {@code Description} of {@link Description#TEST_MECHANISM}, and may be called
+ * on an arbitrary thread.
+ *
+ * @param failure describes the test that failed and the exception that was thrown
+ */
+ public void testFailure(Failure failure) throws Exception {
+ }
+
+ /**
+ * Called when an atomic test flags that it assumes a condition that is
+ * false
+ *
+ * @param failure describes the test that failed and the
+ * {@link org.junit.AssumptionViolatedException} that was thrown
+ */
+ public void testAssumptionFailure(Failure failure) {
+ }
+ /**
+ * Called when a test will not be run, generally because a test method is annotated
+ * with {@link org.junit.Ignore}.
+ *
+ * @param description describes the test that will not be run
+ */
+ public void testIgnored(Description description) throws Exception {
+ }
+
+ /**
+ * Indicates a {@code RunListener} that can have its methods called
+ * concurrently. This implies that the class is thread-safe (i.e. no set of
+ * listener calls can put the listener into an invalid state, even if those
+ * listener calls are being made by multiple threads without
+ * synchronization).
+ *
+ * @since 4.12
+ */
+ @Documented
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface ThreadSafe {
+ }
+}
diff --git a/src/main/java/org/junit/runner/notification/RunNotifier.java b/src/main/java/org/junit/runner/notification/RunNotifier.java
index d0f6c85..6875f76 100644
--- a/src/main/java/org/junit/runner/notification/RunNotifier.java
+++ b/src/main/java/org/junit/runner/notification/RunNotifier.java
@@ -1,166 +1,214 @@
package org.junit.runner.notification;
+import static java.util.Arrays.asList;
+
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
-import org.junit.internal.AssumptionViolatedException;
import org.junit.runner.Description;
import org.junit.runner.Result;
/**
* If you write custom runners, you may need to notify JUnit of your progress running tests.
* Do this by invoking the <code>RunNotifier</code> passed to your implementation of
- * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to
+ * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to
* move {@link #fireTestRunStarted(Description)} and {@link #fireTestRunFinished(Result)}
* to a separate class since they should only be called once per run.
+ *
+ * @since 4.0
*/
public class RunNotifier {
- private final List<RunListener> fListeners=
- Collections.synchronizedList(new ArrayList<RunListener>());
- private boolean fPleaseStop= false;
-
- /** Internal use only
- */
- public void addListener(RunListener listener) {
- fListeners.add(listener);
- }
-
- /** Internal use only
- */
- public void removeListener(RunListener listener) {
- fListeners.remove(listener);
+ private final List<RunListener> listeners = new CopyOnWriteArrayList<RunListener>();
+ private volatile boolean pleaseStop = false;
+
+ /**
+ * Internal use only
+ */
+ public void addListener(RunListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("Cannot add a null listener");
+ }
+ listeners.add(wrapIfNotThreadSafe(listener));
+ }
+
+ /**
+ * Internal use only
+ */
+ public void removeListener(RunListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("Cannot remove a null listener");
+ }
+ listeners.remove(wrapIfNotThreadSafe(listener));
+ }
+
+ /**
+ * Wraps the given listener with {@link SynchronizedRunListener} if
+ * it is not annotated with {@link RunListener.ThreadSafe}.
+ */
+ RunListener wrapIfNotThreadSafe(RunListener listener) {
+ return listener.getClass().isAnnotationPresent(RunListener.ThreadSafe.class) ?
+ listener : new SynchronizedRunListener(listener, this);
+ }
+
+
+ private abstract class SafeNotifier {
+ private final List<RunListener> currentListeners;
+
+ SafeNotifier() {
+ this(listeners);
+ }
+
+ SafeNotifier(List<RunListener> currentListeners) {
+ this.currentListeners = currentListeners;
+ }
+
+ void run() {
+ int capacity = currentListeners.size();
+ ArrayList<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
+ ArrayList<Failure> failures = new ArrayList<Failure>(capacity);
+ for (RunListener listener : currentListeners) {
+ try {
+ notifyListener(listener);
+ safeListeners.add(listener);
+ } catch (Exception e) {
+ failures.add(new Failure(Description.TEST_MECHANISM, e));
+ }
+ }
+ fireTestFailures(safeListeners, failures);
+ }
+
+ abstract protected void notifyListener(RunListener each) throws Exception;
+ }
+
+ /**
+ * Do not invoke.
+ */
+ public void fireTestRunStarted(final Description description) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testRunStarted(description);
+ }
+ }.run();
+ }
+
+ /**
+ * Do not invoke.
+ */
+ public void fireTestRunFinished(final Result result) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testRunFinished(result);
+ }
+ }.run();
+ }
+
+ /**
+ * Invoke to tell listeners that an atomic test is about to start.
+ *
+ * @param description the description of the atomic test (generally a class and method name)
+ * @throws StoppedByUserException thrown if a user has requested that the test run stop
+ */
+ public void fireTestStarted(final Description description) throws StoppedByUserException {
+ if (pleaseStop) {
+ throw new StoppedByUserException();
+ }
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testStarted(description);
+ }
+ }.run();
+ }
+
+ /**
+ * Invoke to tell listeners that an atomic test failed.
+ *
+ * @param failure the description of the test that failed and the exception thrown
+ */
+ public void fireTestFailure(Failure failure) {
+ fireTestFailures(listeners, asList(failure));
+ }
+
+ private void fireTestFailures(List<RunListener> listeners,
+ final List<Failure> failures) {
+ if (!failures.isEmpty()) {
+ new SafeNotifier(listeners) {
+ @Override
+ protected void notifyListener(RunListener listener) throws Exception {
+ for (Failure each : failures) {
+ listener.testFailure(each);
+ }
+ }
+ }.run();
+ }
+ }
+
+ /**
+ * Invoke to tell listeners that an atomic test flagged that it assumed
+ * something false.
+ *
+ * @param failure the description of the test that failed and the
+ * {@link org.junit.AssumptionViolatedException} thrown
+ */
+ public void fireTestAssumptionFailed(final Failure failure) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testAssumptionFailure(failure);
+ }
+ }.run();
+ }
+
+ /**
+ * Invoke to tell listeners that an atomic test was ignored.
+ *
+ * @param description the description of the ignored test
+ */
+ public void fireTestIgnored(final Description description) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testIgnored(description);
+ }
+ }.run();
+ }
+
+ /**
+ * Invoke to tell listeners that an atomic test finished. Always invoke
+ * this method if you invoke {@link #fireTestStarted(Description)}
+ * as listeners are likely to expect them to come in pairs.
+ *
+ * @param description the description of the test that finished
+ */
+ public void fireTestFinished(final Description description) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testFinished(description);
+ }
+ }.run();
+ }
+
+ /**
+ * Ask that the tests run stop before starting the next test. Phrased politely because
+ * the test currently running will not be interrupted. It seems a little odd to put this
+ * functionality here, but the <code>RunNotifier</code> is the only object guaranteed
+ * to be shared amongst the many runners involved.
+ */
+ public void pleaseStop() {
+ pleaseStop = true;
}
- private abstract class SafeNotifier {
- void run() {
- synchronized (fListeners) {
- for (Iterator<RunListener> all= fListeners.iterator(); all.hasNext();)
- try {
- notifyListener(all.next());
- } catch (Exception e) {
- all.remove(); // Remove the offending listener first to avoid an infinite loop
- fireTestFailure(new Failure(Description.TEST_MECHANISM, e));
- }
- }
- }
-
- abstract protected void notifyListener(RunListener each) throws Exception;
- }
-
- /**
- * Do not invoke.
- */
- public void fireTestRunStarted(final Description description) {
- new SafeNotifier() {
- @Override
- protected void notifyListener(RunListener each) throws Exception {
- each.testRunStarted(description);
- };
- }.run();
- }
-
- /**
- * Do not invoke.
- */
- public void fireTestRunFinished(final Result result) {
- new SafeNotifier() {
- @Override
- protected void notifyListener(RunListener each) throws Exception {
- each.testRunFinished(result);
- };
- }.run();
- }
-
- /**
- * Invoke to tell listeners that an atomic test is about to start.
- * @param description the description of the atomic test (generally a class and method name)
- * @throws StoppedByUserException thrown if a user has requested that the test run stop
- */
- public void fireTestStarted(final Description description) throws StoppedByUserException {
- if (fPleaseStop)
- throw new StoppedByUserException();
- new SafeNotifier() {
- @Override
- protected void notifyListener(RunListener each) throws Exception {
- each.testStarted(description);
- };
- }.run();
- }
-
- /**
- * Invoke to tell listeners that an atomic test failed.
- * @param failure the description of the test that failed and the exception thrown
- */
- public void fireTestFailure(final Failure failure) {
- new SafeNotifier() {
- @Override
- protected void notifyListener(RunListener each) throws Exception {
- each.testFailure(failure);
- };
- }.run();
- }
-
- /**
- * Invoke to tell listeners that an atomic test flagged that it assumed
- * something false.
- *
- * @param failure
- * the description of the test that failed and the
- * {@link AssumptionViolatedException} thrown
- */
- public void fireTestAssumptionFailed(final Failure failure) {
- new SafeNotifier() {
- @Override
- protected void notifyListener(RunListener each) throws Exception {
- each.testAssumptionFailure(failure);
- };
- }.run();
- }
-
- /**
- * Invoke to tell listeners that an atomic test was ignored.
- * @param description the description of the ignored test
- */
- public void fireTestIgnored(final Description description) {
- new SafeNotifier() {
- @Override
- protected void notifyListener(RunListener each) throws Exception {
- each.testIgnored(description);
- }
- }.run();
- }
-
- /**
- * Invoke to tell listeners that an atomic test finished. Always invoke
- * {@link #fireTestFinished(Description)} if you invoke {@link #fireTestStarted(Description)}
- * as listeners are likely to expect them to come in pairs.
- * @param description the description of the test that finished
- */
- public void fireTestFinished(final Description description) {
- new SafeNotifier() {
- @Override
- protected void notifyListener(RunListener each) throws Exception {
- each.testFinished(description);
- };
- }.run();
- }
-
- /**
- * Ask that the tests run stop before starting the next test. Phrased politely because
- * the test currently running will not be interrupted. It seems a little odd to put this
- * functionality here, but the <code>RunNotifier</code> is the only object guaranteed
- * to be shared amongst the many runners involved.
- */
- public void pleaseStop() {
- fPleaseStop= true;
- }
-
- /**
- * Internal use only. The Result's listener must be first.
- */
- public void addFirstListener(RunListener listener) {
- fListeners.add(0, listener);
- }
-} \ No newline at end of file
+ /**
+ * Internal use only. The Result's listener must be first.
+ */
+ public void addFirstListener(RunListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("Cannot add a null listener");
+ }
+ listeners.add(0, wrapIfNotThreadSafe(listener));
+ }
+}
diff --git a/src/main/java/org/junit/runner/notification/StoppedByUserException.java b/src/main/java/org/junit/runner/notification/StoppedByUserException.java
index 89be3ba..f5490f7 100644
--- a/src/main/java/org/junit/runner/notification/StoppedByUserException.java
+++ b/src/main/java/org/junit/runner/notification/StoppedByUserException.java
@@ -1,11 +1,12 @@
package org.junit.runner.notification;
/**
- * Thrown when a user has requested that the test run stop. Writers of
+ * Thrown when a user has requested that the test run stop. Writers of
* test running GUIs should be prepared to catch a <code>StoppedByUserException</code>.
- *
+ *
* @see org.junit.runner.notification.RunNotifier
+ * @since 4.0
*/
public class StoppedByUserException extends RuntimeException {
- private static final long serialVersionUID= 1L;
+ private static final long serialVersionUID = 1L;
}
diff --git a/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java b/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
new file mode 100644
index 0000000..c53c1ee
--- /dev/null
+++ b/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
@@ -0,0 +1,103 @@
+package org.junit.runner.notification;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+
+/**
+ * Thread-safe decorator for {@link RunListener} implementations that synchronizes
+ * calls to the delegate.
+ *
+ * <p>This class synchronizes all listener calls on a RunNotifier instance. This is done because
+ * prior to JUnit 4.12, all listeners were called in a synchronized block in RunNotifier,
+ * so no two listeners were ever called concurrently. If we instead made the methods here
+ * sychronized, clients that added multiple listeners that called common code might see
+ * issues due to the reduced synchronization.
+ *
+ * @author Tibor Digana (tibor17)
+ * @author Kevin Cooney (kcooney)
+ * @since 4.12
+ *
+ * @see RunNotifier
+ */
+@RunListener.ThreadSafe
+final class SynchronizedRunListener extends RunListener {
+ private final RunListener listener;
+ private final Object monitor;
+
+ SynchronizedRunListener(RunListener listener, Object monitor) {
+ this.listener = listener;
+ this.monitor = monitor;
+ }
+
+ @Override
+ public void testRunStarted(Description description) throws Exception {
+ synchronized (monitor) {
+ listener.testRunStarted(description);
+ }
+ }
+
+ @Override
+ public void testRunFinished(Result result) throws Exception {
+ synchronized (monitor) {
+ listener.testRunFinished(result);
+ }
+ }
+
+ @Override
+ public void testStarted(Description description) throws Exception {
+ synchronized (monitor) {
+ listener.testStarted(description);
+ }
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ synchronized (monitor) {
+ listener.testFinished(description);
+ }
+ }
+
+ @Override
+ public void testFailure(Failure failure) throws Exception {
+ synchronized (monitor) {
+ listener.testFailure(failure);
+ }
+ }
+
+ @Override
+ public void testAssumptionFailure(Failure failure) {
+ synchronized (monitor) {
+ listener.testAssumptionFailure(failure);
+ }
+ }
+
+ @Override
+ public void testIgnored(Description description) throws Exception {
+ synchronized (monitor) {
+ listener.testIgnored(description);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return listener.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof SynchronizedRunListener)) {
+ return false;
+ }
+ SynchronizedRunListener that = (SynchronizedRunListener) other;
+
+ return listener.equals(that.listener);
+ }
+
+ @Override
+ public String toString() {
+ return listener.toString() + " (with synchronization wrapper)";
+ }
+}