From aa1524950fb5fbffbb61d23c5442984cb731a1eb Mon Sep 17 00:00:00 2001
From: Pete Bentley
Date: Sun, 21 Feb 2021 18:26:28 +0000
Subject: Upgrade external/junit to 4.13.2
Contains just the changes from 4.12 to 4.13.2 and undoes local
Android changes. Will re-patch those in in subsequent CLs.
Bug: 129054170
Test: m
Change-Id: I5ec909df6840f6c54cbab9c509c5addaee3f94e6
---
src/main/java/org/junit/Assert.java | 170 ++++++---
src/main/java/org/junit/Assume.java | 19 +-
.../org/junit/AssumptionViolatedException.java | 4 +-
src/main/java/org/junit/ClassRule.java | 33 +-
src/main/java/org/junit/ComparisonFailure.java | 2 +-
src/main/java/org/junit/Rule.java | 39 +-
src/main/java/org/junit/Test.java | 39 +-
.../org/junit/TestCouldNotBeSkippedException.java | 19 +
.../junit/experimental/categories/Categories.java | 108 +++---
.../categories/CategoryFilterFactory.java | 6 +-
.../org/junit/experimental/max/MaxHistory.java | 15 +-
.../experimental/results/PrintableResult.java | 9 +
.../junit/experimental/results/ResultMatchers.java | 35 +-
.../theories/ParametersSuppliedBy.java | 2 +-
.../org/junit/experimental/theories/Theories.java | 11 +-
.../theories/internal/Assignments.java | 11 +-
.../java/org/junit/function/ThrowingRunnable.java | 14 +
.../org/junit/internal/ArrayComparisonFailure.java | 13 +-
.../internal/AssumptionViolatedException.java | 30 +-
src/main/java/org/junit/internal/Checks.java | 37 ++
src/main/java/org/junit/internal/Classes.java | 28 +-
.../org/junit/internal/ComparisonCriteria.java | 89 ++++-
.../internal/SerializableMatcherDescription.java | 47 +++
.../internal/SerializableValueDescription.java | 38 ++
src/main/java/org/junit/internal/TextListener.java | 4 +-
src/main/java/org/junit/internal/Throwables.java | 231 ++++++++++++
.../builders/AllDefaultPossibilitiesBuilder.java | 11 +
.../org/junit/internal/builders/JUnit4Builder.java | 6 +-
.../internal/management/FakeRuntimeMXBean.java | 21 ++
.../internal/management/FakeThreadMXBean.java | 27 ++
.../internal/management/ManagementFactory.java | 77 ++++
.../management/ReflectiveRuntimeMXBean.java | 61 ++++
.../management/ReflectiveThreadMXBean.java | 92 +++++
.../junit/internal/management/RuntimeMXBean.java | 14 +
.../junit/internal/management/ThreadMXBean.java | 17 +
.../matchers/StacktracePrintingMatcher.java | 9 +-
.../internal/matchers/ThrowableCauseMatcher.java | 6 +-
.../junit/internal/matchers/TypeSafeMatcher.java | 2 +-
.../org/junit/internal/requests/ClassRequest.java | 41 ++-
.../org/junit/internal/requests/FilterRequest.java | 2 +-
.../junit/internal/requests/MemoizingRequest.java | 30 ++
.../junit/internal/requests/OrderingRequest.java | 29 ++
.../internal/runners/ErrorReportingRunner.java | 50 ++-
.../internal/runners/InitializationError.java | 2 +-
.../junit/internal/runners/JUnit38ClassRunner.java | 22 +-
.../junit/internal/runners/MethodValidator.java | 2 +-
.../java/org/junit/internal/runners/TestClass.java | 2 +-
.../internal/runners/model/EachTestNotifier.java | 23 ++
.../internal/runners/rules/ValidationError.java | 3 +
.../runners/statements/ExpectException.java | 4 +-
.../internal/runners/statements/FailOnTimeout.java | 155 +++++++-
.../internal/runners/statements/RunAfters.java | 9 +-
.../internal/runners/statements/RunBefores.java | 9 +-
.../java/org/junit/matchers/JUnitMatchers.java | 4 +-
src/main/java/org/junit/rules/DisableOnDebug.java | 125 +++++++
src/main/java/org/junit/rules/ErrorCollector.java | 38 +-
.../java/org/junit/rules/ExpectedException.java | 57 +--
.../java/org/junit/rules/ExternalResource.java | 15 +-
src/main/java/org/junit/rules/MethodRule.java | 16 +-
src/main/java/org/junit/rules/RuleChain.java | 50 ++-
src/main/java/org/junit/rules/Stopwatch.java | 2 +-
src/main/java/org/junit/rules/TemporaryFolder.java | 277 +++++++++++---
src/main/java/org/junit/rules/TestName.java | 2 +-
src/main/java/org/junit/rules/TestWatcher.java | 12 +-
src/main/java/org/junit/rules/Timeout.java | 37 +-
src/main/java/org/junit/runner/Computer.java | 12 +-
src/main/java/org/junit/runner/Describable.java | 2 +-
src/main/java/org/junit/runner/Description.java | 13 +-
src/main/java/org/junit/runner/FilterFactory.java | 3 +-
.../junit/runner/JUnitCommandLineParseResult.java | 8 +-
src/main/java/org/junit/runner/OrderWith.java | 28 ++
.../java/org/junit/runner/OrderWithValidator.java | 38 ++
src/main/java/org/junit/runner/Request.java | 58 ++-
src/main/java/org/junit/runner/Result.java | 31 +-
.../junit/runner/manipulation/Alphanumeric.java | 27 ++
.../manipulation/InvalidOrderingException.java | 21 ++
.../org/junit/runner/manipulation/Orderable.java | 21 ++
.../org/junit/runner/manipulation/Orderer.java | 62 ++++
.../org/junit/runner/manipulation/Ordering.java | 172 +++++++++
.../org/junit/runner/manipulation/Sortable.java | 2 +-
.../java/org/junit/runner/manipulation/Sorter.java | 54 ++-
.../org/junit/runner/notification/Failure.java | 23 +-
.../org/junit/runner/notification/RunListener.java | 28 ++
.../org/junit/runner/notification/RunNotifier.java | 41 ++-
.../notification/SynchronizedRunListener.java | 33 +-
.../org/junit/runners/BlockJUnit4ClassRunner.java | 172 +++++----
src/main/java/org/junit/runners/JUnit4.java | 3 +-
src/main/java/org/junit/runners/Parameterized.java | 405 ++++++++++++++-------
src/main/java/org/junit/runners/ParentRunner.java | 176 +++++++--
src/main/java/org/junit/runners/RuleContainer.java | 113 ++++++
src/main/java/org/junit/runners/Suite.java | 4 +-
.../org/junit/runners/model/FrameworkField.java | 21 +-
.../org/junit/runners/model/FrameworkMember.java | 24 +-
.../org/junit/runners/model/FrameworkMethod.java | 14 +
.../junit/runners/model/InitializationError.java | 2 +-
.../junit/runners/model/InvalidTestClassError.java | 39 ++
.../junit/runners/model/MemberValueConsumer.java | 18 +
.../runners/model/MultipleFailureException.java | 41 ++-
.../org/junit/runners/model/RunnerBuilder.java | 30 +-
.../java/org/junit/runners/model/TestClass.java | 57 ++-
.../BlockJUnit4ClassRunnerWithParameters.java | 94 ++++-
.../parameterized/ParametersRunnerFactory.java | 2 +-
.../runners/parameterized/TestWithParameters.java | 7 +-
.../validator/AnnotationValidatorFactory.java | 3 -
.../org/junit/validator/AnnotationsValidator.java | 4 +-
.../org/junit/validator/TestClassValidator.java | 2 +-
.../java/org/junit/validator/ValidateWith.java | 3 +
107 files changed, 3674 insertions(+), 651 deletions(-)
mode change 100755 => 100644 src/main/java/org/junit/Assert.java
create mode 100644 src/main/java/org/junit/TestCouldNotBeSkippedException.java
create mode 100644 src/main/java/org/junit/function/ThrowingRunnable.java
create mode 100644 src/main/java/org/junit/internal/Checks.java
create mode 100644 src/main/java/org/junit/internal/SerializableMatcherDescription.java
create mode 100644 src/main/java/org/junit/internal/SerializableValueDescription.java
create mode 100644 src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java
create mode 100644 src/main/java/org/junit/internal/management/FakeThreadMXBean.java
create mode 100644 src/main/java/org/junit/internal/management/ManagementFactory.java
create mode 100644 src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java
create mode 100644 src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java
create mode 100644 src/main/java/org/junit/internal/management/RuntimeMXBean.java
create mode 100644 src/main/java/org/junit/internal/management/ThreadMXBean.java
create mode 100644 src/main/java/org/junit/internal/requests/MemoizingRequest.java
create mode 100644 src/main/java/org/junit/internal/requests/OrderingRequest.java
create mode 100644 src/main/java/org/junit/rules/DisableOnDebug.java
create mode 100644 src/main/java/org/junit/runner/OrderWith.java
create mode 100644 src/main/java/org/junit/runner/OrderWithValidator.java
create mode 100644 src/main/java/org/junit/runner/manipulation/Alphanumeric.java
create mode 100644 src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java
create mode 100644 src/main/java/org/junit/runner/manipulation/Orderable.java
create mode 100644 src/main/java/org/junit/runner/manipulation/Orderer.java
create mode 100644 src/main/java/org/junit/runner/manipulation/Ordering.java
mode change 100755 => 100644 src/main/java/org/junit/runners/ParentRunner.java
create mode 100644 src/main/java/org/junit/runners/RuleContainer.java
create mode 100644 src/main/java/org/junit/runners/model/InvalidTestClassError.java
create mode 100644 src/main/java/org/junit/runners/model/MemberValueConsumer.java
mode change 100755 => 100644 src/main/java/org/junit/runners/model/TestClass.java
(limited to 'src/main/java/org/junit')
diff --git a/src/main/java/org/junit/Assert.java b/src/main/java/org/junit/Assert.java
old mode 100755
new mode 100644
index d7deb06..65bbc9d
--- a/src/main/java/org/junit/Assert.java
+++ b/src/main/java/org/junit/Assert.java
@@ -2,6 +2,7 @@ package org.junit;
import org.hamcrest.Matcher;
import org.hamcrest.MatcherAssert;
+import org.junit.function.ThrowingRunnable;
import org.junit.internal.ArrayComparisonFailure;
import org.junit.internal.ExactComparisonCriteria;
import org.junit.internal.InexactComparisonCriteria;
@@ -36,7 +37,7 @@ public class Assert {
* okay)
* @param condition condition to be checked
*/
- static public void assertTrue(String message, boolean condition) {
+ public static void assertTrue(String message, boolean condition) {
if (!condition) {
fail(message);
}
@@ -48,7 +49,7 @@ public class Assert {
*
* @param condition condition to be checked
*/
- static public void assertTrue(boolean condition) {
+ public static void assertTrue(boolean condition) {
assertTrue(null, condition);
}
@@ -60,7 +61,7 @@ public class Assert {
* okay)
* @param condition condition to be checked
*/
- static public void assertFalse(String message, boolean condition) {
+ public static void assertFalse(String message, boolean condition) {
assertTrue(message, !condition);
}
@@ -70,7 +71,7 @@ public class Assert {
*
* @param condition condition to be checked
*/
- static public void assertFalse(boolean condition) {
+ public static void assertFalse(boolean condition) {
assertFalse(null, condition);
}
@@ -81,7 +82,7 @@ public class Assert {
* okay)
* @see AssertionError
*/
- static public void fail(String message) {
+ public static void fail(String message) {
if (message == null) {
throw new AssertionError();
}
@@ -91,7 +92,7 @@ public class Assert {
/**
* Fails a test with no message.
*/
- static public void fail() {
+ public static void fail() {
fail(null);
}
@@ -106,11 +107,12 @@ public class Assert {
* @param expected expected value
* @param actual actual value
*/
- static public void assertEquals(String message, Object expected,
+ public static void assertEquals(String message, Object expected,
Object actual) {
if (equalsRegardingNull(expected, actual)) {
return;
- } else if (expected instanceof String && actual instanceof String) {
+ }
+ if (expected instanceof String && actual instanceof String) {
String cleanMessage = message == null ? "" : message;
throw new ComparisonFailure(cleanMessage, (String) expected,
(String) actual);
@@ -140,7 +142,7 @@ public class Assert {
* @param expected expected value
* @param actual the value to check against expected
*/
- static public void assertEquals(Object expected, Object actual) {
+ public static void assertEquals(Object expected, Object actual) {
assertEquals(null, expected, actual);
}
@@ -155,7 +157,7 @@ public class Assert {
* @param unexpected unexpected value to check
* @param actual the value to check against unexpected
*/
- static public void assertNotEquals(String message, Object unexpected,
+ public static void assertNotEquals(String message, Object unexpected,
Object actual) {
if (equalsRegardingNull(unexpected, actual)) {
failEquals(message, actual);
@@ -171,7 +173,7 @@ public class Assert {
* @param unexpected unexpected value to check
* @param actual the value to check against unexpected
*/
- static public void assertNotEquals(Object unexpected, Object actual) {
+ public static void assertNotEquals(Object unexpected, Object actual) {
assertNotEquals(null, unexpected, actual);
}
@@ -194,7 +196,7 @@ public class Assert {
* @param unexpected unexpected value to check
* @param actual the value to check against unexpected
*/
- static public void assertNotEquals(String message, long unexpected, long actual) {
+ public static void assertNotEquals(String message, long unexpected, long actual) {
if (unexpected == actual) {
failEquals(message, Long.valueOf(actual));
}
@@ -207,7 +209,7 @@ public class Assert {
* @param unexpected unexpected value to check
* @param actual the value to check against unexpected
*/
- static public void assertNotEquals(long unexpected, long actual) {
+ public static void assertNotEquals(long unexpected, long actual) {
assertNotEquals(null, unexpected, actual);
}
@@ -226,7 +228,7 @@ public class Assert {
* actual for which both numbers are still
* considered equal.
*/
- static public void assertNotEquals(String message, double unexpected,
+ public static void assertNotEquals(String message, double unexpected,
double actual, double delta) {
if (!doubleIsDifferent(unexpected, actual, delta)) {
failEquals(message, Double.valueOf(actual));
@@ -245,7 +247,7 @@ public class Assert {
* actual for which both numbers are still
* considered equal.
*/
- static public void assertNotEquals(double unexpected, double actual, double delta) {
+ public static void assertNotEquals(double unexpected, double actual, double delta) {
assertNotEquals(null, unexpected, actual, delta);
}
@@ -261,7 +263,7 @@ public class Assert {
* actual for which both numbers are still
* considered equal.
*/
- static public void assertNotEquals(float unexpected, float actual, float delta) {
+ public static void assertNotEquals(float unexpected, float actual, float delta) {
assertNotEquals(null, unexpected, actual, delta);
}
@@ -297,7 +299,7 @@ public class Assert {
public static void assertArrayEquals(Object[] expecteds, Object[] actuals) {
assertArrayEquals(null, expecteds, actuals);
}
-
+
/**
* Asserts that two boolean arrays are equal. If they are not, an
* {@link AssertionError} is thrown with the given message. If
@@ -312,8 +314,8 @@ public class Assert {
public static void assertArrayEquals(String message, boolean[] expecteds,
boolean[] actuals) throws ArrayComparisonFailure {
internalArrayEquals(message, expecteds, actuals);
- }
-
+ }
+
/**
* Asserts that two boolean arrays are equal. If they are not, an
* {@link AssertionError} is thrown. If expected and
@@ -547,7 +549,7 @@ public class Assert {
* actual for which both numbers are still
* considered equal.
*/
- static public void assertEquals(String message, double expected,
+ public static void assertEquals(String message, double expected,
double actual, double delta) {
if (doubleIsDifferent(expected, actual, delta)) {
failNotEquals(message, Double.valueOf(expected), Double.valueOf(actual));
@@ -569,7 +571,7 @@ public class Assert {
* actual for which both numbers are still
* considered equal.
*/
- static public void assertEquals(String message, float expected,
+ public static void assertEquals(String message, float expected,
float actual, float delta) {
if (floatIsDifferent(expected, actual, delta)) {
failNotEquals(message, Float.valueOf(expected), Float.valueOf(actual));
@@ -591,14 +593,14 @@ public class Assert {
* actual for which both numbers are still
* considered equal.
*/
- static public void assertNotEquals(String message, float unexpected,
+ public static void assertNotEquals(String message, float unexpected,
float actual, float delta) {
if (!floatIsDifferent(unexpected, actual, delta)) {
failEquals(message, Float.valueOf(actual));
}
}
- static private boolean doubleIsDifferent(double d1, double d2, double delta) {
+ private static boolean doubleIsDifferent(double d1, double d2, double delta) {
if (Double.compare(d1, d2) == 0) {
return false;
}
@@ -609,7 +611,7 @@ public class Assert {
return true;
}
- static private boolean floatIsDifferent(float f1, float f2, float delta) {
+ private static boolean floatIsDifferent(float f1, float f2, float delta) {
if (Float.compare(f1, f2) == 0) {
return false;
}
@@ -627,7 +629,7 @@ public class Assert {
* @param expected expected long value.
* @param actual actual long value
*/
- static public void assertEquals(long expected, long actual) {
+ public static void assertEquals(long expected, long actual) {
assertEquals(null, expected, actual);
}
@@ -640,7 +642,7 @@ public class Assert {
* @param expected long expected value.
* @param actual long actual value
*/
- static public void assertEquals(String message, long expected, long actual) {
+ public static void assertEquals(String message, long expected, long actual) {
if (expected != actual) {
failNotEquals(message, Long.valueOf(expected), Long.valueOf(actual));
}
@@ -652,7 +654,7 @@ public class Assert {
* instead
*/
@Deprecated
- static public void assertEquals(double expected, double actual) {
+ public static void assertEquals(double expected, double actual) {
assertEquals(null, expected, actual);
}
@@ -662,7 +664,7 @@ public class Assert {
* instead
*/
@Deprecated
- static public void assertEquals(String message, double expected,
+ public static void assertEquals(String message, double expected,
double actual) {
fail("Use assertEquals(expected, actual, delta) to compare floating-point numbers");
}
@@ -679,7 +681,7 @@ public class Assert {
* actual for which both numbers are still
* considered equal.
*/
- static public void assertEquals(double expected, double actual, double delta) {
+ public static void assertEquals(double expected, double actual, double delta) {
assertEquals(null, expected, actual, delta);
}
@@ -695,8 +697,7 @@ public class Assert {
* actual for which both numbers are still
* considered equal.
*/
-
- static public void assertEquals(float expected, float actual, float delta) {
+ public static void assertEquals(float expected, float actual, float delta) {
assertEquals(null, expected, actual, delta);
}
@@ -708,7 +709,7 @@ public class Assert {
* okay)
* @param object Object to check or null
*/
- static public void assertNotNull(String message, Object object) {
+ public static void assertNotNull(String message, Object object) {
assertTrue(message, object != null);
}
@@ -718,7 +719,7 @@ public class Assert {
*
* @param object Object to check or null
*/
- static public void assertNotNull(Object object) {
+ public static void assertNotNull(Object object) {
assertNotNull(null, object);
}
@@ -730,7 +731,7 @@ public class Assert {
* okay)
* @param object Object to check or null
*/
- static public void assertNull(String message, Object object) {
+ public static void assertNull(String message, Object object) {
if (object == null) {
return;
}
@@ -743,11 +744,11 @@ public class Assert {
*
* @param object Object to check or null
*/
- static public void assertNull(Object object) {
+ public static void assertNull(Object object) {
assertNull(null, object);
}
- static private void failNotNull(String message, Object actual) {
+ private static void failNotNull(String message, Object actual) {
String formatted = "";
if (message != null) {
formatted = message + " ";
@@ -764,7 +765,7 @@ public class Assert {
* @param expected the expected object
* @param actual the object to compare to expected
*/
- static public void assertSame(String message, Object expected, Object actual) {
+ public static void assertSame(String message, Object expected, Object actual) {
if (expected == actual) {
return;
}
@@ -778,7 +779,7 @@ public class Assert {
* @param expected the expected object
* @param actual the object to compare to expected
*/
- static public void assertSame(Object expected, Object actual) {
+ public static void assertSame(Object expected, Object actual) {
assertSame(null, expected, actual);
}
@@ -792,7 +793,7 @@ public class Assert {
* @param unexpected the object you don't expect
* @param actual the object to compare to unexpected
*/
- static public void assertNotSame(String message, Object unexpected,
+ public static void assertNotSame(String message, Object unexpected,
Object actual) {
if (unexpected == actual) {
failSame(message);
@@ -807,11 +808,11 @@ public class Assert {
* @param unexpected the object you don't expect
* @param actual the object to compare to unexpected
*/
- static public void assertNotSame(Object unexpected, Object actual) {
+ public static void assertNotSame(Object unexpected, Object actual) {
assertNotSame(null, unexpected, actual);
}
- static private void failSame(String message) {
+ private static void failSame(String message) {
String formatted = "";
if (message != null) {
formatted = message + " ";
@@ -819,7 +820,7 @@ public class Assert {
fail(formatted + "expected not same");
}
- static private void failNotSame(String message, Object expected,
+ private static void failNotSame(String message, Object expected,
Object actual) {
String formatted = "";
if (message != null) {
@@ -829,19 +830,19 @@ public class Assert {
+ ">");
}
- static private void failNotEquals(String message, Object expected,
+ private static void failNotEquals(String message, Object expected,
Object actual) {
fail(format(message, expected, actual));
}
static String format(String message, Object expected, Object actual) {
String formatted = "";
- if (message != null && !message.equals("")) {
+ if (message != null && !"".equals(message)) {
formatted = message + " ";
}
String expectedString = String.valueOf(expected);
String actualString = String.valueOf(actual);
- if (expectedString.equals(actualString)) {
+ if (equalsRegardingNull(expectedString, actualString)) {
return formatted + "expected: "
+ formatClassAndValue(expected, expectedString)
+ " but was: " + formatClassAndValue(actual, actualString);
@@ -851,6 +852,11 @@ public class Assert {
}
}
+ private static String formatClass(Class> value) {
+ String className = value.getCanonicalName();
+ return className == null ? value.getName() : className;
+ }
+
private static String formatClassAndValue(Object value, String valueString) {
String className = value == null ? "null" : value.getClass().getName();
return className + "<" + valueString + ">";
@@ -917,8 +923,9 @@ public class Assert {
* @param matcher an expression, built of {@link Matcher}s, specifying allowed
* values
* @see org.hamcrest.CoreMatchers
- * @see org.hamcrest.MatcherAssert
+ * @deprecated use {@code org.hamcrest.MatcherAssert.assertThat()}
*/
+ @Deprecated
public static void assertThat(T actual, Matcher super T> matcher) {
assertThat("", actual, matcher);
}
@@ -949,10 +956,79 @@ public class Assert {
* @param matcher an expression, built of {@link Matcher}s, specifying allowed
* values
* @see org.hamcrest.CoreMatchers
- * @see org.hamcrest.MatcherAssert
+ * @deprecated use {@code org.hamcrest.MatcherAssert.assertThat()}
*/
+ @Deprecated
public static void assertThat(String reason, T actual,
Matcher super T> matcher) {
MatcherAssert.assertThat(reason, actual, matcher);
}
+
+ /**
+ * Asserts that {@code runnable} throws an exception of type {@code expectedThrowable} when
+ * executed. If it does, the exception object is returned. If it does not throw an exception, an
+ * {@link AssertionError} is thrown. If it throws the wrong type of exception, an {@code
+ * AssertionError} is thrown describing the mismatch; the exception that was actually thrown can
+ * be obtained by calling {@link AssertionError#getCause}.
+ *
+ * @param expectedThrowable the expected type of the exception
+ * @param runnable a function that is expected to throw an exception when executed
+ * @return the exception thrown by {@code runnable}
+ * @since 4.13
+ */
+ public static T assertThrows(Class expectedThrowable,
+ ThrowingRunnable runnable) {
+ return assertThrows(null, expectedThrowable, runnable);
+ }
+
+ /**
+ * Asserts that {@code runnable} throws an exception of type {@code expectedThrowable} when
+ * executed. If it does, the exception object is returned. If it does not throw an exception, an
+ * {@link AssertionError} is thrown. If it throws the wrong type of exception, an {@code
+ * AssertionError} is thrown describing the mismatch; the exception that was actually thrown can
+ * be obtained by calling {@link AssertionError#getCause}.
+ *
+ * @param message the identifying message for the {@link AssertionError} (null
+ * okay)
+ * @param expectedThrowable the expected type of the exception
+ * @param runnable a function that is expected to throw an exception when executed
+ * @return the exception thrown by {@code runnable}
+ * @since 4.13
+ */
+ public static T assertThrows(String message, Class expectedThrowable,
+ ThrowingRunnable runnable) {
+ try {
+ runnable.run();
+ } catch (Throwable actualThrown) {
+ if (expectedThrowable.isInstance(actualThrown)) {
+ @SuppressWarnings("unchecked") T retVal = (T) actualThrown;
+ return retVal;
+ } else {
+ String expected = formatClass(expectedThrowable);
+ Class extends Throwable> actualThrowable = actualThrown.getClass();
+ String actual = formatClass(actualThrowable);
+ if (expected.equals(actual)) {
+ // There must be multiple class loaders. Add the identity hash code so the message
+ // doesn't say "expected: java.lang.String ..."
+ expected += "@" + Integer.toHexString(System.identityHashCode(expectedThrowable));
+ actual += "@" + Integer.toHexString(System.identityHashCode(actualThrowable));
+ }
+ String mismatchMessage = buildPrefix(message)
+ + format("unexpected exception type thrown;", expected, actual);
+
+ // The AssertionError(String, Throwable) ctor is only available on JDK7.
+ AssertionError assertionError = new AssertionError(mismatchMessage);
+ assertionError.initCause(actualThrown);
+ throw assertionError;
+ }
+ }
+ String notThrownMessage = buildPrefix(message) + String
+ .format("expected %s to be thrown, but nothing was thrown",
+ formatClass(expectedThrowable));
+ throw new AssertionError(notThrownMessage);
+ }
+
+ private static String buildPrefix(String message) {
+ return message != null && message.length() != 0 ? message + ": " : "";
+ }
}
diff --git a/src/main/java/org/junit/Assume.java b/src/main/java/org/junit/Assume.java
index b7687f7..29b705b 100644
--- a/src/main/java/org/junit/Assume.java
+++ b/src/main/java/org/junit/Assume.java
@@ -14,7 +14,7 @@ import org.hamcrest.Matcher;
* basically means "don't run this test if these conditions don't apply". The default JUnit runner skips tests with
* failing assumptions. Custom runners may behave differently.
*
- * A good example of using assumptions is in Theories where they are needed to exclude certain datapoints that aren't suitable or allowed for a certain test case.
+ * A good example of using assumptions is in Theories where they are needed to exclude certain datapoints that aren't suitable or allowed for a certain test case.
*
* Failed assumptions are usually not logged, because there may be many tests that don't apply to certain
* configurations.
@@ -29,11 +29,20 @@ import org.hamcrest.Matcher;
*
*
*
- * @see Theories
+ * @see Theories
*
* @since 4.4
*/
public class Assume {
+
+ /**
+ * Do not instantiate.
+ * @deprecated since 4.13.
+ */
+ @Deprecated
+ public Assume() {
+ }
+
/**
* If called with an expression evaluating to {@code false}, the test will halt and be ignored.
*/
@@ -45,7 +54,7 @@ public class Assume {
* The inverse of {@link #assumeTrue(boolean)}.
*/
public static void assumeFalse(boolean b) {
- assumeTrue(!b);
+ assumeThat(b, is(false));
}
/**
@@ -67,9 +76,11 @@ public class Assume {
}
/**
- * If called with one or more null elements in objects, the test will halt and be ignored.
+ * If called with a {@code null} array or one or more {@code null} elements in {@code objects},
+ * the test will halt and be ignored.
*/
public static void assumeNotNull(Object... objects) {
+ assumeThat(objects, notNullValue());
assumeThat(asList(objects), everyItem(notNullValue()));
}
diff --git a/src/main/java/org/junit/AssumptionViolatedException.java b/src/main/java/org/junit/AssumptionViolatedException.java
index e48ddf0..1d62190 100644
--- a/src/main/java/org/junit/AssumptionViolatedException.java
+++ b/src/main/java/org/junit/AssumptionViolatedException.java
@@ -40,7 +40,7 @@ public class AssumptionViolatedException extends org.junit.internal.AssumptionVi
/**
* An assumption exception with the given message and a cause.
*/
- public AssumptionViolatedException(String assumption, Throwable t) {
- super(assumption, t);
+ public AssumptionViolatedException(String message, Throwable t) {
+ super(message, t);
}
}
diff --git a/src/main/java/org/junit/ClassRule.java b/src/main/java/org/junit/ClassRule.java
index 02c40a7..94ee29f 100644
--- a/src/main/java/org/junit/ClassRule.java
+++ b/src/main/java/org/junit/ClassRule.java
@@ -28,7 +28,10 @@ import java.lang.annotation.Target;
* annotated {@link ClassRule}s on a class, they will be applied in an order
* that depends on your JVM's implementation of the reflection API, which is
* undefined, in general. However, Rules defined by fields will always be applied
- * before Rules defined by methods.
+ * after Rules defined by methods, i.e. the Statements returned by the former will
+ * be executed around those returned by the latter.
+ *
+ *
Usage
*
* For example, here is a test suite that connects to a server once before
* all the test classes run, and disconnects after they are finished:
@@ -79,9 +82,37 @@ import java.lang.annotation.Target;
*
* For more information and more examples, see {@link org.junit.rules.TestRule}.
*
+ *
Ordering
+ *
+ * You can use {@link #order()} if you want to have control over the order in
+ * which the Rules are applied.
+ *
+ *
+ * public class ThreeClassRules {
+ * @ClassRule(order = 0)
+ * public static LoggingRule outer = new LoggingRule("outer rule");
+ *
+ * @ClassRule(order = 1)
+ * public static LoggingRule middle = new LoggingRule("middle rule");
+ *
+ * @ClassRule(order = 2)
+ * public static LoggingRule inner = new LoggingRule("inner rule");
+ *
+ * // ...
+ * }
+ *
+ *
* @since 4.9
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface ClassRule {
+
+ /**
+ * Specifies the order in which rules are applied. The rules with a higher value are inner.
+ *
+ * @since 4.13
+ */
+ int order() default Rule.DEFAULT_ORDER;
+
}
diff --git a/src/main/java/org/junit/ComparisonFailure.java b/src/main/java/org/junit/ComparisonFailure.java
index 9563e61..d1daa86 100644
--- a/src/main/java/org/junit/ComparisonFailure.java
+++ b/src/main/java/org/junit/ComparisonFailure.java
@@ -21,7 +21,7 @@ public class ComparisonFailure 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
+ * See https://github.com/junit-team/junit4/issues/976
*/
private String fExpected;
private String fActual;
diff --git a/src/main/java/org/junit/Rule.java b/src/main/java/org/junit/Rule.java
index 711235c..9370e94 100644
--- a/src/main/java/org/junit/Rule.java
+++ b/src/main/java/org/junit/Rule.java
@@ -16,12 +16,14 @@ import java.lang.annotation.Target;
* to the {@link org.junit.rules.TestRule} will run any {@link Before} methods,
* then the {@link Test} method, and finally any {@link After} methods,
* throwing an exception if any of these fail. If there are multiple
- * annotated {@link Rule}s on a class, they will be applied in order of fields first, then methods.
+ * annotated {@link Rule}s on a class, they will be applied in order of methods first, then fields.
* However, if there are multiple fields (or methods) they will be applied in an order
* that depends on your JVM's implementation of the reflection API, which is
* undefined, in general. Rules defined by fields will always be applied
- * before Rules defined by methods. You can use a {@link org.junit.rules.RuleChain} if you want
- * to have control over the order in which the Rules are applied.
+ * after Rules defined by methods, i.e. the Statements returned by the former will
+ * be executed around those returned by the latter.
+ *
+ *
Usage
*
* For example, here is a test class that creates a temporary folder before
* each test method, and deletes it after each:
@@ -61,10 +63,39 @@ import java.lang.annotation.Target;
* For more information and more examples, see
* {@link org.junit.rules.TestRule}.
*
+ *
Ordering
+ *
+ * You can use {@link #order()} if you want to have control over the order in
+ * which the Rules are applied.
+ *
+ *
+ * public class ThreeRules {
+ * @Rule(order = 0)
+ * public LoggingRule outer = new LoggingRule("outer rule");
+ *
+ * @Rule(order = 1)
+ * public LoggingRule middle = new LoggingRule("middle rule");
+ *
+ * @Rule(order = 2)
+ * public LoggingRule inner = new LoggingRule("inner rule");
+ *
+ * // ...
+ * }
+ *
+ *
* @since 4.7
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Rule {
-}
\ No newline at end of file
+ int DEFAULT_ORDER = -1;
+
+ /**
+ * Specifies the order in which rules are applied. The rules with a higher value are inner.
+ *
+ * @since 4.13
+ */
+ int order() default DEFAULT_ORDER;
+
+}
diff --git a/src/main/java/org/junit/Test.java b/src/main/java/org/junit/Test.java
index 71ac428..1db6fc7 100644
--- a/src/main/java/org/junit/Test.java
+++ b/src/main/java/org/junit/Test.java
@@ -1,5 +1,7 @@
package org.junit;
+import org.junit.function.ThrowingRunnable;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -23,24 +25,40 @@ import java.lang.annotation.Target;
* }
*
*
- * The Test annotation supports two optional parameters.
- * The first, expected, declares that a test method should throw
+ * The Test annotation supports two optional parameters for
+ * exception testing and for limiting test execution time.
+ *
+ *
Exception Testing
+ *
+ * The parameter expected declares that a test method should throw
* an exception. If it doesn't throw an exception or if it throws a different exception
* than the one declared, the test fails. For example, the following test succeeds:
*
- * @Test(expected=IndexOutOfBoundsException.class) public void outOfBounds() {
+ * @Test(expected=IndexOutOfBoundsException.class)
+ * public void outOfBounds() {
* new ArrayList<Object>().get(1);
* }
*
- * If the exception's message or one of its properties should be verified, the
- * {@link org.junit.rules.ExpectedException ExpectedException} rule can be used. Further
+ *
+ * Using the parameter expected for exception testing comes with
+ * some limitations: only the exception's type can be checked and it is not
+ * possible to precisely specify the code that throws the exception. Therefore
+ * JUnit 4 has improved its support for exception testing with
+ * {@link Assert#assertThrows(Class, ThrowingRunnable)} and the
+ * {@link org.junit.rules.ExpectedException ExpectedException} rule.
+ * With assertThrows the code that throws the exception can be
+ * precisely specified. If the exception's message or one of its properties
+ * should be verified, the ExpectedException rule can be used. Further
* information about exception testing can be found at the
- * JUnit Wiki.
+ * JUnit Wiki.
+ *
+ *
Timeout
*
- * The second optional parameter, timeout, causes a test to fail if it takes
+ * The parameter timeout causes a test to fail if it takes
* longer than a specified amount of clock time (measured in milliseconds). The following test fails:
*
@@ -49,7 +67,8 @@ import java.lang.annotation.Target;
* following test may or may not fail depending on how the operating system
* schedules threads:
*
@@ -66,7 +85,7 @@ import java.lang.annotation.Target;
public @interface Test {
/**
- * Default empty exception
+ * Default empty exception.
*/
static class None extends Throwable {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/org/junit/TestCouldNotBeSkippedException.java b/src/main/java/org/junit/TestCouldNotBeSkippedException.java
new file mode 100644
index 0000000..4804493
--- /dev/null
+++ b/src/main/java/org/junit/TestCouldNotBeSkippedException.java
@@ -0,0 +1,19 @@
+package org.junit;
+
+/**
+ * Indicates that a test that indicated that it should be skipped could not be skipped.
+ * This can be thrown if a test uses the methods in {@link Assume} to indicate that
+ * it should be skipped, but before processing of the test was completed, other failures
+ * occured.
+ *
+ * @see org.junit.Assume
+ * @since 4.13
+ */
+public class TestCouldNotBeSkippedException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /** Creates an instance using the given assumption failure. */
+ public TestCouldNotBeSkippedException(org.junit.internal.AssumptionViolatedException cause) {
+ super("Test could not be skipped due to other failures", cause);
+ }
+}
diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java
index 290c180..0c73ed8 100644
--- a/src/main/java/org/junit/experimental/categories/Categories.java
+++ b/src/main/java/org/junit/experimental/categories/Categories.java
@@ -2,8 +2,10 @@ package org.junit.experimental.categories;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Set;
import org.junit.runner.Description;
@@ -76,7 +78,7 @@ import org.junit.runners.model.RunnerBuilder;
*
*
* @version 4.12
- * @see Categories at JUnit wiki
+ * @see Categories at JUnit wiki
*/
public class Categories extends Suite {
@@ -86,13 +88,13 @@ public class Categories extends Suite {
* Determines the tests to run that are annotated with categories specified in
* the value of this annotation or their subtypes unless excluded with {@link ExcludeCategory}.
*/
- public Class>[] value() default {};
+ Class>[] value() default {};
/**
* If true, runs tests annotated with any of the categories in
* {@link IncludeCategory#value()}. Otherwise, runs tests only if annotated with all of the categories.
*/
- public boolean matchAny() default true;
+ boolean matchAny() default true;
}
@Retention(RetentionPolicy.RUNTIME)
@@ -101,13 +103,13 @@ public class Categories extends Suite {
* Determines the tests which do not run if they are annotated with categories specified in the
* value of this annotation or their subtypes regardless of being included in {@link IncludeCategory#value()}.
*/
- public Class>[] value() default {};
+ Class>[] value() default {};
/**
* If true, the tests annotated with any of the categories in {@link ExcludeCategory#value()}
* do not run. Otherwise, the tests do not run if and only if annotated with all categories.
*/
- public boolean matchAny() default true;
+ boolean matchAny() default true;
}
public static class CategoryFilter extends Filter {
@@ -117,10 +119,7 @@ public class Categories extends Suite {
private final boolean excludedAny;
public static CategoryFilter include(boolean matchAny, Class>... categories) {
- if (hasNull(categories)) {
- throw new NullPointerException("has null category");
- }
- return categoryFilter(matchAny, createSet(categories), true, null);
+ return new CategoryFilter(matchAny, categories, true, null);
}
public static CategoryFilter include(Class> category) {
@@ -132,10 +131,7 @@ public class Categories extends Suite {
}
public static CategoryFilter exclude(boolean matchAny, Class>... categories) {
- if (hasNull(categories)) {
- throw new NullPointerException("has null category");
- }
- return categoryFilter(true, null, matchAny, createSet(categories));
+ return new CategoryFilter(true, null, matchAny, categories);
}
public static CategoryFilter exclude(Class> category) {
@@ -151,14 +147,30 @@ public class Categories extends Suite {
return new CategoryFilter(matchAnyInclusions, inclusions, matchAnyExclusions, exclusions);
}
+ @Deprecated
+ public CategoryFilter(Class> includedCategory, Class> excludedCategory) {
+ includedAny = true;
+ excludedAny = true;
+ included = nullableClassToSet(includedCategory);
+ excluded = nullableClassToSet(excludedCategory);
+ }
+
protected CategoryFilter(boolean matchAnyIncludes, Set> includes,
- boolean matchAnyExcludes, Set> excludes) {
+ boolean matchAnyExcludes, Set> excludes) {
includedAny = matchAnyIncludes;
excludedAny = matchAnyExcludes;
included = copyAndRefine(includes);
excluded = copyAndRefine(excludes);
}
+ private CategoryFilter(boolean matchAnyIncludes, Class>[] inclusions,
+ boolean matchAnyExcludes, Class>[] exclusions) {
+ includedAny = matchAnyIncludes;
+ excludedAny = matchAnyExcludes;
+ included = createSet(inclusions);
+ excluded = createSet(exclusions);
+ }
+
/**
* @see #toString()
*/
@@ -284,23 +296,13 @@ public class Categories extends Suite {
}
private static Set> copyAndRefine(Set> classes) {
- HashSet> c= new HashSet>();
+ Set> c= new LinkedHashSet>();
if (classes != null) {
c.addAll(classes);
}
c.remove(null);
return c;
}
-
- private static boolean hasNull(Class>... classes) {
- if (classes == null) return false;
- for (Class> clazz : classes) {
- if (clazz == null) {
- return true;
- }
- }
- return false;
- }
}
public Categories(Class> klass, RunnerBuilder builder) throws InitializationError {
@@ -315,7 +317,6 @@ public class Categories extends Suite {
} catch (NoTestsRemainException e) {
throw new InitializationError(e);
}
- assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription());
}
private static Set> getIncludedCategory(Class> klass) {
@@ -338,34 +339,6 @@ public class Categories extends Suite {
return annotation == null || annotation.matchAny();
}
- private static void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError {
- if (!canHaveCategorizedChildren(description)) {
- assertNoDescendantsHaveCategoryAnnotations(description);
- }
- for (Description each : description.getChildren()) {
- assertNoCategorizedDescendentsOfUncategorizeableParents(each);
- }
- }
-
- private static void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError {
- for (Description each : description.getChildren()) {
- if (each.getAnnotation(Category.class) != null) {
- throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods.");
- }
- assertNoDescendantsHaveCategoryAnnotations(each);
- }
- }
-
- // If children have names like [0], our current magical category code can't determine their parentage.
- private static boolean canHaveCategorizedChildren(Description description) {
- for (Description each : description.getChildren()) {
- if (each.getTestClass() == null) {
- return false;
- }
- }
- return true;
- }
-
private static boolean hasAssignableTo(Set> assigns, Class> to) {
for (final Class> from : assigns) {
if (to.isAssignableFrom(from)) {
@@ -375,11 +348,28 @@ public class Categories extends Suite {
return false;
}
- private static Set> createSet(Class>... t) {
- final Set> set= new HashSet>();
- if (t != null) {
- Collections.addAll(set, t);
+ private static Set> createSet(Class>[] classes) {
+ // Not throwing a NPE if t is null is a bad idea, but it's the behavior from JUnit 4.12
+ // for include(boolean, Class>...) and exclude(boolean, Class>...)
+ if (classes == null || classes.length == 0) {
+ return Collections.emptySet();
+ }
+ for (Class> category : classes) {
+ if (category == null) {
+ throw new NullPointerException("has null category");
+ }
}
- return set;
+
+ return classes.length == 1
+ ? Collections.>singleton(classes[0])
+ : new LinkedHashSet>(Arrays.asList(classes));
+ }
+
+ private static Set> nullableClassToSet(Class> nullableClass) {
+ // Not throwing a NPE if t is null is a bad idea, but it's the behavior from JUnit 4.11
+ // for CategoryFilter(Class> includedCategory, Class> excludedCategory)
+ return nullableClass == null
+ ? Collections.>emptySet()
+ : Collections.>singleton(nullableClass);
}
}
diff --git a/src/main/java/org/junit/experimental/categories/CategoryFilterFactory.java b/src/main/java/org/junit/experimental/categories/CategoryFilterFactory.java
index cee1ae7..e9bdab7 100644
--- a/src/main/java/org/junit/experimental/categories/CategoryFilterFactory.java
+++ b/src/main/java/org/junit/experimental/categories/CategoryFilterFactory.java
@@ -37,7 +37,11 @@ abstract class CategoryFilterFactory implements FilterFactory {
List> categoryClasses = new ArrayList>();
for (String category : categories.split(",")) {
- Class> categoryClass = Classes.getClass(category);
+ /*
+ * Load the category class using the context class loader.
+ * If there is no context class loader, use the class loader for this class.
+ */
+ Class> categoryClass = Classes.getClass(category, getClass());
categoryClasses.add(categoryClass);
}
diff --git a/src/main/java/org/junit/experimental/max/MaxHistory.java b/src/main/java/org/junit/experimental/max/MaxHistory.java
index 45a4033..ab7443f 100644
--- a/src/main/java/org/junit/experimental/max/MaxHistory.java
+++ b/src/main/java/org/junit/experimental/max/MaxHistory.java
@@ -64,7 +64,7 @@ public class MaxHistory 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 Map fDurations = new HashMap();
private final Map fFailureTimestamps = new HashMap();
@@ -75,10 +75,15 @@ public class MaxHistory implements Serializable {
}
private void save() throws IOException {
- ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(
- fHistoryStore));
- stream.writeObject(this);
- stream.close();
+ ObjectOutputStream stream = null;
+ try {
+ stream = new ObjectOutputStream(new FileOutputStream(fHistoryStore));
+ stream.writeObject(this);
+ } finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
}
Long getFailureTimestamp(Description key) {
diff --git a/src/main/java/org/junit/experimental/results/PrintableResult.java b/src/main/java/org/junit/experimental/results/PrintableResult.java
index ffe22f0..0f67766 100644
--- a/src/main/java/org/junit/experimental/results/PrintableResult.java
+++ b/src/main/java/org/junit/experimental/results/PrintableResult.java
@@ -54,6 +54,15 @@ public class PrintableResult {
return result.getFailures().size();
}
+ /**
+ * Returns the failures in this result.
+ *
+ * @since 4.13
+ */
+ public List failures() {
+ return result.getFailures();
+ }
+
@Override
public String toString() {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
diff --git a/src/main/java/org/junit/experimental/results/ResultMatchers.java b/src/main/java/org/junit/experimental/results/ResultMatchers.java
index cf58f1b..92f2e6b 100644
--- a/src/main/java/org/junit/experimental/results/ResultMatchers.java
+++ b/src/main/java/org/junit/experimental/results/ResultMatchers.java
@@ -14,6 +14,15 @@ import org.hamcrest.TypeSafeMatcher;
*
*/
public class ResultMatchers {
+
+ /**
+ * Do not instantiate.
+ * @deprecated will be private soon.
+ */
+ @Deprecated
+ public ResultMatchers() {
+ }
+
/**
* Matches if the tests are all successful
*/
@@ -52,14 +61,34 @@ public class ResultMatchers {
};
}
+ /**
+ * Matches if the result has exactly one failure matching the given matcher.
+ *
+ * @since 4.13
+ */
+ public static Matcher hasSingleFailureMatching(final Matcher matcher) {
+ return new TypeSafeMatcher() {
+ @Override
+ public boolean matchesSafely(PrintableResult item) {
+ return item.failureCount() == 1 && matcher.matches(item.failures().get(0).getException());
+ }
+
+ public void describeTo(Description description) {
+ description.appendText("has failure with exception matching ");
+ matcher.describeTo(description);
+ }
+ };
+ }
+
/**
* Matches if the result has one or more failures, and at least one of them
* contains {@code string}
*/
public static Matcher hasFailureContaining(final String string) {
- return new BaseMatcher() {
- public boolean matches(Object item) {
- return item.toString().contains(string);
+ return new TypeSafeMatcher() {
+ @Override
+ public boolean matchesSafely(PrintableResult item) {
+ return item.failureCount() > 0 && item.toString().contains(string);
}
public void describeTo(Description description) {
diff --git a/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java b/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java
index 15b5d95..846a39e 100644
--- a/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java
+++ b/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java
@@ -17,7 +17,7 @@ import java.lang.annotation.Target;
*
* In addition, annotations themselves can be annotated with
* @ParametersSuppliedBy, and then used similarly. ParameterSuppliedBy
- * annotations on parameters are detected by searching up this heirarchy such
+ * annotations on parameters are detected by searching up this hierarchy such
* that these act as syntactic sugar, making:
*
*
- * This makes it clear that the user's filename should be included in the config file name,
+ * This makes it clear that the username should be included in the config file name,
* only if it doesn't contain a slash. Another test or theory might define what happens when a username does contain
* a slash. UserTest will attempt to run filenameIncludesUsername on every compatible data
* point defined in the class. If any of the assumptions fail, the data point is silently ignored. If all of the
- * assumptions pass, but an assertion fails, the test fails.
+ * assumptions pass, but an assertion fails, the test fails. If no parameters can be found that satisfy all assumptions, the test fails.
*
* Defining general statements as theories allows data point reuse across a bunch of functionality tests and also
* allows automated tools to search for new, unexpected data points that expose bugs.
@@ -73,6 +73,11 @@ public class Theories extends BlockJUnit4ClassRunner {
super(klass);
}
+ /** @since 4.13 */
+ protected Theories(TestClass testClass) throws InitializationError {
+ super(testClass);
+ }
+
@Override
protected void collectInitializationErrors(List errors) {
super.collectInitializationErrors(errors);
@@ -215,7 +220,7 @@ public class Theories extends BlockJUnit4ClassRunner {
protected void runWithCompleteAssignment(final Assignments complete)
throws Throwable {
- new BlockJUnit4ClassRunner(getTestClass().getJavaClass()) {
+ new BlockJUnit4ClassRunner(getTestClass()) {
@Override
protected void collectInitializationErrors(
List errors) {
diff --git a/src/main/java/org/junit/experimental/theories/internal/Assignments.java b/src/main/java/org/junit/experimental/theories/internal/Assignments.java
index a94c8a5..6626797 100644
--- a/src/main/java/org/junit/experimental/theories/internal/Assignments.java
+++ b/src/main/java/org/junit/experimental/theories/internal/Assignments.java
@@ -47,7 +47,7 @@ public class Assignments {
}
public boolean isComplete() {
- return unassigned.size() == 0;
+ return unassigned.isEmpty();
}
public ParameterSignature nextUnassigned() {
@@ -55,11 +55,10 @@ public class Assignments {
}
public Assignments assignNext(PotentialAssignment source) {
- List assigned = new ArrayList(
- this.assigned);
- assigned.add(source);
+ List potentialAssignments = new ArrayList(assigned);
+ potentialAssignments.add(source);
- return new Assignments(assigned, unassigned.subList(1,
+ return new Assignments(potentialAssignments, unassigned.subList(1,
unassigned.size()), clazz);
}
@@ -77,7 +76,7 @@ public class Assignments {
ParameterSignature unassigned = nextUnassigned();
List assignments = getSupplier(unassigned).getValueSources(unassigned);
- if (assignments.size() == 0) {
+ if (assignments.isEmpty()) {
assignments = generateAssignmentsFromTypeAlone(unassigned);
}
diff --git a/src/main/java/org/junit/function/ThrowingRunnable.java b/src/main/java/org/junit/function/ThrowingRunnable.java
new file mode 100644
index 0000000..d0eb782
--- /dev/null
+++ b/src/main/java/org/junit/function/ThrowingRunnable.java
@@ -0,0 +1,14 @@
+package org.junit.function;
+
+/**
+ * This interface facilitates the use of
+ * {@link org.junit.Assert#assertThrows(Class, ThrowingRunnable)} from Java 8. It allows method
+ * references to void methods (that declare checked exceptions) to be passed directly into
+ * {@code assertThrows}
+ * without wrapping. It is not meant to be implemented directly.
+ *
+ * @since 4.13
+ */
+public interface ThrowingRunnable {
+ void run() throws Throwable;
+}
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 fIndices = new ArrayList();
private final String fMessage;
+ private final AssertionError fCause;
/**
* Construct a new ArrayComparisonFailure 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);
}
@@ -40,6 +42,11 @@ public class ArrayComparisonFailure extends AssertionError {
fIndices.add(0, index);
}
+ @Override
+ public synchronized Throwable getCause() {
+ return super.getCause() == null ? fCause : super.getCause();
+ }
+
@Override
public String getMessage() {
StringBuilder sb = new StringBuilder();
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 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 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 extends BaseMatcher implements Serializable {
+
+ private final String matcherDescription;
+
+ private SerializableMatcherDescription(Matcher 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 Matcher asSerializableMatcher(Matcher matcher) {
+ if (matcher == null || matcher instanceof Serializable) {
+ return matcher;
+ } else {
+ return new SerializableMatcherDescription(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 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 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 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 getTrimmedStackTraceLines(Throwable exception) {
+ List 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 trimmedLines = new ArrayList(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 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 causedByLines = new ArrayList();
+
+ 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 stackTraceLines, StringBuilder destBuilder) {
+ for (String stackTraceLine : stackTraceLines) {
+ destBuilder.append(String.format("%s%n", stackTraceLine));
+ }
+ }
+
+ private static List asReversedList(final List list) {
+ return new AbstractList() {
+
+ @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.(",
+ "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}
+ *
+ *
Always returns an empty list.
+ */
+ public List 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}
+ *
+ *
Always throws an {@link UnsupportedOperationException}
+ */
+ public long getThreadCpuTime(long id) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ *
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 getInputArguments() {
+ if (Holder.getInputArgumentsMethod != null) {
+ try {
+ return (List) 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 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 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 extends
TypeSafeMatcher {
- 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 extends
* @param type of the outer exception
*/
@Factory
- public static Matcher hasCause(final Matcher extends Throwable> matcher) {
+ public static Matcher hasCause(final Matcher> matcher) {
return new ThrowableCauseMatcher(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 extends BaseMatcher {
}
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 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 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 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> getSuperClasses(Class> testClass) {
- ArrayList> results = new ArrayList>();
+ List> results = new ArrayList>();
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/FailOnTimeout.java b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
index 9fad35b..9362cc1 100644
--- a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
+++ b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
@@ -1,5 +1,8 @@
package org.junit.internal.runners.statements;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@@ -7,6 +10,9 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import org.junit.internal.management.ManagementFactory;
+import org.junit.internal.management.ThreadMXBean;
+import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestTimedOutException;
@@ -14,6 +20,7 @@ public class FailOnTimeout extends Statement {
private final Statement originalStatement;
private final TimeUnit timeUnit;
private final long timeout;
+ private final boolean lookForStuckThread;
/**
* Returns a new builder for building an instance.
@@ -40,6 +47,7 @@ public class FailOnTimeout extends Statement {
originalStatement = statement;
timeout = builder.timeout;
timeUnit = builder.unit;
+ lookForStuckThread = builder.lookForStuckThread;
}
/**
@@ -48,6 +56,7 @@ public class FailOnTimeout extends Statement {
* @since 4.12
*/
public static class Builder {
+ private boolean lookForStuckThread = false;
private long timeout = 0;
private TimeUnit unit = TimeUnit.SECONDS;
@@ -79,6 +88,20 @@ public class FailOnTimeout extends Statement {
return this;
}
+ /**
+ * Specifies whether to look for a stuck thread. If a timeout occurs and this
+ * feature is enabled, the test will look for a thread that appears to be stuck
+ * and dump its backtrace. This feature is experimental. Behavior may change
+ * after the 4.12 release in response to feedback.
+ *
+ * @param enable {@code true} to enable the feature
+ * @return {@code this} for method chaining.
+ */
+ public Builder withLookingForStuckThread(boolean enable) {
+ this.lookForStuckThread = enable;
+ return this;
+ }
+
/**
* Builds a {@link FailOnTimeout} instance using the values in this builder,
* wrapping the given statement.
@@ -97,7 +120,8 @@ public class FailOnTimeout extends Statement {
public void evaluate() throws Throwable {
CallableStatement callable = new CallableStatement();
FutureTask task = new FutureTask(callable);
- Thread thread = new Thread(task, "Time-limited test");
+ ThreadGroup threadGroup = threadGroupForNewThread();
+ Thread thread = new Thread(threadGroup, task, "Time-limited test");
thread.setDaemon(true);
thread.start();
callable.awaitStarted();
@@ -107,6 +131,31 @@ public class FailOnTimeout extends Statement {
}
}
+ private ThreadGroup threadGroupForNewThread() {
+ if (!lookForStuckThread) {
+ // Use the default ThreadGroup (usually the one from the current
+ // thread).
+ return null;
+ }
+
+ // Create the thread in a new ThreadGroup, so if the time-limited thread
+ // becomes stuck, getStuckThread() can find the thread likely to be the
+ // culprit.
+ ThreadGroup threadGroup = new ThreadGroup("FailOnTimeoutGroup");
+ if (!threadGroup.isDaemon()) {
+ // Mark the new ThreadGroup as a daemon thread group, so it will be
+ // destroyed after the time-limited thread completes. By ensuring the
+ // ThreadGroup is destroyed, any data associated with the ThreadGroup
+ // (ex: via java.beans.ThreadGroupContext) is destroyed.
+ try {
+ threadGroup.setDaemon(true);
+ } catch (SecurityException e) {
+ // Swallow the exception to keep the same behavior as in JUnit 4.12.
+ }
+ }
+ return threadGroup;
+ }
+
/**
* Wait for the test task, returning the exception thrown by the test if the
* test failed, an exception indicating a timeout if the test timed out, or
@@ -131,12 +180,114 @@ public class FailOnTimeout extends Statement {
private Exception createTimeoutException(Thread thread) {
StackTraceElement[] stackTrace = thread.getStackTrace();
+ final Thread stuckThread = lookForStuckThread ? getStuckThread(thread) : null;
Exception currThreadException = new TestTimedOutException(timeout, timeUnit);
if (stackTrace != null) {
currThreadException.setStackTrace(stackTrace);
thread.interrupt();
}
- return currThreadException;
+ if (stuckThread != null) {
+ Exception stuckThreadException =
+ new Exception("Appears to be stuck in thread " +
+ stuckThread.getName());
+ stuckThreadException.setStackTrace(getStackTrace(stuckThread));
+ return new MultipleFailureException(
+ Arrays.asList(currThreadException, stuckThreadException));
+ } else {
+ return currThreadException;
+ }
+ }
+
+ /**
+ * Retrieves the stack trace for a given thread.
+ * @param thread The thread whose stack is to be retrieved.
+ * @return The stack trace; returns a zero-length array if the thread has
+ * terminated or the stack cannot be retrieved for some other reason.
+ */
+ private StackTraceElement[] getStackTrace(Thread thread) {
+ try {
+ return thread.getStackTrace();
+ } catch (SecurityException e) {
+ return new StackTraceElement[0];
+ }
+ }
+
+ /**
+ * Determines whether the test appears to be stuck in some thread other than
+ * the "main thread" (the one created to run the test). This feature is experimental.
+ * Behavior may change after the 4.12 release in response to feedback.
+ * @param mainThread The main thread created by {@code evaluate()}
+ * @return The thread which appears to be causing the problem, if different from
+ * {@code mainThread}, or {@code null} if the main thread appears to be the
+ * problem or if the thread cannot be determined. The return value is never equal
+ * to {@code mainThread}.
+ */
+ private Thread getStuckThread(Thread mainThread) {
+ List threadsInGroup = getThreadsInGroup(mainThread.getThreadGroup());
+ if (threadsInGroup.isEmpty()) {
+ return null;
+ }
+
+ // Now that we have all the threads in the test's thread group: Assume that
+ // any thread we're "stuck" in is RUNNABLE. Look for all RUNNABLE threads.
+ // If just one, we return that (unless it equals threadMain). If there's more
+ // than one, pick the one that's using the most CPU time, if this feature is
+ // supported.
+ Thread stuckThread = null;
+ long maxCpuTime = 0;
+ for (Thread thread : threadsInGroup) {
+ if (thread.getState() == Thread.State.RUNNABLE) {
+ long threadCpuTime = cpuTime(thread);
+ if (stuckThread == null || threadCpuTime > maxCpuTime) {
+ stuckThread = thread;
+ maxCpuTime = threadCpuTime;
+ }
+ }
+ }
+ return (stuckThread == mainThread) ? null : stuckThread;
+ }
+
+ /**
+ * Returns all active threads belonging to a thread group.
+ * @param group The thread group.
+ * @return The active threads in the thread group. The result should be a
+ * complete list of the active threads at some point in time. Returns an empty list
+ * if this cannot be determined, e.g. because new threads are being created at an
+ * extremely fast rate.
+ */
+ private List getThreadsInGroup(ThreadGroup group) {
+ final int activeThreadCount = group.activeCount(); // this is just an estimate
+ int threadArraySize = Math.max(activeThreadCount * 2, 100);
+ for (int loopCount = 0; loopCount < 5; loopCount++) {
+ Thread[] threads = new Thread[threadArraySize];
+ int enumCount = group.enumerate(threads);
+ if (enumCount < threadArraySize) {
+ return Arrays.asList(threads).subList(0, enumCount);
+ }
+ // if there are too many threads to fit into the array, enumerate's result
+ // is >= the array's length; therefore we can't trust that it returned all
+ // the threads. Try again.
+ threadArraySize += 100;
+ }
+ // threads are proliferating too fast for us. Bail before we get into
+ // trouble.
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns the CPU time used by a thread, if possible.
+ * @param thr The thread to query.
+ * @return The CPU time used by {@code thr}, or 0 if it cannot be determined.
+ */
+ private long cpuTime(Thread thr) {
+ ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
+ if (mxBean.isThreadCpuTimeSupported()) {
+ try {
+ return mxBean.getThreadCpuTime(thr.getId());
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+ return 0;
}
private class CallableStatement implements Callable {
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
diff --git a/src/main/java/org/junit/matchers/JUnitMatchers.java b/src/main/java/org/junit/matchers/JUnitMatchers.java
index 5bb48d7..13407cc 100644
--- a/src/main/java/org/junit/matchers/JUnitMatchers.java
+++ b/src/main/java/org/junit/matchers/JUnitMatchers.java
@@ -48,7 +48,7 @@ public class JUnitMatchers {
*/
@Deprecated
public static Matcher> hasItems(Matcher super T>... elementMatchers) {
- return CoreMatchers.hasItems(elementMatchers);
+ return CoreMatchers.hasItems(elementMatchers);
}
/**
@@ -57,7 +57,7 @@ public class JUnitMatchers {
*/
@Deprecated
public static Matcher> everyItem(final Matcher elementMatcher) {
- return CoreMatchers.everyItem((Matcher) elementMatcher);
+ return CoreMatchers.everyItem(elementMatcher);
}
/**
diff --git a/src/main/java/org/junit/rules/DisableOnDebug.java b/src/main/java/org/junit/rules/DisableOnDebug.java
new file mode 100644
index 0000000..3bca103
--- /dev/null
+++ b/src/main/java/org/junit/rules/DisableOnDebug.java
@@ -0,0 +1,125 @@
+package org.junit.rules;
+
+import java.util.List;
+
+import org.junit.internal.management.ManagementFactory;
+import org.junit.internal.management.RuntimeMXBean;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * The {@code DisableOnDebug} Rule allows you to label certain rules to be
+ * disabled when debugging.
+ *
+ * The most illustrative use case is for tests that make use of the
+ * {@link Timeout} rule, when ran in debug mode the test may terminate on
+ * timeout abruptly during debugging. Developers may disable the timeout, or
+ * increase the timeout by making a code change on tests that need debugging and
+ * remember revert the change afterwards or rules such as {@link Timeout} that
+ * may be disabled during debugging may be wrapped in a {@code DisableOnDebug}.
+ *
+ * The important benefit of this feature is that you can disable such rules
+ * without any making any modifications to your test class to remove them during
+ * debugging.
+ *
+ * This does nothing to tackle timeouts or time sensitive code under test when
+ * debugging and may make this less useful in such circumstances.
+ *
+ * Example usage:
+ *
+ *
+ * public static class DisableTimeoutOnDebugSampleTest {
+ *
+ * @Rule
+ * public TestRule timeout = new DisableOnDebug(new Timeout(20));
+ *
+ * @Test
+ * public void myTest() {
+ * int i = 0;
+ * assertEquals(0, i); // suppose you had a break point here to inspect i
+ * }
+ * }
+ *
+ *
+ * @since 4.12
+ */
+public class DisableOnDebug implements TestRule {
+ private final TestRule rule;
+ private final boolean debugging;
+
+ /**
+ * Create a {@code DisableOnDebug} instance with the timeout specified in
+ * milliseconds.
+ *
+ * @param rule to disable during debugging
+ */
+ public DisableOnDebug(TestRule rule) {
+ this(rule, ManagementFactory.getRuntimeMXBean()
+ .getInputArguments());
+ }
+
+ /**
+ * Visible for testing purposes only.
+ *
+ * @param rule the rule to disable during debugging
+ * @param inputArguments
+ * arguments provided to the Java runtime
+ */
+ DisableOnDebug(TestRule rule, List inputArguments) {
+ this.rule = rule;
+ debugging = isDebugging(inputArguments);
+ }
+
+ /**
+ * @see TestRule#apply(Statement, Description)
+ */
+ public Statement apply(Statement base, Description description) {
+ if (debugging) {
+ return base;
+ } else {
+ return rule.apply(base, description);
+ }
+ }
+
+ /**
+ * Parses arguments passed to the runtime environment for debug flags
+ *
+ *
+ *
+ * @param arguments
+ * the arguments passed to the runtime environment, usually this
+ * will be {@link RuntimeMXBean#getInputArguments()}
+ * @return true if the current JVM was started in debug mode, false
+ * otherwise.
+ */
+ private static boolean isDebugging(List arguments) {
+ for (final String argument : arguments) {
+ if ("-Xdebug".equals(argument) || argument.startsWith("-agentlib:jdwp")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if the JVM is in debug mode. This method may be used
+ * by test classes to take additional action to disable code paths that
+ * interfere with debugging if required.
+ *
+ * @return {@code true} if the current JVM is in debug mode, {@code false}
+ * otherwise
+ */
+ public boolean isDebugging() {
+ return debugging;
+ }
+
+}
diff --git a/src/main/java/org/junit/rules/ErrorCollector.java b/src/main/java/org/junit/rules/ErrorCollector.java
index 8c6600e..9711e50 100644
--- a/src/main/java/org/junit/rules/ErrorCollector.java
+++ b/src/main/java/org/junit/rules/ErrorCollector.java
@@ -1,11 +1,14 @@
package org.junit.rules;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertThrows;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
+import org.junit.function.ThrowingRunnable;
+import org.junit.internal.AssumptionViolatedException;
import org.hamcrest.Matcher;
import org.junit.runners.model.MultipleFailureException;
@@ -43,7 +46,16 @@ public class ErrorCollector extends Verifier {
* Adds a Throwable to the table. Execution continues, but the test will fail at the end.
*/
public void addError(Throwable error) {
- errors.add(error);
+ if (error == null) {
+ throw new NullPointerException("Error cannot be null");
+ }
+ if (error instanceof AssumptionViolatedException) {
+ AssertionError e = new AssertionError(error.getMessage());
+ e.initCause(error);
+ errors.add(e);
+ } else {
+ errors.add(error);
+ }
}
/**
@@ -76,9 +88,33 @@ public class ErrorCollector extends Verifier {
public T checkSucceeds(Callable callable) {
try {
return callable.call();
+ } catch (AssumptionViolatedException e) {
+ AssertionError error = new AssertionError("Callable threw AssumptionViolatedException");
+ error.initCause(e);
+ addError(error);
+ return null;
} catch (Throwable e) {
addError(e);
return null;
}
}
+
+ /**
+ * Adds a failure to the table if {@code runnable} does not throw an
+ * exception of type {@code expectedThrowable} when executed.
+ * Execution continues, but the test will fail at the end if the runnable
+ * does not throw an exception, or if it throws a different exception.
+ *
+ * @param expectedThrowable the expected type of the exception
+ * @param runnable a function that is expected to throw an exception when executed
+ * @since 4.13
+ */
+ public void checkThrows(Class extends Throwable> expectedThrowable, ThrowingRunnable runnable) {
+ try {
+ assertThrows(expectedThrowable, runnable);
+ } catch (AssertionError e) {
+ addError(e);
+ }
+ }
+
}
diff --git a/src/main/java/org/junit/rules/ExpectedException.java b/src/main/java/org/junit/rules/ExpectedException.java
index 4d61712..431ad49 100644
--- a/src/main/java/org/junit/rules/ExpectedException.java
+++ b/src/main/java/org/junit/rules/ExpectedException.java
@@ -7,7 +7,6 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause;
import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
-
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.junit.AssumptionViolatedException;
@@ -21,7 +20,7 @@ import org.junit.runners.model.Statement;
*
*
public class SimpleExpectedExceptionTest {
* @Rule
- * public ExpectedException thrown= ExpectedException.none();
+ * public ExpectedException thrown = ExpectedException.none();
*
* @Test
* public void throwsNothing() {
@@ -35,16 +34,19 @@ import org.junit.runners.model.Statement;
* }
* }
*
- *
- * You have to add the {@code ExpectedException} rule to your test.
+ *
You have to add the {@code ExpectedException} rule to your test.
* This doesn't affect your existing tests (see {@code throwsNothing()}).
- * After specifiying the type of the expected exception your test is
+ * After specifying the type of the expected exception your test is
* successful when such an exception is thrown and it fails if a
* different or no exception is thrown.
*
- *
- * Instead of specifying the exception's type you can characterize the
- * expected exception based on other criterias, too:
+ *
This rule does not perform any special magic to make execution continue
+ * as if the exception had not been thrown. So it is nearly always a mistake
+ * for a test method to have statements after the one that is expected to
+ * throw the exception.
+ *
+ *
Instead of specifying the exception's type you can characterize the
+ * expected exception based on other criteria, too:
*
*
*
The exception's message contains a specific text: {@link #expectMessage(String)}
The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}
*
*
- *
- * You can combine any of the presented expect-methods. The test is
+ *
You can combine any of the presented expect-methods. The test is
* successful if all specifications are met.
*
@Test
* public void throwsException() {
@@ -63,9 +64,15 @@ import org.junit.runners.model.Statement;
* throw new NullPointerException("What happened?");
* }
*
+ *
It is recommended to set the {@link org.junit.Rule#order() order} of the
+ * {@code ExpectedException} to {@code Integer.MAX_VALUE} if it is used together
+ * with another rule that handles exceptions, e.g. {@link ErrorCollector}.
+ * Otherwise failing tests may be successful.
+ *
@Rule(order = Integer.MAX_VALUE)
+ * public ExpectedException thrown = ExpectedException.none();
+ *
*
AssumptionViolatedExceptions
- *
- * JUnit uses {@link AssumptionViolatedException}s for indicating that a test
+ *
JUnit uses {@link AssumptionViolatedException}s for indicating that a test
* provides no useful information. (See {@link org.junit.Assume} for more
* information.) You have to call {@code assume} methods before you set
* expectations of the {@code ExpectedException} rule. In this case the rule
@@ -80,8 +87,7 @@ import org.junit.runners.model.Statement;
*
*
AssertionErrors
*
- *
- * JUnit uses {@link AssertionError}s for indicating that a test is failing. You
+ *
JUnit uses {@link AssertionError}s for indicating that a test is failing. You
* have to call {@code assert} methods before you set expectations of the
* {@code ExpectedException} rule, if they should be handled by the framework.
* E.g. the following test fails because of the {@code assertTrue} statement.
@@ -93,8 +99,7 @@ import org.junit.runners.model.Statement;
* }
*
*
Missing Exceptions
- *
- * By default missing exceptions are reported with an error message
+ *
By default missing exceptions are reported with an error message
* like "Expected test to throw an instance of foo". You can configure a different
* message by means of {@link #reportMissingExceptionWithMessage(String)}. You
* can use a {@code %s} placeholder for the description of the expected
@@ -107,7 +112,13 @@ public class ExpectedException implements TestRule {
/**
* Returns a {@linkplain TestRule rule} that expects no exception to
* be thrown (identical to behavior without this rule).
+ *
+ * @deprecated Since 4.13
+ * {@link org.junit.Assert#assertThrows(Class, org.junit.function.ThrowingRunnable)
+ * Assert.assertThrows} can be used to verify that your code throws a specific
+ * exception.
*/
+ @Deprecated
public static ExpectedException none() {
return new ExpectedException();
}
@@ -222,10 +233,18 @@ public class ExpectedException implements TestRule {
* throw new IllegalArgumentException("What happened?", cause);
* }
*/
- public void expectCause(Matcher extends Throwable> expectedCause) {
+ public void expectCause(Matcher> expectedCause) {
expect(hasCause(expectedCause));
}
+ /**
+ * Check if any Exception is expected.
+ * @since 4.13
+ */
+ public final boolean isAnyExceptionExpected() {
+ return matcherBuilder.expectsThrowable();
+ }
+
private class ExpectedExceptionStatement extends Statement {
private final Statement next;
@@ -255,10 +274,6 @@ public class ExpectedException implements TestRule {
}
}
- private boolean isAnyExceptionExpected() {
- return matcherBuilder.expectsThrowable();
- }
-
private void failDueToMissingException() throws AssertionError {
fail(missingExceptionMessage());
}
diff --git a/src/main/java/org/junit/rules/ExternalResource.java b/src/main/java/org/junit/rules/ExternalResource.java
index 71ca287..71fc842 100644
--- a/src/main/java/org/junit/rules/ExternalResource.java
+++ b/src/main/java/org/junit/rules/ExternalResource.java
@@ -1,6 +1,10 @@
package org.junit.rules;
+import java.util.ArrayList;
+import java.util.List;
+
import org.junit.runner.Description;
+import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
/**
@@ -44,11 +48,20 @@ public abstract class ExternalResource implements TestRule {
@Override
public void evaluate() throws Throwable {
before();
+
+ List errors = new ArrayList();
try {
base.evaluate();
+ } catch (Throwable t) {
+ errors.add(t);
} finally {
- after();
+ try {
+ after();
+ } catch (Throwable t) {
+ errors.add(t);
+ }
}
+ MultipleFailureException.assertEmpty(errors);
}
};
}
diff --git a/src/main/java/org/junit/rules/MethodRule.java b/src/main/java/org/junit/rules/MethodRule.java
index 823ee78..94608f5 100644
--- a/src/main/java/org/junit/rules/MethodRule.java
+++ b/src/main/java/org/junit/rules/MethodRule.java
@@ -10,21 +10,9 @@ import org.junit.runners.model.Statement;
* {@link Statement} that executes the method is passed to each annotated
* {@link Rule} in turn, and each may return a substitute or modified
* {@link Statement}, which is passed to the next {@link Rule}, if any. For
- * examples of how this can be useful, see these provided MethodRules,
- * or write your own:
+ * an example of how this can be useful, see {@link TestWatchman}.
*
- *
- *
{@link ErrorCollector}: collect multiple errors in one test method
- *
{@link ExpectedException}: make flexible assertions about thrown exceptions
- *
{@link ExternalResource}: start and stop a server, for example
- *
{@link TemporaryFolder}: create fresh files, and delete after test
- *
{@link TestName}: remember the test name for use during the method
- *
{@link TestWatchman}: add logic at events during method execution
- *
{@link Timeout}: cause test to fail after a set time
- *
{@link Verifier}: fail test if object state ends up incorrect
- *
- *
- * Note that {@link MethodRule} has been replaced by {@link TestRule},
+ *
Note that {@link MethodRule} has been replaced by {@link TestRule},
* which has the added benefit of supporting class rules.
*
* @since 4.7
diff --git a/src/main/java/org/junit/rules/RuleChain.java b/src/main/java/org/junit/rules/RuleChain.java
index f43d8f5..bf93aae 100644
--- a/src/main/java/org/junit/rules/RuleChain.java
+++ b/src/main/java/org/junit/rules/RuleChain.java
@@ -4,26 +4,34 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.junit.Rule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
- * The RuleChain rule allows ordering of TestRules. You create a
+ * The {@code RuleChain} can be used for creating composite rules. You create a
* {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of
* {@link #around(TestRule)}:
*
*
+ * public class UseRuleChain {
+ * @Rule
+ * public final TestRule extendedLogging = CompositeRules.extendedLogging();
*
- * @Test
- * public void example() {
- * assertTrue(true);
- * }
+ * @Test
+ * public void example() {
+ * assertTrue(true);
+ * }
* }
*
*
@@ -38,6 +46,13 @@ import org.junit.runners.model.Statement;
* finished outer rule
*
*
+ * In older versions of JUnit (before 4.13) {@code RuleChain} was used for
+ * ordering rules. We recommend to not use it for this purpose anymore. You can
+ * use the attribute {@code order} of the annotation {@link Rule#order() Rule}
+ * or {@link org.junit.ClassRule#order() ClassRule} for ordering rules.
+ *
+ * @see org.junit.Rule#order()
+ * @see org.junit.ClassRule#order()
* @since 4.10
*/
public class RuleChain implements TestRule {
@@ -72,13 +87,17 @@ public class RuleChain implements TestRule {
}
/**
- * Create a new {@code RuleChain}, which encloses the {@code nextRule} with
+ * Create a new {@code RuleChain}, which encloses the given {@link TestRule} with
* the rules of the current {@code RuleChain}.
*
- * @param enclosedRule the rule to enclose.
+ * @param enclosedRule the rule to enclose; must not be {@code null}.
* @return a new {@code RuleChain}.
+ * @throws NullPointerException if the argument {@code enclosedRule} is {@code null}
*/
public RuleChain around(TestRule enclosedRule) {
+ if (enclosedRule == null) {
+ throw new NullPointerException("The enclosed rule must not be null");
+ }
List rulesOfNewChain = new ArrayList();
rulesOfNewChain.add(enclosedRule);
rulesOfNewChain.addAll(rulesStartingWithInnerMost);
@@ -89,9 +108,6 @@ public class RuleChain implements TestRule {
* {@inheritDoc}
*/
public Statement apply(Statement base, Description description) {
- for (TestRule each : rulesStartingWithInnerMost) {
- base = each.apply(base, description);
- }
- return base;
+ return new RunRules(base, rulesStartingWithInnerMost, description);
}
}
\ No newline at end of file
diff --git a/src/main/java/org/junit/rules/Stopwatch.java b/src/main/java/org/junit/rules/Stopwatch.java
index 5d34e7f..6900a48 100644
--- a/src/main/java/org/junit/rules/Stopwatch.java
+++ b/src/main/java/org/junit/rules/Stopwatch.java
@@ -76,7 +76,7 @@ import java.util.concurrent.TimeUnit;
* @author tibor17
* @since 4.12
*/
-public abstract class Stopwatch implements TestRule {
+public class Stopwatch implements TestRule {
private final Clock clock;
private volatile long startNanos;
private volatile long endNanos;
diff --git a/src/main/java/org/junit/rules/TemporaryFolder.java b/src/main/java/org/junit/rules/TemporaryFolder.java
index dc75c93..a726c66 100644
--- a/src/main/java/org/junit/rules/TemporaryFolder.java
+++ b/src/main/java/org/junit/rules/TemporaryFolder.java
@@ -1,15 +1,20 @@
package org.junit.rules;
+import static org.junit.Assert.fail;
+
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import org.junit.Rule;
/**
* The TemporaryFolder Rule allows creation of files and folders that should
* be deleted when the test method finishes (whether it passes or
- * fails). Whether the deletion is successful or not is not checked by this rule.
- * No exception will be thrown in case the deletion fails.
+ * fails).
+ * By default no exception will be thrown in case the deletion fails.
*
*
Example of usage:
*
@@ -26,18 +31,104 @@ import org.junit.Rule;
* }
*
*
+ *
TemporaryFolder rule supports assured deletion mode, which
+ * will fail the test in case deletion fails with {@link AssertionError}.
+ *
+ *
Creating TemporaryFolder with assured deletion:
+ *
+ *
* @since 4.7
*/
public class TemporaryFolder extends ExternalResource {
private final File parentFolder;
+ private final boolean assureDeletion;
private File folder;
+ private static final int TEMP_DIR_ATTEMPTS = 10000;
+ private static final String TMP_PREFIX = "junit";
+
+ /**
+ * Create a temporary folder which uses system default temporary-file
+ * directory to create temporary resources.
+ */
public TemporaryFolder() {
- this(null);
+ this((File) null);
}
+ /**
+ * Create a temporary folder which uses the specified directory to create
+ * temporary resources.
+ *
+ * @param parentFolder folder where temporary resources will be created.
+ * If {@code null} then system default temporary-file directory is used.
+ */
public TemporaryFolder(File parentFolder) {
this.parentFolder = parentFolder;
+ this.assureDeletion = false;
+ }
+
+ /**
+ * Create a {@link TemporaryFolder} initialized with
+ * values from a builder.
+ */
+ protected TemporaryFolder(Builder builder) {
+ this.parentFolder = builder.parentFolder;
+ this.assureDeletion = builder.assureDeletion;
+ }
+
+ /**
+ * Returns a new builder for building an instance of {@link TemporaryFolder}.
+ *
+ * @since 4.13
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builds an instance of {@link TemporaryFolder}.
+ *
+ * @since 4.13
+ */
+ public static class Builder {
+ private File parentFolder;
+ private boolean assureDeletion;
+
+ protected Builder() {}
+
+ /**
+ * Specifies which folder to use for creating temporary resources.
+ * If {@code null} then system default temporary-file directory is
+ * used.
+ *
+ * @return this
+ */
+ public Builder parentFolder(File parentFolder) {
+ this.parentFolder = parentFolder;
+ return this;
+ }
+
+ /**
+ * Setting this flag assures that no resources are left undeleted. Failure
+ * to fulfill the assurance results in failure of tests with an
+ * {@link AssertionError}.
+ *
+ * @return this
+ */
+ public Builder assureDeletion() {
+ this.assureDeletion = true;
+ return this;
+ }
+
+ /**
+ * Builds a {@link TemporaryFolder} instance using the values in this builder.
+ */
+ public TemporaryFolder build() {
+ return new TemporaryFolder(this);
+ }
}
@Override
@@ -75,52 +166,63 @@ public class TemporaryFolder extends ExternalResource {
* Returns a new fresh file with a random name under the temporary folder.
*/
public File newFile() throws IOException {
- return File.createTempFile("junit", null, getRoot());
+ return File.createTempFile(TMP_PREFIX, null, getRoot());
}
/**
- * Returns a new fresh folder with the given name under the temporary
+ * Returns a new fresh folder with the given path under the temporary
* folder.
*/
- public File newFolder(String folder) throws IOException {
- return newFolder(new String[]{folder});
+ public File newFolder(String path) throws IOException {
+ return newFolder(new String[]{path});
}
/**
- * Returns a new fresh folder with the given name(s) under the temporary
- * folder.
+ * Returns a new fresh folder with the given paths under the temporary
+ * folder. For example, if you pass in the strings {@code "parent"} and {@code "child"}
+ * then a directory named {@code "parent"} will be created under the temporary folder
+ * and a directory named {@code "child"} will be created under the newly-created
+ * {@code "parent"} directory.
*/
- public File newFolder(String... folderNames) throws IOException {
- File file = getRoot();
- for (int i = 0; i < folderNames.length; i++) {
- String folderName = folderNames[i];
- validateFolderName(folderName);
- file = new File(file, folderName);
- if (!file.mkdir() && isLastElementInArray(i, folderNames)) {
- throw new IOException(
- "a folder with the name \'" + folderName + "\' already exists");
- }
+ public File newFolder(String... paths) throws IOException {
+ if (paths.length == 0) {
+ throw new IllegalArgumentException("must pass at least one path");
}
- return file;
- }
-
- /**
- * Validates if multiple path components were used while creating a folder.
- *
- * @param folderName
- * Name of the folder being created
- */
- private void validateFolderName(String folderName) throws IOException {
- File tempFile = new File(folderName);
- if (tempFile.getParent() != null) {
- String errorMsg = "Folder name cannot consist of multiple path components separated by a file separator."
- + " Please use newFolder('MyParentFolder','MyFolder') to create hierarchies of folders";
- throw new IOException(errorMsg);
+
+ /*
+ * Before checking if the paths are absolute paths, check if create() was ever called,
+ * and if it wasn't, throw IllegalStateException.
+ */
+ File root = getRoot();
+ for (String path : paths) {
+ if (new File(path).isAbsolute()) {
+ throw new IOException("folder path \'" + path + "\' is not a relative path");
+ }
}
- }
- private boolean isLastElementInArray(int index, String[] array) {
- return index == array.length - 1;
+ File relativePath = null;
+ File file = root;
+ boolean lastMkdirsCallSuccessful = true;
+ for (String path : paths) {
+ relativePath = new File(relativePath, path);
+ file = new File(root, relativePath.getPath());
+
+ lastMkdirsCallSuccessful = file.mkdirs();
+ if (!lastMkdirsCallSuccessful && !file.isDirectory()) {
+ if (file.exists()) {
+ throw new IOException(
+ "a file with the path \'" + relativePath.getPath() + "\' exists");
+ } else {
+ throw new IOException(
+ "could not create a folder with the path \'" + relativePath.getPath() + "\'");
+ }
+ }
+ }
+ if (!lastMkdirsCallSuccessful) {
+ throw new IOException(
+ "a folder with the path \'" + relativePath.getPath() + "\' already exists");
+ }
+ return file;
}
/**
@@ -130,11 +232,63 @@ public class TemporaryFolder extends ExternalResource {
return createTemporaryFolderIn(getRoot());
}
- private File createTemporaryFolderIn(File parentFolder) throws IOException {
- File createdFolder = File.createTempFile("junit", "", parentFolder);
- createdFolder.delete();
- createdFolder.mkdir();
- return createdFolder;
+ private static File createTemporaryFolderIn(File parentFolder) throws IOException {
+ try {
+ return createTemporaryFolderWithNioApi(parentFolder);
+ } catch (ClassNotFoundException ignore) {
+ // Fallback for Java 5 and 6
+ return createTemporaryFolderWithFileApi(parentFolder);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ }
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ }
+ IOException exception = new IOException("Failed to create temporary folder in " + parentFolder);
+ exception.initCause(cause);
+ throw exception;
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create temporary folder in " + parentFolder, e);
+ }
+ }
+
+ private static File createTemporaryFolderWithNioApi(File parentFolder) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ Class> filesClass = Class.forName("java.nio.file.Files");
+ Object fileAttributeArray = Array.newInstance(Class.forName("java.nio.file.attribute.FileAttribute"), 0);
+ Class> pathClass = Class.forName("java.nio.file.Path");
+ Object tempDir;
+ if (parentFolder != null) {
+ Method createTempDirectoryMethod = filesClass.getDeclaredMethod("createTempDirectory", pathClass, String.class, fileAttributeArray.getClass());
+ Object parentPath = File.class.getDeclaredMethod("toPath").invoke(parentFolder);
+ tempDir = createTempDirectoryMethod.invoke(null, parentPath, TMP_PREFIX, fileAttributeArray);
+ } else {
+ Method createTempDirectoryMethod = filesClass.getDeclaredMethod("createTempDirectory", String.class, fileAttributeArray.getClass());
+ tempDir = createTempDirectoryMethod.invoke(null, TMP_PREFIX, fileAttributeArray);
+ }
+ return (File) pathClass.getDeclaredMethod("toFile").invoke(tempDir);
+ }
+
+ private static File createTemporaryFolderWithFileApi(File parentFolder) throws IOException {
+ File createdFolder = null;
+ for (int i = 0; i < TEMP_DIR_ATTEMPTS; ++i) {
+ // Use createTempFile to get a suitable folder name.
+ String suffix = ".tmp";
+ File tmpFile = File.createTempFile(TMP_PREFIX, suffix, parentFolder);
+ String tmpName = tmpFile.toString();
+ // Discard .tmp suffix of tmpName.
+ String folderName = tmpName.substring(0, tmpName.length() - suffix.length());
+ createdFolder = new File(folderName);
+ if (createdFolder.mkdir()) {
+ tmpFile.delete();
+ return createdFolder;
+ }
+ tmpFile.delete();
+ }
+ throw new IOException("Unable to create temporary directory in: "
+ + parentFolder.toString() + ". Tried " + TEMP_DIR_ATTEMPTS + " times. "
+ + "Last attempted to create: " + createdFolder.toString());
}
/**
@@ -150,21 +304,48 @@ public class TemporaryFolder extends ExternalResource {
/**
* Delete all files and folders under the temporary folder. Usually not
- * called directly, since it is automatically applied by the {@link Rule}
+ * called directly, since it is automatically applied by the {@link Rule}.
+ *
+ * @throws AssertionError if unable to clean up resources
+ * and deletion of resources is assured.
*/
public void delete() {
- if (folder != null) {
- recursiveDelete(folder);
+ if (!tryDelete()) {
+ if (assureDeletion) {
+ fail("Unable to clean up temporary folder " + folder);
+ }
+ }
+ }
+
+ /**
+ * Tries to delete all files and folders under the temporary folder and
+ * returns whether deletion was successful or not.
+ *
+ * @return {@code true} if all resources are deleted successfully,
+ * {@code false} otherwise.
+ */
+ private boolean tryDelete() {
+ if (folder == null) {
+ return true;
}
+
+ return recursiveDelete(folder);
}
- private void recursiveDelete(File file) {
+ private boolean recursiveDelete(File file) {
+ // Try deleting file before assuming file is a directory
+ // to prevent following symbolic links.
+ if (file.delete()) {
+ return true;
+ }
File[] files = file.listFiles();
if (files != null) {
for (File each : files) {
- recursiveDelete(each);
+ if (!recursiveDelete(each)) {
+ return false;
+ }
}
}
- file.delete();
+ return file.delete();
}
}
diff --git a/src/main/java/org/junit/rules/TestName.java b/src/main/java/org/junit/rules/TestName.java
index bf72602..e2ebc2e 100644
--- a/src/main/java/org/junit/rules/TestName.java
+++ b/src/main/java/org/junit/rules/TestName.java
@@ -25,7 +25,7 @@ import org.junit.runner.Description;
* @since 4.7
*/
public class TestName extends TestWatcher {
- private String name;
+ private volatile String name;
@Override
protected void starting(Description d) {
diff --git a/src/main/java/org/junit/rules/TestWatcher.java b/src/main/java/org/junit/rules/TestWatcher.java
index 5492b6b..a28514d 100644
--- a/src/main/java/org/junit/rules/TestWatcher.java
+++ b/src/main/java/org/junit/rules/TestWatcher.java
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import org.junit.AssumptionViolatedException;
+import org.junit.Rule;
import org.junit.runner.Description;
import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
@@ -17,7 +18,7 @@ import org.junit.runners.model.Statement;
* public static class WatchmanTest {
* private static String watchedLog;
*
- * @Rule
+ * @Rule(order = Integer.MIN_VALUE)
* public TestWatcher watchman= new TestWatcher() {
* @Override
* protected void failed(Throwable e, Description description) {
@@ -40,6 +41,11 @@ import org.junit.runners.model.Statement;
* }
* }
*
+ *
It is recommended to always set the {@link Rule#order() order} of the
+ * {@code TestWatcher} to {@code Integer.MIN_VALUE} so that it encloses all
+ * other rules. Otherwise it may see failed tests as successful and vice versa
+ * if some rule changes the result of a test (e.g. {@link ErrorCollector} or
+ * {@link ExpectedException}).
*
* @since 4.9
*/
@@ -54,7 +60,7 @@ public abstract class TestWatcher implements TestRule {
try {
base.evaluate();
succeededQuietly(description, errors);
- } catch (@SuppressWarnings("deprecation") org.junit.internal.AssumptionViolatedException e) {
+ } catch (org.junit.internal.AssumptionViolatedException e) {
errors.add(e);
skippedQuietly(e, description, errors);
} catch (Throwable e) {
@@ -87,7 +93,6 @@ public abstract class TestWatcher implements TestRule {
}
}
- @SuppressWarnings("deprecation")
private void skippedQuietly(
org.junit.internal.AssumptionViolatedException e, Description description,
List errors) {
@@ -135,7 +140,6 @@ public abstract class TestWatcher implements TestRule {
/**
* Invoked when a test is skipped due to a failed assumption.
*/
- @SuppressWarnings("deprecation")
protected void skipped(AssumptionViolatedException e, Description description) {
// For backwards compatibility with JUnit 4.11 and earlier, call the legacy version
org.junit.internal.AssumptionViolatedException asInternalException = e;
diff --git a/src/main/java/org/junit/rules/Timeout.java b/src/main/java/org/junit/rules/Timeout.java
index 8d382df..334a923 100644
--- a/src/main/java/org/junit/rules/Timeout.java
+++ b/src/main/java/org/junit/rules/Timeout.java
@@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit;
* public static class HasGlobalLongTimeout {
*
* @Rule
- * public Timeout globalTimeout= new Timeout(20);
+ * public Timeout globalTimeout = Timeout.millis(20);
*
* @Test
* public void run1() throws InterruptedException {
@@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit;
public class Timeout implements TestRule {
private final long timeout;
private final TimeUnit timeUnit;
+ private final boolean lookForStuckThread;
/**
* Returns a new builder for building an instance.
@@ -79,10 +80,11 @@ public class Timeout implements TestRule {
public Timeout(long timeout, TimeUnit timeUnit) {
this.timeout = timeout;
this.timeUnit = timeUnit;
+ lookForStuckThread = false;
}
/**
- * Create a {@code Timeout} instance initialized with values form
+ * Create a {@code Timeout} instance initialized with values from
* a builder.
*
* @since 4.12
@@ -90,6 +92,7 @@ public class Timeout implements TestRule {
protected Timeout(Builder builder) {
timeout = builder.getTimeout();
timeUnit = builder.getTimeUnit();
+ lookForStuckThread = builder.getLookingForStuckThread();
}
/**
@@ -121,6 +124,16 @@ public class Timeout implements TestRule {
return unit.convert(timeout, timeUnit);
}
+ /**
+ * Gets whether this {@code Timeout} will look for a stuck thread
+ * when the test times out.
+ *
+ * @since 4.12
+ */
+ protected final boolean getLookingForStuckThread() {
+ return lookForStuckThread;
+ }
+
/**
* Creates a {@link Statement} that will run the given
* {@code statement}, and timeout the operation based
@@ -133,6 +146,7 @@ public class Timeout implements TestRule {
Statement statement) throws Exception {
return FailOnTimeout.builder()
.withTimeout(timeout, timeUnit)
+ .withLookingForStuckThread(lookForStuckThread)
.build(statement);
}
@@ -190,6 +204,25 @@ public class Timeout implements TestRule {
return timeUnit;
}
+ /**
+ * Specifies whether to look for a stuck thread. If a timeout occurs and this
+ * feature is enabled, the rule will look for a thread that appears to be stuck
+ * and dump its backtrace. This feature is experimental. Behavior may change
+ * after the 4.12 release in response to feedback.
+ *
+ * @param enable {@code true} to enable the feature
+ * @return {@code this} for method chaining.
+ */
+ public Builder withLookingForStuckThread(boolean enable) {
+ this.lookForStuckThread = enable;
+ return this;
+ }
+
+ protected boolean getLookingForStuckThread() {
+ return lookForStuckThread;
+ }
+
+
/**
* Builds a {@link Timeout} instance using the values in this builder.,
*/
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
@@ -124,6 +124,17 @@ public class Description implements Serializable {
return new Description(testClass, testClass.getName(), testClass.getAnnotations());
}
+ /**
+ * Create a Description named after testClass
+ *
+ * @param testClass A not null {@link Class} containing tests
+ * @param annotations meta-data about the test, for downstream interpreters
+ * @return a Description of testClass
+ */
+ public static Description createSuiteDescription(Class> testClass, Annotation... annotations) {
+ return new Description(testClass, testClass.getName(), annotations);
+ }
+
/**
* Describes a Runner which runs 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 fChildren = new ConcurrentLinkedQueue();
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 result = new ArrayList();
-
+ 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 @OrderWith or extends a class annotated
+ * with @OrderWith, 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 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 desiredDescription
+ * 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
+ *
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.
+ *
+ * @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:
*
* private static Comparator<Description> forward() {
- * return new Comparator<Description>() {
- * public int compare(Description o1, Description o2) {
- * return o1.getDisplayName().compareTo(o2.getDisplayName());
- * }
- * };
+ * return new Comparator<Description>() {
+ * public int compare(Description o1, Description o2) {
+ * return o1.getDisplayName().compareTo(o2.getDisplayName());
+ * }
+ * };
* }
*
* public static main() {
- * new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
+ * new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
* }
*
*
@@ -167,4 +171,32 @@ public abstract class Request {
public Request sortWith(Comparator comparator) {
return new SortingRequest(this, comparator);
}
+
+ /**
+ * Returns a Request whose Tests can be run in a certain order, defined by
+ * ordering
+ *
+ * For example, here is code to run a test suite in reverse order:
+ *
+ *
+ * @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 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();
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(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 getFailures() {
return failures;
@@ -86,6 +89,20 @@ public class Result implements Serializable {
return ignoreCount.get();
}
+ /**
+ * 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 true if all tests succeeded
*/
@@ -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 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(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) 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 COMPARATOR = new Comparator() {
+ 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.
+ *
+ *
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 orderer
+ *
+ * @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 order(Collection descriptions)
+ throws InvalidOrderingException {
+ List inOrder = ordering.orderItems(
+ Collections.unmodifiableCollection(descriptions));
+ if (!ordering.validateOrderingIsCorrect()) {
+ return inOrder;
+ }
+
+ Set uniqueDescriptions = new HashSet(descriptions);
+ if (!uniqueDescriptions.containsAll(inOrder)) {
+ throw new InvalidOrderingException("Ordering added items");
+ }
+ Set resultAsSet = new HashSet(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 target.
+ *
+ * @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.
+ *
+ *
In general you will not need to use a Ordering 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 orderItems(Collection descriptions) {
+ List shuffled = new ArrayList(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 target 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 orderItems(Collection 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.
+ *
+ *
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 Sorter orders tests. In general you will not need
- * to use a Sorter directly. Instead, use {@link org.junit.runner.Request#sortWith(Comparator)}.
+ * to use a Sorter directly. Instead, use
+ * {@link org.junit.runner.Request#sortWith(Comparator)}.
*
* @since 4.0
*/
-public class Sorter implements Comparator {
+public class Sorter extends Ordering implements Comparator {
/**
* NULL is a Sorter that leaves elements in an undefined order
*/
@@ -27,17 +32,26 @@ public class Sorter implements Comparator {
* to sort tests
*
* @param comparator the {@link Comparator} to use when sorting tests
+ * @since 4.0
*/
public Sorter(Comparator comparator) {
this.comparator = comparator;
}
/**
- * Sorts the test in runner using comparator
+ * Sorts the tests in target using comparator.
+ *
+ * @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 {
public int compare(Description o1, Description o2) {
return comparator.compare(o1, o2);
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 4.13
+ */
+ @Override
+ protected final List orderItems(Collection 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 sorted = new ArrayList(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
@@ -69,6 +69,34 @@ public class RunListener {
public void testRunFinished(Result result) throws Exception {
}
+ /**
+ * 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}.
+ *
+ *
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.
*
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 safeListeners = new ArrayList(capacity);
- ArrayList failures = new ArrayList(capacity);
+ List safeListeners = new ArrayList(capacity);
+ List failures = new ArrayList(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;
}
/**
@@ -105,6 +105,41 @@ public class RunNotifier {
}.run();
}
+ /**
+ * 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.
*
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;
*
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}
+ *
+ * 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}
+ *
+ * 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) {
diff --git a/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java b/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java
index 4d06199..455341a 100644
--- a/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java
+++ b/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java
@@ -3,8 +3,10 @@ package org.junit.runners;
import static org.junit.internal.runners.rules.RuleMemberValidator.RULE_METHOD_VALIDATOR;
import static org.junit.internal.runners.rules.RuleMemberValidator.RULE_VALIDATOR;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.junit.After;
@@ -21,14 +23,18 @@ import org.junit.internal.runners.statements.InvokeMethod;
import org.junit.internal.runners.statements.RunAfters;
import org.junit.internal.runners.statements.RunBefores;
import org.junit.rules.MethodRule;
-import org.junit.rules.RunRules;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.FrameworkMember;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.MemberValueConsumer;
import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+import org.junit.validator.PublicClassValidator;
+import org.junit.validator.TestClassValidator;
/**
* Implements the JUnit 4 standard test case class model, as defined by the
@@ -55,14 +61,27 @@ import org.junit.runners.model.Statement;
* @since 4.5
*/
public class BlockJUnit4ClassRunner extends ParentRunner {
- private final ConcurrentHashMap methodDescriptions = new ConcurrentHashMap();
+ private static TestClassValidator PUBLIC_CLASS_VALIDATOR = new PublicClassValidator();
+
+ private final ConcurrentMap methodDescriptions = new ConcurrentHashMap();
+
+ /**
+ * Creates a BlockJUnit4ClassRunner to run {@code testClass}
+ *
+ * @throws InitializationError if the test class is malformed.
+ */
+ public BlockJUnit4ClassRunner(Class> testClass) throws InitializationError {
+ super(testClass);
+ }
+
/**
- * Creates a BlockJUnit4ClassRunner to run {@code klass}
+ * Creates a BlockJUnit4ClassRunner to run {@code testClass}.
*
* @throws InitializationError if the test class is malformed.
+ * @since 4.13
*/
- public BlockJUnit4ClassRunner(Class> klass) throws InitializationError {
- super(klass);
+ protected BlockJUnit4ClassRunner(TestClass testClass) throws InitializationError {
+ super(testClass);
}
//
@@ -75,10 +94,16 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
if (isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
- runLeaf(methodBlock(method), description, notifier);
+ Statement statement = new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ methodBlock(method).evaluate();
+ }
+ };
+ runLeaf(statement, description, notifier);
}
}
-
+
/**
* Evaluates whether {@link FrameworkMethod}s are ignored based on the
* {@link Ignore} annotation.
@@ -123,6 +148,7 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
protected void collectInitializationErrors(List errors) {
super.collectInitializationErrors(errors);
+ validatePublicConstructor(errors);
validateNoNonStaticInnerClass(errors);
validateConstructor(errors);
validateInstanceMethods(errors);
@@ -130,6 +156,12 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
validateMethods(errors);
}
+ private void validatePublicConstructor(List errors) {
+ if (getTestClass().getJavaClass() != null) {
+ errors.addAll(PUBLIC_CLASS_VALIDATOR.validateTestClass(getTestClass()));
+ }
+ }
+
protected void validateNoNonStaticInnerClass(List errors) {
if (getTestClass().isANonStaticInnerClass()) {
String gripe = "The inner class " + getTestClass().getName()
@@ -180,6 +212,7 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
* Adds to {@code errors} for each method annotated with {@code @Test},
* {@code @Before}, or {@code @After} that is not a public, void instance
* method with no arguments.
+ * @deprecated
*/
@Deprecated
protected void validateInstanceMethods(List errors) {
@@ -187,7 +220,7 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
validatePublicVoidNoArgMethods(Before.class, false, errors);
validateTestMethods(errors);
- if (computeTestMethods().size() == 0) {
+ if (computeTestMethods().isEmpty()) {
errors.add(new Exception("No runnable methods"));
}
}
@@ -217,6 +250,16 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
return getTestClass().getOnlyConstructor().newInstance();
}
+ /**
+ * Returns a new fixture to run a particular test {@code method} against.
+ * Default implementation executes the no-argument {@link #createTest()} method.
+ *
+ * @since 4.13
+ */
+ protected Object createTest(FrameworkMethod method) throws Exception {
+ return createTest();
+ }
+
/**
* Returns the name that describes {@code method} for {@link Description}s.
* Default implementation is the method's name
@@ -232,10 +275,10 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
* Here is an outline of the default implementation:
*
*
- *
Invoke {@code method} on the result of {@code createTest()}, and
+ *
Invoke {@code method} on the result of {@link #createTest(org.junit.runners.model.FrameworkMethod)}, and
* throw any exceptions thrown by either operation.
- *
HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
- * expecting} attribute, return normally only if the previous step threw an
+ *
HOWEVER, if {@code method}'s {@code @Test} annotation has the {@link Test#expected()}
+ * attribute, return normally only if the previous step threw an
* exception of the correct type, and throw an exception otherwise.
*
HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
* timeout} attribute, throw an exception if the previous step takes more
@@ -257,13 +300,13 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
* This can be overridden in subclasses, either by overriding this method,
* or the implementations creating each sub-statement.
*/
- protected Statement methodBlock(FrameworkMethod method) {
+ protected Statement methodBlock(final FrameworkMethod method) {
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
- return createTest();
+ return createTest(method);
}
}.run();
} catch (Throwable e) {
@@ -276,6 +319,7 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
statement = withBefores(method, test, statement);
statement = withAfters(method, test, statement);
statement = withRules(method, test, statement);
+ statement = withInterruptIsolation(statement);
return statement;
}
@@ -292,21 +336,22 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
/**
* Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
- * has the {@code expecting} attribute, return normally only if {@code next}
+ * has the {@link Test#expected()} attribute, return normally only if {@code next}
* throws an exception of the correct type, and throw an exception
* otherwise.
*/
protected Statement possiblyExpectingExceptions(FrameworkMethod method,
Object test, Statement next) {
Test annotation = method.getAnnotation(Test.class);
- return expectsException(annotation) ? new ExpectException(next,
- getExpectedException(annotation)) : next;
+ Class extends Throwable> expectedExceptionClass = getExpectedException(annotation);
+ return expectedExceptionClass != null ? new ExpectException(next, expectedExceptionClass) : next;
}
/**
* Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
* has the {@code timeout} attribute, throw an exception if {@code next}
* takes more than the specified number of milliseconds.
+ * @deprecated
*/
@Deprecated
protected Statement withPotentialTimeout(FrameworkMethod method,
@@ -348,28 +393,23 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
target);
}
- private Statement withRules(FrameworkMethod method, Object target,
- Statement statement) {
- List testRules = getTestRules(target);
- Statement result = statement;
- result = withMethodRules(method, testRules, target, result);
- result = withTestRules(method, testRules, result);
-
- return result;
- }
-
- private Statement withMethodRules(FrameworkMethod method, List testRules,
- Object target, Statement result) {
- for (org.junit.rules.MethodRule each : getMethodRules(target)) {
- if (!testRules.contains(each)) {
- result = each.apply(result, method, target);
+ private Statement withRules(FrameworkMethod method, Object target, Statement statement) {
+ RuleContainer ruleContainer = new RuleContainer();
+ CURRENT_RULE_CONTAINER.set(ruleContainer);
+ try {
+ List testRules = getTestRules(target);
+ for (MethodRule each : rules(target)) {
+ if (!(each instanceof TestRule && testRules.contains(each))) {
+ ruleContainer.add(each);
+ }
}
+ for (TestRule rule : testRules) {
+ ruleContainer.add(rule);
+ }
+ } finally {
+ CURRENT_RULE_CONTAINER.remove();
}
- return result;
- }
-
- private List getMethodRules(Object target) {
- return rules(target);
+ return ruleContainer.apply(method, describeChild(method), target, statement);
}
/**
@@ -378,27 +418,12 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
* test
*/
protected List rules(Object target) {
- List rules = getTestClass().getAnnotatedMethodValues(target,
- Rule.class, MethodRule.class);
-
- rules.addAll(getTestClass().getAnnotatedFieldValues(target,
- Rule.class, MethodRule.class));
-
- return rules;
- }
-
- /**
- * Returns a {@link Statement}: apply all non-static fields
- * annotated with {@link Rule}.
- *
- * @param statement The base statement
- * @return a RunRules statement if any class-level {@link Rule}s are
- * found, or the base statement
- */
- private Statement withTestRules(FrameworkMethod method, List testRules,
- Statement statement) {
- return testRules.isEmpty() ? statement :
- new RunRules(statement, testRules, describeChild(method));
+ RuleCollector collector = new RuleCollector();
+ getTestClass().collectAnnotatedMethodValues(target, Rule.class, MethodRule.class,
+ collector);
+ getTestClass().collectAnnotatedFieldValues(target, Rule.class, MethodRule.class,
+ collector);
+ return collector.result;
}
/**
@@ -407,13 +432,10 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
* test
*/
protected List getTestRules(Object target) {
- List result = getTestClass().getAnnotatedMethodValues(target,
- Rule.class, TestRule.class);
-
- result.addAll(getTestClass().getAnnotatedFieldValues(target,
- Rule.class, TestRule.class));
-
- return result;
+ RuleCollector collector = new RuleCollector();
+ getTestClass().collectAnnotatedMethodValues(target, Rule.class, TestRule.class, collector);
+ getTestClass().collectAnnotatedFieldValues(target, Rule.class, TestRule.class, collector);
+ return collector.result;
}
private Class extends Throwable> getExpectedException(Test annotation) {
@@ -424,14 +446,28 @@ public class BlockJUnit4ClassRunner extends ParentRunner {
}
}
- private boolean expectsException(Test annotation) {
- return getExpectedException(annotation) != null;
- }
-
private long getTimeout(Test annotation) {
if (annotation == null) {
return 0;
}
return annotation.timeout();
}
+
+ private static final ThreadLocal CURRENT_RULE_CONTAINER =
+ new ThreadLocal();
+
+ private static class RuleCollector implements MemberValueConsumer {
+ final List result = new ArrayList();
+
+ public void accept(FrameworkMember> member, T value) {
+ Rule rule = member.getAnnotation(Rule.class);
+ if (rule != null) {
+ RuleContainer container = CURRENT_RULE_CONTAINER.get();
+ if (container != null) {
+ container.setOrder(value, rule.order());
+ }
+ }
+ result.add(value);
+ }
+ }
}
diff --git a/src/main/java/org/junit/runners/JUnit4.java b/src/main/java/org/junit/runners/JUnit4.java
index 6ba28c2..28eafb3 100644
--- a/src/main/java/org/junit/runners/JUnit4.java
+++ b/src/main/java/org/junit/runners/JUnit4.java
@@ -1,6 +1,7 @@
package org.junit.runners;
import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.TestClass;
/**
* Aliases the current default JUnit 4 class runner, for future-proofing. If
@@ -19,6 +20,6 @@ public final class JUnit4 extends BlockJUnit4ClassRunner {
* Constructs a new instance of the default runner
*/
public JUnit4(Class> klass) throws InitializationError {
- super(klass);
+ super(new TestClass(klass));
}
}
diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java
index 829c8f0..d11b66a 100644
--- a/src/main/java/org/junit/runners/Parameterized.java
+++ b/src/main/java/org/junit/runners/Parameterized.java
@@ -1,5 +1,6 @@
package org.junit.runners;
+import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
@@ -8,12 +9,18 @@ import java.lang.annotation.Target;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import org.junit.internal.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
import org.junit.runner.Runner;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.InvalidTestClassError;
import org.junit.runners.model.TestClass;
import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory;
import org.junit.runners.parameterized.ParametersRunnerFactory;
@@ -24,34 +31,37 @@ import org.junit.runners.parameterized.TestWithParameters;
* When running a parameterized test class, instances are created for the
* cross-product of the test methods and the test data elements.
*
- * For example, to test a Fibonacci function, write:
+ * For example, to test the + operator, write:
*
- * Each instance of FibonacciTest will be constructed using the
- * two-argument constructor and the data values in the
+ * Each instance of AdditionTest will be constructed using the
+ * three-argument constructor and the data values in the
* @Parameters method.
*
* In order that you can easily identify the individual tests, you may provide a
@@ -69,33 +79,36 @@ import org.junit.runners.parameterized.TestWithParameters;
*
*
* In the example given above, the Parameterized runner creates
- * names like [1: fib(3)=2]. If you don't use the name parameter,
+ * names like [2: 3 + 2 = 5]. If you don't use the name parameter,
* then the current parameter index is used as name.
*
- * Each instance of FibonacciTest will be constructed with the default constructor
+ * Each instance of AdditionTest will be constructed with the default constructor
* and fields annotated by @Parameter will be initialized
* with the data values in the @Parameters method.
*
@@ -105,8 +118,7 @@ import org.junit.runners.parameterized.TestWithParameters;
*
Executing code before/after executing tests for specific parameters
+ *
+ * If your test needs to perform some preparation or cleanup based on the
+ * parameters, this can be done by adding public static methods annotated with
+ * {@code @BeforeParam}/{@code @AfterParam}. Such methods should either have no
+ * parameters or the same parameters as the test.
+ *
* By default the {@code Parameterized} runner creates a slightly modified
@@ -141,7 +166,7 @@ import org.junit.runners.parameterized.TestWithParameters;
* The factory must have a public zero-arg constructor.
*
*
- * public class YourRunnerFactory implements ParameterizedRunnerFactory {
+ * public class YourRunnerFactory implements ParametersRunnerFactory {
* public Runner createRunnerForTestWithParameters(TestWithParameters test)
* throws InitializationError {
* return YourRunner(test);
@@ -160,6 +185,21 @@ import org.junit.runners.parameterized.TestWithParameters;
* }
*
*
+ *
Avoid creating parameters
+ *
With {@link org.junit.Assume assumptions} you can dynamically skip tests.
+ * Assumptions are also supported by the @Parameters method.
+ * Creating parameters is stopped when the assumption fails and none of the
+ * tests in the test class is executed. JUnit reports a
+ * {@link Result#getAssumptionFailureCount() single assumption failure} for the
+ * whole test class in this case.
+ *