aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/junit/internal
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/junit/internal')
-rw-r--r--src/main/java/org/junit/internal/ArrayComparisonFailure.java13
-rw-r--r--src/main/java/org/junit/internal/AssumptionViolatedException.java30
-rw-r--r--src/main/java/org/junit/internal/Checks.java37
-rw-r--r--src/main/java/org/junit/internal/Classes.java28
-rw-r--r--src/main/java/org/junit/internal/ComparisonCriteria.java89
-rw-r--r--src/main/java/org/junit/internal/SerializableMatcherDescription.java47
-rw-r--r--src/main/java/org/junit/internal/SerializableValueDescription.java38
-rw-r--r--src/main/java/org/junit/internal/TextListener.java4
-rw-r--r--src/main/java/org/junit/internal/Throwables.java231
-rw-r--r--src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java11
-rw-r--r--src/main/java/org/junit/internal/builders/JUnit4Builder.java6
-rw-r--r--src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java21
-rw-r--r--src/main/java/org/junit/internal/management/FakeThreadMXBean.java27
-rw-r--r--src/main/java/org/junit/internal/management/ManagementFactory.java77
-rw-r--r--src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java61
-rw-r--r--src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java92
-rw-r--r--src/main/java/org/junit/internal/management/RuntimeMXBean.java14
-rw-r--r--src/main/java/org/junit/internal/management/ThreadMXBean.java17
-rw-r--r--src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java9
-rw-r--r--src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java6
-rw-r--r--src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java2
-rw-r--r--src/main/java/org/junit/internal/requests/ClassRequest.java41
-rw-r--r--src/main/java/org/junit/internal/requests/FilterRequest.java2
-rw-r--r--src/main/java/org/junit/internal/requests/MemoizingRequest.java30
-rw-r--r--src/main/java/org/junit/internal/requests/OrderingRequest.java29
-rw-r--r--src/main/java/org/junit/internal/runners/ErrorReportingRunner.java50
-rw-r--r--src/main/java/org/junit/internal/runners/InitializationError.java2
-rw-r--r--src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java22
-rw-r--r--src/main/java/org/junit/internal/runners/MethodValidator.java2
-rw-r--r--src/main/java/org/junit/internal/runners/TestClass.java2
-rw-r--r--src/main/java/org/junit/internal/runners/model/EachTestNotifier.java23
-rw-r--r--src/main/java/org/junit/internal/runners/rules/ValidationError.java3
-rw-r--r--src/main/java/org/junit/internal/runners/statements/ExpectException.java4
-rw-r--r--src/main/java/org/junit/internal/runners/statements/RunAfters.java9
-rw-r--r--src/main/java/org/junit/internal/runners/statements/RunBefores.java9
35 files changed, 1012 insertions, 76 deletions
diff --git a/src/main/java/org/junit/internal/ArrayComparisonFailure.java b/src/main/java/org/junit/internal/ArrayComparisonFailure.java
index 8627d6e..d300e7e 100644
--- a/src/main/java/org/junit/internal/ArrayComparisonFailure.java
+++ b/src/main/java/org/junit/internal/ArrayComparisonFailure.java
@@ -16,11 +16,12 @@ public class ArrayComparisonFailure extends AssertionError {
/*
* 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
+ * serialization compatibility.
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final List<Integer> fIndices = new ArrayList<Integer>();
private final String fMessage;
+ private final AssertionError fCause;
/**
* Construct a new <code>ArrayComparisonFailure</code> with an error text and the array's
@@ -32,7 +33,8 @@ public class ArrayComparisonFailure extends AssertionError {
*/
public ArrayComparisonFailure(String message, AssertionError cause, int index) {
this.fMessage = message;
- initCause(cause);
+ this.fCause = cause;
+ initCause(fCause);
addDimension(index);
}
@@ -41,6 +43,11 @@ public class ArrayComparisonFailure extends AssertionError {
}
@Override
+ public synchronized Throwable getCause() {
+ return super.getCause() == null ? fCause : super.getCause();
+ }
+
+ @Override
public String getMessage() {
StringBuilder sb = new StringBuilder();
if (fMessage != null) {
diff --git a/src/main/java/org/junit/internal/AssumptionViolatedException.java b/src/main/java/org/junit/internal/AssumptionViolatedException.java
index 880d73f..0e79b56 100644
--- a/src/main/java/org/junit/internal/AssumptionViolatedException.java
+++ b/src/main/java/org/junit/internal/AssumptionViolatedException.java
@@ -1,5 +1,8 @@
package org.junit.internal;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.SelfDescribing;
@@ -18,7 +21,7 @@ public class AssumptionViolatedException extends RuntimeException implements Sel
/*
* 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 String fAssumption;
private final boolean fValueMatcher;
@@ -108,4 +111,29 @@ public class AssumptionViolatedException extends RuntimeException implements Sel
}
}
}
+
+ /**
+ * Override default Java object serialization to correctly deal with potentially unserializable matchers or values.
+ * By not implementing readObject, we assure ourselves of backwards compatibility and compatibility with the
+ * standard way of Java serialization.
+ *
+ * @param objectOutputStream The outputStream to write our representation to
+ * @throws IOException When serialization fails
+ */
+ private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
+ ObjectOutputStream.PutField putField = objectOutputStream.putFields();
+ putField.put("fAssumption", fAssumption);
+ putField.put("fValueMatcher", fValueMatcher);
+
+ // We have to wrap the matcher into a serializable form.
+ putField.put("fMatcher", SerializableMatcherDescription.asSerializableMatcher(fMatcher));
+
+ // We have to wrap the value inside a non-String class (instead of serializing the String value directly) as
+ // A Description will handle a String and non-String object differently (1st is surrounded by '"' while the
+ // latter will be surrounded by '<' '>'. Wrapping it makes sure that the description of a serialized and
+ // non-serialized instance produce the exact same description
+ putField.put("fValue", SerializableValueDescription.asSerializableValue(fValue));
+
+ objectOutputStream.writeFields();
+ }
}
diff --git a/src/main/java/org/junit/internal/Checks.java b/src/main/java/org/junit/internal/Checks.java
new file mode 100644
index 0000000..9724947
--- /dev/null
+++ b/src/main/java/org/junit/internal/Checks.java
@@ -0,0 +1,37 @@
+package org.junit.internal;
+
+/** @since 4.13 */
+public final class Checks {
+
+ private Checks() {}
+
+ /**
+ * Checks that the given value is not {@code null}.
+ *
+ * @param value object reference to check
+ * @return the passed-in value, if not {@code null}
+ * @throws NullPointerException if {@code value} is {@code null}
+ */
+ public static <T> T notNull(T value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ return value;
+ }
+
+ /**
+ * Checks that the given value is not {@code null}, using the given message
+ * as the exception message if an exception is thrown.
+ *
+ * @param value object reference to check
+ * @param message message to use if {@code value} is {@code null}
+ * @return the passed-in value, if not {@code null}
+ * @throws NullPointerException if {@code value} is {@code null}
+ */
+ public static <T> T notNull(T value, String message) {
+ if (value == null) {
+ throw new NullPointerException(message);
+ }
+ return value;
+ }
+}
diff --git a/src/main/java/org/junit/internal/Classes.java b/src/main/java/org/junit/internal/Classes.java
index 154603d..e8404f6 100644
--- a/src/main/java/org/junit/internal/Classes.java
+++ b/src/main/java/org/junit/internal/Classes.java
@@ -6,13 +6,39 @@ import static java.lang.Thread.currentThread;
* Miscellaneous functions dealing with classes.
*/
public class Classes {
+
+ /**
+ * Do not instantiate.
+ * @deprecated will be private soon.
+ */
+ @Deprecated
+ public Classes() {
+ }
+
/**
* Returns Class.forName for {@code className} using the current thread's class loader.
+ * If the current thread does not have a class loader, falls back to the class loader for
+ * {@link Classes}.
*
* @param className Name of the class.
* @throws ClassNotFoundException
*/
public static Class<?> getClass(String className) throws ClassNotFoundException {
- return Class.forName(className, true, currentThread().getContextClassLoader());
+ return getClass(className, Classes.class);
+ }
+
+ /**
+ * Returns Class.forName for {@code className} using the current thread's class loader.
+ * If the current thread does not have a class loader, falls back to the class loader for the
+ * passed-in class.
+ *
+ * @param className Name of the class.
+ * @param callingClass Class that is requesting a the class
+ * @throws ClassNotFoundException
+ * @since 4.13
+ */
+ public static Class<?> getClass(String className, Class<?> callingClass) throws ClassNotFoundException {
+ ClassLoader classLoader = currentThread().getContextClassLoader();
+ return Class.forName(className, true, classLoader == null ? callingClass.getClassLoader() : classLoader);
}
}
diff --git a/src/main/java/org/junit/internal/ComparisonCriteria.java b/src/main/java/org/junit/internal/ComparisonCriteria.java
index e6d49a4..ed1c674 100644
--- a/src/main/java/org/junit/internal/ComparisonCriteria.java
+++ b/src/main/java/org/junit/internal/ComparisonCriteria.java
@@ -25,6 +25,11 @@ public abstract class ComparisonCriteria {
*/
public void arrayEquals(String message, Object expecteds, Object actuals)
throws ArrayComparisonFailure {
+ arrayEquals(message, expecteds, actuals, true);
+ }
+
+ private void arrayEquals(String message, Object expecteds, Object actuals, boolean outer)
+ throws ArrayComparisonFailure {
if (expecteds == actuals
|| Arrays.deepEquals(new Object[] {expecteds}, new Object[] {actuals})) {
// The reflection-based loop below is potentially very slow, especially for primitive
@@ -34,19 +39,37 @@ public abstract class ComparisonCriteria {
}
String header = message == null ? "" : message + ": ";
- int expectedsLength = assertArraysAreSameLength(expecteds,
- actuals, header);
+ // Only include the user-provided message in the outer exception.
+ String exceptionMessage = outer ? header : "";
+
+ if (expecteds == null) {
+ Assert.fail(exceptionMessage + "expected array was null");
+ }
+ if (actuals == null) {
+ Assert.fail(exceptionMessage + "actual array was null");
+ }
+
+ int actualsLength = Array.getLength(actuals);
+ int expectedsLength = Array.getLength(expecteds);
+ if (actualsLength != expectedsLength) {
+ header += "array lengths differed, expected.length="
+ + expectedsLength + " actual.length=" + actualsLength + "; ";
+ }
+ int prefixLength = Math.min(actualsLength, expectedsLength);
- for (int i = 0; i < expectedsLength; i++) {
+ for (int i = 0; i < prefixLength; i++) {
Object expected = Array.get(expecteds, i);
Object actual = Array.get(actuals, i);
if (isArray(expected) && isArray(actual)) {
try {
- arrayEquals(message, expected, actual);
+ arrayEquals(message, expected, actual, false);
} catch (ArrayComparisonFailure e) {
e.addDimension(i);
throw e;
+ } catch (AssertionError e) {
+ // Array lengths differed.
+ throw new ArrayComparisonFailure(header, e, i);
}
} else {
try {
@@ -56,27 +79,53 @@ public abstract class ComparisonCriteria {
}
}
}
- }
- private boolean isArray(Object expected) {
- return expected != null && expected.getClass().isArray();
+ if (actualsLength != expectedsLength) {
+ Object expected = getToStringableArrayElement(expecteds, expectedsLength, prefixLength);
+ Object actual = getToStringableArrayElement(actuals, actualsLength, prefixLength);
+ try {
+ Assert.assertEquals(expected, actual);
+ } catch (AssertionError e) {
+ throw new ArrayComparisonFailure(header, e, prefixLength);
+ }
+ }
}
- private int assertArraysAreSameLength(Object expecteds,
- Object actuals, String header) {
- if (expecteds == null) {
- Assert.fail(header + "expected array was null");
- }
- if (actuals == null) {
- Assert.fail(header + "actual array was null");
+ private static final Object END_OF_ARRAY_SENTINEL = objectWithToString("end of array");
+
+ private Object getToStringableArrayElement(Object array, int length, int index) {
+ if (index < length) {
+ Object element = Array.get(array, index);
+ if (isArray(element)) {
+ return objectWithToString(componentTypeName(element.getClass()) + "[" + Array.getLength(element) + "]");
+ } else {
+ return element;
+ }
+ } else {
+ return END_OF_ARRAY_SENTINEL;
}
- int actualsLength = Array.getLength(actuals);
- int expectedsLength = Array.getLength(expecteds);
- if (actualsLength != expectedsLength) {
- Assert.fail(header + "array lengths differed, expected.length="
- + expectedsLength + " actual.length=" + actualsLength);
+ }
+
+ private static Object objectWithToString(final String string) {
+ return new Object() {
+ @Override
+ public String toString() {
+ return string;
+ }
+ };
+ }
+
+ private String componentTypeName(Class<?> arrayClass) {
+ Class<?> componentType = arrayClass.getComponentType();
+ if (componentType.isArray()) {
+ return componentTypeName(componentType) + "[]";
+ } else {
+ return componentType.getName();
}
- return expectedsLength;
+ }
+
+ private boolean isArray(Object expected) {
+ return expected != null && expected.getClass().isArray();
}
protected abstract void assertElementsEqual(Object expected, Object actual);
diff --git a/src/main/java/org/junit/internal/SerializableMatcherDescription.java b/src/main/java/org/junit/internal/SerializableMatcherDescription.java
new file mode 100644
index 0000000..e036557
--- /dev/null
+++ b/src/main/java/org/junit/internal/SerializableMatcherDescription.java
@@ -0,0 +1,47 @@
+package org.junit.internal;
+
+import java.io.Serializable;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.StringDescription;
+
+/**
+ * This class exists solely to provide a serializable description of a matcher to be serialized as a field in
+ * {@link AssumptionViolatedException}. Being a {@link Throwable}, it is required to be {@link Serializable}, but most
+ * implementations of {@link Matcher} are not. This class works around that limitation as
+ * {@link AssumptionViolatedException} only every uses the description of the {@link Matcher}, while still retaining
+ * backwards compatibility with classes compiled against its class signature before 4.14 and/or deserialization of
+ * previously serialized instances.
+ */
+class SerializableMatcherDescription<T> extends BaseMatcher<T> implements Serializable {
+
+ private final String matcherDescription;
+
+ private SerializableMatcherDescription(Matcher<T> matcher) {
+ matcherDescription = StringDescription.asString(matcher);
+ }
+
+ public boolean matches(Object o) {
+ throw new UnsupportedOperationException("This Matcher implementation only captures the description");
+ }
+
+ public void describeTo(Description description) {
+ description.appendText(matcherDescription);
+ }
+
+ /**
+ * Factory method that checks to see if the matcher is already serializable.
+ * @param matcher the matcher to make serializable
+ * @return The provided matcher if it is null or already serializable,
+ * the SerializableMatcherDescription representation of it if it is not.
+ */
+ static <T> Matcher<T> asSerializableMatcher(Matcher<T> matcher) {
+ if (matcher == null || matcher instanceof Serializable) {
+ return matcher;
+ } else {
+ return new SerializableMatcherDescription<T>(matcher);
+ }
+ }
+}
diff --git a/src/main/java/org/junit/internal/SerializableValueDescription.java b/src/main/java/org/junit/internal/SerializableValueDescription.java
new file mode 100644
index 0000000..4d055d7
--- /dev/null
+++ b/src/main/java/org/junit/internal/SerializableValueDescription.java
@@ -0,0 +1,38 @@
+package org.junit.internal;
+
+import java.io.Serializable;
+
+/**
+ * This class exists solely to provide a serializable description of a value to be serialized as a field in
+ * {@link AssumptionViolatedException}. Being a {@link Throwable}, it is required to be {@link Serializable}, but a
+ * value of type Object provides no guarantee to be serializable. This class works around that limitation as
+ * {@link AssumptionViolatedException} only every uses the string representation of the value, while still retaining
+ * backwards compatibility with classes compiled against its class signature before 4.14 and/or deserialization of
+ * previously serialized instances.
+ */
+class SerializableValueDescription implements Serializable {
+ private final String value;
+
+ private SerializableValueDescription(Object value) {
+ this.value = String.valueOf(value);
+ }
+
+ /**
+ * Factory method that checks to see if the value is already serializable.
+ * @param value the value to make serializable
+ * @return The provided value if it is null or already serializable,
+ * the SerializableValueDescription representation of it if it is not.
+ */
+ static Object asSerializableValue(Object value) {
+ if (value == null || value instanceof Serializable) {
+ return value;
+ } else {
+ return new SerializableValueDescription(value);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+}
diff --git a/src/main/java/org/junit/internal/TextListener.java b/src/main/java/org/junit/internal/TextListener.java
index 9aa56c7..d548aeb 100644
--- a/src/main/java/org/junit/internal/TextListener.java
+++ b/src/main/java/org/junit/internal/TextListener.java
@@ -58,7 +58,7 @@ public class TextListener extends RunListener {
protected void printFailures(Result result) {
List<Failure> failures = result.getFailures();
- if (failures.size() == 0) {
+ if (failures.isEmpty()) {
return;
}
if (failures.size() == 1) {
@@ -74,7 +74,7 @@ public class TextListener extends RunListener {
protected void printFailure(Failure each, String prefix) {
getWriter().println(prefix + ") " + each.getTestHeader());
- getWriter().print(each.getTrace());
+ getWriter().print(each.getTrimmedTrace());
}
protected void printFooter(Result result) {
diff --git a/src/main/java/org/junit/internal/Throwables.java b/src/main/java/org/junit/internal/Throwables.java
index 86dceef..3f0f7a3 100644
--- a/src/main/java/org/junit/internal/Throwables.java
+++ b/src/main/java/org/junit/internal/Throwables.java
@@ -1,5 +1,17 @@
package org.junit.internal;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
/**
* Miscellaneous functions dealing with {@code Throwable}.
*
@@ -39,4 +51,223 @@ public final class Throwables {
private static <T extends Throwable> void rethrow(Throwable e) throws T {
throw (T) e;
}
+
+ /**
+ * Returns the stacktrace of the given Throwable as a String.
+ *
+ * @since 4.13
+ */
+ public static String getStacktrace(Throwable exception) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ exception.printStackTrace(writer);
+ return stringWriter.toString();
+ }
+
+ /**
+ * Gets a trimmed version of the stack trace of the given exception. Stack trace
+ * elements that are below the test method are filtered out.
+ *
+ * @return a trimmed stack trace, or the original trace if trimming wasn't possible
+ */
+ public static String getTrimmedStackTrace(Throwable exception) {
+ List<String> trimmedStackTraceLines = getTrimmedStackTraceLines(exception);
+ if (trimmedStackTraceLines.isEmpty()) {
+ return getFullStackTrace(exception);
+ }
+
+ StringBuilder result = new StringBuilder(exception.toString());
+ appendStackTraceLines(trimmedStackTraceLines, result);
+ appendStackTraceLines(getCauseStackTraceLines(exception), result);
+ return result.toString();
+ }
+
+ private static List<String> getTrimmedStackTraceLines(Throwable exception) {
+ List<StackTraceElement> stackTraceElements = Arrays.asList(exception.getStackTrace());
+ int linesToInclude = stackTraceElements.size();
+
+ State state = State.PROCESSING_OTHER_CODE;
+ for (StackTraceElement stackTraceElement : asReversedList(stackTraceElements)) {
+ state = state.processStackTraceElement(stackTraceElement);
+ if (state == State.DONE) {
+ List<String> trimmedLines = new ArrayList<String>(linesToInclude + 2);
+ trimmedLines.add("");
+ for (StackTraceElement each : stackTraceElements.subList(0, linesToInclude)) {
+ trimmedLines.add("\tat " + each);
+ }
+ if (exception.getCause() != null) {
+ trimmedLines.add("\t... " + (stackTraceElements.size() - trimmedLines.size()) + " trimmed");
+ }
+ return trimmedLines;
+ }
+ linesToInclude--;
+ }
+ return Collections.emptyList();
+ }
+
+ private static final Method getSuppressed = initGetSuppressed();
+
+ private static Method initGetSuppressed() {
+ try {
+ return Throwable.class.getMethod("getSuppressed");
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+
+ private static boolean hasSuppressed(Throwable exception) {
+ if (getSuppressed == null) {
+ return false;
+ }
+ try {
+ Throwable[] suppressed = (Throwable[]) getSuppressed.invoke(exception);
+ return suppressed.length != 0;
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ private static List<String> getCauseStackTraceLines(Throwable exception) {
+ if (exception.getCause() != null || hasSuppressed(exception)) {
+ String fullTrace = getFullStackTrace(exception);
+ BufferedReader reader = new BufferedReader(
+ new StringReader(fullTrace.substring(exception.toString().length())));
+ List<String> causedByLines = new ArrayList<String>();
+
+ try {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("Caused by: ") || line.trim().startsWith("Suppressed: ")) {
+ causedByLines.add(line);
+ while ((line = reader.readLine()) != null) {
+ causedByLines.add(line);
+ }
+ return causedByLines;
+ }
+ }
+ } catch (IOException e) {
+ // We should never get here, because we are reading from a StringReader
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+ private static String getFullStackTrace(Throwable exception) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ exception.printStackTrace(writer);
+ return stringWriter.toString();
+ }
+
+ private static void appendStackTraceLines(
+ List<String> stackTraceLines, StringBuilder destBuilder) {
+ for (String stackTraceLine : stackTraceLines) {
+ destBuilder.append(String.format("%s%n", stackTraceLine));
+ }
+ }
+
+ private static <T> List<T> asReversedList(final List<T> list) {
+ return new AbstractList<T>() {
+
+ @Override
+ public T get(int index) {
+ return list.get(list.size() - index - 1);
+ }
+
+ @Override
+ public int size() {
+ return list.size();
+ }
+ };
+ }
+
+ private enum State {
+ PROCESSING_OTHER_CODE {
+ @Override public State processLine(String methodName) {
+ if (isTestFrameworkMethod(methodName)) {
+ return PROCESSING_TEST_FRAMEWORK_CODE;
+ }
+ return this;
+ }
+ },
+ PROCESSING_TEST_FRAMEWORK_CODE {
+ @Override public State processLine(String methodName) {
+ if (isReflectionMethod(methodName)) {
+ return PROCESSING_REFLECTION_CODE;
+ } else if (isTestFrameworkMethod(methodName)) {
+ return this;
+ }
+ return PROCESSING_OTHER_CODE;
+ }
+ },
+ PROCESSING_REFLECTION_CODE {
+ @Override public State processLine(String methodName) {
+ if (isReflectionMethod(methodName)) {
+ return this;
+ } else if (isTestFrameworkMethod(methodName)) {
+ // This is here to handle TestCase.runBare() calling TestCase.runTest().
+ return PROCESSING_TEST_FRAMEWORK_CODE;
+ }
+ return DONE;
+ }
+ },
+ DONE {
+ @Override public State processLine(String methodName) {
+ return this;
+ }
+ };
+
+ /** Processes a stack trace element method name, possibly moving to a new state. */
+ protected abstract State processLine(String methodName);
+
+ /** Processes a stack trace element, possibly moving to a new state. */
+ public final State processStackTraceElement(StackTraceElement element) {
+ return processLine(element.getClassName() + "." + element.getMethodName() + "()");
+ }
+ }
+
+ private static final String[] TEST_FRAMEWORK_METHOD_NAME_PREFIXES = {
+ "org.junit.runner.",
+ "org.junit.runners.",
+ "org.junit.experimental.runners.",
+ "org.junit.internal.",
+ "junit.extensions",
+ "junit.framework",
+ "junit.runner",
+ "junit.textui",
+ };
+
+ private static final String[] TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES = {
+ "org.junit.internal.StackTracesTest",
+ };
+
+ private static boolean isTestFrameworkMethod(String methodName) {
+ return isMatchingMethod(methodName, TEST_FRAMEWORK_METHOD_NAME_PREFIXES) &&
+ !isMatchingMethod(methodName, TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES);
+ }
+
+ private static final String[] REFLECTION_METHOD_NAME_PREFIXES = {
+ "sun.reflect.",
+ "java.lang.reflect.",
+ "jdk.internal.reflect.",
+ "org.junit.rules.RunRules.<init>(",
+ "org.junit.rules.RunRules.applyAll(", // calls TestRules
+ "org.junit.runners.RuleContainer.apply(", // calls MethodRules & TestRules
+ "junit.framework.TestCase.runBare(", // runBare() directly calls setUp() and tearDown()
+ };
+
+ private static boolean isReflectionMethod(String methodName) {
+ return isMatchingMethod(methodName, REFLECTION_METHOD_NAME_PREFIXES);
+ }
+
+ private static boolean isMatchingMethod(String methodName, String[] methodNamePrefixes) {
+ for (String methodNamePrefix : methodNamePrefixes) {
+ if (methodName.startsWith(methodNamePrefix)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java b/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
index d86ec95..8704a54 100644
--- a/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
+++ b/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
@@ -9,6 +9,17 @@ import org.junit.runners.model.RunnerBuilder;
public class AllDefaultPossibilitiesBuilder extends RunnerBuilder {
private final boolean canUseSuiteMethod;
+ /**
+ * @since 4.13
+ */
+ public AllDefaultPossibilitiesBuilder() {
+ canUseSuiteMethod = true;
+ }
+
+ /**
+ * @deprecated used {@link #AllDefaultPossibilitiesBuilder()}.
+ */
+ @Deprecated
public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) {
this.canUseSuiteMethod = canUseSuiteMethod;
}
diff --git a/src/main/java/org/junit/internal/builders/JUnit4Builder.java b/src/main/java/org/junit/internal/builders/JUnit4Builder.java
index 6a00678..7959e75 100644
--- a/src/main/java/org/junit/internal/builders/JUnit4Builder.java
+++ b/src/main/java/org/junit/internal/builders/JUnit4Builder.java
@@ -1,12 +1,12 @@
package org.junit.internal.builders;
import org.junit.runner.Runner;
-import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.JUnit4;
import org.junit.runners.model.RunnerBuilder;
public class JUnit4Builder extends RunnerBuilder {
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable {
- return new BlockJUnit4ClassRunner(testClass);
+ return new JUnit4(testClass);
}
-} \ No newline at end of file
+}
diff --git a/src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java b/src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java
new file mode 100644
index 0000000..477b150
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java
@@ -0,0 +1,21 @@
+package org.junit.internal.management;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * No-op implementation of RuntimeMXBean when the platform doesn't provide it.
+ */
+class FakeRuntimeMXBean implements RuntimeMXBean {
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Always returns an empty list.
+ */
+ public List<String> getInputArguments() {
+ return Collections.emptyList();
+ }
+
+}
+
diff --git a/src/main/java/org/junit/internal/management/FakeThreadMXBean.java b/src/main/java/org/junit/internal/management/FakeThreadMXBean.java
new file mode 100644
index 0000000..893f2e3
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/FakeThreadMXBean.java
@@ -0,0 +1,27 @@
+package org.junit.internal.management;
+
+/**
+ * No-op implementation of ThreadMXBean when the platform doesn't provide it.
+ */
+final class FakeThreadMXBean implements ThreadMXBean {
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Always throws an {@link UnsupportedOperationException}
+ */
+ public long getThreadCpuTime(long id) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Always returns false.
+ */
+ public boolean isThreadCpuTimeSupported() {
+ return false;
+ }
+
+}
+
diff --git a/src/main/java/org/junit/internal/management/ManagementFactory.java b/src/main/java/org/junit/internal/management/ManagementFactory.java
new file mode 100644
index 0000000..5be1447
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/ManagementFactory.java
@@ -0,0 +1,77 @@
+package org.junit.internal.management;
+
+import org.junit.internal.Classes;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Reflective wrapper around {@link java.lang.management.ManagementFactory}
+ */
+public class ManagementFactory {
+ private static final class FactoryHolder {
+ private static final Class<?> MANAGEMENT_FACTORY_CLASS;
+
+ static {
+ Class<?> managementFactoryClass = null;
+ try {
+ managementFactoryClass = Classes.getClass("java.lang.management.ManagementFactory");
+ } catch (ClassNotFoundException e) {
+ // do nothing, managementFactoryClass will be none on failure
+ }
+ MANAGEMENT_FACTORY_CLASS = managementFactoryClass;
+ }
+
+ static Object getBeanObject(String methodName) {
+ if (MANAGEMENT_FACTORY_CLASS != null) {
+ try {
+ return MANAGEMENT_FACTORY_CLASS.getMethod(methodName).invoke(null);
+ } catch (IllegalAccessException e) {
+ // fallthrough
+ } catch (IllegalArgumentException e) {
+ // fallthrough
+ } catch (InvocationTargetException e) {
+ // fallthrough
+ } catch (NoSuchMethodException e) {
+ // fallthrough
+ } catch (SecurityException e) {
+ // fallthrough
+ }
+ }
+ return null;
+ }
+ }
+
+ private static final class RuntimeHolder {
+ private static final RuntimeMXBean RUNTIME_MX_BEAN =
+ getBean(FactoryHolder.getBeanObject("getRuntimeMXBean"));
+
+ private static final RuntimeMXBean getBean(Object runtimeMxBean) {
+ return runtimeMxBean != null
+ ? new ReflectiveRuntimeMXBean(runtimeMxBean) : new FakeRuntimeMXBean();
+ }
+ }
+
+ private static final class ThreadHolder {
+ private static final ThreadMXBean THREAD_MX_BEAN =
+ getBean(FactoryHolder.getBeanObject("getThreadMXBean"));
+
+ private static final ThreadMXBean getBean(Object threadMxBean) {
+ return threadMxBean != null
+ ? new ReflectiveThreadMXBean(threadMxBean) : new FakeThreadMXBean();
+ }
+ }
+
+ /**
+ * @see java.lang.management.ManagementFactory#getRuntimeMXBean()
+ */
+ public static RuntimeMXBean getRuntimeMXBean() {
+ return RuntimeHolder.RUNTIME_MX_BEAN;
+ }
+
+ /**
+ * @see java.lang.management.ManagementFactory#getThreadMXBean()
+ */
+ public static ThreadMXBean getThreadMXBean() {
+ return ThreadHolder.THREAD_MX_BEAN;
+ }
+}
diff --git a/src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java b/src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java
new file mode 100644
index 0000000..289587a
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java
@@ -0,0 +1,61 @@
+package org.junit.internal.management;
+
+import org.junit.internal.Classes;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Implementation of {@link RuntimeMXBean} using the JVM reflectively.
+ */
+final class ReflectiveRuntimeMXBean implements RuntimeMXBean {
+ private final Object runtimeMxBean;
+
+ private static final class Holder {
+ private static final Method getInputArgumentsMethod;
+ static {
+ Method inputArguments = null;
+ try {
+ Class<?> threadMXBeanClass = Classes.getClass("java.lang.management.RuntimeMXBean");
+ inputArguments = threadMXBeanClass.getMethod("getInputArguments");
+ } catch (ClassNotFoundException e) {
+ // do nothing, input arguments will be null on failure
+ } catch (NoSuchMethodException e) {
+ // do nothing, input arguments will be null on failure
+ } catch (SecurityException e) {
+ // do nothing, input arguments will be null on failure
+ }
+ getInputArgumentsMethod = inputArguments;
+ }
+ }
+
+ ReflectiveRuntimeMXBean(Object runtimeMxBean) {
+ super();
+ this.runtimeMxBean = runtimeMxBean;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public List<String> getInputArguments() {
+ if (Holder.getInputArgumentsMethod != null) {
+ try {
+ return (List<String>) Holder.getInputArgumentsMethod.invoke(runtimeMxBean);
+ } catch (ClassCastException e) { // no multi-catch with source level 6
+ // fallthrough
+ } catch (IllegalAccessException e) {
+ // fallthrough
+ } catch (IllegalArgumentException e) {
+ // fallthrough
+ } catch (InvocationTargetException e) {
+ // fallthrough
+ }
+ }
+ return Collections.emptyList();
+ }
+
+}
+
diff --git a/src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java b/src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java
new file mode 100644
index 0000000..bc741be
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java
@@ -0,0 +1,92 @@
+package org.junit.internal.management;
+
+import org.junit.internal.Classes;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Implementation of {@link ThreadMXBean} using the JVM reflectively.
+ */
+final class ReflectiveThreadMXBean implements ThreadMXBean {
+ private final Object threadMxBean;
+
+
+ private static final class Holder {
+ static final Method getThreadCpuTimeMethod;
+ static final Method isThreadCpuTimeSupportedMethod;
+
+ private static final String FAILURE_MESSAGE = "Unable to access ThreadMXBean";
+
+ static {
+ Method threadCpuTime = null;
+ Method threadCpuTimeSupported = null;
+ try {
+ Class<?> threadMXBeanClass = Classes.getClass("java.lang.management.ThreadMXBean");
+ threadCpuTime = threadMXBeanClass.getMethod("getThreadCpuTime", long.class);
+ threadCpuTimeSupported = threadMXBeanClass.getMethod("isThreadCpuTimeSupported");
+ } catch (ClassNotFoundException e) {
+ // do nothing, the methods will be null on failure
+ } catch (NoSuchMethodException e) {
+ // do nothing, the methods will be null on failure
+ } catch (SecurityException e) {
+ // do nothing, the methods will be null on failure
+ }
+ getThreadCpuTimeMethod = threadCpuTime;
+ isThreadCpuTimeSupportedMethod = threadCpuTimeSupported;
+ }
+ }
+
+ ReflectiveThreadMXBean(Object threadMxBean) {
+ super();
+ this.threadMxBean = threadMxBean;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getThreadCpuTime(long id) {
+ if (Holder.getThreadCpuTimeMethod != null) {
+ Exception error = null;
+ try {
+ return (Long) Holder.getThreadCpuTimeMethod.invoke(threadMxBean, id);
+ } catch (ClassCastException e) {
+ error = e;
+ // fallthrough
+ } catch (IllegalAccessException e) {
+ error = e;
+ // fallthrough
+ } catch (IllegalArgumentException e) {
+ error = e;
+ // fallthrough
+ } catch (InvocationTargetException e) {
+ error = e;
+ // fallthrough
+ }
+ throw new UnsupportedOperationException(Holder.FAILURE_MESSAGE, error);
+ }
+ throw new UnsupportedOperationException(Holder.FAILURE_MESSAGE);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isThreadCpuTimeSupported() {
+ if (Holder.isThreadCpuTimeSupportedMethod != null) {
+ try {
+ return (Boolean) Holder.isThreadCpuTimeSupportedMethod.invoke(threadMxBean);
+ } catch (ClassCastException e) {
+ // fallthrough
+ } catch (IllegalAccessException e) {
+ // fallthrough
+ } catch (IllegalArgumentException e) {
+ // fallthrough
+ } catch (InvocationTargetException e) {
+ // fallthrough
+ }
+ }
+ return false;
+ }
+
+}
+
diff --git a/src/main/java/org/junit/internal/management/RuntimeMXBean.java b/src/main/java/org/junit/internal/management/RuntimeMXBean.java
new file mode 100644
index 0000000..84f8861
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/RuntimeMXBean.java
@@ -0,0 +1,14 @@
+package org.junit.internal.management;
+
+import java.util.List;
+
+/**
+ * Wrapper for {@link java.lang.management.RuntimeMXBean}.
+ */
+public interface RuntimeMXBean {
+
+ /**
+ * @see java.lang.management.RuntimeMXBean#getInputArguments()
+ */
+ List<String> getInputArguments();
+}
diff --git a/src/main/java/org/junit/internal/management/ThreadMXBean.java b/src/main/java/org/junit/internal/management/ThreadMXBean.java
new file mode 100644
index 0000000..f9225c9
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/ThreadMXBean.java
@@ -0,0 +1,17 @@
+package org.junit.internal.management;
+
+/**
+ * Wrapper for {@link java.lang.management.ThreadMXBean}.
+ */
+public interface ThreadMXBean {
+ /**
+ * @see java.lang.management.ThreadMXBean#getThreadCpuTime(long)
+ */
+ long getThreadCpuTime(long id);
+
+ /**
+ * @see java.lang.management.ThreadMXBean#isThreadCpuTimeSupported()
+ */
+ boolean isThreadCpuTimeSupported();
+}
+
diff --git a/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java b/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java
index 5d45ba3..93a6827 100644
--- a/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java
+++ b/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java
@@ -1,12 +1,11 @@
package org.junit.internal.matchers;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
+import org.junit.internal.Throwables;
+
/**
* A matcher that delegates to throwableMatcher and in addition appends the
* stacktrace of the actual Throwable in case of a mismatch.
@@ -37,9 +36,7 @@ public class StacktracePrintingMatcher<T extends Throwable> extends
}
private String readStacktrace(Throwable throwable) {
- StringWriter stringWriter = new StringWriter();
- throwable.printStackTrace(new PrintWriter(stringWriter));
- return stringWriter.toString();
+ return Throwables.getStacktrace(throwable);
}
@Factory
diff --git a/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java b/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java
index 22ce8bd..6e2ff5e 100644
--- a/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java
+++ b/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java
@@ -14,9 +14,9 @@ import org.hamcrest.TypeSafeMatcher;
public class ThrowableCauseMatcher<T extends Throwable> extends
TypeSafeMatcher<T> {
- private final Matcher<? extends Throwable> causeMatcher;
+ private final Matcher<?> causeMatcher;
- public ThrowableCauseMatcher(Matcher<? extends Throwable> causeMatcher) {
+ public ThrowableCauseMatcher(Matcher<?> causeMatcher) {
this.causeMatcher = causeMatcher;
}
@@ -44,7 +44,7 @@ public class ThrowableCauseMatcher<T extends Throwable> extends
* @param <T> type of the outer exception
*/
@Factory
- public static <T extends Throwable> Matcher<T> hasCause(final Matcher<? extends Throwable> matcher) {
+ public static <T extends Throwable> Matcher<T> hasCause(final Matcher<?> matcher) {
return new ThrowableCauseMatcher<T>(matcher);
}
} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
index 4e2cc12..fb25982 100644
--- a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
+++ b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
@@ -40,7 +40,7 @@ public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
}
private static boolean isMatchesSafelyMethod(Method method) {
- return method.getName().equals("matchesSafely")
+ return "matchesSafely".equals(method.getName())
&& method.getParameterTypes().length == 1
&& !method.isSynthetic();
}
diff --git a/src/main/java/org/junit/internal/requests/ClassRequest.java b/src/main/java/org/junit/internal/requests/ClassRequest.java
index 3d6b100..d60e360 100644
--- a/src/main/java/org/junit/internal/requests/ClassRequest.java
+++ b/src/main/java/org/junit/internal/requests/ClassRequest.java
@@ -1,20 +1,18 @@
package org.junit.internal.requests;
import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
-import org.junit.runner.Request;
+import org.junit.internal.builders.SuiteMethodBuilder;
import org.junit.runner.Runner;
+import org.junit.runners.model.RunnerBuilder;
-public class ClassRequest extends Request {
- private final Object runnerLock = new Object();
-
+public class ClassRequest extends MemoizingRequest {
/*
* We have to use the f prefix, because IntelliJ's JUnit4IdeaTestRunner uses
* reflection to access this field. See
- * https://github.com/junit-team/junit/issues/960
+ * https://github.com/junit-team/junit4/issues/960
*/
private final Class<?> fTestClass;
private final boolean canUseSuiteMethod;
- private volatile Runner runner;
public ClassRequest(Class<?> testClass, boolean canUseSuiteMethod) {
this.fTestClass = testClass;
@@ -26,14 +24,31 @@ public class ClassRequest extends Request {
}
@Override
- public Runner getRunner() {
- if (runner == null) {
- synchronized (runnerLock) {
- if (runner == null) {
- runner = new AllDefaultPossibilitiesBuilder(canUseSuiteMethod).safeRunnerForClass(fTestClass);
- }
+ protected Runner createRunner() {
+ return new CustomAllDefaultPossibilitiesBuilder().safeRunnerForClass(fTestClass);
+ }
+
+ private class CustomAllDefaultPossibilitiesBuilder extends AllDefaultPossibilitiesBuilder {
+
+ @Override
+ protected RunnerBuilder suiteMethodBuilder() {
+ return new CustomSuiteMethodBuilder();
+ }
+ }
+
+ /*
+ * Customization of {@link SuiteMethodBuilder} that prevents use of the
+ * suite method when creating a runner for fTestClass when canUseSuiteMethod
+ * is false.
+ */
+ private class CustomSuiteMethodBuilder extends SuiteMethodBuilder {
+
+ @Override
+ public Runner runnerForClass(Class<?> testClass) throws Throwable {
+ if (testClass == fTestClass && !canUseSuiteMethod) {
+ return null;
}
+ return super.runnerForClass(testClass);
}
- return runner;
}
} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/requests/FilterRequest.java b/src/main/java/org/junit/internal/requests/FilterRequest.java
index 066cba3..5f00399 100644
--- a/src/main/java/org/junit/internal/requests/FilterRequest.java
+++ b/src/main/java/org/junit/internal/requests/FilterRequest.java
@@ -14,7 +14,7 @@ public final class FilterRequest extends Request {
/*
* We have to use the f prefix, because IntelliJ's JUnit4IdeaTestRunner uses
* reflection to access this field. See
- * https://github.com/junit-team/junit/issues/960
+ * https://github.com/junit-team/junit4/issues/960
*/
private final Filter fFilter;
diff --git a/src/main/java/org/junit/internal/requests/MemoizingRequest.java b/src/main/java/org/junit/internal/requests/MemoizingRequest.java
new file mode 100644
index 0000000..191c230
--- /dev/null
+++ b/src/main/java/org/junit/internal/requests/MemoizingRequest.java
@@ -0,0 +1,30 @@
+package org.junit.internal.requests;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.junit.runner.Request;
+import org.junit.runner.Runner;
+
+abstract class MemoizingRequest extends Request {
+ private final Lock runnerLock = new ReentrantLock();
+ private volatile Runner runner;
+
+ @Override
+ public final Runner getRunner() {
+ if (runner == null) {
+ runnerLock.lock();
+ try {
+ if (runner == null) {
+ runner = createRunner();
+ }
+ } finally {
+ runnerLock.unlock();
+ }
+ }
+ return runner;
+ }
+
+ /** Creates the {@link Runner} to return from {@link #getRunner()}. Called at most once. */
+ protected abstract Runner createRunner();
+}
diff --git a/src/main/java/org/junit/internal/requests/OrderingRequest.java b/src/main/java/org/junit/internal/requests/OrderingRequest.java
new file mode 100644
index 0000000..441e595
--- /dev/null
+++ b/src/main/java/org/junit/internal/requests/OrderingRequest.java
@@ -0,0 +1,29 @@
+package org.junit.internal.requests;
+
+import org.junit.internal.runners.ErrorReportingRunner;
+import org.junit.runner.Request;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.InvalidOrderingException;
+import org.junit.runner.manipulation.Ordering;
+
+/** @since 4.13 */
+public class OrderingRequest extends MemoizingRequest {
+ private final Request request;
+ private final Ordering ordering;
+
+ public OrderingRequest(Request request, Ordering ordering) {
+ this.request = request;
+ this.ordering = ordering;
+ }
+
+ @Override
+ protected Runner createRunner() {
+ Runner runner = request.getRunner();
+ try {
+ ordering.apply(runner);
+ } catch (InvalidOrderingException e) {
+ return new ErrorReportingRunner(ordering.getClass(), e);
+ }
+ return runner;
+ }
+}
diff --git a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
index 1d32beb..f52abab 100644
--- a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
+++ b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
@@ -1,33 +1,44 @@
package org.junit.internal.runners;
import java.lang.reflect.InvocationTargetException;
-import java.util.Arrays;
import java.util.List;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.InvalidTestClassError;
import org.junit.runners.model.InitializationError;
+import static java.util.Collections.singletonList;
+
public class ErrorReportingRunner extends Runner {
private final List<Throwable> causes;
- private final Class<?> testClass;
+ private final String classNames;
public ErrorReportingRunner(Class<?> testClass, Throwable cause) {
- if (testClass == null) {
- throw new NullPointerException("Test class cannot be null");
+ this(cause, testClass);
+ }
+
+ public ErrorReportingRunner(Throwable cause, Class<?>... testClasses) {
+ if (testClasses == null || testClasses.length == 0) {
+ throw new NullPointerException("Test classes cannot be null or empty");
}
- this.testClass = testClass;
+ for (Class<?> testClass : testClasses) {
+ if (testClass == null) {
+ throw new NullPointerException("Test class cannot be null");
+ }
+ }
+ classNames = getClassNames(testClasses);
causes = getCauses(cause);
}
-
+
@Override
public Description getDescription() {
- Description description = Description.createSuiteDescription(testClass);
+ Description description = Description.createSuiteDescription(classNames);
for (Throwable each : causes) {
- description.addChild(describeCause(each));
+ description.addChild(describeCause());
}
return description;
}
@@ -39,11 +50,25 @@ public class ErrorReportingRunner extends Runner {
}
}
+ private String getClassNames(Class<?>... testClasses) {
+ final StringBuilder builder = new StringBuilder();
+ for (Class<?> testClass : testClasses) {
+ if (builder.length() != 0) {
+ builder.append(", ");
+ }
+ builder.append(testClass.getName());
+ }
+ return builder.toString();
+ }
+
@SuppressWarnings("deprecation")
private List<Throwable> getCauses(Throwable cause) {
if (cause instanceof InvocationTargetException) {
return getCauses(cause.getCause());
}
+ if (cause instanceof InvalidTestClassError) {
+ return singletonList(cause);
+ }
if (cause instanceof InitializationError) {
return ((InitializationError) cause).getCauses();
}
@@ -51,16 +76,15 @@ public class ErrorReportingRunner extends Runner {
return ((org.junit.internal.runners.InitializationError) cause)
.getCauses();
}
- return Arrays.asList(cause);
+ return singletonList(cause);
}
- private Description describeCause(Throwable child) {
- return Description.createTestDescription(testClass,
- "initializationError");
+ private Description describeCause() {
+ return Description.createTestDescription(classNames, "initializationError");
}
private void runCause(Throwable child, RunNotifier notifier) {
- Description description = describeCause(child);
+ Description description = describeCause();
notifier.fireTestStarted(description);
notifier.fireTestFailure(new Failure(description, child));
notifier.fireTestFinished(description);
diff --git a/src/main/java/org/junit/internal/runners/InitializationError.java b/src/main/java/org/junit/internal/runners/InitializationError.java
index 52065ec..484f58d 100644
--- a/src/main/java/org/junit/internal/runners/InitializationError.java
+++ b/src/main/java/org/junit/internal/runners/InitializationError.java
@@ -15,7 +15,7 @@ public class InitializationError extends Exception {
/*
* 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 List<Throwable> fErrors;
diff --git a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
index 631fcf2..0d51541 100644
--- a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
+++ b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
@@ -1,5 +1,8 @@
package org.junit.internal.runners;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
import junit.extensions.TestDecorator;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
@@ -12,15 +15,16 @@ import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.Orderer;
+import org.junit.runner.manipulation.InvalidOrderingException;
import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.manipulation.Orderable;
import org.junit.runner.manipulation.Sortable;
import org.junit.runner.manipulation.Sorter;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-public class JUnit38ClassRunner extends Runner implements Filterable, Sortable {
+public class JUnit38ClassRunner extends Runner implements Filterable, Orderable {
private static final class OldTestClassAdaptingListener implements
TestListener {
private final RunNotifier notifier;
@@ -170,6 +174,18 @@ public class JUnit38ClassRunner extends Runner implements Filterable, Sortable {
}
}
+ /**
+ * {@inheritDoc}
+ *
+ * @since 4.13
+ */
+ public void order(Orderer orderer) throws InvalidOrderingException {
+ if (getTest() instanceof Orderable) {
+ Orderable adapter = (Orderable) getTest();
+ adapter.order(orderer);
+ }
+ }
+
private void setTest(Test test) {
this.test = test;
}
diff --git a/src/main/java/org/junit/internal/runners/MethodValidator.java b/src/main/java/org/junit/internal/runners/MethodValidator.java
index ba9c9d1..e656ee5 100644
--- a/src/main/java/org/junit/internal/runners/MethodValidator.java
+++ b/src/main/java/org/junit/internal/runners/MethodValidator.java
@@ -86,7 +86,7 @@ public class MethodValidator {
}
if (each.getReturnType() != Void.TYPE) {
errors.add(new Exception("Method " + each.getName()
- + " should be void"));
+ + "should have a return type of void"));
}
if (each.getParameterTypes().length != 0) {
errors.add(new Exception("Method " + each.getName()
diff --git a/src/main/java/org/junit/internal/runners/TestClass.java b/src/main/java/org/junit/internal/runners/TestClass.java
index 1abaeea..6d24f4f 100644
--- a/src/main/java/org/junit/internal/runners/TestClass.java
+++ b/src/main/java/org/junit/internal/runners/TestClass.java
@@ -85,7 +85,7 @@ public class TestClass {
}
private List<Class<?>> getSuperClasses(Class<?> testClass) {
- ArrayList<Class<?>> results = new ArrayList<Class<?>>();
+ List<Class<?>> results = new ArrayList<Class<?>>();
Class<?> current = testClass;
while (current != null) {
results.add(current);
diff --git a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
index e094809..c5a0764 100644
--- a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
+++ b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
@@ -45,4 +45,27 @@ public class EachTestNotifier {
public void fireTestIgnored() {
notifier.fireTestIgnored(description);
}
+
+ /**
+ * Calls {@link RunNotifier#fireTestSuiteStarted(Description)}, passing the
+ * {@link Description} that was passed to the {@code EachTestNotifier} constructor.
+ * This should be called when a test suite is about to be started.
+ * @see RunNotifier#fireTestSuiteStarted(Description)
+ * @since 4.13
+ */
+ public void fireTestSuiteStarted() {
+ notifier.fireTestSuiteStarted(description);
+ }
+
+ /**
+ * Calls {@link RunNotifier#fireTestSuiteFinished(Description)}, passing the
+ * {@link Description} that was passed to the {@code EachTestNotifier} constructor.
+ * This should be called when a test suite has finished, whether the test suite succeeds
+ * or fails.
+ * @see RunNotifier#fireTestSuiteFinished(Description)
+ * @since 4.13
+ */
+ public void fireTestSuiteFinished() {
+ notifier.fireTestSuiteFinished(description);
+ }
} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/rules/ValidationError.java b/src/main/java/org/junit/internal/runners/rules/ValidationError.java
index d1af8ae..31bd660 100644
--- a/src/main/java/org/junit/internal/runners/rules/ValidationError.java
+++ b/src/main/java/org/junit/internal/runners/rules/ValidationError.java
@@ -5,6 +5,9 @@ import org.junit.runners.model.FrameworkMember;
import java.lang.annotation.Annotation;
class ValidationError extends Exception {
+
+ private static final long serialVersionUID = 3176511008672645574L;
+
public ValidationError(FrameworkMember<?> member, Class<? extends Annotation> annotation, String suffix) {
super(String.format("The @%s '%s' %s", annotation.getSimpleName(), member.getName(), suffix));
}
diff --git a/src/main/java/org/junit/internal/runners/statements/ExpectException.java b/src/main/java/org/junit/internal/runners/statements/ExpectException.java
index d0636bd..9a2a952 100644
--- a/src/main/java/org/junit/internal/runners/statements/ExpectException.java
+++ b/src/main/java/org/junit/internal/runners/statements/ExpectException.java
@@ -19,7 +19,9 @@ public class ExpectException extends Statement {
next.evaluate();
complete = true;
} catch (AssumptionViolatedException e) {
- throw e;
+ if (!expected.isAssignableFrom(e.getClass())) {
+ throw e;
+ }
} catch (Throwable e) {
if (!expected.isAssignableFrom(e.getClass())) {
String message = "Unexpected exception, expected<"
diff --git a/src/main/java/org/junit/internal/runners/statements/RunAfters.java b/src/main/java/org/junit/internal/runners/statements/RunAfters.java
index 7512a7d..5e56c33 100644
--- a/src/main/java/org/junit/internal/runners/statements/RunAfters.java
+++ b/src/main/java/org/junit/internal/runners/statements/RunAfters.java
@@ -30,7 +30,7 @@ public class RunAfters extends Statement {
} finally {
for (FrameworkMethod each : afters) {
try {
- each.invokeExplosively(target);
+ invokeMethod(each);
} catch (Throwable e) {
errors.add(e);
}
@@ -38,4 +38,11 @@ public class RunAfters extends Statement {
}
MultipleFailureException.assertEmpty(errors);
}
+
+ /**
+ * @since 4.13
+ */
+ protected void invokeMethod(FrameworkMethod method) throws Throwable {
+ method.invokeExplosively(target);
+ }
} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/statements/RunBefores.java b/src/main/java/org/junit/internal/runners/statements/RunBefores.java
index 238fbe7..bd835c7 100644
--- a/src/main/java/org/junit/internal/runners/statements/RunBefores.java
+++ b/src/main/java/org/junit/internal/runners/statements/RunBefores.java
@@ -21,8 +21,15 @@ public class RunBefores extends Statement {
@Override
public void evaluate() throws Throwable {
for (FrameworkMethod before : befores) {
- before.invokeExplosively(target);
+ invokeMethod(before);
}
next.evaluate();
}
+
+ /**
+ * @since 4.13
+ */
+ protected void invokeMethod(FrameworkMethod method) throws Throwable {
+ method.invokeExplosively(target);
+ }
} \ No newline at end of file