aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/junit/runner
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/junit/runner')
-rw-r--r--src/main/java/org/junit/runner/Computer.java12
-rw-r--r--src/main/java/org/junit/runner/Describable.java2
-rw-r--r--src/main/java/org/junit/runner/Description.java13
-rw-r--r--src/main/java/org/junit/runner/FilterFactory.java3
-rw-r--r--src/main/java/org/junit/runner/JUnitCommandLineParseResult.java8
-rw-r--r--src/main/java/org/junit/runner/OrderWith.java28
-rw-r--r--src/main/java/org/junit/runner/OrderWithValidator.java38
-rw-r--r--src/main/java/org/junit/runner/Request.java58
-rw-r--r--src/main/java/org/junit/runner/Result.java31
-rw-r--r--src/main/java/org/junit/runner/manipulation/Alphanumeric.java27
-rw-r--r--src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java21
-rw-r--r--src/main/java/org/junit/runner/manipulation/Orderable.java21
-rw-r--r--src/main/java/org/junit/runner/manipulation/Orderer.java62
-rw-r--r--src/main/java/org/junit/runner/manipulation/Ordering.java172
-rw-r--r--src/main/java/org/junit/runner/manipulation/Sortable.java2
-rw-r--r--src/main/java/org/junit/runner/manipulation/Sorter.java54
-rw-r--r--src/main/java/org/junit/runner/notification/Failure.java23
-rw-r--r--src/main/java/org/junit/runner/notification/RunListener.java28
-rw-r--r--src/main/java/org/junit/runner/notification/RunNotifier.java41
-rw-r--r--src/main/java/org/junit/runner/notification/SynchronizedRunListener.java33
20 files changed, 629 insertions, 48 deletions
diff --git a/src/main/java/org/junit/runner/Computer.java b/src/main/java/org/junit/runner/Computer.java
index 8bb4b20..18d0d31 100644
--- a/src/main/java/org/junit/runner/Computer.java
+++ b/src/main/java/org/junit/runner/Computer.java
@@ -30,7 +30,17 @@ public class Computer {
public Runner runnerForClass(Class<?> testClass) throws Throwable {
return getRunner(builder, testClass);
}
- }, classes);
+ }, classes) {
+ @Override
+ protected String getName() {
+ /*
+ * #1320 The generated suite is not based on a real class so
+ * only a 'null' description can be generated from it. This name
+ * will be overridden here.
+ */
+ return "classes";
+ }
+ };
}
/**
diff --git a/src/main/java/org/junit/runner/Describable.java b/src/main/java/org/junit/runner/Describable.java
index 1514141..293fdb3 100644
--- a/src/main/java/org/junit/runner/Describable.java
+++ b/src/main/java/org/junit/runner/Describable.java
@@ -10,5 +10,5 @@ public interface Describable {
/**
* @return a {@link Description} showing the tests to be run by the receiver
*/
- public abstract Description getDescription();
+ Description getDescription();
} \ No newline at end of file
diff --git a/src/main/java/org/junit/runner/Description.java b/src/main/java/org/junit/runner/Description.java
index fe47eac..0846a1e 100644
--- a/src/main/java/org/junit/runner/Description.java
+++ b/src/main/java/org/junit/runner/Description.java
@@ -125,6 +125,17 @@ public class Description implements Serializable {
}
/**
+ * Create a <code>Description</code> named after <code>testClass</code>
+ *
+ * @param testClass A not null {@link Class} containing tests
+ * @param annotations meta-data about the test, for downstream interpreters
+ * @return a <code>Description</code> of <code>testClass</code>
+ */
+ public static Description createSuiteDescription(Class<?> testClass, Annotation... annotations) {
+ return new Description(testClass, testClass.getName(), annotations);
+ }
+
+ /**
* Describes a Runner which runs no tests
*/
public static final Description EMPTY = new Description(null, "No Tests");
@@ -139,7 +150,7 @@ public class Description implements Serializable {
/*
* 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
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final Collection<Description> fChildren = new ConcurrentLinkedQueue<Description>();
private final String fDisplayName;
diff --git a/src/main/java/org/junit/runner/FilterFactory.java b/src/main/java/org/junit/runner/FilterFactory.java
index 57b4eaa..e2bfb73 100644
--- a/src/main/java/org/junit/runner/FilterFactory.java
+++ b/src/main/java/org/junit/runner/FilterFactory.java
@@ -16,7 +16,8 @@ public interface FilterFactory {
/**
* Exception thrown if the {@link Filter} cannot be created.
*/
- public static class FilterNotCreatedException extends Exception {
+ @SuppressWarnings("serial")
+ class FilterNotCreatedException extends Exception {
public FilterNotCreatedException(Exception exception) {
super(exception.getMessage(), exception);
}
diff --git a/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
index 434157c..3383407 100644
--- a/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
+++ b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
@@ -85,13 +85,11 @@ class JUnitCommandLineParseResult {
}
private String[] copyArray(String[] args, int from, int to) {
- ArrayList<String> result = new ArrayList<String>();
-
+ String[] result = new String[to - from];
for (int j = from; j != to; ++j) {
- result.add(args[j]);
+ result[j - from] = args[j];
}
-
- return result.toArray(new String[result.size()]);
+ return result;
}
void parseParameters(String[] args) {
diff --git a/src/main/java/org/junit/runner/OrderWith.java b/src/main/java/org/junit/runner/OrderWith.java
new file mode 100644
index 0000000..e8470c9
--- /dev/null
+++ b/src/main/java/org/junit/runner/OrderWith.java
@@ -0,0 +1,28 @@
+package org.junit.runner;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.runner.manipulation.Ordering;
+import org.junit.validator.ValidateWith;
+
+/**
+ * When a test class is annotated with <code>&#064;OrderWith</code> or extends a class annotated
+ * with <code>&#064;OrderWith</code>, JUnit will order the tests in the test class (and child
+ * test classes, if any) using the ordering defined by the {@link Ordering} class.
+ *
+ * @since 4.13
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+@ValidateWith(OrderWithValidator.class)
+public @interface OrderWith {
+ /**
+ * Gets a class that extends {@link Ordering}. The class must have a public no-arg constructor.
+ */
+ Class<? extends Ordering.Factory> value();
+}
diff --git a/src/main/java/org/junit/runner/OrderWithValidator.java b/src/main/java/org/junit/runner/OrderWithValidator.java
new file mode 100644
index 0000000..f8eab25
--- /dev/null
+++ b/src/main/java/org/junit/runner/OrderWithValidator.java
@@ -0,0 +1,38 @@
+package org.junit.runner;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
+import java.util.List;
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.model.TestClass;
+import org.junit.validator.AnnotationValidator;
+
+/**
+ * Validates that there are no errors in the use of the {@code OrderWith}
+ * annotation. If there is, a {@code Throwable} object will be added to the list
+ * of errors.
+ *
+ * @since 4.13
+ */
+public final class OrderWithValidator extends AnnotationValidator {
+
+ /**
+ * Adds to {@code errors} a throwable for each problem detected. Looks for
+ * {@code FixMethodOrder} annotations.
+ *
+ * @param testClass that is being validated
+ * @return A list of exceptions detected
+ *
+ * @since 4.13
+ */
+ @Override
+ public List<Exception> validateAnnotatedClass(TestClass testClass) {
+ if (testClass.getAnnotation(FixMethodOrder.class) != null) {
+ return singletonList(
+ new Exception("@FixMethodOrder cannot be combined with @OrderWith"));
+ }
+ return emptyList();
+ }
+}
diff --git a/src/main/java/org/junit/runner/Request.java b/src/main/java/org/junit/runner/Request.java
index 79c0f1e..7b9a990 100644
--- a/src/main/java/org/junit/runner/Request.java
+++ b/src/main/java/org/junit/runner/Request.java
@@ -5,9 +5,11 @@ import java.util.Comparator;
import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
import org.junit.internal.requests.ClassRequest;
import org.junit.internal.requests.FilterRequest;
+import org.junit.internal.requests.OrderingRequest;
import org.junit.internal.requests.SortingRequest;
import org.junit.internal.runners.ErrorReportingRunner;
import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Ordering;
import org.junit.runners.model.InitializationError;
/**
@@ -71,12 +73,11 @@ public abstract class Request {
*/
public static Request classes(Computer computer, Class<?>... classes) {
try {
- AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
+ AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder();
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");
+ return runner(new ErrorReportingRunner(e, classes));
}
}
@@ -132,13 +133,16 @@ public abstract class Request {
}
/**
- * Returns a Request that only runs contains tests whose {@link Description}
- * equals <code>desiredDescription</code>
+ * Returns a Request that only runs tests whose {@link Description}
+ * matches the given description.
*
- * @param desiredDescription {@link Description} of those tests that should be run
+ * <p>Returns an empty {@code Request} if {@code desiredDescription} is not a single test and filters all but the single
+ * test if {@code desiredDescription} is a single test.</p>
+ *
+ * @param desiredDescription {@code Description} of those tests that should be run
* @return the filtered Request
*/
- public Request filterWith(final Description desiredDescription) {
+ public Request filterWith(Description desiredDescription) {
return filterWith(Filter.matchMethodDescription(desiredDescription));
}
@@ -149,15 +153,15 @@ public abstract class Request {
* 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());
- * }
- * };
+ * 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()));
+ * new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
* }
* </pre>
*
@@ -167,4 +171,32 @@ public abstract class Request {
public Request sortWith(Comparator<Description> comparator) {
return new SortingRequest(this, comparator);
}
+
+ /**
+ * Returns a Request whose Tests can be run in a certain order, defined by
+ * <code>ordering</code>
+ * <p>
+ * For example, here is code to run a test suite in reverse order:
+ * <pre>
+ * private static Ordering reverse() {
+ * return new Ordering() {
+ * public List&lt;Description&gt; orderItems(Collection&lt;Description&gt; descriptions) {
+ * List&lt;Description&gt; ordered = new ArrayList&lt;&gt;(descriptions);
+ * Collections.reverse(ordered);
+ * return ordered;
+ * }
+ * }
+ * }
+ *
+ * public static main() {
+ * new JUnitCore().run(Request.aClass(AllTests.class).orderWith(reverse()));
+ * }
+ * </pre>
+ *
+ * @return a Request with ordered Tests
+ * @since 4.13
+ */
+ public Request orderWith(Ordering ordering) {
+ return new OrderingRequest(this, ordering);
+ }
}
diff --git a/src/main/java/org/junit/runner/Result.java b/src/main/java/org/junit/runner/Result.java
index 73ad059..4b5f4a4 100644
--- a/src/main/java/org/junit/runner/Result.java
+++ b/src/main/java/org/junit/runner/Result.java
@@ -28,6 +28,7 @@ public class Result implements Serializable {
ObjectStreamClass.lookup(SerializedForm.class).getFields();
private final AtomicInteger count;
private final AtomicInteger ignoreCount;
+ private final AtomicInteger assumptionFailureCount;
private final CopyOnWriteArrayList<Failure> failures;
private final AtomicLong runTime;
private final AtomicLong startTime;
@@ -38,6 +39,7 @@ public class Result implements Serializable {
public Result() {
count = new AtomicInteger();
ignoreCount = new AtomicInteger();
+ assumptionFailureCount = new AtomicInteger();
failures = new CopyOnWriteArrayList<Failure>();
runTime = new AtomicLong();
startTime = new AtomicLong();
@@ -46,34 +48,35 @@ public class Result implements Serializable {
private Result(SerializedForm serializedForm) {
count = serializedForm.fCount;
ignoreCount = serializedForm.fIgnoreCount;
+ assumptionFailureCount = serializedForm.assumptionFailureCount;
failures = new CopyOnWriteArrayList<Failure>(serializedForm.fFailures);
runTime = new AtomicLong(serializedForm.fRunTime);
startTime = new AtomicLong(serializedForm.fStartTime);
}
/**
- * @return the number of tests run
+ * Returns the number of tests run
*/
public int getRunCount() {
return count.get();
}
/**
- * @return the number of tests that failed during the run
+ * Returns 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
+ * Returns 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
+ * Returns the {@link Failure}s describing tests that failed and the problems they encountered
*/
public List<Failure> getFailures() {
return failures;
@@ -87,6 +90,20 @@ public class Result implements Serializable {
}
/**
+ * Returns the number of tests skipped because of an assumption failure
+ *
+ * @throws UnsupportedOperationException if the result was serialized in a version before JUnit 4.13
+ * @since 4.13
+ */
+ public int getAssumptionFailureCount() {
+ if (assumptionFailureCount == null) {
+ throw new UnsupportedOperationException(
+ "Result was serialized from a version of JUnit that doesn't support this method");
+ }
+ return assumptionFailureCount.get();
+ }
+
+ /**
* @return <code>true</code> if all tests succeeded
*/
public boolean wasSuccessful() {
@@ -137,7 +154,7 @@ public class Result implements Serializable {
@Override
public void testAssumptionFailure(Failure failure) {
- // do nothing: same as passing (for 4.5; may change in 4.6)
+ assumptionFailureCount.getAndIncrement();
}
}
@@ -156,6 +173,7 @@ public class Result implements Serializable {
private static final long serialVersionUID = 1L;
private final AtomicInteger fCount;
private final AtomicInteger fIgnoreCount;
+ private final AtomicInteger assumptionFailureCount;
private final List<Failure> fFailures;
private final long fRunTime;
private final long fStartTime;
@@ -163,6 +181,7 @@ public class Result implements Serializable {
public SerializedForm(Result result) {
fCount = result.count;
fIgnoreCount = result.ignoreCount;
+ assumptionFailureCount = result.assumptionFailureCount;
fFailures = Collections.synchronizedList(new ArrayList<Failure>(result.failures));
fRunTime = result.runTime.longValue();
fStartTime = result.startTime.longValue();
@@ -172,6 +191,7 @@ public class Result implements Serializable {
private SerializedForm(ObjectInputStream.GetField fields) throws IOException {
fCount = (AtomicInteger) fields.get("fCount", null);
fIgnoreCount = (AtomicInteger) fields.get("fIgnoreCount", null);
+ assumptionFailureCount = (AtomicInteger) fields.get("assumptionFailureCount", null);
fFailures = (List<Failure>) fields.get("fFailures", null);
fRunTime = fields.get("fRunTime", 0L);
fStartTime = fields.get("fStartTime", 0L);
@@ -184,6 +204,7 @@ public class Result implements Serializable {
fields.put("fFailures", fFailures);
fields.put("fRunTime", fRunTime);
fields.put("fStartTime", fStartTime);
+ fields.put("assumptionFailureCount", assumptionFailureCount);
s.writeFields();
}
diff --git a/src/main/java/org/junit/runner/manipulation/Alphanumeric.java b/src/main/java/org/junit/runner/manipulation/Alphanumeric.java
new file mode 100644
index 0000000..8388d21
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/Alphanumeric.java
@@ -0,0 +1,27 @@
+package org.junit.runner.manipulation;
+
+import java.util.Comparator;
+
+import org.junit.runner.Description;
+
+/**
+ * A sorter that orders tests alphanumerically by test name.
+ *
+ * @since 4.13
+ */
+public final class Alphanumeric extends Sorter implements Ordering.Factory {
+
+ public Alphanumeric() {
+ super(COMPARATOR);
+ }
+
+ public Ordering create(Context context) {
+ return this;
+ }
+
+ private static final Comparator<Description> COMPARATOR = new Comparator<Description>() {
+ public int compare(Description o1, Description o2) {
+ return o1.getDisplayName().compareTo(o2.getDisplayName());
+ }
+ };
+}
diff --git a/src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java b/src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java
new file mode 100644
index 0000000..d9d60f7
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java
@@ -0,0 +1,21 @@
+package org.junit.runner.manipulation;
+
+/**
+ * Thrown when an ordering does something invalid (like remove or add children)
+ *
+ * @since 4.13
+ */
+public class InvalidOrderingException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidOrderingException() {
+ }
+
+ public InvalidOrderingException(String message) {
+ super(message);
+ }
+
+ public InvalidOrderingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Orderable.java b/src/main/java/org/junit/runner/manipulation/Orderable.java
new file mode 100644
index 0000000..9a12a3b
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/Orderable.java
@@ -0,0 +1,21 @@
+package org.junit.runner.manipulation;
+
+/**
+ * Interface for runners that allow ordering of tests.
+ *
+ * <p>Beware of using this interface 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.13
+ */
+public interface Orderable extends Sortable {
+
+ /**
+ * Orders the tests using <code>orderer</code>
+ *
+ * @throws InvalidOrderingException if orderer does something invalid (like remove or add
+ * children)
+ */
+ void order(Orderer orderer) throws InvalidOrderingException;
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Orderer.java b/src/main/java/org/junit/runner/manipulation/Orderer.java
new file mode 100644
index 0000000..eb13054
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/Orderer.java
@@ -0,0 +1,62 @@
+package org.junit.runner.manipulation;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.runner.Description;
+
+/**
+ * Orders tests.
+ *
+ * @since 4.13
+ */
+public final class Orderer {
+ private final Ordering ordering;
+
+ Orderer(Ordering delegate) {
+ this.ordering = delegate;
+ }
+
+ /**
+ * Orders the descriptions.
+ *
+ * @return descriptions in order
+ */
+ public List<Description> order(Collection<Description> descriptions)
+ throws InvalidOrderingException {
+ List<Description> inOrder = ordering.orderItems(
+ Collections.unmodifiableCollection(descriptions));
+ if (!ordering.validateOrderingIsCorrect()) {
+ return inOrder;
+ }
+
+ Set<Description> uniqueDescriptions = new HashSet<Description>(descriptions);
+ if (!uniqueDescriptions.containsAll(inOrder)) {
+ throw new InvalidOrderingException("Ordering added items");
+ }
+ Set<Description> resultAsSet = new HashSet<Description>(inOrder);
+ if (resultAsSet.size() != inOrder.size()) {
+ throw new InvalidOrderingException("Ordering duplicated items");
+ } else if (!resultAsSet.containsAll(uniqueDescriptions)) {
+ throw new InvalidOrderingException("Ordering removed items");
+ }
+
+ return inOrder;
+ }
+
+ /**
+ * Order the tests in <code>target</code>.
+ *
+ * @throws InvalidOrderingException if ordering does something invalid (like remove or add
+ * children)
+ */
+ public void apply(Object target) throws InvalidOrderingException {
+ if (target instanceof Orderable) {
+ Orderable orderable = (Orderable) target;
+ orderable.order(this);
+ }
+ }
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Ordering.java b/src/main/java/org/junit/runner/manipulation/Ordering.java
new file mode 100644
index 0000000..0d0ce93
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/Ordering.java
@@ -0,0 +1,172 @@
+package org.junit.runner.manipulation;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.runner.Description;
+import org.junit.runner.OrderWith;
+
+/**
+ * Reorders tests. An {@code Ordering} can reverse the order of tests, sort the
+ * order or even shuffle the order.
+ *
+ * <p>In general you will not need to use a <code>Ordering</code> directly.
+ * Instead, use {@link org.junit.runner.Request#orderWith(Ordering)}.
+ *
+ * @since 4.13
+ */
+public abstract class Ordering {
+ private static final String CONSTRUCTOR_ERROR_FORMAT
+ = "Ordering class %s should have a public constructor with signature "
+ + "%s(Ordering.Context context)";
+
+ /**
+ * Creates an {@link Ordering} that shuffles the items using the given
+ * {@link Random} instance.
+ */
+ public static Ordering shuffledBy(final Random random) {
+ return new Ordering() {
+ @Override
+ boolean validateOrderingIsCorrect() {
+ return false;
+ }
+
+ @Override
+ protected List<Description> orderItems(Collection<Description> descriptions) {
+ List<Description> shuffled = new ArrayList<Description>(descriptions);
+ Collections.shuffle(shuffled, random);
+ return shuffled;
+ }
+ };
+ }
+
+ /**
+ * Creates an {@link Ordering} from the given factory class. The class must have a public no-arg
+ * constructor.
+ *
+ * @param factoryClass class to use to create the ordering
+ * @param annotatedTestClass test class that is annotated with {@link OrderWith}.
+ * @throws InvalidOrderingException if the instance could not be created
+ */
+ public static Ordering definedBy(
+ Class<? extends Ordering.Factory> factoryClass, Description annotatedTestClass)
+ throws InvalidOrderingException {
+ if (factoryClass == null) {
+ throw new NullPointerException("factoryClass cannot be null");
+ }
+ if (annotatedTestClass == null) {
+ throw new NullPointerException("annotatedTestClass cannot be null");
+ }
+
+ Ordering.Factory factory;
+ try {
+ Constructor<? extends Ordering.Factory> constructor = factoryClass.getConstructor();
+ factory = constructor.newInstance();
+ } catch (NoSuchMethodException e) {
+ throw new InvalidOrderingException(String.format(
+ CONSTRUCTOR_ERROR_FORMAT,
+ getClassName(factoryClass),
+ factoryClass.getSimpleName()));
+ } catch (Exception e) {
+ throw new InvalidOrderingException(
+ "Could not create ordering for " + annotatedTestClass, e);
+ }
+ return definedBy(factory, annotatedTestClass);
+ }
+
+ /**
+ * Creates an {@link Ordering} from the given factory.
+ *
+ * @param factory factory to use to create the ordering
+ * @param annotatedTestClass test class that is annotated with {@link OrderWith}.
+ * @throws InvalidOrderingException if the instance could not be created
+ */
+ public static Ordering definedBy(
+ Ordering.Factory factory, Description annotatedTestClass)
+ throws InvalidOrderingException {
+ if (factory == null) {
+ throw new NullPointerException("factory cannot be null");
+ }
+ if (annotatedTestClass == null) {
+ throw new NullPointerException("annotatedTestClass cannot be null");
+ }
+
+ return factory.create(new Ordering.Context(annotatedTestClass));
+ }
+
+ private static String getClassName(Class<?> clazz) {
+ String name = clazz.getCanonicalName();
+ if (name == null) {
+ return clazz.getName();
+ }
+ return name;
+ }
+
+ /**
+ * Order the tests in <code>target</code> using this ordering.
+ *
+ * @throws InvalidOrderingException if ordering does something invalid (like remove or add
+ * children)
+ */
+ public void apply(Object target) throws InvalidOrderingException {
+ /*
+ * Note that some subclasses of Ordering override apply(). The Sorter
+ * subclass of Ordering overrides apply() to apply the sort (this is
+ * done because sorting is more efficient than ordering).
+ */
+ if (target instanceof Orderable) {
+ Orderable orderable = (Orderable) target;
+ orderable.order(new Orderer(this));
+ }
+ }
+
+ /**
+ * Returns {@code true} if this ordering could produce invalid results (i.e.
+ * if it could add or remove values).
+ */
+ boolean validateOrderingIsCorrect() {
+ return true;
+ }
+
+ /**
+ * Implemented by sub-classes to order the descriptions.
+ *
+ * @return descriptions in order
+ */
+ protected abstract List<Description> orderItems(Collection<Description> descriptions);
+
+ /** Context about the ordering being applied. */
+ public static class Context {
+ private final Description description;
+
+ /**
+ * Gets the description for the top-level target being ordered.
+ */
+ public Description getTarget() {
+ return description;
+ }
+
+ private Context(Description description) {
+ this.description = description;
+ }
+ }
+
+ /**
+ * Factory for creating {@link Ordering} instances.
+ *
+ * <p>For a factory to be used with {@code @OrderWith} it needs to have a public no-arg
+ * constructor.
+ */
+ public interface Factory {
+ /**
+ * Creates an Ordering instance using the given context. Implementations
+ * of this method that do not need to use the context can return the
+ * same instance every time.
+ */
+ Ordering create(Context context);
+ }
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Sortable.java b/src/main/java/org/junit/runner/manipulation/Sortable.java
index 9ac864c..0c59f33 100644
--- a/src/main/java/org/junit/runner/manipulation/Sortable.java
+++ b/src/main/java/org/junit/runner/manipulation/Sortable.java
@@ -15,6 +15,6 @@ public interface Sortable {
*
* @param sorter the {@link Sorter} to use for sorting the tests
*/
- public void sort(Sorter sorter);
+ 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 20192d0..4b5274c 100644
--- a/src/main/java/org/junit/runner/manipulation/Sorter.java
+++ b/src/main/java/org/junit/runner/manipulation/Sorter.java
@@ -1,16 +1,21 @@
package org.junit.runner.manipulation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
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)}.
+ * 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> {
+public class Sorter extends Ordering implements Comparator<Description> {
/**
* NULL is a <code>Sorter</code> that leaves elements in an undefined order
*/
@@ -27,17 +32,26 @@ public class Sorter implements Comparator<Description> {
* to sort tests
*
* @param comparator the {@link Comparator} to use when sorting tests
+ * @since 4.0
*/
public Sorter(Comparator<Description> comparator) {
this.comparator = comparator;
}
/**
- * Sorts the test in <code>runner</code> using <code>comparator</code>
+ * Sorts the tests in <code>target</code> using <code>comparator</code>.
+ *
+ * @since 4.0
*/
- public void apply(Object object) {
- if (object instanceof Sortable) {
- Sortable sortable = (Sortable) object;
+ @Override
+ public void apply(Object target) {
+ /*
+ * Note that all runners that are Orderable are also Sortable (because
+ * Orderable extends Sortable). Sorting is more efficient than ordering,
+ * so we override the parent behavior so we sort instead.
+ */
+ if (target instanceof Sortable) {
+ Sortable sortable = (Sortable) target;
sortable.sort(this);
}
}
@@ -45,4 +59,32 @@ public class Sorter implements Comparator<Description> {
public int compare(Description o1, Description o2) {
return comparator.compare(o1, o2);
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 4.13
+ */
+ @Override
+ protected final List<Description> orderItems(Collection<Description> descriptions) {
+ /*
+ * In practice, we will never get here--Sorters do their work in the
+ * compare() method--but the Liskov substitution principle demands that
+ * we obey the general contract of Orderable. Luckily, it's trivial to
+ * implement.
+ */
+ List<Description> sorted = new ArrayList<Description>(descriptions);
+ Collections.sort(sorted, this); // Note: it would be incorrect to pass in "comparator"
+ return sorted;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 4.13
+ */
+ @Override
+ boolean validateOrderingIsCorrect() {
+ return false;
+ }
}
diff --git a/src/main/java/org/junit/runner/notification/Failure.java b/src/main/java/org/junit/runner/notification/Failure.java
index c03b4c1..4551302 100644
--- a/src/main/java/org/junit/runner/notification/Failure.java
+++ b/src/main/java/org/junit/runner/notification/Failure.java
@@ -1,9 +1,8 @@
package org.junit.runner.notification;
-import java.io.PrintWriter;
import java.io.Serializable;
-import java.io.StringWriter;
+import org.junit.internal.Throwables;
import org.junit.runner.Description;
/**
@@ -21,7 +20,7 @@ public class Failure implements Serializable {
/*
* 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
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final Description fDescription;
private final Throwable fThrownException;
@@ -65,15 +64,19 @@ public class Failure implements Serializable {
}
/**
- * Convenience method
- *
- * @return the printed form of the exception
+ * Gets the printed form of the exception and its stack trace.
*/
public String getTrace() {
- StringWriter stringWriter = new StringWriter();
- PrintWriter writer = new PrintWriter(stringWriter);
- getException().printStackTrace(writer);
- return stringWriter.toString();
+ return Throwables.getStacktrace(getException());
+ }
+
+ /**
+ * Gets a the printed form of the exception, with a trimmed version of the stack trace.
+ * This method will attempt to filter out frames of the stack trace that are below
+ * the test method call.
+ */
+ public String getTrimmedTrace() {
+ return Throwables.getTrimmedStackTrace(getException());
}
/**
diff --git a/src/main/java/org/junit/runner/notification/RunListener.java b/src/main/java/org/junit/runner/notification/RunListener.java
index db9d8c1..d7cac00 100644
--- a/src/main/java/org/junit/runner/notification/RunListener.java
+++ b/src/main/java/org/junit/runner/notification/RunListener.java
@@ -70,6 +70,34 @@ public class RunListener {
}
/**
+ * Called when a test suite is about to be started. If this method is
+ * called for a given {@link Description}, then {@link #testSuiteFinished(Description)}
+ * will also be called for the same {@code Description}.
+ *
+ * <p>Note that not all runners will call this method, so runners should
+ * be prepared to handle {@link #testStarted(Description)} calls for tests
+ * where there was no corresponding {@code testSuiteStarted()} call for
+ * the parent {@code Description}.
+ *
+ * @param description the description of the test suite that is about to be run
+ * (generally a class name)
+ * @since 4.13
+ */
+ public void testSuiteStarted(Description description) throws Exception {
+ }
+
+ /**
+ * Called when a test suite has finished, whether the test suite succeeds or fails.
+ * This method will not be called for a given {@link Description} unless
+ * {@link #testSuiteStarted(Description)} was called for the same @code Description}.
+ *
+ * @param description the description of the test suite that just ran
+ * @since 4.13
+ */
+ public void testSuiteFinished(Description description) 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
diff --git a/src/main/java/org/junit/runner/notification/RunNotifier.java b/src/main/java/org/junit/runner/notification/RunNotifier.java
index 6875f76..752fa3b 100644
--- a/src/main/java/org/junit/runner/notification/RunNotifier.java
+++ b/src/main/java/org/junit/runner/notification/RunNotifier.java
@@ -65,8 +65,8 @@ public class RunNotifier {
void run() {
int capacity = currentListeners.size();
- ArrayList<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
- ArrayList<Failure> failures = new ArrayList<Failure>(capacity);
+ List<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
+ List<Failure> failures = new ArrayList<Failure>(capacity);
for (RunListener listener : currentListeners) {
try {
notifyListener(listener);
@@ -78,7 +78,7 @@ public class RunNotifier {
fireTestFailures(safeListeners, failures);
}
- abstract protected void notifyListener(RunListener each) throws Exception;
+ protected abstract void notifyListener(RunListener each) throws Exception;
}
/**
@@ -106,6 +106,41 @@ public class RunNotifier {
}
/**
+ * Invoke to tell listeners that a test suite is about to start. Runners are strongly
+ * encouraged--but not required--to call this method. If this method is called for
+ * a given {@link Description} then {@link #fireTestSuiteFinished(Description)} MUST
+ * be called for the same {@code Description}.
+ *
+ * @param description the description of the suite test (generally a class name)
+ * @since 4.13
+ */
+ public void fireTestSuiteStarted(final Description description) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testSuiteStarted(description);
+ }
+ }.run();
+ }
+
+ /**
+ * Invoke to tell listeners that a test suite is about to finish. Always invoke
+ * this method if you invoke {@link #fireTestSuiteStarted(Description)}
+ * as listeners are likely to expect them to come in pairs.
+ *
+ * @param description the description of the suite test (generally a class name)
+ * @since 4.13
+ */
+ public void fireTestSuiteFinished(final Description description) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testSuiteFinished(description);
+ }
+ }.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)
diff --git a/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java b/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
index c53c1ee..400fed8 100644
--- a/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
+++ b/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
@@ -10,7 +10,7 @@ import org.junit.runner.Result;
* <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
+ * synchronized, clients that added multiple listeners that called common code might see
* issues due to the reduced synchronization.
*
* @author Tibor Digana (tibor17)
@@ -43,6 +43,37 @@ final class SynchronizedRunListener extends RunListener {
}
}
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * Synchronized decorator for {@link RunListener#testSuiteStarted(Description)}.
+ * @param description the description of the test suite that is about to be run
+ * (generally a class name).
+ * @throws Exception if any occurs.
+ * @since 4.13
+ */
+ @Override
+ public void testSuiteStarted(Description description) throws Exception {
+ synchronized (monitor) {
+ listener.testSuiteStarted(description);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * Synchronized decorator for {@link RunListener#testSuiteFinished(Description)}.
+ * @param description the description of the test suite that just ran.
+ * @throws Exception
+ * @since 4.13
+ */
+ @Override
+ public void testSuiteFinished(Description description) throws Exception {
+ synchronized (monitor) {
+ listener.testSuiteFinished(description);
+ }
+ }
+
@Override
public void testStarted(Description description) throws Exception {
synchronized (monitor) {