aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Bentley <prb@google.com>2021-02-21 18:26:28 +0000
committerPete Bentley <prb@google.com>2021-03-02 23:54:01 +0000
commit8e80a2a7b89329f95cb41e8b2981044362478c04 (patch)
treec863bdca20bca95fc2dcfa52c63c84b048d61751
parent565f36d28118dce0c0a08fe71924dcd25e039022 (diff)
downloadjunit-8e80a2a7b89329f95cb41e8b2981044362478c04.tar.gz
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. This change re-lands https://r.android.com/1598413. Bug: 129054170 Test: m Change-Id: I6135799c8be5db2ec4c3f13951c18c072427e30d
-rw-r--r--README.version7
-rw-r--r--src/main/java/junit/extensions/ActiveTestSuite.java2
-rw-r--r--src/main/java/junit/extensions/TestDecorator.java1
-rw-r--r--src/main/java/junit/framework/Assert.java78
-rw-r--r--src/main/java/junit/framework/ComparisonCompactor.java1
-rw-r--r--src/main/java/junit/framework/JUnit4TestAdapter.java25
-rw-r--r--src/main/java/junit/framework/Protectable.java4
-rw-r--r--src/main/java/junit/framework/TestCase.java47
-rw-r--r--src/main/java/junit/framework/TestFailure.java8
-rw-r--r--src/main/java/junit/framework/TestResult.java10
-rw-r--r--src/main/java/junit/framework/TestSuite.java29
-rw-r--r--src/main/java/junit/runner/BaseTestRunner.java9
-rw-r--r--src/main/java/junit/runner/TestRunListener.java16
-rw-r--r--src/main/java/junit/runner/Version.java2
-rw-r--r--src/main/java/junit/textui/TestRunner.java4
-rw-r--r--[-rwxr-xr-x]src/main/java/org/junit/Assert.java170
-rw-r--r--src/main/java/org/junit/Assume.java19
-rw-r--r--src/main/java/org/junit/AssumptionViolatedException.java4
-rw-r--r--src/main/java/org/junit/ClassRule.java33
-rw-r--r--src/main/java/org/junit/ComparisonFailure.java2
-rw-r--r--src/main/java/org/junit/Rule.java39
-rw-r--r--src/main/java/org/junit/Test.java39
-rw-r--r--src/main/java/org/junit/TestCouldNotBeSkippedException.java19
-rw-r--r--src/main/java/org/junit/experimental/categories/Categories.java108
-rw-r--r--src/main/java/org/junit/experimental/categories/CategoryFilterFactory.java6
-rw-r--r--src/main/java/org/junit/experimental/max/MaxHistory.java15
-rw-r--r--src/main/java/org/junit/experimental/results/PrintableResult.java9
-rw-r--r--src/main/java/org/junit/experimental/results/ResultMatchers.java35
-rw-r--r--src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java2
-rw-r--r--src/main/java/org/junit/experimental/theories/Theories.java11
-rw-r--r--src/main/java/org/junit/experimental/theories/internal/Assignments.java11
-rw-r--r--src/main/java/org/junit/function/ThrowingRunnable.java14
-rw-r--r--src/main/java/org/junit/internal/ArrayComparisonFailure.java13
-rw-r--r--src/main/java/org/junit/internal/AssumptionViolatedException.java30
-rw-r--r--src/main/java/org/junit/internal/Checks.java37
-rw-r--r--src/main/java/org/junit/internal/Classes.java28
-rw-r--r--src/main/java/org/junit/internal/ComparisonCriteria.java89
-rw-r--r--src/main/java/org/junit/internal/SerializableMatcherDescription.java47
-rw-r--r--src/main/java/org/junit/internal/SerializableValueDescription.java38
-rw-r--r--src/main/java/org/junit/internal/TextListener.java4
-rw-r--r--src/main/java/org/junit/internal/Throwables.java231
-rw-r--r--src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java11
-rw-r--r--src/main/java/org/junit/internal/builders/JUnit4Builder.java6
-rw-r--r--src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java21
-rw-r--r--src/main/java/org/junit/internal/management/FakeThreadMXBean.java27
-rw-r--r--src/main/java/org/junit/internal/management/ManagementFactory.java77
-rw-r--r--src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java61
-rw-r--r--src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java92
-rw-r--r--src/main/java/org/junit/internal/management/RuntimeMXBean.java14
-rw-r--r--src/main/java/org/junit/internal/management/ThreadMXBean.java17
-rw-r--r--src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java9
-rw-r--r--src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java6
-rw-r--r--src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java2
-rw-r--r--src/main/java/org/junit/internal/requests/ClassRequest.java41
-rw-r--r--src/main/java/org/junit/internal/requests/FilterRequest.java2
-rw-r--r--src/main/java/org/junit/internal/requests/MemoizingRequest.java30
-rw-r--r--src/main/java/org/junit/internal/requests/OrderingRequest.java29
-rw-r--r--src/main/java/org/junit/internal/runners/ErrorReportingRunner.java50
-rw-r--r--src/main/java/org/junit/internal/runners/InitializationError.java2
-rw-r--r--src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java22
-rw-r--r--src/main/java/org/junit/internal/runners/MethodValidator.java2
-rw-r--r--src/main/java/org/junit/internal/runners/TestClass.java2
-rw-r--r--src/main/java/org/junit/internal/runners/model/EachTestNotifier.java23
-rw-r--r--src/main/java/org/junit/internal/runners/rules/ValidationError.java3
-rw-r--r--src/main/java/org/junit/internal/runners/statements/ExpectException.java4
-rw-r--r--src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java155
-rw-r--r--src/main/java/org/junit/internal/runners/statements/RunAfters.java9
-rw-r--r--src/main/java/org/junit/internal/runners/statements/RunBefores.java9
-rw-r--r--src/main/java/org/junit/matchers/JUnitMatchers.java4
-rw-r--r--src/main/java/org/junit/rules/DisableOnDebug.java125
-rw-r--r--src/main/java/org/junit/rules/ErrorCollector.java38
-rw-r--r--src/main/java/org/junit/rules/ExpectedException.java57
-rw-r--r--src/main/java/org/junit/rules/ExternalResource.java15
-rw-r--r--src/main/java/org/junit/rules/MethodRule.java16
-rw-r--r--src/main/java/org/junit/rules/RuleChain.java50
-rw-r--r--src/main/java/org/junit/rules/Stopwatch.java2
-rw-r--r--src/main/java/org/junit/rules/TemporaryFolder.java277
-rw-r--r--src/main/java/org/junit/rules/TestName.java2
-rw-r--r--src/main/java/org/junit/rules/TestWatcher.java12
-rw-r--r--src/main/java/org/junit/rules/Timeout.java37
-rw-r--r--src/main/java/org/junit/runner/Computer.java12
-rw-r--r--src/main/java/org/junit/runner/Describable.java2
-rw-r--r--src/main/java/org/junit/runner/Description.java13
-rw-r--r--src/main/java/org/junit/runner/FilterFactory.java3
-rw-r--r--src/main/java/org/junit/runner/JUnitCommandLineParseResult.java8
-rw-r--r--src/main/java/org/junit/runner/OrderWith.java28
-rw-r--r--src/main/java/org/junit/runner/OrderWithValidator.java38
-rw-r--r--src/main/java/org/junit/runner/Request.java58
-rw-r--r--src/main/java/org/junit/runner/Result.java31
-rw-r--r--src/main/java/org/junit/runner/manipulation/Alphanumeric.java27
-rw-r--r--src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java21
-rw-r--r--src/main/java/org/junit/runner/manipulation/Orderable.java21
-rw-r--r--src/main/java/org/junit/runner/manipulation/Orderer.java62
-rw-r--r--src/main/java/org/junit/runner/manipulation/Ordering.java172
-rw-r--r--src/main/java/org/junit/runner/manipulation/Sortable.java2
-rw-r--r--src/main/java/org/junit/runner/manipulation/Sorter.java54
-rw-r--r--src/main/java/org/junit/runner/notification/Failure.java23
-rw-r--r--src/main/java/org/junit/runner/notification/RunListener.java28
-rw-r--r--src/main/java/org/junit/runner/notification/RunNotifier.java41
-rw-r--r--src/main/java/org/junit/runner/notification/SynchronizedRunListener.java33
-rw-r--r--src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java172
-rw-r--r--src/main/java/org/junit/runners/JUnit4.java3
-rw-r--r--src/main/java/org/junit/runners/Parameterized.java405
-rw-r--r--[-rwxr-xr-x]src/main/java/org/junit/runners/ParentRunner.java176
-rw-r--r--src/main/java/org/junit/runners/RuleContainer.java113
-rw-r--r--src/main/java/org/junit/runners/Suite.java4
-rw-r--r--src/main/java/org/junit/runners/model/FrameworkField.java21
-rw-r--r--src/main/java/org/junit/runners/model/FrameworkMember.java24
-rw-r--r--src/main/java/org/junit/runners/model/FrameworkMethod.java14
-rw-r--r--src/main/java/org/junit/runners/model/InitializationError.java2
-rw-r--r--src/main/java/org/junit/runners/model/InvalidTestClassError.java39
-rw-r--r--src/main/java/org/junit/runners/model/MemberValueConsumer.java18
-rw-r--r--src/main/java/org/junit/runners/model/MultipleFailureException.java41
-rw-r--r--src/main/java/org/junit/runners/model/RunnerBuilder.java30
-rw-r--r--[-rwxr-xr-x]src/main/java/org/junit/runners/model/TestClass.java57
-rw-r--r--src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java94
-rw-r--r--src/main/java/org/junit/runners/parameterized/ParametersRunnerFactory.java2
-rw-r--r--src/main/java/org/junit/runners/parameterized/TestWithParameters.java7
-rw-r--r--src/main/java/org/junit/validator/AnnotationValidatorFactory.java3
-rw-r--r--src/main/java/org/junit/validator/AnnotationsValidator.java4
-rw-r--r--src/main/java/org/junit/validator/TestClassValidator.java2
-rw-r--r--src/main/java/org/junit/validator/ValidateWith.java3
-rw-r--r--version2
123 files changed, 3780 insertions, 790 deletions
diff --git a/README.version b/README.version
index fd9d421..2f38283 100644
--- a/README.version
+++ b/README.version
@@ -1,8 +1,5 @@
-URL: https://github.com/junit-team/junit/archive/r4.12.tar.gz
-Version: 4.12
+URL: https://github.com/junit-team/junit/archive/r4.13.2.tar.gz
+Version: 4.13.2
BugComponent: 40416
Local Changes:
- Remove DisableOnDebug (new in 4.12) as it is not supported on Android
- Remove support for stuck threads
- Extra generic type information to aid certain javacs.
diff --git a/src/main/java/junit/extensions/ActiveTestSuite.java b/src/main/java/junit/extensions/ActiveTestSuite.java
index 95c5e2e..6f0f99d 100644
--- a/src/main/java/junit/extensions/ActiveTestSuite.java
+++ b/src/main/java/junit/extensions/ActiveTestSuite.java
@@ -63,7 +63,7 @@ public class ActiveTestSuite extends TestSuite {
}
}
- synchronized public void runFinished() {
+ public synchronized void runFinished() {
fActiveTestDeathCount++;
notifyAll();
}
diff --git a/src/main/java/junit/extensions/TestDecorator.java b/src/main/java/junit/extensions/TestDecorator.java
index 2b74f30..a3c5e08 100644
--- a/src/main/java/junit/extensions/TestDecorator.java
+++ b/src/main/java/junit/extensions/TestDecorator.java
@@ -9,6 +9,7 @@ import junit.framework.TestResult;
* test decorators. Test decorator subclasses can be introduced to add behaviour
* before or after a test is run.
*/
+@SuppressWarnings("deprecation")
public class TestDecorator extends Assert implements Test {
protected Test fTest;
diff --git a/src/main/java/junit/framework/Assert.java b/src/main/java/junit/framework/Assert.java
index 663461c..43482a1 100644
--- a/src/main/java/junit/framework/Assert.java
+++ b/src/main/java/junit/framework/Assert.java
@@ -17,7 +17,7 @@ public class Assert {
* Asserts that a condition is true. If it isn't it throws
* an AssertionFailedError with the given message.
*/
- static public void assertTrue(String message, boolean condition) {
+ public static void assertTrue(String message, boolean condition) {
if (!condition) {
fail(message);
}
@@ -27,7 +27,7 @@ public class Assert {
* Asserts that a condition is true. If it isn't it throws
* an AssertionFailedError.
*/
- static public void assertTrue(boolean condition) {
+ public static void assertTrue(boolean condition) {
assertTrue(null, condition);
}
@@ -35,7 +35,7 @@ public class Assert {
* Asserts that a condition is false. If it isn't it throws
* an AssertionFailedError with the given message.
*/
- static public void assertFalse(String message, boolean condition) {
+ public static void assertFalse(String message, boolean condition) {
assertTrue(message, !condition);
}
@@ -43,14 +43,14 @@ public class Assert {
* Asserts that a condition is false. If it isn't it throws
* an AssertionFailedError.
*/
- static public void assertFalse(boolean condition) {
+ public static void assertFalse(boolean condition) {
assertFalse(null, condition);
}
/**
* Fails a test with the given message.
*/
- static public void fail(String message) {
+ public static void fail(String message) {
if (message == null) {
throw new AssertionFailedError();
}
@@ -60,7 +60,7 @@ public class Assert {
/**
* Fails a test with no message.
*/
- static public void fail() {
+ public static void fail() {
fail(null);
}
@@ -68,7 +68,7 @@ public class Assert {
* Asserts that two objects are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertEquals(String message, Object expected, Object actual) {
+ public static void assertEquals(String message, Object expected, Object actual) {
if (expected == null && actual == null) {
return;
}
@@ -82,14 +82,14 @@ public class Assert {
* Asserts that two objects are equal. If they are not
* an AssertionFailedError is thrown.
*/
- static public void assertEquals(Object expected, Object actual) {
+ public static void assertEquals(Object expected, Object actual) {
assertEquals(null, expected, actual);
}
/**
* Asserts that two Strings are equal.
*/
- static public void assertEquals(String message, String expected, String actual) {
+ public static void assertEquals(String message, String expected, String actual) {
if (expected == null && actual == null) {
return;
}
@@ -103,7 +103,7 @@ public class Assert {
/**
* Asserts that two Strings are equal.
*/
- static public void assertEquals(String expected, String actual) {
+ public static void assertEquals(String expected, String actual) {
assertEquals(null, expected, actual);
}
@@ -112,12 +112,12 @@ public class Assert {
* an AssertionFailedError is thrown with the given message. If the expected
* value is infinity then the delta value is ignored.
*/
- static public void assertEquals(String message, double expected, double actual, double delta) {
+ public static void assertEquals(String message, double expected, double actual, double delta) {
if (Double.compare(expected, actual) == 0) {
return;
}
if (!(Math.abs(expected - actual) <= delta)) {
- failNotEquals(message, new Double(expected), new Double(actual));
+ failNotEquals(message, Double.valueOf(expected), Double.valueOf(actual));
}
}
@@ -125,7 +125,7 @@ public class Assert {
* Asserts that two doubles are equal concerning a delta. If the expected
* value is infinity then the delta value is ignored.
*/
- 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);
}
@@ -134,12 +134,12 @@ public class Assert {
* are not an AssertionFailedError is thrown with the given message. If the
* expected value is infinity then the delta value is ignored.
*/
- static public void assertEquals(String message, float expected, float actual, float delta) {
+ public static void assertEquals(String message, float expected, float actual, float delta) {
if (Float.compare(expected, actual) == 0) {
return;
}
if (!(Math.abs(expected - actual) <= delta)) {
- failNotEquals(message, new Float(expected), new Float(actual));
+ failNotEquals(message, Float.valueOf(expected), Float.valueOf(actual));
}
}
@@ -147,7 +147,7 @@ public class Assert {
* Asserts that two floats are equal concerning a delta. If the expected
* value is infinity then the delta value is ignored.
*/
- 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);
}
@@ -155,14 +155,14 @@ public class Assert {
* Asserts that two longs are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertEquals(String message, long expected, long actual) {
+ public static void assertEquals(String message, long expected, long actual) {
assertEquals(message, Long.valueOf(expected), Long.valueOf(actual));
}
/**
* Asserts that two longs are equal.
*/
- static public void assertEquals(long expected, long actual) {
+ public static void assertEquals(long expected, long actual) {
assertEquals(null, expected, actual);
}
@@ -170,14 +170,14 @@ public class Assert {
* Asserts that two booleans are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertEquals(String message, boolean expected, boolean actual) {
+ public static void assertEquals(String message, boolean expected, boolean actual) {
assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
}
/**
* Asserts that two booleans are equal.
*/
- static public void assertEquals(boolean expected, boolean actual) {
+ public static void assertEquals(boolean expected, boolean actual) {
assertEquals(null, expected, actual);
}
@@ -185,14 +185,14 @@ public class Assert {
* Asserts that two bytes are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertEquals(String message, byte expected, byte actual) {
+ public static void assertEquals(String message, byte expected, byte actual) {
assertEquals(message, Byte.valueOf(expected), Byte.valueOf(actual));
}
/**
* Asserts that two bytes are equal.
*/
- static public void assertEquals(byte expected, byte actual) {
+ public static void assertEquals(byte expected, byte actual) {
assertEquals(null, expected, actual);
}
@@ -200,14 +200,14 @@ public class Assert {
* Asserts that two chars are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertEquals(String message, char expected, char actual) {
+ public static void assertEquals(String message, char expected, char actual) {
assertEquals(message, Character.valueOf(expected), Character.valueOf(actual));
}
/**
* Asserts that two chars are equal.
*/
- static public void assertEquals(char expected, char actual) {
+ public static void assertEquals(char expected, char actual) {
assertEquals(null, expected, actual);
}
@@ -215,14 +215,14 @@ public class Assert {
* Asserts that two shorts are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertEquals(String message, short expected, short actual) {
+ public static void assertEquals(String message, short expected, short actual) {
assertEquals(message, Short.valueOf(expected), Short.valueOf(actual));
}
/**
* Asserts that two shorts are equal.
*/
- static public void assertEquals(short expected, short actual) {
+ public static void assertEquals(short expected, short actual) {
assertEquals(null, expected, actual);
}
@@ -230,21 +230,21 @@ public class Assert {
* Asserts that two ints are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertEquals(String message, int expected, int actual) {
+ public static void assertEquals(String message, int expected, int actual) {
assertEquals(message, Integer.valueOf(expected), Integer.valueOf(actual));
}
/**
* Asserts that two ints are equal.
*/
- static public void assertEquals(int expected, int actual) {
+ public static void assertEquals(int expected, int actual) {
assertEquals(null, expected, actual);
}
/**
* Asserts that an object isn't null.
*/
- static public void assertNotNull(Object object) {
+ public static void assertNotNull(Object object) {
assertNotNull(null, object);
}
@@ -252,7 +252,7 @@ public class Assert {
* Asserts that an object isn't null. If it is
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertNotNull(String message, Object object) {
+ public static void assertNotNull(String message, Object object) {
assertTrue(message, object != null);
}
@@ -263,7 +263,7 @@ public class Assert {
*
* @param object Object to check or <code>null</code>
*/
- static public void assertNull(Object object) {
+ public static void assertNull(Object object) {
if (object != null) {
assertNull("Expected: <null> but was: " + object.toString(), object);
}
@@ -273,7 +273,7 @@ public class Assert {
* Asserts that an object is null. If it is not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertNull(String message, Object object) {
+ public static void assertNull(String message, Object object) {
assertTrue(message, object == null);
}
@@ -281,7 +281,7 @@ public class Assert {
* Asserts that two objects refer to the same object. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- static public void assertSame(String message, Object expected, Object actual) {
+ public static void assertSame(String message, Object expected, Object actual) {
if (expected == actual) {
return;
}
@@ -292,7 +292,7 @@ public class Assert {
* Asserts that two objects refer to the same object. If they are not
* the same an AssertionFailedError is thrown.
*/
- static public void assertSame(Object expected, Object actual) {
+ public static void assertSame(Object expected, Object actual) {
assertSame(null, expected, actual);
}
@@ -301,7 +301,7 @@ public class Assert {
* refer to the same object an AssertionFailedError is thrown with the
* given message.
*/
- static public void assertNotSame(String message, Object expected, Object actual) {
+ public static void assertNotSame(String message, Object expected, Object actual) {
if (expected == actual) {
failSame(message);
}
@@ -311,21 +311,21 @@ public class Assert {
* Asserts that two objects do not refer to the same object. If they do
* refer to the same object an AssertionFailedError is thrown.
*/
- static public void assertNotSame(Object expected, Object actual) {
+ public static void assertNotSame(Object expected, Object actual) {
assertNotSame(null, expected, actual);
}
- static public void failSame(String message) {
+ public static void failSame(String message) {
String formatted = (message != null) ? message + " " : "";
fail(formatted + "expected not same");
}
- static public void failNotSame(String message, Object expected, Object actual) {
+ public static void failNotSame(String message, Object expected, Object actual) {
String formatted = (message != null) ? message + " " : "";
fail(formatted + "expected same:<" + expected + "> was not:<" + actual + ">");
}
- static public void failNotEquals(String message, Object expected, Object actual) {
+ public static void failNotEquals(String message, Object expected, Object actual) {
fail(format(message, expected, actual));
}
diff --git a/src/main/java/junit/framework/ComparisonCompactor.java b/src/main/java/junit/framework/ComparisonCompactor.java
index fa20a8e..81ddd5b 100644
--- a/src/main/java/junit/framework/ComparisonCompactor.java
+++ b/src/main/java/junit/framework/ComparisonCompactor.java
@@ -18,6 +18,7 @@ public class ComparisonCompactor {
fActual = actual;
}
+ @SuppressWarnings("deprecation")
public String compact(String message) {
if (fExpected == null || fActual == null || areStringsEqual()) {
return Assert.format(message, fExpected, fActual);
diff --git a/src/main/java/junit/framework/JUnit4TestAdapter.java b/src/main/java/junit/framework/JUnit4TestAdapter.java
index cbb66db..9d32031 100644
--- a/src/main/java/junit/framework/JUnit4TestAdapter.java
+++ b/src/main/java/junit/framework/JUnit4TestAdapter.java
@@ -9,11 +9,23 @@ import org.junit.runner.Request;
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.Sortable;
+import org.junit.runner.manipulation.Orderable;
import org.junit.runner.manipulation.Sorter;
-public class JUnit4TestAdapter implements Test, Filterable, Sortable, Describable {
+/**
+ * The JUnit4TestAdapter enables running JUnit-4-style tests using a JUnit-3-style test runner.
+ *
+ * <p> To use it, add the following to a test class:
+ * <pre>
+ public static Test suite() {
+ return new JUnit4TestAdapter(<em>YourJUnit4TestClass</em>.class);
+ }
+</pre>
+ */
+public class JUnit4TestAdapter implements Test, Filterable, Orderable, Describable {
private final Class<?> fNewTestClass;
private final Runner fRunner;
@@ -83,4 +95,13 @@ public class JUnit4TestAdapter implements Test, Filterable, Sortable, Describabl
public void sort(Sorter sorter) {
sorter.apply(fRunner);
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 4.13
+ */
+ public void order(Orderer orderer) throws InvalidOrderingException {
+ orderer.apply(fRunner);
+ }
} \ No newline at end of file
diff --git a/src/main/java/junit/framework/Protectable.java b/src/main/java/junit/framework/Protectable.java
index 9f30b10..c5ceb16 100644
--- a/src/main/java/junit/framework/Protectable.java
+++ b/src/main/java/junit/framework/Protectable.java
@@ -8,7 +8,7 @@ package junit.framework;
public interface Protectable {
/**
- * Run the the following method protected.
+ * Run the following method protected.
*/
public abstract void protect() throws Throwable;
-} \ No newline at end of file
+}
diff --git a/src/main/java/junit/framework/TestCase.java b/src/main/java/junit/framework/TestCase.java
index b89ce71..e474a64 100644
--- a/src/main/java/junit/framework/TestCase.java
+++ b/src/main/java/junit/framework/TestCase.java
@@ -73,6 +73,7 @@ import java.lang.reflect.Modifier;
* @see TestResult
* @see TestSuite
*/
+@SuppressWarnings("deprecation")
public abstract class TestCase extends Assert implements Test {
/**
* the name of the test case
@@ -102,7 +103,7 @@ public abstract class TestCase extends Assert implements Test {
}
/**
- * Creates a default TestResult object
+ * Creates a default TestResult object.
*
* @see TestResult
*/
@@ -187,7 +188,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that a condition is true. If it isn't it throws
* an AssertionFailedError with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertTrue(String message, boolean condition) {
Assert.assertTrue(message, condition);
}
@@ -196,7 +196,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that a condition is true. If it isn't it throws
* an AssertionFailedError.
*/
- @SuppressWarnings("deprecation")
public static void assertTrue(boolean condition) {
Assert.assertTrue(condition);
}
@@ -205,7 +204,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that a condition is false. If it isn't it throws
* an AssertionFailedError with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertFalse(String message, boolean condition) {
Assert.assertFalse(message, condition);
}
@@ -214,7 +212,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that a condition is false. If it isn't it throws
* an AssertionFailedError.
*/
- @SuppressWarnings("deprecation")
public static void assertFalse(boolean condition) {
Assert.assertFalse(condition);
}
@@ -222,7 +219,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Fails a test with the given message.
*/
- @SuppressWarnings("deprecation")
public static void fail(String message) {
Assert.fail(message);
}
@@ -230,7 +226,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Fails a test with no message.
*/
- @SuppressWarnings("deprecation")
public static void fail() {
Assert.fail();
}
@@ -239,7 +234,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two objects are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, Object expected, Object actual) {
Assert.assertEquals(message, expected, actual);
}
@@ -248,7 +242,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two objects are equal. If they are not
* an AssertionFailedError is thrown.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(Object expected, Object actual) {
Assert.assertEquals(expected, actual);
}
@@ -256,7 +249,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that two Strings are equal.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, String expected, String actual) {
Assert.assertEquals(message, expected, actual);
}
@@ -264,7 +256,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that two Strings are equal.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String expected, String actual) {
Assert.assertEquals(expected, actual);
}
@@ -274,7 +265,6 @@ public abstract class TestCase extends Assert implements Test {
* an AssertionFailedError is thrown with the given message. If the expected
* value is infinity then the delta value is ignored.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, double expected, double actual, double delta) {
Assert.assertEquals(message, expected, actual, delta);
}
@@ -283,7 +273,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two doubles are equal concerning a delta. If the expected
* value is infinity then the delta value is ignored.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(double expected, double actual, double delta) {
Assert.assertEquals(expected, actual, delta);
}
@@ -293,7 +282,6 @@ public abstract class TestCase extends Assert implements Test {
* are not an AssertionFailedError is thrown with the given message. If the
* expected value is infinity then the delta value is ignored.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, float expected, float actual, float delta) {
Assert.assertEquals(message, expected, actual, delta);
}
@@ -302,7 +290,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two floats are equal concerning a delta. If the expected
* value is infinity then the delta value is ignored.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(float expected, float actual, float delta) {
Assert.assertEquals(expected, actual, delta);
}
@@ -311,7 +298,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two longs are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, long expected, long actual) {
Assert.assertEquals(message, expected, actual);
}
@@ -319,7 +305,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that two longs are equal.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(long expected, long actual) {
Assert.assertEquals(expected, actual);
}
@@ -328,7 +313,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two booleans are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, boolean expected, boolean actual) {
Assert.assertEquals(message, expected, actual);
}
@@ -336,7 +320,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that two booleans are equal.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(boolean expected, boolean actual) {
Assert.assertEquals(expected, actual);
}
@@ -345,7 +328,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two bytes are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, byte expected, byte actual) {
Assert.assertEquals(message, expected, actual);
}
@@ -353,7 +335,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that two bytes are equal.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(byte expected, byte actual) {
Assert.assertEquals(expected, actual);
}
@@ -362,7 +343,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two chars are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, char expected, char actual) {
Assert.assertEquals(message, expected, actual);
}
@@ -370,7 +350,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that two chars are equal.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(char expected, char actual) {
Assert.assertEquals(expected, actual);
}
@@ -379,7 +358,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two shorts are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, short expected, short actual) {
Assert.assertEquals(message, expected, actual);
}
@@ -387,7 +365,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that two shorts are equal.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(short expected, short actual) {
Assert.assertEquals(expected, actual);
}
@@ -396,7 +373,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two ints are equal. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(String message, int expected, int actual) {
Assert.assertEquals(message, expected, actual);
}
@@ -404,7 +380,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that two ints are equal.
*/
- @SuppressWarnings("deprecation")
public static void assertEquals(int expected, int actual) {
Assert.assertEquals(expected, actual);
}
@@ -412,7 +387,6 @@ public abstract class TestCase extends Assert implements Test {
/**
* Asserts that an object isn't null.
*/
- @SuppressWarnings("deprecation")
public static void assertNotNull(Object object) {
Assert.assertNotNull(object);
}
@@ -421,7 +395,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that an object isn't null. If it is
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertNotNull(String message, Object object) {
Assert.assertNotNull(message, object);
}
@@ -433,7 +406,6 @@ public abstract class TestCase extends Assert implements Test {
*
* @param object Object to check or <code>null</code>
*/
- @SuppressWarnings("deprecation")
public static void assertNull(Object object) {
Assert.assertNull(object);
}
@@ -442,7 +414,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that an object is null. If it is not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertNull(String message, Object object) {
Assert.assertNull(message, object);
}
@@ -451,7 +422,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two objects refer to the same object. If they are not
* an AssertionFailedError is thrown with the given message.
*/
- @SuppressWarnings("deprecation")
public static void assertSame(String message, Object expected, Object actual) {
Assert.assertSame(message, expected, actual);
}
@@ -460,7 +430,6 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two objects refer to the same object. If they are not
* the same an AssertionFailedError is thrown.
*/
- @SuppressWarnings("deprecation")
public static void assertSame(Object expected, Object actual) {
Assert.assertSame(expected, actual);
}
@@ -470,7 +439,6 @@ public abstract class TestCase extends Assert implements Test {
* refer to the same object an AssertionFailedError is thrown with the
* given message.
*/
- @SuppressWarnings("deprecation")
public static void assertNotSame(String message, Object expected, Object actual) {
Assert.assertNotSame(message, expected, actual);
}
@@ -479,27 +447,22 @@ public abstract class TestCase extends Assert implements Test {
* Asserts that two objects do not refer to the same object. If they do
* refer to the same object an AssertionFailedError is thrown.
*/
- @SuppressWarnings("deprecation")
public static void assertNotSame(Object expected, Object actual) {
Assert.assertNotSame(expected, actual);
}
- @SuppressWarnings("deprecation")
public static void failSame(String message) {
Assert.failSame(message);
}
- @SuppressWarnings("deprecation")
public static void failNotSame(String message, Object expected, Object actual) {
Assert.failNotSame(message, expected, actual);
}
- @SuppressWarnings("deprecation")
public static void failNotEquals(String message, Object expected, Object actual) {
Assert.failNotEquals(message, expected, actual);
}
- @SuppressWarnings("deprecation")
public static String format(String message, Object expected, Object actual) {
return Assert.format(message, expected, actual);
}
@@ -519,7 +482,7 @@ public abstract class TestCase extends Assert implements Test {
}
/**
- * Returns a string representation of the test case
+ * Returns a string representation of the test case.
*/
@Override
public String toString() {
@@ -527,7 +490,7 @@ public abstract class TestCase extends Assert implements Test {
}
/**
- * Gets the name of a TestCase
+ * Gets the name of a TestCase.
*
* @return the name of the TestCase
*/
@@ -536,7 +499,7 @@ public abstract class TestCase extends Assert implements Test {
}
/**
- * Sets the name of a TestCase
+ * Sets the name of a TestCase.
*
* @param name the name to set
*/
diff --git a/src/main/java/junit/framework/TestFailure.java b/src/main/java/junit/framework/TestFailure.java
index 6168b58..d1ddfbc 100644
--- a/src/main/java/junit/framework/TestFailure.java
+++ b/src/main/java/junit/framework/TestFailure.java
@@ -1,7 +1,6 @@
package junit.framework;
-import java.io.PrintWriter;
-import java.io.StringWriter;
+import org.junit.internal.Throwables;
/**
@@ -49,10 +48,7 @@ public class TestFailure {
* thrown by TestFailure.
*/
public String trace() {
- StringWriter stringWriter = new StringWriter();
- PrintWriter writer = new PrintWriter(stringWriter);
- thrownException().printStackTrace(writer);
- return stringWriter.toString();
+ return Throwables.getStacktrace(thrownException());
}
/**
diff --git a/src/main/java/junit/framework/TestResult.java b/src/main/java/junit/framework/TestResult.java
index 8332542..e01a2b0 100644
--- a/src/main/java/junit/framework/TestResult.java
+++ b/src/main/java/junit/framework/TestResult.java
@@ -52,14 +52,14 @@ public class TestResult {
}
/**
- * Registers a TestListener
+ * Registers a TestListener.
*/
public synchronized void addListener(TestListener listener) {
fListeners.add(listener);
}
/**
- * Unregisters a TestListener
+ * Unregisters a TestListener.
*/
public synchronized void removeListener(TestListener listener) {
fListeners.remove(listener);
@@ -91,7 +91,7 @@ public class TestResult {
}
/**
- * Returns an Enumeration for the errors
+ * Returns an Enumeration for the errors.
*/
public synchronized Enumeration<TestFailure> errors() {
return Collections.enumeration(fErrors);
@@ -106,7 +106,7 @@ public class TestResult {
}
/**
- * Returns an Enumeration for the failures
+ * Returns an Enumeration for the failures.
*/
public synchronized Enumeration<TestFailure> failures() {
return Collections.enumeration(fFailures);
@@ -150,7 +150,7 @@ public class TestResult {
}
/**
- * Checks whether the test run should stop
+ * Checks whether the test run should stop.
*/
public synchronized boolean shouldStop() {
return fStop;
diff --git a/src/main/java/junit/framework/TestSuite.java b/src/main/java/junit/framework/TestSuite.java
index 366f1cf..50cd5f8 100644
--- a/src/main/java/junit/framework/TestSuite.java
+++ b/src/main/java/junit/framework/TestSuite.java
@@ -1,7 +1,5 @@
package junit.framework;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -12,6 +10,7 @@ import java.util.List;
import java.util.Vector;
import org.junit.internal.MethodSorter;
+import org.junit.internal.Throwables;
/**
* A <code>TestSuite</code> is a <code>Composite</code> of Tests.
@@ -35,7 +34,7 @@ import org.junit.internal.MethodSorter;
* <p>
* A final option is to do the same for a large array of test classes.
* <pre>
- * Class[] testClasses = { MathTest.class, AnotherTest.class }
+ * Class[] testClasses = { MathTest.class, AnotherTest.class };
* TestSuite suite= new TestSuite(testClasses);
* </pre>
*
@@ -65,11 +64,11 @@ public class TestSuite implements Test {
test = constructor.newInstance(new Object[]{name});
}
} catch (InstantiationException e) {
- return (warning("Cannot instantiate test case: " + name + " (" + exceptionToString(e) + ")"));
+ return (warning("Cannot instantiate test case: " + name + " (" + Throwables.getStacktrace(e) + ")"));
} catch (InvocationTargetException e) {
- return (warning("Exception in constructor: " + name + " (" + exceptionToString(e.getTargetException()) + ")"));
+ return (warning("Exception in constructor: " + name + " (" + Throwables.getStacktrace(e.getTargetException()) + ")"));
} catch (IllegalAccessException e) {
- return (warning("Cannot access test case: " + name + " (" + exceptionToString(e) + ")"));
+ return (warning("Cannot access test case: " + name + " (" + Throwables.getStacktrace(e) + ")"));
}
return (Test) test;
}
@@ -99,16 +98,6 @@ public class TestSuite implements Test {
};
}
- /**
- * Converts the stack trace into a string
- */
- private static String exceptionToString(Throwable e) {
- StringWriter stringWriter = new StringWriter();
- PrintWriter writer = new PrintWriter(stringWriter);
- e.printStackTrace(writer);
- return stringWriter.toString();
- }
-
private String fName;
private Vector<Test> fTests = new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners
@@ -210,7 +199,7 @@ public class TestSuite implements Test {
}
/**
- * Adds the tests from the given class to the suite
+ * Adds the tests from the given class to the suite.
*/
public void addTestSuite(Class<? extends TestCase> testClass) {
addTest(new TestSuite(testClass));
@@ -262,21 +251,21 @@ public class TestSuite implements Test {
}
/**
- * Returns the test at the given index
+ * Returns the test at the given index.
*/
public Test testAt(int index) {
return fTests.get(index);
}
/**
- * Returns the number of tests in this suite
+ * Returns the number of tests in this suite.
*/
public int testCount() {
return fTests.size();
}
/**
- * Returns the tests as an enumeration
+ * Returns the tests as an enumeration.
*/
public Enumeration<Test> tests() {
return fTests.elements();
diff --git a/src/main/java/junit/runner/BaseTestRunner.java b/src/main/java/junit/runner/BaseTestRunner.java
index 8268323..d63fae7 100644
--- a/src/main/java/junit/runner/BaseTestRunner.java
+++ b/src/main/java/junit/runner/BaseTestRunner.java
@@ -20,6 +20,8 @@ import junit.framework.Test;
import junit.framework.TestListener;
import junit.framework.TestSuite;
+import org.junit.internal.Throwables;
+
/**
* Base class for all test runners.
* This class was born live on stage in Sardinia during XP2000.
@@ -233,6 +235,7 @@ public abstract class BaseTestRunner implements TestListener {
setPreferences(new Properties(getPreferences()));
getPreferences().load(is);
} catch (IOException ignored) {
+ } catch (SecurityException ignored) {
} finally {
try {
if (is != null) {
@@ -264,11 +267,7 @@ public abstract class BaseTestRunner implements TestListener {
* Returns a filtered stack trace
*/
public static String getFilteredTrace(Throwable e) {
- StringWriter stringWriter = new StringWriter();
- PrintWriter writer = new PrintWriter(stringWriter);
- e.printStackTrace(writer);
- String trace = stringWriter.toString();
- return BaseTestRunner.getFilteredTrace(trace);
+ return BaseTestRunner.getFilteredTrace(Throwables.getStacktrace(e));
}
/**
diff --git a/src/main/java/junit/runner/TestRunListener.java b/src/main/java/junit/runner/TestRunListener.java
index b5e22f5..ce5b3d5 100644
--- a/src/main/java/junit/runner/TestRunListener.java
+++ b/src/main/java/junit/runner/TestRunListener.java
@@ -8,18 +8,18 @@ package junit.runner;
*/
public interface TestRunListener {
/* test status constants*/
- public static final int STATUS_ERROR = 1;
- public static final int STATUS_FAILURE = 2;
+ int STATUS_ERROR = 1;
+ int STATUS_FAILURE = 2;
- public void testRunStarted(String testSuiteName, int testCount);
+ void testRunStarted(String testSuiteName, int testCount);
- public void testRunEnded(long elapsedTime);
+ void testRunEnded(long elapsedTime);
- public void testRunStopped(long elapsedTime);
+ void testRunStopped(long elapsedTime);
- public void testStarted(String testName);
+ void testStarted(String testName);
- public void testEnded(String testName);
+ void testEnded(String testName);
- public void testFailed(int status, String testName, String trace);
+ void testFailed(int status, String testName, String trace);
}
diff --git a/src/main/java/junit/runner/Version.java b/src/main/java/junit/runner/Version.java
index eaf3db7..6c7862e 100644
--- a/src/main/java/junit/runner/Version.java
+++ b/src/main/java/junit/runner/Version.java
@@ -9,7 +9,7 @@ public class Version {
}
public static String id() {
- return "4.12-SNAPSHOT";
+ return "4.13.3-SNAPSHOT";
}
public static void main(String[] args) {
diff --git a/src/main/java/junit/textui/TestRunner.java b/src/main/java/junit/textui/TestRunner.java
index 4d78f77..913020a 100644
--- a/src/main/java/junit/textui/TestRunner.java
+++ b/src/main/java/junit/textui/TestRunner.java
@@ -131,7 +131,7 @@ public class TestRunner extends BaseTestRunner {
}
}
- public static void main(String args[]) {
+ public static void main(String[] args) {
TestRunner aTestRunner = new TestRunner();
try {
TestResult r = aTestRunner.start(args);
@@ -149,7 +149,7 @@ public class TestRunner extends BaseTestRunner {
* Starts a test run. Analyzes the command line arguments and runs the given
* test suite.
*/
- public TestResult start(String args[]) throws Exception {
+ public TestResult start(String[] args) throws Exception {
String testCase = "";
String method = "";
boolean wait = false;
diff --git a/src/main/java/org/junit/Assert.java b/src/main/java/org/junit/Assert.java
index d7deb06..65bbc9d 100755..100644
--- 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 <code>expected</code>
*/
- 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 <code>unexpected</code>
*/
- 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 <code>unexpected</code>
*/
- 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 <code>unexpected</code>
*/
- 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 <code>unexpected</code>
*/
- 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 {
* <code>actual</code> 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 {
* <code>actual</code> 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 {
* <code>actual</code> 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 <code>expected</code> and
@@ -547,7 +549,7 @@ public class Assert {
* <code>actual</code> 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 {
* <code>actual</code> 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 {
* <code>actual</code> 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 {
* <code>actual</code> 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 {
* <code>actual</code> 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 <code>null</code>
*/
- 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 <code>null</code>
*/
- 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 <code>null</code>
*/
- 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 <code>null</code>
*/
- 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 <code>expected</code>
*/
- 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 <code>expected</code>
*/
- 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 <code>unexpected</code>
*/
- 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 <code>unexpected</code>
*/
- 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 <T> 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 <T> 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 extends Throwable> T assertThrows(Class<T> 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} (<code>null</code>
+ * 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 extends Throwable> T assertThrows(String message, Class<T> 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<my.package.MyException> ..."
+ 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.
* <p>
- * A good example of using assumptions is in <a href="https://github.com/junit-team/junit/wiki/Theories">Theories</a> 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 <a href="https://github.com/junit-team/junit4/wiki/Theories">Theories</a> where they are needed to exclude certain datapoints that aren't suitable or allowed for a certain test case.
* </p>
* 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;
* </pre>
* </p>
*
- * @see <a href="https://github.com/junit-team/junit/wiki/Theories">Theories</a>
+ * @see <a href="https://github.com/junit-team/junit4/wiki/Theories">Theories</a>
*
* @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 <code>objects</code>, 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.
+ *
+ * <h3>Usage</h3>
* <p>
* 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;
* <p>
* For more information and more examples, see {@link org.junit.rules.TestRule}.
*
+ * <h3>Ordering</h3>
+ * <p>
+ * You can use {@link #order()} if you want to have control over the order in
+ * which the Rules are applied.
+ *
+ * <pre>
+ * public class ThreeClassRules {
+ * &#064;ClassRule(order = 0)
+ * public static LoggingRule outer = new LoggingRule("outer rule");
+ *
+ * &#064;ClassRule(order = 1)
+ * public static LoggingRule middle = new LoggingRule("middle rule");
+ *
+ * &#064;ClassRule(order = 2)
+ * public static LoggingRule inner = new LoggingRule("inner rule");
+ *
+ * // ...
+ * }
+ * </pre>
+ *
* @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.
+ *
+ * <h3>Usage</h3>
* <p>
* 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}.
*
+ * <h3>Ordering</h3>
+ * <p>
+ * You can use {@link #order()} if you want to have control over the order in
+ * which the Rules are applied.
+ *
+ * <pre>
+ * public class ThreeRules {
+ * &#064;Rule(order = 0)
+ * public LoggingRule outer = new LoggingRule("outer rule");
+ *
+ * &#064;Rule(order = 1)
+ * public LoggingRule middle = new LoggingRule("middle rule");
+ *
+ * &#064;Rule(order = 2)
+ * public LoggingRule inner = new LoggingRule("inner rule");
+ *
+ * // ...
+ * }
+ * </pre>
+ *
* @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;
* }
* </pre>
* <p>
- * The <code>Test</code> annotation supports two optional parameters.
- * The first, <code>expected</code>, declares that a test method should throw
+ * The <code>Test</code> annotation supports two optional parameters for
+ * exception testing and for limiting test execution time.
+ *
+ * <h3>Exception Testing</h3>
+ * <p>
+ * The parameter <code>expected</code> 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:
* <pre>
- * &#064;Test(<b>expected=IndexOutOfBoundsException.class</b>) public void outOfBounds() {
+ * &#064;Test(<b>expected=IndexOutOfBoundsException.class</b>)
+ * public void outOfBounds() {
* new ArrayList&lt;Object&gt;().get(1);
* }
* </pre>
- * 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 <code>expected</code> 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 <code>assertThrows</code> the code that throws the exception can be
+ * precisely specified. If the exception's message or one of its properties
+ * should be verified, the <code>ExpectedException</code> rule can be used. Further
* information about exception testing can be found at the
- * <a href="https://github.com/junit-team/junit/wiki/Exception-testing">JUnit Wiki</a>.
+ * <a href="https://github.com/junit-team/junit4/wiki/Exception-testing">JUnit Wiki</a>.
+ *
+ * <h3>Timeout</h3>
* <p>
- * The second optional parameter, <code>timeout</code>, causes a test to fail if it takes
+ * The parameter <code>timeout</code> causes a test to fail if it takes
* longer than a specified amount of clock time (measured in milliseconds). The following test fails:
* <pre>
- * &#064;Test(<b>timeout=100</b>) public void infinity() {
+ * &#064;Test(<b>timeout=100</b>)
+ * public void infinity() {
* while(true);
* }
* </pre>
@@ -49,7 +67,8 @@ import java.lang.annotation.Target;
* following test may or may not fail depending on how the operating system
* schedules threads:
* <pre>
- * &#064;Test(<b>timeout=100</b>) public void sleep100() {
+ * &#064;Test(<b>timeout=100</b>)
+ * public void sleep100() {
* Thread.sleep(100);
* }
* </pre>
@@ -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;
* </pre>
*
* @version 4.12
- * @see <a href="https://github.com/junit-team/junit/wiki/Categories">Categories at JUnit wiki</a>
+ * @see <a href="https://github.com/junit-team/junit4/wiki/Categories">Categories at JUnit wiki</a>
*/
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 <tt>true</tt>, runs tests annotated with <em>any</em> of the categories in
* {@link IncludeCategory#value()}. Otherwise, runs tests only if annotated with <em>all</em> 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 <tt>true</tt>, the tests annotated with <em>any</em> of the categories in {@link ExcludeCategory#value()}
* do not run. Otherwise, the tests do not run if and only if annotated with <em>all</em> 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<Class<?>> includes,
- boolean matchAnyExcludes, Set<Class<?>> excludes) {
+ boolean matchAnyExcludes, Set<Class<?>> 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<Class<?>> copyAndRefine(Set<Class<?>> classes) {
- HashSet<Class<?>> c= new HashSet<Class<?>>();
+ Set<Class<?>> c= new LinkedHashSet<Class<?>>();
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<Class<?>> 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<Class<?>> 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<Class<?>> createSet(Class<?>... t) {
- final Set<Class<?>> set= new HashSet<Class<?>>();
- if (t != null) {
- Collections.addAll(set, t);
+ private static Set<Class<?>> 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.<Class<?>>singleton(classes[0])
+ : new LinkedHashSet<Class<?>>(Arrays.asList(classes));
+ }
+
+ private static Set<Class<?>> 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.<Class<?>>emptySet()
+ : Collections.<Class<?>>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<Class<?>> categoryClasses = new ArrayList<Class<?>>();
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<String, Long> fDurations = new HashMap<String, Long>();
private final Map<String, Long> fFailureTimestamps = new HashMap<String, Long>();
@@ -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<Failure> 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;
* </pre>
*/
public class ResultMatchers {
+
+ /**
+ * Do not instantiate.
+ * @deprecated will be private soon.
+ */
+ @Deprecated
+ public ResultMatchers() {
+ }
+
/**
* Matches if the tests are all successful
*/
@@ -53,13 +62,33 @@ public class ResultMatchers {
}
/**
+ * Matches if the result has exactly one failure matching the given matcher.
+ *
+ * @since 4.13
+ */
+ public static Matcher<PrintableResult> hasSingleFailureMatching(final Matcher<Throwable> matcher) {
+ return new TypeSafeMatcher<PrintableResult>() {
+ @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<PrintableResult> hasFailureContaining(final String string) {
- return new BaseMatcher<PrintableResult>() {
- public boolean matches(Object item) {
- return item.toString().contains(string);
+ return new TypeSafeMatcher<PrintableResult>() {
+ @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
* &#064;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:
*
* <pre>
diff --git a/src/main/java/org/junit/experimental/theories/Theories.java b/src/main/java/org/junit/experimental/theories/Theories.java
index 817f553..ac88a36 100644
--- a/src/main/java/org/junit/experimental/theories/Theories.java
+++ b/src/main/java/org/junit/experimental/theories/Theories.java
@@ -51,11 +51,11 @@ import org.junit.runners.model.TestClass;
* }
* }
* </pre>
- * 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. <code>UserTest</code> will attempt to run <code>filenameIncludesUsername</code> 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.
* <p>
* 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<Throwable> 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<Throwable> 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<PotentialAssignment> assigned = new ArrayList<PotentialAssignment>(
- this.assigned);
- assigned.add(source);
+ List<PotentialAssignment> potentialAssignments = new ArrayList<PotentialAssignment>(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<PotentialAssignment> 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<Integer> fIndices = new ArrayList<Integer>();
private final String fMessage;
+ private final AssertionError fCause;
/**
* Construct a new <code>ArrayComparisonFailure</code> with an error text and the array's
@@ -32,7 +33,8 @@ public class ArrayComparisonFailure extends AssertionError {
*/
public ArrayComparisonFailure(String message, AssertionError cause, int index) {
this.fMessage = message;
- initCause(cause);
+ this.fCause = cause;
+ initCause(fCause);
addDimension(index);
}
@@ -41,6 +43,11 @@ public class ArrayComparisonFailure extends AssertionError {
}
@Override
+ public synchronized Throwable getCause() {
+ return super.getCause() == null ? fCause : super.getCause();
+ }
+
+ @Override
public String getMessage() {
StringBuilder sb = new StringBuilder();
if (fMessage != null) {
diff --git a/src/main/java/org/junit/internal/AssumptionViolatedException.java b/src/main/java/org/junit/internal/AssumptionViolatedException.java
index 880d73f..0e79b56 100644
--- a/src/main/java/org/junit/internal/AssumptionViolatedException.java
+++ b/src/main/java/org/junit/internal/AssumptionViolatedException.java
@@ -1,5 +1,8 @@
package org.junit.internal;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.SelfDescribing;
@@ -18,7 +21,7 @@ public class AssumptionViolatedException extends RuntimeException implements Sel
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
- * See https://github.com/junit-team/junit/issues/976
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final String fAssumption;
private final boolean fValueMatcher;
@@ -108,4 +111,29 @@ public class AssumptionViolatedException extends RuntimeException implements Sel
}
}
}
+
+ /**
+ * Override default Java object serialization to correctly deal with potentially unserializable matchers or values.
+ * By not implementing readObject, we assure ourselves of backwards compatibility and compatibility with the
+ * standard way of Java serialization.
+ *
+ * @param objectOutputStream The outputStream to write our representation to
+ * @throws IOException When serialization fails
+ */
+ private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
+ ObjectOutputStream.PutField putField = objectOutputStream.putFields();
+ putField.put("fAssumption", fAssumption);
+ putField.put("fValueMatcher", fValueMatcher);
+
+ // We have to wrap the matcher into a serializable form.
+ putField.put("fMatcher", SerializableMatcherDescription.asSerializableMatcher(fMatcher));
+
+ // We have to wrap the value inside a non-String class (instead of serializing the String value directly) as
+ // A Description will handle a String and non-String object differently (1st is surrounded by '"' while the
+ // latter will be surrounded by '<' '>'. Wrapping it makes sure that the description of a serialized and
+ // non-serialized instance produce the exact same description
+ putField.put("fValue", SerializableValueDescription.asSerializableValue(fValue));
+
+ objectOutputStream.writeFields();
+ }
}
diff --git a/src/main/java/org/junit/internal/Checks.java b/src/main/java/org/junit/internal/Checks.java
new file mode 100644
index 0000000..9724947
--- /dev/null
+++ b/src/main/java/org/junit/internal/Checks.java
@@ -0,0 +1,37 @@
+package org.junit.internal;
+
+/** @since 4.13 */
+public final class Checks {
+
+ private Checks() {}
+
+ /**
+ * Checks that the given value is not {@code null}.
+ *
+ * @param value object reference to check
+ * @return the passed-in value, if not {@code null}
+ * @throws NullPointerException if {@code value} is {@code null}
+ */
+ public static <T> T notNull(T value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ return value;
+ }
+
+ /**
+ * Checks that the given value is not {@code null}, using the given message
+ * as the exception message if an exception is thrown.
+ *
+ * @param value object reference to check
+ * @param message message to use if {@code value} is {@code null}
+ * @return the passed-in value, if not {@code null}
+ * @throws NullPointerException if {@code value} is {@code null}
+ */
+ public static <T> T notNull(T value, String message) {
+ if (value == null) {
+ throw new NullPointerException(message);
+ }
+ return value;
+ }
+}
diff --git a/src/main/java/org/junit/internal/Classes.java b/src/main/java/org/junit/internal/Classes.java
index 154603d..e8404f6 100644
--- a/src/main/java/org/junit/internal/Classes.java
+++ b/src/main/java/org/junit/internal/Classes.java
@@ -6,13 +6,39 @@ import static java.lang.Thread.currentThread;
* Miscellaneous functions dealing with classes.
*/
public class Classes {
+
+ /**
+ * Do not instantiate.
+ * @deprecated will be private soon.
+ */
+ @Deprecated
+ public Classes() {
+ }
+
/**
* Returns Class.forName for {@code className} using the current thread's class loader.
+ * If the current thread does not have a class loader, falls back to the class loader for
+ * {@link Classes}.
*
* @param className Name of the class.
* @throws ClassNotFoundException
*/
public static Class<?> getClass(String className) throws ClassNotFoundException {
- return Class.forName(className, true, currentThread().getContextClassLoader());
+ return getClass(className, Classes.class);
+ }
+
+ /**
+ * Returns Class.forName for {@code className} using the current thread's class loader.
+ * If the current thread does not have a class loader, falls back to the class loader for the
+ * passed-in class.
+ *
+ * @param className Name of the class.
+ * @param callingClass Class that is requesting a the class
+ * @throws ClassNotFoundException
+ * @since 4.13
+ */
+ public static Class<?> getClass(String className, Class<?> callingClass) throws ClassNotFoundException {
+ ClassLoader classLoader = currentThread().getContextClassLoader();
+ return Class.forName(className, true, classLoader == null ? callingClass.getClassLoader() : classLoader);
}
}
diff --git a/src/main/java/org/junit/internal/ComparisonCriteria.java b/src/main/java/org/junit/internal/ComparisonCriteria.java
index e6d49a4..ed1c674 100644
--- a/src/main/java/org/junit/internal/ComparisonCriteria.java
+++ b/src/main/java/org/junit/internal/ComparisonCriteria.java
@@ -25,6 +25,11 @@ public abstract class ComparisonCriteria {
*/
public void arrayEquals(String message, Object expecteds, Object actuals)
throws ArrayComparisonFailure {
+ arrayEquals(message, expecteds, actuals, true);
+ }
+
+ private void arrayEquals(String message, Object expecteds, Object actuals, boolean outer)
+ throws ArrayComparisonFailure {
if (expecteds == actuals
|| Arrays.deepEquals(new Object[] {expecteds}, new Object[] {actuals})) {
// The reflection-based loop below is potentially very slow, especially for primitive
@@ -34,19 +39,37 @@ public abstract class ComparisonCriteria {
}
String header = message == null ? "" : message + ": ";
- int expectedsLength = assertArraysAreSameLength(expecteds,
- actuals, header);
+ // Only include the user-provided message in the outer exception.
+ String exceptionMessage = outer ? header : "";
+
+ if (expecteds == null) {
+ Assert.fail(exceptionMessage + "expected array was null");
+ }
+ if (actuals == null) {
+ Assert.fail(exceptionMessage + "actual array was null");
+ }
+
+ int actualsLength = Array.getLength(actuals);
+ int expectedsLength = Array.getLength(expecteds);
+ if (actualsLength != expectedsLength) {
+ header += "array lengths differed, expected.length="
+ + expectedsLength + " actual.length=" + actualsLength + "; ";
+ }
+ int prefixLength = Math.min(actualsLength, expectedsLength);
- for (int i = 0; i < expectedsLength; i++) {
+ for (int i = 0; i < prefixLength; i++) {
Object expected = Array.get(expecteds, i);
Object actual = Array.get(actuals, i);
if (isArray(expected) && isArray(actual)) {
try {
- arrayEquals(message, expected, actual);
+ arrayEquals(message, expected, actual, false);
} catch (ArrayComparisonFailure e) {
e.addDimension(i);
throw e;
+ } catch (AssertionError e) {
+ // Array lengths differed.
+ throw new ArrayComparisonFailure(header, e, i);
}
} else {
try {
@@ -56,27 +79,53 @@ public abstract class ComparisonCriteria {
}
}
}
- }
- private boolean isArray(Object expected) {
- return expected != null && expected.getClass().isArray();
+ if (actualsLength != expectedsLength) {
+ Object expected = getToStringableArrayElement(expecteds, expectedsLength, prefixLength);
+ Object actual = getToStringableArrayElement(actuals, actualsLength, prefixLength);
+ try {
+ Assert.assertEquals(expected, actual);
+ } catch (AssertionError e) {
+ throw new ArrayComparisonFailure(header, e, prefixLength);
+ }
+ }
}
- private int assertArraysAreSameLength(Object expecteds,
- Object actuals, String header) {
- if (expecteds == null) {
- Assert.fail(header + "expected array was null");
- }
- if (actuals == null) {
- Assert.fail(header + "actual array was null");
+ private static final Object END_OF_ARRAY_SENTINEL = objectWithToString("end of array");
+
+ private Object getToStringableArrayElement(Object array, int length, int index) {
+ if (index < length) {
+ Object element = Array.get(array, index);
+ if (isArray(element)) {
+ return objectWithToString(componentTypeName(element.getClass()) + "[" + Array.getLength(element) + "]");
+ } else {
+ return element;
+ }
+ } else {
+ return END_OF_ARRAY_SENTINEL;
}
- int actualsLength = Array.getLength(actuals);
- int expectedsLength = Array.getLength(expecteds);
- if (actualsLength != expectedsLength) {
- Assert.fail(header + "array lengths differed, expected.length="
- + expectedsLength + " actual.length=" + actualsLength);
+ }
+
+ private static Object objectWithToString(final String string) {
+ return new Object() {
+ @Override
+ public String toString() {
+ return string;
+ }
+ };
+ }
+
+ private String componentTypeName(Class<?> arrayClass) {
+ Class<?> componentType = arrayClass.getComponentType();
+ if (componentType.isArray()) {
+ return componentTypeName(componentType) + "[]";
+ } else {
+ return componentType.getName();
}
- return expectedsLength;
+ }
+
+ private boolean isArray(Object expected) {
+ return expected != null && expected.getClass().isArray();
}
protected abstract void assertElementsEqual(Object expected, Object actual);
diff --git a/src/main/java/org/junit/internal/SerializableMatcherDescription.java b/src/main/java/org/junit/internal/SerializableMatcherDescription.java
new file mode 100644
index 0000000..e036557
--- /dev/null
+++ b/src/main/java/org/junit/internal/SerializableMatcherDescription.java
@@ -0,0 +1,47 @@
+package org.junit.internal;
+
+import java.io.Serializable;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.StringDescription;
+
+/**
+ * This class exists solely to provide a serializable description of a matcher to be serialized as a field in
+ * {@link AssumptionViolatedException}. Being a {@link Throwable}, it is required to be {@link Serializable}, but most
+ * implementations of {@link Matcher} are not. This class works around that limitation as
+ * {@link AssumptionViolatedException} only every uses the description of the {@link Matcher}, while still retaining
+ * backwards compatibility with classes compiled against its class signature before 4.14 and/or deserialization of
+ * previously serialized instances.
+ */
+class SerializableMatcherDescription<T> extends BaseMatcher<T> implements Serializable {
+
+ private final String matcherDescription;
+
+ private SerializableMatcherDescription(Matcher<T> matcher) {
+ matcherDescription = StringDescription.asString(matcher);
+ }
+
+ public boolean matches(Object o) {
+ throw new UnsupportedOperationException("This Matcher implementation only captures the description");
+ }
+
+ public void describeTo(Description description) {
+ description.appendText(matcherDescription);
+ }
+
+ /**
+ * Factory method that checks to see if the matcher is already serializable.
+ * @param matcher the matcher to make serializable
+ * @return The provided matcher if it is null or already serializable,
+ * the SerializableMatcherDescription representation of it if it is not.
+ */
+ static <T> Matcher<T> asSerializableMatcher(Matcher<T> matcher) {
+ if (matcher == null || matcher instanceof Serializable) {
+ return matcher;
+ } else {
+ return new SerializableMatcherDescription<T>(matcher);
+ }
+ }
+}
diff --git a/src/main/java/org/junit/internal/SerializableValueDescription.java b/src/main/java/org/junit/internal/SerializableValueDescription.java
new file mode 100644
index 0000000..4d055d7
--- /dev/null
+++ b/src/main/java/org/junit/internal/SerializableValueDescription.java
@@ -0,0 +1,38 @@
+package org.junit.internal;
+
+import java.io.Serializable;
+
+/**
+ * This class exists solely to provide a serializable description of a value to be serialized as a field in
+ * {@link AssumptionViolatedException}. Being a {@link Throwable}, it is required to be {@link Serializable}, but a
+ * value of type Object provides no guarantee to be serializable. This class works around that limitation as
+ * {@link AssumptionViolatedException} only every uses the string representation of the value, while still retaining
+ * backwards compatibility with classes compiled against its class signature before 4.14 and/or deserialization of
+ * previously serialized instances.
+ */
+class SerializableValueDescription implements Serializable {
+ private final String value;
+
+ private SerializableValueDescription(Object value) {
+ this.value = String.valueOf(value);
+ }
+
+ /**
+ * Factory method that checks to see if the value is already serializable.
+ * @param value the value to make serializable
+ * @return The provided value if it is null or already serializable,
+ * the SerializableValueDescription representation of it if it is not.
+ */
+ static Object asSerializableValue(Object value) {
+ if (value == null || value instanceof Serializable) {
+ return value;
+ } else {
+ return new SerializableValueDescription(value);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+}
diff --git a/src/main/java/org/junit/internal/TextListener.java b/src/main/java/org/junit/internal/TextListener.java
index 9aa56c7..d548aeb 100644
--- a/src/main/java/org/junit/internal/TextListener.java
+++ b/src/main/java/org/junit/internal/TextListener.java
@@ -58,7 +58,7 @@ public class TextListener extends RunListener {
protected void printFailures(Result result) {
List<Failure> failures = result.getFailures();
- if (failures.size() == 0) {
+ if (failures.isEmpty()) {
return;
}
if (failures.size() == 1) {
@@ -74,7 +74,7 @@ public class TextListener extends RunListener {
protected void printFailure(Failure each, String prefix) {
getWriter().println(prefix + ") " + each.getTestHeader());
- getWriter().print(each.getTrace());
+ getWriter().print(each.getTrimmedTrace());
}
protected void printFooter(Result result) {
diff --git a/src/main/java/org/junit/internal/Throwables.java b/src/main/java/org/junit/internal/Throwables.java
index 86dceef..3f0f7a3 100644
--- a/src/main/java/org/junit/internal/Throwables.java
+++ b/src/main/java/org/junit/internal/Throwables.java
@@ -1,5 +1,17 @@
package org.junit.internal;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
/**
* Miscellaneous functions dealing with {@code Throwable}.
*
@@ -39,4 +51,223 @@ public final class Throwables {
private static <T extends Throwable> void rethrow(Throwable e) throws T {
throw (T) e;
}
+
+ /**
+ * Returns the stacktrace of the given Throwable as a String.
+ *
+ * @since 4.13
+ */
+ public static String getStacktrace(Throwable exception) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ exception.printStackTrace(writer);
+ return stringWriter.toString();
+ }
+
+ /**
+ * Gets a trimmed version of the stack trace of the given exception. Stack trace
+ * elements that are below the test method are filtered out.
+ *
+ * @return a trimmed stack trace, or the original trace if trimming wasn't possible
+ */
+ public static String getTrimmedStackTrace(Throwable exception) {
+ List<String> trimmedStackTraceLines = getTrimmedStackTraceLines(exception);
+ if (trimmedStackTraceLines.isEmpty()) {
+ return getFullStackTrace(exception);
+ }
+
+ StringBuilder result = new StringBuilder(exception.toString());
+ appendStackTraceLines(trimmedStackTraceLines, result);
+ appendStackTraceLines(getCauseStackTraceLines(exception), result);
+ return result.toString();
+ }
+
+ private static List<String> getTrimmedStackTraceLines(Throwable exception) {
+ List<StackTraceElement> stackTraceElements = Arrays.asList(exception.getStackTrace());
+ int linesToInclude = stackTraceElements.size();
+
+ State state = State.PROCESSING_OTHER_CODE;
+ for (StackTraceElement stackTraceElement : asReversedList(stackTraceElements)) {
+ state = state.processStackTraceElement(stackTraceElement);
+ if (state == State.DONE) {
+ List<String> trimmedLines = new ArrayList<String>(linesToInclude + 2);
+ trimmedLines.add("");
+ for (StackTraceElement each : stackTraceElements.subList(0, linesToInclude)) {
+ trimmedLines.add("\tat " + each);
+ }
+ if (exception.getCause() != null) {
+ trimmedLines.add("\t... " + (stackTraceElements.size() - trimmedLines.size()) + " trimmed");
+ }
+ return trimmedLines;
+ }
+ linesToInclude--;
+ }
+ return Collections.emptyList();
+ }
+
+ private static final Method getSuppressed = initGetSuppressed();
+
+ private static Method initGetSuppressed() {
+ try {
+ return Throwable.class.getMethod("getSuppressed");
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+
+ private static boolean hasSuppressed(Throwable exception) {
+ if (getSuppressed == null) {
+ return false;
+ }
+ try {
+ Throwable[] suppressed = (Throwable[]) getSuppressed.invoke(exception);
+ return suppressed.length != 0;
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ private static List<String> getCauseStackTraceLines(Throwable exception) {
+ if (exception.getCause() != null || hasSuppressed(exception)) {
+ String fullTrace = getFullStackTrace(exception);
+ BufferedReader reader = new BufferedReader(
+ new StringReader(fullTrace.substring(exception.toString().length())));
+ List<String> causedByLines = new ArrayList<String>();
+
+ try {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("Caused by: ") || line.trim().startsWith("Suppressed: ")) {
+ causedByLines.add(line);
+ while ((line = reader.readLine()) != null) {
+ causedByLines.add(line);
+ }
+ return causedByLines;
+ }
+ }
+ } catch (IOException e) {
+ // We should never get here, because we are reading from a StringReader
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+ private static String getFullStackTrace(Throwable exception) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ exception.printStackTrace(writer);
+ return stringWriter.toString();
+ }
+
+ private static void appendStackTraceLines(
+ List<String> stackTraceLines, StringBuilder destBuilder) {
+ for (String stackTraceLine : stackTraceLines) {
+ destBuilder.append(String.format("%s%n", stackTraceLine));
+ }
+ }
+
+ private static <T> List<T> asReversedList(final List<T> list) {
+ return new AbstractList<T>() {
+
+ @Override
+ public T get(int index) {
+ return list.get(list.size() - index - 1);
+ }
+
+ @Override
+ public int size() {
+ return list.size();
+ }
+ };
+ }
+
+ private enum State {
+ PROCESSING_OTHER_CODE {
+ @Override public State processLine(String methodName) {
+ if (isTestFrameworkMethod(methodName)) {
+ return PROCESSING_TEST_FRAMEWORK_CODE;
+ }
+ return this;
+ }
+ },
+ PROCESSING_TEST_FRAMEWORK_CODE {
+ @Override public State processLine(String methodName) {
+ if (isReflectionMethod(methodName)) {
+ return PROCESSING_REFLECTION_CODE;
+ } else if (isTestFrameworkMethod(methodName)) {
+ return this;
+ }
+ return PROCESSING_OTHER_CODE;
+ }
+ },
+ PROCESSING_REFLECTION_CODE {
+ @Override public State processLine(String methodName) {
+ if (isReflectionMethod(methodName)) {
+ return this;
+ } else if (isTestFrameworkMethod(methodName)) {
+ // This is here to handle TestCase.runBare() calling TestCase.runTest().
+ return PROCESSING_TEST_FRAMEWORK_CODE;
+ }
+ return DONE;
+ }
+ },
+ DONE {
+ @Override public State processLine(String methodName) {
+ return this;
+ }
+ };
+
+ /** Processes a stack trace element method name, possibly moving to a new state. */
+ protected abstract State processLine(String methodName);
+
+ /** Processes a stack trace element, possibly moving to a new state. */
+ public final State processStackTraceElement(StackTraceElement element) {
+ return processLine(element.getClassName() + "." + element.getMethodName() + "()");
+ }
+ }
+
+ private static final String[] TEST_FRAMEWORK_METHOD_NAME_PREFIXES = {
+ "org.junit.runner.",
+ "org.junit.runners.",
+ "org.junit.experimental.runners.",
+ "org.junit.internal.",
+ "junit.extensions",
+ "junit.framework",
+ "junit.runner",
+ "junit.textui",
+ };
+
+ private static final String[] TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES = {
+ "org.junit.internal.StackTracesTest",
+ };
+
+ private static boolean isTestFrameworkMethod(String methodName) {
+ return isMatchingMethod(methodName, TEST_FRAMEWORK_METHOD_NAME_PREFIXES) &&
+ !isMatchingMethod(methodName, TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES);
+ }
+
+ private static final String[] REFLECTION_METHOD_NAME_PREFIXES = {
+ "sun.reflect.",
+ "java.lang.reflect.",
+ "jdk.internal.reflect.",
+ "org.junit.rules.RunRules.<init>(",
+ "org.junit.rules.RunRules.applyAll(", // calls TestRules
+ "org.junit.runners.RuleContainer.apply(", // calls MethodRules & TestRules
+ "junit.framework.TestCase.runBare(", // runBare() directly calls setUp() and tearDown()
+ };
+
+ private static boolean isReflectionMethod(String methodName) {
+ return isMatchingMethod(methodName, REFLECTION_METHOD_NAME_PREFIXES);
+ }
+
+ private static boolean isMatchingMethod(String methodName, String[] methodNamePrefixes) {
+ for (String methodNamePrefix : methodNamePrefixes) {
+ if (methodName.startsWith(methodNamePrefix)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java b/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
index d86ec95..8704a54 100644
--- a/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
+++ b/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
@@ -9,6 +9,17 @@ import org.junit.runners.model.RunnerBuilder;
public class AllDefaultPossibilitiesBuilder extends RunnerBuilder {
private final boolean canUseSuiteMethod;
+ /**
+ * @since 4.13
+ */
+ public AllDefaultPossibilitiesBuilder() {
+ canUseSuiteMethod = true;
+ }
+
+ /**
+ * @deprecated used {@link #AllDefaultPossibilitiesBuilder()}.
+ */
+ @Deprecated
public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) {
this.canUseSuiteMethod = canUseSuiteMethod;
}
diff --git a/src/main/java/org/junit/internal/builders/JUnit4Builder.java b/src/main/java/org/junit/internal/builders/JUnit4Builder.java
index 6a00678..7959e75 100644
--- a/src/main/java/org/junit/internal/builders/JUnit4Builder.java
+++ b/src/main/java/org/junit/internal/builders/JUnit4Builder.java
@@ -1,12 +1,12 @@
package org.junit.internal.builders;
import org.junit.runner.Runner;
-import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.JUnit4;
import org.junit.runners.model.RunnerBuilder;
public class JUnit4Builder extends RunnerBuilder {
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable {
- return new BlockJUnit4ClassRunner(testClass);
+ return new JUnit4(testClass);
}
-} \ No newline at end of file
+}
diff --git a/src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java b/src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java
new file mode 100644
index 0000000..477b150
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/FakeRuntimeMXBean.java
@@ -0,0 +1,21 @@
+package org.junit.internal.management;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * No-op implementation of RuntimeMXBean when the platform doesn't provide it.
+ */
+class FakeRuntimeMXBean implements RuntimeMXBean {
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Always returns an empty list.
+ */
+ public List<String> getInputArguments() {
+ return Collections.emptyList();
+ }
+
+}
+
diff --git a/src/main/java/org/junit/internal/management/FakeThreadMXBean.java b/src/main/java/org/junit/internal/management/FakeThreadMXBean.java
new file mode 100644
index 0000000..893f2e3
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/FakeThreadMXBean.java
@@ -0,0 +1,27 @@
+package org.junit.internal.management;
+
+/**
+ * No-op implementation of ThreadMXBean when the platform doesn't provide it.
+ */
+final class FakeThreadMXBean implements ThreadMXBean {
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Always throws an {@link UnsupportedOperationException}
+ */
+ public long getThreadCpuTime(long id) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Always returns false.
+ */
+ public boolean isThreadCpuTimeSupported() {
+ return false;
+ }
+
+}
+
diff --git a/src/main/java/org/junit/internal/management/ManagementFactory.java b/src/main/java/org/junit/internal/management/ManagementFactory.java
new file mode 100644
index 0000000..5be1447
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/ManagementFactory.java
@@ -0,0 +1,77 @@
+package org.junit.internal.management;
+
+import org.junit.internal.Classes;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Reflective wrapper around {@link java.lang.management.ManagementFactory}
+ */
+public class ManagementFactory {
+ private static final class FactoryHolder {
+ private static final Class<?> MANAGEMENT_FACTORY_CLASS;
+
+ static {
+ Class<?> managementFactoryClass = null;
+ try {
+ managementFactoryClass = Classes.getClass("java.lang.management.ManagementFactory");
+ } catch (ClassNotFoundException e) {
+ // do nothing, managementFactoryClass will be none on failure
+ }
+ MANAGEMENT_FACTORY_CLASS = managementFactoryClass;
+ }
+
+ static Object getBeanObject(String methodName) {
+ if (MANAGEMENT_FACTORY_CLASS != null) {
+ try {
+ return MANAGEMENT_FACTORY_CLASS.getMethod(methodName).invoke(null);
+ } catch (IllegalAccessException e) {
+ // fallthrough
+ } catch (IllegalArgumentException e) {
+ // fallthrough
+ } catch (InvocationTargetException e) {
+ // fallthrough
+ } catch (NoSuchMethodException e) {
+ // fallthrough
+ } catch (SecurityException e) {
+ // fallthrough
+ }
+ }
+ return null;
+ }
+ }
+
+ private static final class RuntimeHolder {
+ private static final RuntimeMXBean RUNTIME_MX_BEAN =
+ getBean(FactoryHolder.getBeanObject("getRuntimeMXBean"));
+
+ private static final RuntimeMXBean getBean(Object runtimeMxBean) {
+ return runtimeMxBean != null
+ ? new ReflectiveRuntimeMXBean(runtimeMxBean) : new FakeRuntimeMXBean();
+ }
+ }
+
+ private static final class ThreadHolder {
+ private static final ThreadMXBean THREAD_MX_BEAN =
+ getBean(FactoryHolder.getBeanObject("getThreadMXBean"));
+
+ private static final ThreadMXBean getBean(Object threadMxBean) {
+ return threadMxBean != null
+ ? new ReflectiveThreadMXBean(threadMxBean) : new FakeThreadMXBean();
+ }
+ }
+
+ /**
+ * @see java.lang.management.ManagementFactory#getRuntimeMXBean()
+ */
+ public static RuntimeMXBean getRuntimeMXBean() {
+ return RuntimeHolder.RUNTIME_MX_BEAN;
+ }
+
+ /**
+ * @see java.lang.management.ManagementFactory#getThreadMXBean()
+ */
+ public static ThreadMXBean getThreadMXBean() {
+ return ThreadHolder.THREAD_MX_BEAN;
+ }
+}
diff --git a/src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java b/src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java
new file mode 100644
index 0000000..289587a
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/ReflectiveRuntimeMXBean.java
@@ -0,0 +1,61 @@
+package org.junit.internal.management;
+
+import org.junit.internal.Classes;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Implementation of {@link RuntimeMXBean} using the JVM reflectively.
+ */
+final class ReflectiveRuntimeMXBean implements RuntimeMXBean {
+ private final Object runtimeMxBean;
+
+ private static final class Holder {
+ private static final Method getInputArgumentsMethod;
+ static {
+ Method inputArguments = null;
+ try {
+ Class<?> threadMXBeanClass = Classes.getClass("java.lang.management.RuntimeMXBean");
+ inputArguments = threadMXBeanClass.getMethod("getInputArguments");
+ } catch (ClassNotFoundException e) {
+ // do nothing, input arguments will be null on failure
+ } catch (NoSuchMethodException e) {
+ // do nothing, input arguments will be null on failure
+ } catch (SecurityException e) {
+ // do nothing, input arguments will be null on failure
+ }
+ getInputArgumentsMethod = inputArguments;
+ }
+ }
+
+ ReflectiveRuntimeMXBean(Object runtimeMxBean) {
+ super();
+ this.runtimeMxBean = runtimeMxBean;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public List<String> getInputArguments() {
+ if (Holder.getInputArgumentsMethod != null) {
+ try {
+ return (List<String>) Holder.getInputArgumentsMethod.invoke(runtimeMxBean);
+ } catch (ClassCastException e) { // no multi-catch with source level 6
+ // fallthrough
+ } catch (IllegalAccessException e) {
+ // fallthrough
+ } catch (IllegalArgumentException e) {
+ // fallthrough
+ } catch (InvocationTargetException e) {
+ // fallthrough
+ }
+ }
+ return Collections.emptyList();
+ }
+
+}
+
diff --git a/src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java b/src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java
new file mode 100644
index 0000000..bc741be
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/ReflectiveThreadMXBean.java
@@ -0,0 +1,92 @@
+package org.junit.internal.management;
+
+import org.junit.internal.Classes;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Implementation of {@link ThreadMXBean} using the JVM reflectively.
+ */
+final class ReflectiveThreadMXBean implements ThreadMXBean {
+ private final Object threadMxBean;
+
+
+ private static final class Holder {
+ static final Method getThreadCpuTimeMethod;
+ static final Method isThreadCpuTimeSupportedMethod;
+
+ private static final String FAILURE_MESSAGE = "Unable to access ThreadMXBean";
+
+ static {
+ Method threadCpuTime = null;
+ Method threadCpuTimeSupported = null;
+ try {
+ Class<?> threadMXBeanClass = Classes.getClass("java.lang.management.ThreadMXBean");
+ threadCpuTime = threadMXBeanClass.getMethod("getThreadCpuTime", long.class);
+ threadCpuTimeSupported = threadMXBeanClass.getMethod("isThreadCpuTimeSupported");
+ } catch (ClassNotFoundException e) {
+ // do nothing, the methods will be null on failure
+ } catch (NoSuchMethodException e) {
+ // do nothing, the methods will be null on failure
+ } catch (SecurityException e) {
+ // do nothing, the methods will be null on failure
+ }
+ getThreadCpuTimeMethod = threadCpuTime;
+ isThreadCpuTimeSupportedMethod = threadCpuTimeSupported;
+ }
+ }
+
+ ReflectiveThreadMXBean(Object threadMxBean) {
+ super();
+ this.threadMxBean = threadMxBean;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getThreadCpuTime(long id) {
+ if (Holder.getThreadCpuTimeMethod != null) {
+ Exception error = null;
+ try {
+ return (Long) Holder.getThreadCpuTimeMethod.invoke(threadMxBean, id);
+ } catch (ClassCastException e) {
+ error = e;
+ // fallthrough
+ } catch (IllegalAccessException e) {
+ error = e;
+ // fallthrough
+ } catch (IllegalArgumentException e) {
+ error = e;
+ // fallthrough
+ } catch (InvocationTargetException e) {
+ error = e;
+ // fallthrough
+ }
+ throw new UnsupportedOperationException(Holder.FAILURE_MESSAGE, error);
+ }
+ throw new UnsupportedOperationException(Holder.FAILURE_MESSAGE);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isThreadCpuTimeSupported() {
+ if (Holder.isThreadCpuTimeSupportedMethod != null) {
+ try {
+ return (Boolean) Holder.isThreadCpuTimeSupportedMethod.invoke(threadMxBean);
+ } catch (ClassCastException e) {
+ // fallthrough
+ } catch (IllegalAccessException e) {
+ // fallthrough
+ } catch (IllegalArgumentException e) {
+ // fallthrough
+ } catch (InvocationTargetException e) {
+ // fallthrough
+ }
+ }
+ return false;
+ }
+
+}
+
diff --git a/src/main/java/org/junit/internal/management/RuntimeMXBean.java b/src/main/java/org/junit/internal/management/RuntimeMXBean.java
new file mode 100644
index 0000000..84f8861
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/RuntimeMXBean.java
@@ -0,0 +1,14 @@
+package org.junit.internal.management;
+
+import java.util.List;
+
+/**
+ * Wrapper for {@link java.lang.management.RuntimeMXBean}.
+ */
+public interface RuntimeMXBean {
+
+ /**
+ * @see java.lang.management.RuntimeMXBean#getInputArguments()
+ */
+ List<String> getInputArguments();
+}
diff --git a/src/main/java/org/junit/internal/management/ThreadMXBean.java b/src/main/java/org/junit/internal/management/ThreadMXBean.java
new file mode 100644
index 0000000..f9225c9
--- /dev/null
+++ b/src/main/java/org/junit/internal/management/ThreadMXBean.java
@@ -0,0 +1,17 @@
+package org.junit.internal.management;
+
+/**
+ * Wrapper for {@link java.lang.management.ThreadMXBean}.
+ */
+public interface ThreadMXBean {
+ /**
+ * @see java.lang.management.ThreadMXBean#getThreadCpuTime(long)
+ */
+ long getThreadCpuTime(long id);
+
+ /**
+ * @see java.lang.management.ThreadMXBean#isThreadCpuTimeSupported()
+ */
+ boolean isThreadCpuTimeSupported();
+}
+
diff --git a/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java b/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java
index 5d45ba3..93a6827 100644
--- a/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java
+++ b/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java
@@ -1,12 +1,11 @@
package org.junit.internal.matchers;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
+import org.junit.internal.Throwables;
+
/**
* A matcher that delegates to throwableMatcher and in addition appends the
* stacktrace of the actual Throwable in case of a mismatch.
@@ -37,9 +36,7 @@ public class StacktracePrintingMatcher<T extends Throwable> extends
}
private String readStacktrace(Throwable throwable) {
- StringWriter stringWriter = new StringWriter();
- throwable.printStackTrace(new PrintWriter(stringWriter));
- return stringWriter.toString();
+ return Throwables.getStacktrace(throwable);
}
@Factory
diff --git a/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java b/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java
index 22ce8bd..6e2ff5e 100644
--- a/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java
+++ b/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java
@@ -14,9 +14,9 @@ import org.hamcrest.TypeSafeMatcher;
public class ThrowableCauseMatcher<T extends Throwable> extends
TypeSafeMatcher<T> {
- private final Matcher<? extends Throwable> causeMatcher;
+ private final Matcher<?> causeMatcher;
- public ThrowableCauseMatcher(Matcher<? extends Throwable> causeMatcher) {
+ public ThrowableCauseMatcher(Matcher<?> causeMatcher) {
this.causeMatcher = causeMatcher;
}
@@ -44,7 +44,7 @@ public class ThrowableCauseMatcher<T extends Throwable> extends
* @param <T> type of the outer exception
*/
@Factory
- public static <T extends Throwable> Matcher<T> hasCause(final Matcher<? extends Throwable> matcher) {
+ public static <T extends Throwable> Matcher<T> hasCause(final Matcher<?> matcher) {
return new ThrowableCauseMatcher<T>(matcher);
}
} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
index 4e2cc12..fb25982 100644
--- a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
+++ b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
@@ -40,7 +40,7 @@ public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
}
private static boolean isMatchesSafelyMethod(Method method) {
- return method.getName().equals("matchesSafely")
+ return "matchesSafely".equals(method.getName())
&& method.getParameterTypes().length == 1
&& !method.isSynthetic();
}
diff --git a/src/main/java/org/junit/internal/requests/ClassRequest.java b/src/main/java/org/junit/internal/requests/ClassRequest.java
index 3d6b100..d60e360 100644
--- a/src/main/java/org/junit/internal/requests/ClassRequest.java
+++ b/src/main/java/org/junit/internal/requests/ClassRequest.java
@@ -1,20 +1,18 @@
package org.junit.internal.requests;
import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
-import org.junit.runner.Request;
+import org.junit.internal.builders.SuiteMethodBuilder;
import org.junit.runner.Runner;
+import org.junit.runners.model.RunnerBuilder;
-public class ClassRequest extends Request {
- private final Object runnerLock = new Object();
-
+public class ClassRequest extends MemoizingRequest {
/*
* We have to use the f prefix, because IntelliJ's JUnit4IdeaTestRunner uses
* reflection to access this field. See
- * https://github.com/junit-team/junit/issues/960
+ * https://github.com/junit-team/junit4/issues/960
*/
private final Class<?> fTestClass;
private final boolean canUseSuiteMethod;
- private volatile Runner runner;
public ClassRequest(Class<?> testClass, boolean canUseSuiteMethod) {
this.fTestClass = testClass;
@@ -26,14 +24,31 @@ public class ClassRequest extends Request {
}
@Override
- public Runner getRunner() {
- if (runner == null) {
- synchronized (runnerLock) {
- if (runner == null) {
- runner = new AllDefaultPossibilitiesBuilder(canUseSuiteMethod).safeRunnerForClass(fTestClass);
- }
+ protected Runner createRunner() {
+ return new CustomAllDefaultPossibilitiesBuilder().safeRunnerForClass(fTestClass);
+ }
+
+ private class CustomAllDefaultPossibilitiesBuilder extends AllDefaultPossibilitiesBuilder {
+
+ @Override
+ protected RunnerBuilder suiteMethodBuilder() {
+ return new CustomSuiteMethodBuilder();
+ }
+ }
+
+ /*
+ * Customization of {@link SuiteMethodBuilder} that prevents use of the
+ * suite method when creating a runner for fTestClass when canUseSuiteMethod
+ * is false.
+ */
+ private class CustomSuiteMethodBuilder extends SuiteMethodBuilder {
+
+ @Override
+ public Runner runnerForClass(Class<?> testClass) throws Throwable {
+ if (testClass == fTestClass && !canUseSuiteMethod) {
+ return null;
}
+ return super.runnerForClass(testClass);
}
- return runner;
}
} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/requests/FilterRequest.java b/src/main/java/org/junit/internal/requests/FilterRequest.java
index 066cba3..5f00399 100644
--- a/src/main/java/org/junit/internal/requests/FilterRequest.java
+++ b/src/main/java/org/junit/internal/requests/FilterRequest.java
@@ -14,7 +14,7 @@ public final class FilterRequest extends Request {
/*
* We have to use the f prefix, because IntelliJ's JUnit4IdeaTestRunner uses
* reflection to access this field. See
- * https://github.com/junit-team/junit/issues/960
+ * https://github.com/junit-team/junit4/issues/960
*/
private final Filter fFilter;
diff --git a/src/main/java/org/junit/internal/requests/MemoizingRequest.java b/src/main/java/org/junit/internal/requests/MemoizingRequest.java
new file mode 100644
index 0000000..191c230
--- /dev/null
+++ b/src/main/java/org/junit/internal/requests/MemoizingRequest.java
@@ -0,0 +1,30 @@
+package org.junit.internal.requests;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.junit.runner.Request;
+import org.junit.runner.Runner;
+
+abstract class MemoizingRequest extends Request {
+ private final Lock runnerLock = new ReentrantLock();
+ private volatile Runner runner;
+
+ @Override
+ public final Runner getRunner() {
+ if (runner == null) {
+ runnerLock.lock();
+ try {
+ if (runner == null) {
+ runner = createRunner();
+ }
+ } finally {
+ runnerLock.unlock();
+ }
+ }
+ return runner;
+ }
+
+ /** Creates the {@link Runner} to return from {@link #getRunner()}. Called at most once. */
+ protected abstract Runner createRunner();
+}
diff --git a/src/main/java/org/junit/internal/requests/OrderingRequest.java b/src/main/java/org/junit/internal/requests/OrderingRequest.java
new file mode 100644
index 0000000..441e595
--- /dev/null
+++ b/src/main/java/org/junit/internal/requests/OrderingRequest.java
@@ -0,0 +1,29 @@
+package org.junit.internal.requests;
+
+import org.junit.internal.runners.ErrorReportingRunner;
+import org.junit.runner.Request;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.InvalidOrderingException;
+import org.junit.runner.manipulation.Ordering;
+
+/** @since 4.13 */
+public class OrderingRequest extends MemoizingRequest {
+ private final Request request;
+ private final Ordering ordering;
+
+ public OrderingRequest(Request request, Ordering ordering) {
+ this.request = request;
+ this.ordering = ordering;
+ }
+
+ @Override
+ protected Runner createRunner() {
+ Runner runner = request.getRunner();
+ try {
+ ordering.apply(runner);
+ } catch (InvalidOrderingException e) {
+ return new ErrorReportingRunner(ordering.getClass(), e);
+ }
+ return runner;
+ }
+}
diff --git a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
index 1d32beb..f52abab 100644
--- a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
+++ b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
@@ -1,33 +1,44 @@
package org.junit.internal.runners;
import java.lang.reflect.InvocationTargetException;
-import java.util.Arrays;
import java.util.List;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.InvalidTestClassError;
import org.junit.runners.model.InitializationError;
+import static java.util.Collections.singletonList;
+
public class ErrorReportingRunner extends Runner {
private final List<Throwable> causes;
- private final Class<?> testClass;
+ private final String classNames;
public ErrorReportingRunner(Class<?> testClass, Throwable cause) {
- if (testClass == null) {
- throw new NullPointerException("Test class cannot be null");
+ this(cause, testClass);
+ }
+
+ public ErrorReportingRunner(Throwable cause, Class<?>... testClasses) {
+ if (testClasses == null || testClasses.length == 0) {
+ throw new NullPointerException("Test classes cannot be null or empty");
}
- this.testClass = testClass;
+ for (Class<?> testClass : testClasses) {
+ if (testClass == null) {
+ throw new NullPointerException("Test class cannot be null");
+ }
+ }
+ classNames = getClassNames(testClasses);
causes = getCauses(cause);
}
-
+
@Override
public Description getDescription() {
- Description description = Description.createSuiteDescription(testClass);
+ Description description = Description.createSuiteDescription(classNames);
for (Throwable each : causes) {
- description.addChild(describeCause(each));
+ description.addChild(describeCause());
}
return description;
}
@@ -39,11 +50,25 @@ public class ErrorReportingRunner extends Runner {
}
}
+ private String getClassNames(Class<?>... testClasses) {
+ final StringBuilder builder = new StringBuilder();
+ for (Class<?> testClass : testClasses) {
+ if (builder.length() != 0) {
+ builder.append(", ");
+ }
+ builder.append(testClass.getName());
+ }
+ return builder.toString();
+ }
+
@SuppressWarnings("deprecation")
private List<Throwable> getCauses(Throwable cause) {
if (cause instanceof InvocationTargetException) {
return getCauses(cause.getCause());
}
+ if (cause instanceof InvalidTestClassError) {
+ return singletonList(cause);
+ }
if (cause instanceof InitializationError) {
return ((InitializationError) cause).getCauses();
}
@@ -51,16 +76,15 @@ public class ErrorReportingRunner extends Runner {
return ((org.junit.internal.runners.InitializationError) cause)
.getCauses();
}
- return Arrays.asList(cause);
+ return singletonList(cause);
}
- private Description describeCause(Throwable child) {
- return Description.createTestDescription(testClass,
- "initializationError");
+ private Description describeCause() {
+ return Description.createTestDescription(classNames, "initializationError");
}
private void runCause(Throwable child, RunNotifier notifier) {
- Description description = describeCause(child);
+ Description description = describeCause();
notifier.fireTestStarted(description);
notifier.fireTestFailure(new Failure(description, child));
notifier.fireTestFinished(description);
diff --git a/src/main/java/org/junit/internal/runners/InitializationError.java b/src/main/java/org/junit/internal/runners/InitializationError.java
index 52065ec..484f58d 100644
--- a/src/main/java/org/junit/internal/runners/InitializationError.java
+++ b/src/main/java/org/junit/internal/runners/InitializationError.java
@@ -15,7 +15,7 @@ public class InitializationError extends Exception {
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
- * See https://github.com/junit-team/junit/issues/976
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final List<Throwable> fErrors;
diff --git a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
index 631fcf2..0d51541 100644
--- a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
+++ b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
@@ -1,5 +1,8 @@
package org.junit.internal.runners;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
import junit.extensions.TestDecorator;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
@@ -12,15 +15,16 @@ import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.Orderer;
+import org.junit.runner.manipulation.InvalidOrderingException;
import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.manipulation.Orderable;
import org.junit.runner.manipulation.Sortable;
import org.junit.runner.manipulation.Sorter;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-public class JUnit38ClassRunner extends Runner implements Filterable, Sortable {
+public class JUnit38ClassRunner extends Runner implements Filterable, Orderable {
private static final class OldTestClassAdaptingListener implements
TestListener {
private final RunNotifier notifier;
@@ -170,6 +174,18 @@ public class JUnit38ClassRunner extends Runner implements Filterable, Sortable {
}
}
+ /**
+ * {@inheritDoc}
+ *
+ * @since 4.13
+ */
+ public void order(Orderer orderer) throws InvalidOrderingException {
+ if (getTest() instanceof Orderable) {
+ Orderable adapter = (Orderable) getTest();
+ adapter.order(orderer);
+ }
+ }
+
private void setTest(Test test) {
this.test = test;
}
diff --git a/src/main/java/org/junit/internal/runners/MethodValidator.java b/src/main/java/org/junit/internal/runners/MethodValidator.java
index ba9c9d1..e656ee5 100644
--- a/src/main/java/org/junit/internal/runners/MethodValidator.java
+++ b/src/main/java/org/junit/internal/runners/MethodValidator.java
@@ -86,7 +86,7 @@ public class MethodValidator {
}
if (each.getReturnType() != Void.TYPE) {
errors.add(new Exception("Method " + each.getName()
- + " should be void"));
+ + "should have a return type of void"));
}
if (each.getParameterTypes().length != 0) {
errors.add(new Exception("Method " + each.getName()
diff --git a/src/main/java/org/junit/internal/runners/TestClass.java b/src/main/java/org/junit/internal/runners/TestClass.java
index 1abaeea..6d24f4f 100644
--- a/src/main/java/org/junit/internal/runners/TestClass.java
+++ b/src/main/java/org/junit/internal/runners/TestClass.java
@@ -85,7 +85,7 @@ public class TestClass {
}
private List<Class<?>> getSuperClasses(Class<?> testClass) {
- ArrayList<Class<?>> results = new ArrayList<Class<?>>();
+ List<Class<?>> results = new ArrayList<Class<?>>();
Class<?> current = testClass;
while (current != null) {
results.add(current);
diff --git a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
index e094809..c5a0764 100644
--- a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
+++ b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
@@ -45,4 +45,27 @@ public class EachTestNotifier {
public void fireTestIgnored() {
notifier.fireTestIgnored(description);
}
+
+ /**
+ * Calls {@link RunNotifier#fireTestSuiteStarted(Description)}, passing the
+ * {@link Description} that was passed to the {@code EachTestNotifier} constructor.
+ * This should be called when a test suite is about to be started.
+ * @see RunNotifier#fireTestSuiteStarted(Description)
+ * @since 4.13
+ */
+ public void fireTestSuiteStarted() {
+ notifier.fireTestSuiteStarted(description);
+ }
+
+ /**
+ * Calls {@link RunNotifier#fireTestSuiteFinished(Description)}, passing the
+ * {@link Description} that was passed to the {@code EachTestNotifier} constructor.
+ * This should be called when a test suite has finished, whether the test suite succeeds
+ * or fails.
+ * @see RunNotifier#fireTestSuiteFinished(Description)
+ * @since 4.13
+ */
+ public void fireTestSuiteFinished() {
+ notifier.fireTestSuiteFinished(description);
+ }
} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/rules/ValidationError.java b/src/main/java/org/junit/internal/runners/rules/ValidationError.java
index d1af8ae..31bd660 100644
--- a/src/main/java/org/junit/internal/runners/rules/ValidationError.java
+++ b/src/main/java/org/junit/internal/runners/rules/ValidationError.java
@@ -5,6 +5,9 @@ import org.junit.runners.model.FrameworkMember;
import java.lang.annotation.Annotation;
class ValidationError extends Exception {
+
+ private static final long serialVersionUID = 3176511008672645574L;
+
public ValidationError(FrameworkMember<?> member, Class<? extends Annotation> annotation, String suffix) {
super(String.format("The @%s '%s' %s", annotation.getSimpleName(), member.getName(), suffix));
}
diff --git a/src/main/java/org/junit/internal/runners/statements/ExpectException.java b/src/main/java/org/junit/internal/runners/statements/ExpectException.java
index d0636bd..9a2a952 100644
--- a/src/main/java/org/junit/internal/runners/statements/ExpectException.java
+++ b/src/main/java/org/junit/internal/runners/statements/ExpectException.java
@@ -19,7 +19,9 @@ public class ExpectException extends Statement {
next.evaluate();
complete = true;
} catch (AssumptionViolatedException e) {
- throw e;
+ if (!expected.isAssignableFrom(e.getClass())) {
+ throw e;
+ }
} catch (Throwable e) {
if (!expected.isAssignableFrom(e.getClass())) {
String message = "Unexpected exception, expected<"
diff --git a/src/main/java/org/junit/internal/runners/statements/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;
@@ -80,6 +89,20 @@ public class FailOnTimeout extends Statement {
}
/**
+ * 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<Throwable> task = new FutureTask<Throwable>(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.<Throwable>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<Thread> 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<Thread> 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<Throwable> {
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 <T> Matcher<Iterable<T>> hasItems(Matcher<? super T>... elementMatchers) {
- return CoreMatchers.<T>hasItems(elementMatchers);
+ return CoreMatchers.hasItems(elementMatchers);
}
/**
@@ -57,7 +57,7 @@ public class JUnitMatchers {
*/
@Deprecated
public static <T> Matcher<Iterable<T>> everyItem(final Matcher<T> 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.
+ * <p>
+ * 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}.
+ * <p>
+ * 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.
+ * <p>
+ * This does nothing to tackle timeouts or time sensitive code under test when
+ * debugging and may make this less useful in such circumstances.
+ * <p>
+ * Example usage:
+ *
+ * <pre>
+ * public static class DisableTimeoutOnDebugSampleTest {
+ *
+ * &#064;Rule
+ * public TestRule timeout = new DisableOnDebug(new Timeout(20));
+ *
+ * &#064;Test
+ * public void myTest() {
+ * int i = 0;
+ * assertEquals(0, i); // suppose you had a break point here to inspect i
+ * }
+ * }
+ * </pre>
+ *
+ * @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<String> 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
+ * <p>
+ * Options specified in:
+ * <ul>
+ * <li>
+ * <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/jpda/conninv.html#Invocation"
+ * >javase-6</a></li>
+ * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/guides/jpda/conninv.html#Invocation"
+ * >javase-7</a></li>
+ * <li><a href="http://docs.oracle.com/javase/8/docs/technotes/guides/jpda/conninv.html#Invocation"
+ * >javase-8</a></li>
+ *
+ *
+ * @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<String> 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> T checkSucceeds(Callable<T> 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;
*
* <pre> public class SimpleExpectedExceptionTest {
* &#064;Rule
- * public ExpectedException thrown= ExpectedException.none();
+ * public ExpectedException thrown = ExpectedException.none();
*
* &#064;Test
* public void throwsNothing() {
@@ -35,16 +34,19 @@ import org.junit.runners.model.Statement;
* }
* }</pre>
*
- * <p>
- * You have to add the {@code ExpectedException} rule to your test.
+ * <p>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.
*
- * <p>
- * Instead of specifying the exception's type you can characterize the
- * expected exception based on other criterias, too:
+ * <p>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.
+ *
+ * <p>Instead of specifying the exception's type you can characterize the
+ * expected exception based on other criteria, too:
*
* <ul>
* <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li>
@@ -53,8 +55,7 @@ import org.junit.runners.model.Statement;
* <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li>
* </ul>
*
- * <p>
- * You can combine any of the presented expect-methods. The test is
+ * <p>You can combine any of the presented expect-methods. The test is
* successful if all specifications are met.
* <pre> &#064;Test
* public void throwsException() {
@@ -63,9 +64,15 @@ import org.junit.runners.model.Statement;
* throw new NullPointerException(&quot;What happened?&quot;);
* }</pre>
*
+ * <p>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.
+ * <pre> &#064;Rule(order = Integer.MAX_VALUE)
+ * public ExpectedException thrown = ExpectedException.none();</pre>
+ *
* <h3>AssumptionViolatedExceptions</h3>
- * <p>
- * JUnit uses {@link AssumptionViolatedException}s for indicating that a test
+ * <p>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;
*
* <h3>AssertionErrors</h3>
*
- * <p>
- * JUnit uses {@link AssertionError}s for indicating that a test is failing. You
+ * <p>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;
* }</pre>
*
* <h3>Missing Exceptions</h3>
- * <p>
- * By default missing exceptions are reported with an error message
+ * <p>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(&quot;What happened?&quot;, cause);
* }</pre>
*/
- 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<Throwable> errors = new ArrayList<Throwable>();
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}.
*
- * <ul>
- * <li>{@link ErrorCollector}: collect multiple errors in one test method</li>
- * <li>{@link ExpectedException}: make flexible assertions about thrown exceptions</li>
- * <li>{@link ExternalResource}: start and stop a server, for example</li>
- * <li>{@link TemporaryFolder}: create fresh files, and delete after test</li>
- * <li>{@link TestName}: remember the test name for use during the method</li>
- * <li>{@link TestWatchman}: add logic at events during method execution</li>
- * <li>{@link Timeout}: cause test to fail after a set time</li>
- * <li>{@link Verifier}: fail test if object state ends up incorrect</li>
- * </ul>
- *
- * Note that {@link MethodRule} has been replaced by {@link TestRule},
+ * <p>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)}:
*
* <pre>
- * public static class UseRuleChain {
- * &#064;Rule
- * public RuleChain chain= RuleChain
- * .outerRule(new LoggingRule("outer rule")
- * .around(new LoggingRule("middle rule")
- * .around(new LoggingRule("inner rule");
+ * public abstract class CompositeRules {
+ * public static TestRule extendedLogging() {
+ * return RuleChain.outerRule(new LoggingRule("outer rule"))
+ * .around(new LoggingRule("middle rule"))
+ * .around(new LoggingRule("inner rule"));
+ * }
+ * }
+ * </pre>
+ *
+ * <pre>
+ * public class UseRuleChain {
+ * &#064;Rule
+ * public final TestRule extendedLogging = CompositeRules.extendedLogging();
*
- * &#064;Test
- * public void example() {
- * assertTrue(true);
- * }
+ * &#064;Test
+ * public void example() {
+ * assertTrue(true);
+ * }
* }
* </pre>
*
@@ -38,6 +46,13 @@ import org.junit.runners.model.Statement;
* finished outer rule
* </pre>
*
+ * 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<TestRule> rulesOfNewChain = new ArrayList<TestRule>();
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.
*
* <p>Example of usage:
* <pre>
@@ -26,18 +31,104 @@ import org.junit.Rule;
* }
* </pre>
*
+ * <p>TemporaryFolder rule supports assured deletion mode, which
+ * will fail the test in case deletion fails with {@link AssertionError}.
+ *
+ * <p>Creating TemporaryFolder with assured deletion:
+ * <pre>
+ * &#064;Rule
+ * public TemporaryFolder folder= TemporaryFolder.builder().assureDeletion().build();
+ * </pre>
+ *
* @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;
*
- * &#064;Rule
+ * &#064;Rule(order = Integer.MIN_VALUE)
* public TestWatcher watchman= new TestWatcher() {
* &#064;Override
* protected void failed(Throwable e, Description description) {
@@ -40,6 +41,11 @@ import org.junit.runners.model.Statement;
* }
* }
* </pre>
+ * <p>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<Throwable> 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 {
*
* &#064;Rule
- * public Timeout globalTimeout= new Timeout(20);
+ * public Timeout globalTimeout = Timeout.millis(20);
*
* &#064;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();
}
/**
@@ -122,6 +125,16 @@ public class Timeout implements TestRule {
}
/**
+ * 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
* on the values configured in this rule. Subclasses
@@ -133,6 +146,7 @@ public class Timeout implements TestRule {
Statement statement) throws Exception {
return FailOnTimeout.builder()
.withTimeout(timeout, timeUnit)
+ .withLookingForStuckThread(lookForStuckThread)
.build(statement);
}
@@ -191,6 +205,25 @@ public class Timeout implements TestRule {
}
/**
+ * 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.,
*/
public Timeout build() {
diff --git a/src/main/java/org/junit/runner/Computer.java b/src/main/java/org/junit/runner/Computer.java
index 8bb4b20..18d0d31 100644
--- a/src/main/java/org/junit/runner/Computer.java
+++ b/src/main/java/org/junit/runner/Computer.java
@@ -30,7 +30,17 @@ public class Computer {
public Runner runnerForClass(Class<?> testClass) throws Throwable {
return getRunner(builder, testClass);
}
- }, classes);
+ }, classes) {
+ @Override
+ protected String getName() {
+ /*
+ * #1320 The generated suite is not based on a real class so
+ * only a 'null' description can be generated from it. This name
+ * will be overridden here.
+ */
+ return "classes";
+ }
+ };
}
/**
diff --git a/src/main/java/org/junit/runner/Describable.java b/src/main/java/org/junit/runner/Describable.java
index 1514141..293fdb3 100644
--- a/src/main/java/org/junit/runner/Describable.java
+++ b/src/main/java/org/junit/runner/Describable.java
@@ -10,5 +10,5 @@ public interface Describable {
/**
* @return a {@link Description} showing the tests to be run by the receiver
*/
- public abstract Description getDescription();
+ Description getDescription();
} \ No newline at end of file
diff --git a/src/main/java/org/junit/runner/Description.java b/src/main/java/org/junit/runner/Description.java
index fe47eac..0846a1e 100644
--- a/src/main/java/org/junit/runner/Description.java
+++ b/src/main/java/org/junit/runner/Description.java
@@ -125,6 +125,17 @@ public class Description implements Serializable {
}
/**
+ * Create a <code>Description</code> named after <code>testClass</code>
+ *
+ * @param testClass A not null {@link Class} containing tests
+ * @param annotations meta-data about the test, for downstream interpreters
+ * @return a <code>Description</code> of <code>testClass</code>
+ */
+ public static Description createSuiteDescription(Class<?> testClass, Annotation... annotations) {
+ return new Description(testClass, testClass.getName(), annotations);
+ }
+
+ /**
* Describes a Runner which runs no tests
*/
public static final Description EMPTY = new Description(null, "No Tests");
@@ -139,7 +150,7 @@ public class Description implements Serializable {
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
- * See https://github.com/junit-team/junit/issues/976
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final Collection<Description> fChildren = new ConcurrentLinkedQueue<Description>();
private final String fDisplayName;
diff --git a/src/main/java/org/junit/runner/FilterFactory.java b/src/main/java/org/junit/runner/FilterFactory.java
index 57b4eaa..e2bfb73 100644
--- a/src/main/java/org/junit/runner/FilterFactory.java
+++ b/src/main/java/org/junit/runner/FilterFactory.java
@@ -16,7 +16,8 @@ public interface FilterFactory {
/**
* Exception thrown if the {@link Filter} cannot be created.
*/
- public static class FilterNotCreatedException extends Exception {
+ @SuppressWarnings("serial")
+ class FilterNotCreatedException extends Exception {
public FilterNotCreatedException(Exception exception) {
super(exception.getMessage(), exception);
}
diff --git a/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
index 434157c..3383407 100644
--- a/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
+++ b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
@@ -85,13 +85,11 @@ class JUnitCommandLineParseResult {
}
private String[] copyArray(String[] args, int from, int to) {
- ArrayList<String> result = new ArrayList<String>();
-
+ String[] result = new String[to - from];
for (int j = from; j != to; ++j) {
- result.add(args[j]);
+ result[j - from] = args[j];
}
-
- return result.toArray(new String[result.size()]);
+ return result;
}
void parseParameters(String[] args) {
diff --git a/src/main/java/org/junit/runner/OrderWith.java b/src/main/java/org/junit/runner/OrderWith.java
new file mode 100644
index 0000000..e8470c9
--- /dev/null
+++ b/src/main/java/org/junit/runner/OrderWith.java
@@ -0,0 +1,28 @@
+package org.junit.runner;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.runner.manipulation.Ordering;
+import org.junit.validator.ValidateWith;
+
+/**
+ * When a test class is annotated with <code>&#064;OrderWith</code> or extends a class annotated
+ * with <code>&#064;OrderWith</code>, JUnit will order the tests in the test class (and child
+ * test classes, if any) using the ordering defined by the {@link Ordering} class.
+ *
+ * @since 4.13
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+@ValidateWith(OrderWithValidator.class)
+public @interface OrderWith {
+ /**
+ * Gets a class that extends {@link Ordering}. The class must have a public no-arg constructor.
+ */
+ Class<? extends Ordering.Factory> value();
+}
diff --git a/src/main/java/org/junit/runner/OrderWithValidator.java b/src/main/java/org/junit/runner/OrderWithValidator.java
new file mode 100644
index 0000000..f8eab25
--- /dev/null
+++ b/src/main/java/org/junit/runner/OrderWithValidator.java
@@ -0,0 +1,38 @@
+package org.junit.runner;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
+import java.util.List;
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.model.TestClass;
+import org.junit.validator.AnnotationValidator;
+
+/**
+ * Validates that there are no errors in the use of the {@code OrderWith}
+ * annotation. If there is, a {@code Throwable} object will be added to the list
+ * of errors.
+ *
+ * @since 4.13
+ */
+public final class OrderWithValidator extends AnnotationValidator {
+
+ /**
+ * Adds to {@code errors} a throwable for each problem detected. Looks for
+ * {@code FixMethodOrder} annotations.
+ *
+ * @param testClass that is being validated
+ * @return A list of exceptions detected
+ *
+ * @since 4.13
+ */
+ @Override
+ public List<Exception> validateAnnotatedClass(TestClass testClass) {
+ if (testClass.getAnnotation(FixMethodOrder.class) != null) {
+ return singletonList(
+ new Exception("@FixMethodOrder cannot be combined with @OrderWith"));
+ }
+ return emptyList();
+ }
+}
diff --git a/src/main/java/org/junit/runner/Request.java b/src/main/java/org/junit/runner/Request.java
index 79c0f1e..7b9a990 100644
--- a/src/main/java/org/junit/runner/Request.java
+++ b/src/main/java/org/junit/runner/Request.java
@@ -5,9 +5,11 @@ import java.util.Comparator;
import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
import org.junit.internal.requests.ClassRequest;
import org.junit.internal.requests.FilterRequest;
+import org.junit.internal.requests.OrderingRequest;
import org.junit.internal.requests.SortingRequest;
import org.junit.internal.runners.ErrorReportingRunner;
import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Ordering;
import org.junit.runners.model.InitializationError;
/**
@@ -71,12 +73,11 @@ public abstract class Request {
*/
public static Request classes(Computer computer, Class<?>... classes) {
try {
- AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
+ AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder();
Runner suite = computer.getSuite(builder, classes);
return runner(suite);
} catch (InitializationError e) {
- throw new RuntimeException(
- "Bug in saff's brain: Suite constructor, called as above, should always complete");
+ return runner(new ErrorReportingRunner(e, classes));
}
}
@@ -132,13 +133,16 @@ public abstract class Request {
}
/**
- * Returns a Request that only runs contains tests whose {@link Description}
- * equals <code>desiredDescription</code>
+ * Returns a Request that only runs tests whose {@link Description}
+ * matches the given description.
*
- * @param desiredDescription {@link Description} of those tests that should be run
+ * <p>Returns an empty {@code Request} if {@code desiredDescription} is not a single test and filters all but the single
+ * test if {@code desiredDescription} is a single test.</p>
+ *
+ * @param desiredDescription {@code Description} of those tests that should be run
* @return the filtered Request
*/
- public Request filterWith(final Description desiredDescription) {
+ public Request filterWith(Description desiredDescription) {
return filterWith(Filter.matchMethodDescription(desiredDescription));
}
@@ -149,15 +153,15 @@ public abstract class Request {
* For example, here is code to run a test suite in alphabetical order:
* <pre>
* private static Comparator&lt;Description&gt; forward() {
- * return new Comparator&lt;Description&gt;() {
- * public int compare(Description o1, Description o2) {
- * return o1.getDisplayName().compareTo(o2.getDisplayName());
- * }
- * };
+ * return new Comparator&lt;Description&gt;() {
+ * public int compare(Description o1, Description o2) {
+ * return o1.getDisplayName().compareTo(o2.getDisplayName());
+ * }
+ * };
* }
*
* public static main() {
- * new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
+ * new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
* }
* </pre>
*
@@ -167,4 +171,32 @@ public abstract class Request {
public Request sortWith(Comparator<Description> comparator) {
return new SortingRequest(this, comparator);
}
+
+ /**
+ * Returns a Request whose Tests can be run in a certain order, defined by
+ * <code>ordering</code>
+ * <p>
+ * For example, here is code to run a test suite in reverse order:
+ * <pre>
+ * private static Ordering reverse() {
+ * return new Ordering() {
+ * public List&lt;Description&gt; orderItems(Collection&lt;Description&gt; descriptions) {
+ * List&lt;Description&gt; ordered = new ArrayList&lt;&gt;(descriptions);
+ * Collections.reverse(ordered);
+ * return ordered;
+ * }
+ * }
+ * }
+ *
+ * public static main() {
+ * new JUnitCore().run(Request.aClass(AllTests.class).orderWith(reverse()));
+ * }
+ * </pre>
+ *
+ * @return a Request with ordered Tests
+ * @since 4.13
+ */
+ public Request orderWith(Ordering ordering) {
+ return new OrderingRequest(this, ordering);
+ }
}
diff --git a/src/main/java/org/junit/runner/Result.java b/src/main/java/org/junit/runner/Result.java
index 73ad059..4b5f4a4 100644
--- a/src/main/java/org/junit/runner/Result.java
+++ b/src/main/java/org/junit/runner/Result.java
@@ -28,6 +28,7 @@ public class Result implements Serializable {
ObjectStreamClass.lookup(SerializedForm.class).getFields();
private final AtomicInteger count;
private final AtomicInteger ignoreCount;
+ private final AtomicInteger assumptionFailureCount;
private final CopyOnWriteArrayList<Failure> failures;
private final AtomicLong runTime;
private final AtomicLong startTime;
@@ -38,6 +39,7 @@ public class Result implements Serializable {
public Result() {
count = new AtomicInteger();
ignoreCount = new AtomicInteger();
+ assumptionFailureCount = new AtomicInteger();
failures = new CopyOnWriteArrayList<Failure>();
runTime = new AtomicLong();
startTime = new AtomicLong();
@@ -46,34 +48,35 @@ public class Result implements Serializable {
private Result(SerializedForm serializedForm) {
count = serializedForm.fCount;
ignoreCount = serializedForm.fIgnoreCount;
+ assumptionFailureCount = serializedForm.assumptionFailureCount;
failures = new CopyOnWriteArrayList<Failure>(serializedForm.fFailures);
runTime = new AtomicLong(serializedForm.fRunTime);
startTime = new AtomicLong(serializedForm.fStartTime);
}
/**
- * @return the number of tests run
+ * Returns the number of tests run
*/
public int getRunCount() {
return count.get();
}
/**
- * @return the number of tests that failed during the run
+ * Returns the number of tests that failed during the run
*/
public int getFailureCount() {
return failures.size();
}
/**
- * @return the number of milliseconds it took to run the entire suite to run
+ * Returns the number of milliseconds it took to run the entire suite to run
*/
public long getRunTime() {
return runTime.get();
}
/**
- * @return the {@link Failure}s describing tests that failed and the problems they encountered
+ * Returns the {@link Failure}s describing tests that failed and the problems they encountered
*/
public List<Failure> getFailures() {
return failures;
@@ -87,6 +90,20 @@ public class Result implements Serializable {
}
/**
+ * Returns the number of tests skipped because of an assumption failure
+ *
+ * @throws UnsupportedOperationException if the result was serialized in a version before JUnit 4.13
+ * @since 4.13
+ */
+ public int getAssumptionFailureCount() {
+ if (assumptionFailureCount == null) {
+ throw new UnsupportedOperationException(
+ "Result was serialized from a version of JUnit that doesn't support this method");
+ }
+ return assumptionFailureCount.get();
+ }
+
+ /**
* @return <code>true</code> if all tests succeeded
*/
public boolean wasSuccessful() {
@@ -137,7 +154,7 @@ public class Result implements Serializable {
@Override
public void testAssumptionFailure(Failure failure) {
- // do nothing: same as passing (for 4.5; may change in 4.6)
+ assumptionFailureCount.getAndIncrement();
}
}
@@ -156,6 +173,7 @@ public class Result implements Serializable {
private static final long serialVersionUID = 1L;
private final AtomicInteger fCount;
private final AtomicInteger fIgnoreCount;
+ private final AtomicInteger assumptionFailureCount;
private final List<Failure> fFailures;
private final long fRunTime;
private final long fStartTime;
@@ -163,6 +181,7 @@ public class Result implements Serializable {
public SerializedForm(Result result) {
fCount = result.count;
fIgnoreCount = result.ignoreCount;
+ assumptionFailureCount = result.assumptionFailureCount;
fFailures = Collections.synchronizedList(new ArrayList<Failure>(result.failures));
fRunTime = result.runTime.longValue();
fStartTime = result.startTime.longValue();
@@ -172,6 +191,7 @@ public class Result implements Serializable {
private SerializedForm(ObjectInputStream.GetField fields) throws IOException {
fCount = (AtomicInteger) fields.get("fCount", null);
fIgnoreCount = (AtomicInteger) fields.get("fIgnoreCount", null);
+ assumptionFailureCount = (AtomicInteger) fields.get("assumptionFailureCount", null);
fFailures = (List<Failure>) fields.get("fFailures", null);
fRunTime = fields.get("fRunTime", 0L);
fStartTime = fields.get("fStartTime", 0L);
@@ -184,6 +204,7 @@ public class Result implements Serializable {
fields.put("fFailures", fFailures);
fields.put("fRunTime", fRunTime);
fields.put("fStartTime", fStartTime);
+ fields.put("assumptionFailureCount", assumptionFailureCount);
s.writeFields();
}
diff --git a/src/main/java/org/junit/runner/manipulation/Alphanumeric.java b/src/main/java/org/junit/runner/manipulation/Alphanumeric.java
new file mode 100644
index 0000000..8388d21
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/Alphanumeric.java
@@ -0,0 +1,27 @@
+package org.junit.runner.manipulation;
+
+import java.util.Comparator;
+
+import org.junit.runner.Description;
+
+/**
+ * A sorter that orders tests alphanumerically by test name.
+ *
+ * @since 4.13
+ */
+public final class Alphanumeric extends Sorter implements Ordering.Factory {
+
+ public Alphanumeric() {
+ super(COMPARATOR);
+ }
+
+ public Ordering create(Context context) {
+ return this;
+ }
+
+ private static final Comparator<Description> COMPARATOR = new Comparator<Description>() {
+ public int compare(Description o1, Description o2) {
+ return o1.getDisplayName().compareTo(o2.getDisplayName());
+ }
+ };
+}
diff --git a/src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java b/src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java
new file mode 100644
index 0000000..d9d60f7
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/InvalidOrderingException.java
@@ -0,0 +1,21 @@
+package org.junit.runner.manipulation;
+
+/**
+ * Thrown when an ordering does something invalid (like remove or add children)
+ *
+ * @since 4.13
+ */
+public class InvalidOrderingException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidOrderingException() {
+ }
+
+ public InvalidOrderingException(String message) {
+ super(message);
+ }
+
+ public InvalidOrderingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Orderable.java b/src/main/java/org/junit/runner/manipulation/Orderable.java
new file mode 100644
index 0000000..9a12a3b
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/Orderable.java
@@ -0,0 +1,21 @@
+package org.junit.runner.manipulation;
+
+/**
+ * Interface for runners that allow ordering of tests.
+ *
+ * <p>Beware of using this interface to cope with order dependencies between tests.
+ * Tests that are isolated from each other are less expensive to maintain and
+ * can be run individually.
+ *
+ * @since 4.13
+ */
+public interface Orderable extends Sortable {
+
+ /**
+ * Orders the tests using <code>orderer</code>
+ *
+ * @throws InvalidOrderingException if orderer does something invalid (like remove or add
+ * children)
+ */
+ void order(Orderer orderer) throws InvalidOrderingException;
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Orderer.java b/src/main/java/org/junit/runner/manipulation/Orderer.java
new file mode 100644
index 0000000..eb13054
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/Orderer.java
@@ -0,0 +1,62 @@
+package org.junit.runner.manipulation;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.runner.Description;
+
+/**
+ * Orders tests.
+ *
+ * @since 4.13
+ */
+public final class Orderer {
+ private final Ordering ordering;
+
+ Orderer(Ordering delegate) {
+ this.ordering = delegate;
+ }
+
+ /**
+ * Orders the descriptions.
+ *
+ * @return descriptions in order
+ */
+ public List<Description> order(Collection<Description> descriptions)
+ throws InvalidOrderingException {
+ List<Description> inOrder = ordering.orderItems(
+ Collections.unmodifiableCollection(descriptions));
+ if (!ordering.validateOrderingIsCorrect()) {
+ return inOrder;
+ }
+
+ Set<Description> uniqueDescriptions = new HashSet<Description>(descriptions);
+ if (!uniqueDescriptions.containsAll(inOrder)) {
+ throw new InvalidOrderingException("Ordering added items");
+ }
+ Set<Description> resultAsSet = new HashSet<Description>(inOrder);
+ if (resultAsSet.size() != inOrder.size()) {
+ throw new InvalidOrderingException("Ordering duplicated items");
+ } else if (!resultAsSet.containsAll(uniqueDescriptions)) {
+ throw new InvalidOrderingException("Ordering removed items");
+ }
+
+ return inOrder;
+ }
+
+ /**
+ * Order the tests in <code>target</code>.
+ *
+ * @throws InvalidOrderingException if ordering does something invalid (like remove or add
+ * children)
+ */
+ public void apply(Object target) throws InvalidOrderingException {
+ if (target instanceof Orderable) {
+ Orderable orderable = (Orderable) target;
+ orderable.order(this);
+ }
+ }
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Ordering.java b/src/main/java/org/junit/runner/manipulation/Ordering.java
new file mode 100644
index 0000000..0d0ce93
--- /dev/null
+++ b/src/main/java/org/junit/runner/manipulation/Ordering.java
@@ -0,0 +1,172 @@
+package org.junit.runner.manipulation;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.runner.Description;
+import org.junit.runner.OrderWith;
+
+/**
+ * Reorders tests. An {@code Ordering} can reverse the order of tests, sort the
+ * order or even shuffle the order.
+ *
+ * <p>In general you will not need to use a <code>Ordering</code> directly.
+ * Instead, use {@link org.junit.runner.Request#orderWith(Ordering)}.
+ *
+ * @since 4.13
+ */
+public abstract class Ordering {
+ private static final String CONSTRUCTOR_ERROR_FORMAT
+ = "Ordering class %s should have a public constructor with signature "
+ + "%s(Ordering.Context context)";
+
+ /**
+ * Creates an {@link Ordering} that shuffles the items using the given
+ * {@link Random} instance.
+ */
+ public static Ordering shuffledBy(final Random random) {
+ return new Ordering() {
+ @Override
+ boolean validateOrderingIsCorrect() {
+ return false;
+ }
+
+ @Override
+ protected List<Description> orderItems(Collection<Description> descriptions) {
+ List<Description> shuffled = new ArrayList<Description>(descriptions);
+ Collections.shuffle(shuffled, random);
+ return shuffled;
+ }
+ };
+ }
+
+ /**
+ * Creates an {@link Ordering} from the given factory class. The class must have a public no-arg
+ * constructor.
+ *
+ * @param factoryClass class to use to create the ordering
+ * @param annotatedTestClass test class that is annotated with {@link OrderWith}.
+ * @throws InvalidOrderingException if the instance could not be created
+ */
+ public static Ordering definedBy(
+ Class<? extends Ordering.Factory> factoryClass, Description annotatedTestClass)
+ throws InvalidOrderingException {
+ if (factoryClass == null) {
+ throw new NullPointerException("factoryClass cannot be null");
+ }
+ if (annotatedTestClass == null) {
+ throw new NullPointerException("annotatedTestClass cannot be null");
+ }
+
+ Ordering.Factory factory;
+ try {
+ Constructor<? extends Ordering.Factory> constructor = factoryClass.getConstructor();
+ factory = constructor.newInstance();
+ } catch (NoSuchMethodException e) {
+ throw new InvalidOrderingException(String.format(
+ CONSTRUCTOR_ERROR_FORMAT,
+ getClassName(factoryClass),
+ factoryClass.getSimpleName()));
+ } catch (Exception e) {
+ throw new InvalidOrderingException(
+ "Could not create ordering for " + annotatedTestClass, e);
+ }
+ return definedBy(factory, annotatedTestClass);
+ }
+
+ /**
+ * Creates an {@link Ordering} from the given factory.
+ *
+ * @param factory factory to use to create the ordering
+ * @param annotatedTestClass test class that is annotated with {@link OrderWith}.
+ * @throws InvalidOrderingException if the instance could not be created
+ */
+ public static Ordering definedBy(
+ Ordering.Factory factory, Description annotatedTestClass)
+ throws InvalidOrderingException {
+ if (factory == null) {
+ throw new NullPointerException("factory cannot be null");
+ }
+ if (annotatedTestClass == null) {
+ throw new NullPointerException("annotatedTestClass cannot be null");
+ }
+
+ return factory.create(new Ordering.Context(annotatedTestClass));
+ }
+
+ private static String getClassName(Class<?> clazz) {
+ String name = clazz.getCanonicalName();
+ if (name == null) {
+ return clazz.getName();
+ }
+ return name;
+ }
+
+ /**
+ * Order the tests in <code>target</code> using this ordering.
+ *
+ * @throws InvalidOrderingException if ordering does something invalid (like remove or add
+ * children)
+ */
+ public void apply(Object target) throws InvalidOrderingException {
+ /*
+ * Note that some subclasses of Ordering override apply(). The Sorter
+ * subclass of Ordering overrides apply() to apply the sort (this is
+ * done because sorting is more efficient than ordering).
+ */
+ if (target instanceof Orderable) {
+ Orderable orderable = (Orderable) target;
+ orderable.order(new Orderer(this));
+ }
+ }
+
+ /**
+ * Returns {@code true} if this ordering could produce invalid results (i.e.
+ * if it could add or remove values).
+ */
+ boolean validateOrderingIsCorrect() {
+ return true;
+ }
+
+ /**
+ * Implemented by sub-classes to order the descriptions.
+ *
+ * @return descriptions in order
+ */
+ protected abstract List<Description> orderItems(Collection<Description> descriptions);
+
+ /** Context about the ordering being applied. */
+ public static class Context {
+ private final Description description;
+
+ /**
+ * Gets the description for the top-level target being ordered.
+ */
+ public Description getTarget() {
+ return description;
+ }
+
+ private Context(Description description) {
+ this.description = description;
+ }
+ }
+
+ /**
+ * Factory for creating {@link Ordering} instances.
+ *
+ * <p>For a factory to be used with {@code @OrderWith} it needs to have a public no-arg
+ * constructor.
+ */
+ public interface Factory {
+ /**
+ * Creates an Ordering instance using the given context. Implementations
+ * of this method that do not need to use the context can return the
+ * same instance every time.
+ */
+ Ordering create(Context context);
+ }
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Sortable.java b/src/main/java/org/junit/runner/manipulation/Sortable.java
index 9ac864c..0c59f33 100644
--- a/src/main/java/org/junit/runner/manipulation/Sortable.java
+++ b/src/main/java/org/junit/runner/manipulation/Sortable.java
@@ -15,6 +15,6 @@ public interface Sortable {
*
* @param sorter the {@link Sorter} to use for sorting the tests
*/
- public void sort(Sorter sorter);
+ void sort(Sorter sorter);
}
diff --git a/src/main/java/org/junit/runner/manipulation/Sorter.java b/src/main/java/org/junit/runner/manipulation/Sorter.java
index 20192d0..4b5274c 100644
--- a/src/main/java/org/junit/runner/manipulation/Sorter.java
+++ b/src/main/java/org/junit/runner/manipulation/Sorter.java
@@ -1,16 +1,21 @@
package org.junit.runner.manipulation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
import org.junit.runner.Description;
/**
* A <code>Sorter</code> orders tests. In general you will not need
- * to use a <code>Sorter</code> directly. Instead, use {@link org.junit.runner.Request#sortWith(Comparator)}.
+ * to use a <code>Sorter</code> directly. Instead, use
+ * {@link org.junit.runner.Request#sortWith(Comparator)}.
*
* @since 4.0
*/
-public class Sorter implements Comparator<Description> {
+public class Sorter extends Ordering implements Comparator<Description> {
/**
* NULL is a <code>Sorter</code> that leaves elements in an undefined order
*/
@@ -27,17 +32,26 @@ public class Sorter implements Comparator<Description> {
* to sort tests
*
* @param comparator the {@link Comparator} to use when sorting tests
+ * @since 4.0
*/
public Sorter(Comparator<Description> comparator) {
this.comparator = comparator;
}
/**
- * Sorts the test in <code>runner</code> using <code>comparator</code>
+ * Sorts the tests in <code>target</code> using <code>comparator</code>.
+ *
+ * @since 4.0
*/
- public void apply(Object object) {
- if (object instanceof Sortable) {
- Sortable sortable = (Sortable) object;
+ @Override
+ public void apply(Object target) {
+ /*
+ * Note that all runners that are Orderable are also Sortable (because
+ * Orderable extends Sortable). Sorting is more efficient than ordering,
+ * so we override the parent behavior so we sort instead.
+ */
+ if (target instanceof Sortable) {
+ Sortable sortable = (Sortable) target;
sortable.sort(this);
}
}
@@ -45,4 +59,32 @@ public class Sorter implements Comparator<Description> {
public int compare(Description o1, Description o2) {
return comparator.compare(o1, o2);
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 4.13
+ */
+ @Override
+ protected final List<Description> orderItems(Collection<Description> descriptions) {
+ /*
+ * In practice, we will never get here--Sorters do their work in the
+ * compare() method--but the Liskov substitution principle demands that
+ * we obey the general contract of Orderable. Luckily, it's trivial to
+ * implement.
+ */
+ List<Description> sorted = new ArrayList<Description>(descriptions);
+ Collections.sort(sorted, this); // Note: it would be incorrect to pass in "comparator"
+ return sorted;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 4.13
+ */
+ @Override
+ boolean validateOrderingIsCorrect() {
+ return false;
+ }
}
diff --git a/src/main/java/org/junit/runner/notification/Failure.java b/src/main/java/org/junit/runner/notification/Failure.java
index c03b4c1..4551302 100644
--- a/src/main/java/org/junit/runner/notification/Failure.java
+++ b/src/main/java/org/junit/runner/notification/Failure.java
@@ -1,9 +1,8 @@
package org.junit.runner.notification;
-import java.io.PrintWriter;
import java.io.Serializable;
-import java.io.StringWriter;
+import org.junit.internal.Throwables;
import org.junit.runner.Description;
/**
@@ -21,7 +20,7 @@ public class Failure implements Serializable {
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
- * See https://github.com/junit-team/junit/issues/976
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final Description fDescription;
private final Throwable fThrownException;
@@ -65,15 +64,19 @@ public class Failure implements Serializable {
}
/**
- * Convenience method
- *
- * @return the printed form of the exception
+ * Gets the printed form of the exception and its stack trace.
*/
public String getTrace() {
- StringWriter stringWriter = new StringWriter();
- PrintWriter writer = new PrintWriter(stringWriter);
- getException().printStackTrace(writer);
- return stringWriter.toString();
+ return Throwables.getStacktrace(getException());
+ }
+
+ /**
+ * Gets a the printed form of the exception, with a trimmed version of the stack trace.
+ * This method will attempt to filter out frames of the stack trace that are below
+ * the test method call.
+ */
+ public String getTrimmedTrace() {
+ return Throwables.getTrimmedStackTrace(getException());
}
/**
diff --git a/src/main/java/org/junit/runner/notification/RunListener.java b/src/main/java/org/junit/runner/notification/RunListener.java
index db9d8c1..d7cac00 100644
--- a/src/main/java/org/junit/runner/notification/RunListener.java
+++ b/src/main/java/org/junit/runner/notification/RunListener.java
@@ -70,6 +70,34 @@ public class RunListener {
}
/**
+ * Called when a test suite is about to be started. If this method is
+ * called for a given {@link Description}, then {@link #testSuiteFinished(Description)}
+ * will also be called for the same {@code Description}.
+ *
+ * <p>Note that not all runners will call this method, so runners should
+ * be prepared to handle {@link #testStarted(Description)} calls for tests
+ * where there was no corresponding {@code testSuiteStarted()} call for
+ * the parent {@code Description}.
+ *
+ * @param description the description of the test suite that is about to be run
+ * (generally a class name)
+ * @since 4.13
+ */
+ public void testSuiteStarted(Description description) throws Exception {
+ }
+
+ /**
+ * Called when a test suite has finished, whether the test suite succeeds or fails.
+ * This method will not be called for a given {@link Description} unless
+ * {@link #testSuiteStarted(Description)} was called for the same @code Description}.
+ *
+ * @param description the description of the test suite that just ran
+ * @since 4.13
+ */
+ public void testSuiteFinished(Description description) throws Exception {
+ }
+
+ /**
* Called when an atomic test is about to be started.
*
* @param description the description of the test that is about to be run
diff --git a/src/main/java/org/junit/runner/notification/RunNotifier.java b/src/main/java/org/junit/runner/notification/RunNotifier.java
index 6875f76..752fa3b 100644
--- a/src/main/java/org/junit/runner/notification/RunNotifier.java
+++ b/src/main/java/org/junit/runner/notification/RunNotifier.java
@@ -65,8 +65,8 @@ public class RunNotifier {
void run() {
int capacity = currentListeners.size();
- ArrayList<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
- ArrayList<Failure> failures = new ArrayList<Failure>(capacity);
+ List<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
+ List<Failure> failures = new ArrayList<Failure>(capacity);
for (RunListener listener : currentListeners) {
try {
notifyListener(listener);
@@ -78,7 +78,7 @@ public class RunNotifier {
fireTestFailures(safeListeners, failures);
}
- abstract protected void notifyListener(RunListener each) throws Exception;
+ protected abstract void notifyListener(RunListener each) throws Exception;
}
/**
@@ -106,6 +106,41 @@ public class RunNotifier {
}
/**
+ * Invoke to tell listeners that a test suite is about to start. Runners are strongly
+ * encouraged--but not required--to call this method. If this method is called for
+ * a given {@link Description} then {@link #fireTestSuiteFinished(Description)} MUST
+ * be called for the same {@code Description}.
+ *
+ * @param description the description of the suite test (generally a class name)
+ * @since 4.13
+ */
+ public void fireTestSuiteStarted(final Description description) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testSuiteStarted(description);
+ }
+ }.run();
+ }
+
+ /**
+ * Invoke to tell listeners that a test suite is about to finish. Always invoke
+ * this method if you invoke {@link #fireTestSuiteStarted(Description)}
+ * as listeners are likely to expect them to come in pairs.
+ *
+ * @param description the description of the suite test (generally a class name)
+ * @since 4.13
+ */
+ public void fireTestSuiteFinished(final Description description) {
+ new SafeNotifier() {
+ @Override
+ protected void notifyListener(RunListener each) throws Exception {
+ each.testSuiteFinished(description);
+ }
+ }.run();
+ }
+
+ /**
* Invoke to tell listeners that an atomic test is about to start.
*
* @param description the description of the atomic test (generally a class and method name)
diff --git a/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java b/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
index c53c1ee..400fed8 100644
--- a/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
+++ b/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
@@ -10,7 +10,7 @@ import org.junit.runner.Result;
* <p>This class synchronizes all listener calls on a RunNotifier instance. This is done because
* prior to JUnit 4.12, all listeners were called in a synchronized block in RunNotifier,
* so no two listeners were ever called concurrently. If we instead made the methods here
- * sychronized, clients that added multiple listeners that called common code might see
+ * synchronized, clients that added multiple listeners that called common code might see
* issues due to the reduced synchronization.
*
* @author Tibor Digana (tibor17)
@@ -43,6 +43,37 @@ final class SynchronizedRunListener extends RunListener {
}
}
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * Synchronized decorator for {@link RunListener#testSuiteStarted(Description)}.
+ * @param description the description of the test suite that is about to be run
+ * (generally a class name).
+ * @throws Exception if any occurs.
+ * @since 4.13
+ */
+ @Override
+ public void testSuiteStarted(Description description) throws Exception {
+ synchronized (monitor) {
+ listener.testSuiteStarted(description);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * Synchronized decorator for {@link RunListener#testSuiteFinished(Description)}.
+ * @param description the description of the test suite that just ran.
+ * @throws Exception
+ * @since 4.13
+ */
+ @Override
+ public void testSuiteFinished(Description description) throws Exception {
+ synchronized (monitor) {
+ listener.testSuiteFinished(description);
+ }
+ }
+
@Override
public void testStarted(Description description) throws Exception {
synchronized (monitor) {
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<FrameworkMethod> {
- private final ConcurrentHashMap<FrameworkMethod, Description> methodDescriptions = new ConcurrentHashMap<FrameworkMethod, Description>();
+ private static TestClassValidator PUBLIC_CLASS_VALIDATOR = new PublicClassValidator();
+
+ private final ConcurrentMap<FrameworkMethod, Description> methodDescriptions = new ConcurrentHashMap<FrameworkMethod, Description>();
+
+ /**
+ * 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<FrameworkMethod> {
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<FrameworkMethod> {
protected void collectInitializationErrors(List<Throwable> errors) {
super.collectInitializationErrors(errors);
+ validatePublicConstructor(errors);
validateNoNonStaticInnerClass(errors);
validateConstructor(errors);
validateInstanceMethods(errors);
@@ -130,6 +156,12 @@ public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
validateMethods(errors);
}
+ private void validatePublicConstructor(List<Throwable> errors) {
+ if (getTestClass().getJavaClass() != null) {
+ errors.addAll(PUBLIC_CLASS_VALIDATOR.validateTestClass(getTestClass()));
+ }
+ }
+
protected void validateNoNonStaticInnerClass(List<Throwable> errors) {
if (getTestClass().isANonStaticInnerClass()) {
String gripe = "The inner class " + getTestClass().getName()
@@ -180,6 +212,7 @@ public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
* 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<Throwable> errors) {
@@ -187,7 +220,7 @@ public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
validatePublicVoidNoArgMethods(Before.class, false, errors);
validateTestMethods(errors);
- if (computeTestMethods().size() == 0) {
+ if (computeTestMethods().isEmpty()) {
errors.add(new Exception("No runnable methods"));
}
}
@@ -218,6 +251,16 @@ public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
}
/**
+ * 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<FrameworkMethod> {
* Here is an outline of the default implementation:
*
* <ul>
- * <li>Invoke {@code method} on the result of {@code createTest()}, and
+ * <li>Invoke {@code method} on the result of {@link #createTest(org.junit.runners.model.FrameworkMethod)}, and
* throw any exceptions thrown by either operation.
- * <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
- * expecting} attribute, return normally only if the previous step threw an
+ * <li>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.
* <li>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<FrameworkMethod> {
* 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<FrameworkMethod> {
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<FrameworkMethod> {
/**
* 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<FrameworkMethod> {
target);
}
- private Statement withRules(FrameworkMethod method, Object target,
- Statement statement) {
- List<TestRule> 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<TestRule> 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<TestRule> 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<org.junit.rules.MethodRule> getMethodRules(Object target) {
- return rules(target);
+ return ruleContainer.apply(method, describeChild(method), target, statement);
}
/**
@@ -378,27 +418,12 @@ public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
* test
*/
protected List<MethodRule> rules(Object target) {
- List<MethodRule> 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<TestRule> testRules,
- Statement statement) {
- return testRules.isEmpty() ? statement :
- new RunRules(statement, testRules, describeChild(method));
+ RuleCollector<MethodRule> collector = new RuleCollector<MethodRule>();
+ 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<FrameworkMethod> {
* test
*/
protected List<TestRule> getTestRules(Object target) {
- List<TestRule> result = getTestClass().getAnnotatedMethodValues(target,
- Rule.class, TestRule.class);
-
- result.addAll(getTestClass().getAnnotatedFieldValues(target,
- Rule.class, TestRule.class));
-
- return result;
+ RuleCollector<TestRule> collector = new RuleCollector<TestRule>();
+ 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<FrameworkMethod> {
}
}
- 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<RuleContainer> CURRENT_RULE_CONTAINER =
+ new ThreadLocal<RuleContainer>();
+
+ private static class RuleCollector<T> implements MemberValueConsumer<T> {
+ final List<T> result = new ArrayList<T>();
+
+ 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.
* <p>
- * For example, to test a Fibonacci function, write:
+ * For example, to test the <code>+</code> operator, write:
* <pre>
* &#064;RunWith(Parameterized.class)
- * public class FibonacciTest {
- * &#064;Parameters(name= &quot;{index}: fib[{0}]={1}&quot;)
+ * public class AdditionTest {
+ * &#064;Parameters(name = &quot;{index}: {0} + {1} = {2}&quot;)
* public static Iterable&lt;Object[]&gt; data() {
- * return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
- * { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
+ * return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 },
+ * { 3, 2, 5 }, { 4, 3, 7 } });
* }
*
- * private int fInput;
+ * private int firstSummand;
*
- * private int fExpected;
+ * private int secondSummand;
*
- * public FibonacciTest(int input, int expected) {
- * fInput= input;
- * fExpected= expected;
+ * private int sum;
+ *
+ * public AdditionTest(int firstSummand, int secondSummand, int sum) {
+ * this.firstSummand = firstSummand;
+ * this.secondSummand = secondSummand;
+ * this.sum = sum;
* }
*
* &#064;Test
* public void test() {
- * assertEquals(fExpected, Fibonacci.compute(fInput));
+ * assertEquals(sum, firstSummand + secondSummand);
* }
* }
* </pre>
* <p>
- * Each instance of <code>FibonacciTest</code> will be constructed using the
- * two-argument constructor and the data values in the
+ * Each instance of <code>AdditionTest</code> will be constructed using the
+ * three-argument constructor and the data values in the
* <code>&#064;Parameters</code> method.
* <p>
* In order that you can easily identify the individual tests, you may provide a
@@ -69,33 +79,36 @@ import org.junit.runners.parameterized.TestWithParameters;
* </dl>
* <p>
* In the example given above, the <code>Parameterized</code> runner creates
- * names like <code>[1: fib(3)=2]</code>. If you don't use the name parameter,
+ * names like <code>[2: 3 + 2 = 5]</code>. If you don't use the name parameter,
* then the current parameter index is used as name.
* <p>
* You can also write:
* <pre>
* &#064;RunWith(Parameterized.class)
- * public class FibonacciTest {
- * &#064;Parameters
- * public static Iterable&lt;Object[]&gt; data() {
- * return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
- * { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
- * }
- *
- * &#064;Parameter(0)
- * public int fInput;
+ * public class AdditionTest {
+ * &#064;Parameters(name = &quot;{index}: {0} + {1} = {2}&quot;)
+ * public static Iterable&lt;Object[]&gt; data() {
+ * return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 },
+ * { 3, 2, 5 }, { 4, 3, 7 } });
+ * }
+ *
+ * &#064;Parameter(0)
+ * public int firstSummand;
*
- * &#064;Parameter(1)
- * public int fExpected;
+ * &#064;Parameter(1)
+ * public int secondSummand;
*
- * &#064;Test
- * public void test() {
- * assertEquals(fExpected, Fibonacci.compute(fInput));
- * }
+ * &#064;Parameter(2)
+ * public int sum;
+ *
+ * &#064;Test
+ * public void test() {
+ * assertEquals(sum, firstSummand + secondSummand);
+ * }
* }
* </pre>
* <p>
- * Each instance of <code>FibonacciTest</code> will be constructed with the default constructor
+ * Each instance of <code>AdditionTest</code> will be constructed with the default constructor
* and fields annotated by <code>&#064;Parameter</code> will be initialized
* with the data values in the <code>&#064;Parameters</code> method.
*
@@ -105,8 +118,7 @@ import org.junit.runners.parameterized.TestWithParameters;
* <pre>
* &#064;Parameters
* public static Object[][] data() {
- * return new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 },
- * { 5, 5 }, { 6, 8 } };
+ * return new Object[][] { { 0, 0, 0 }, { 1, 1, 2 }, { 3, 2, 5 }, { 4, 3, 7 } } };
* }
* </pre>
*
@@ -130,6 +142,19 @@ import org.junit.runners.parameterized.TestWithParameters;
* }
* </pre>
*
+ * <h3>Executing code before/after executing tests for specific parameters</h3>
+ * <p>
+ * 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.
+ * <pre>
+ * &#064;BeforeParam
+ * public static void beforeTestsForParameter(String onlyParameter) {
+ * System.out.println("Testing " + onlyParameter);
+ * }
+ * </pre>
+ *
* <h3>Create different runners</h3>
* <p>
* 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.
*
* <pre>
- * 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;
* }
* </pre>
*
+ * <h3>Avoid creating parameters</h3>
+ * <p>With {@link org.junit.Assume assumptions} you can dynamically skip tests.
+ * Assumptions are also supported by the <code>&#064;Parameters</code> 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.
+ * <pre>
+ * &#064;Parameters
+ * public static Iterable&lt;? extends Object&gt; data() {
+ * String os = System.getProperty("os.name").toLowerCase()
+ * Assume.assumeTrue(os.contains("win"));
+ * return Arrays.asList(&quot;first test&quot;, &quot;second test&quot;);
+ * }
+ * </pre>
* @since 4.0
*/
public class Parameterized extends Suite {
@@ -170,7 +210,7 @@ public class Parameterized extends Suite {
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
- public static @interface Parameters {
+ public @interface Parameters {
/**
* Optional pattern to derive the test's name from the parameters. Use
* numbers in braces to refer to the parameters or the additional data
@@ -201,7 +241,7 @@ public class Parameterized extends Suite {
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
- public static @interface Parameter {
+ public @interface Parameter {
/**
* Method that returns the index of the parameter in the array
* returned by the method annotated by <code>Parameters</code>.
@@ -230,122 +270,235 @@ public class Parameterized extends Suite {
Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class;
}
- private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory();
-
- private static final List<Runner> NO_RUNNERS = Collections.<Runner>emptyList();
+ /**
+ * Annotation for {@code public static void} methods which should be executed before
+ * evaluating tests with particular parameters.
+ *
+ * @see org.junit.BeforeClass
+ * @see org.junit.Before
+ * @since 4.13
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ public @interface BeforeParam {
+ }
- private final List<Runner> runners;
+ /**
+ * Annotation for {@code public static void} methods which should be executed after
+ * evaluating tests with particular parameters.
+ *
+ * @see org.junit.AfterClass
+ * @see org.junit.After
+ * @since 4.13
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ public @interface AfterParam {
+ }
/**
* Only called reflectively. Do not use programmatically.
*/
public Parameterized(Class<?> klass) throws Throwable {
- super(klass, NO_RUNNERS);
- ParametersRunnerFactory runnerFactory = getParametersRunnerFactory(
- klass);
- Parameters parameters = getParametersMethod().getAnnotation(
- Parameters.class);
- runners = Collections.unmodifiableList(createRunnersForParameters(
- allParameters(), parameters.name(), runnerFactory));
+ this(klass, new RunnersFactory(klass));
}
- private ParametersRunnerFactory getParametersRunnerFactory(Class<?> klass)
- throws InstantiationException, IllegalAccessException {
- UseParametersRunnerFactory annotation = klass
- .getAnnotation(UseParametersRunnerFactory.class);
- if (annotation == null) {
- return DEFAULT_FACTORY;
- } else {
- Class<? extends ParametersRunnerFactory> factoryClass = annotation
- .value();
- return factoryClass.newInstance();
- }
+ private Parameterized(Class<?> klass, RunnersFactory runnersFactory) throws Exception {
+ super(klass, runnersFactory.createRunners());
+ validateBeforeParamAndAfterParamMethods(runnersFactory.parameterCount);
}
- @Override
- protected List<Runner> getChildren() {
- return runners;
+ private void validateBeforeParamAndAfterParamMethods(Integer parameterCount)
+ throws InvalidTestClassError {
+ List<Throwable> errors = new ArrayList<Throwable>();
+ validatePublicStaticVoidMethods(Parameterized.BeforeParam.class, parameterCount, errors);
+ validatePublicStaticVoidMethods(Parameterized.AfterParam.class, parameterCount, errors);
+ if (!errors.isEmpty()) {
+ throw new InvalidTestClassError(getTestClass().getJavaClass(), errors);
+ }
}
- private TestWithParameters createTestWithNotNormalizedParameters(
- String pattern, int index, Object parametersOrSingleParameter) {
- Object[] parameters= (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter
- : new Object[] { parametersOrSingleParameter };
- return createTestWithParameters(getTestClass(), pattern, index,
- parameters);
+ private void validatePublicStaticVoidMethods(
+ Class<? extends Annotation> annotation, Integer parameterCount,
+ List<Throwable> errors) {
+ List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(annotation);
+ for (FrameworkMethod fm : methods) {
+ fm.validatePublicVoid(true, errors);
+ if (parameterCount != null) {
+ int methodParameterCount = fm.getMethod().getParameterTypes().length;
+ if (methodParameterCount != 0 && methodParameterCount != parameterCount) {
+ errors.add(new Exception("Method " + fm.getName()
+ + "() should have 0 or " + parameterCount + " parameter(s)"));
+ }
+ }
+ }
}
- @SuppressWarnings("unchecked")
- private Iterable<Object> allParameters() throws Throwable {
- Object parameters = getParametersMethod().invokeExplosively(null);
- if (parameters instanceof Iterable) {
- return (Iterable<Object>) parameters;
- } else if (parameters instanceof Object[]) {
- return Arrays.asList((Object[]) parameters);
- } else {
- throw parametersMethodReturnedWrongType();
+ private static class AssumptionViolationRunner extends Runner {
+ private final Description description;
+ private final AssumptionViolatedException exception;
+
+ AssumptionViolationRunner(TestClass testClass, String methodName,
+ AssumptionViolatedException exception) {
+ this.description = Description
+ .createTestDescription(testClass.getJavaClass(),
+ methodName + "() assumption violation");
+ this.exception = exception;
+ }
+
+ @Override
+ public Description getDescription() {
+ return description;
+ }
+
+ @Override
+ public void run(RunNotifier notifier) {
+ notifier.fireTestAssumptionFailed(new Failure(description, exception));
}
}
- private FrameworkMethod getParametersMethod() throws Exception {
- List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(
- Parameters.class);
- for (FrameworkMethod each : methods) {
- if (each.isStatic() && each.isPublic()) {
- return each;
+ private static class RunnersFactory {
+ private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory();
+
+ private final TestClass testClass;
+ private final FrameworkMethod parametersMethod;
+ private final List<Object> allParameters;
+ private final int parameterCount;
+ private final Runner runnerOverride;
+
+ private RunnersFactory(Class<?> klass) throws Throwable {
+ testClass = new TestClass(klass);
+ parametersMethod = getParametersMethod(testClass);
+ List<Object> allParametersResult;
+ AssumptionViolationRunner assumptionViolationRunner = null;
+ try {
+ allParametersResult = allParameters(testClass, parametersMethod);
+ } catch (AssumptionViolatedException e) {
+ allParametersResult = Collections.emptyList();
+ assumptionViolationRunner = new AssumptionViolationRunner(testClass,
+ parametersMethod.getName(), e);
}
+ allParameters = allParametersResult;
+ runnerOverride = assumptionViolationRunner;
+ parameterCount =
+ allParameters.isEmpty() ? 0 : normalizeParameters(allParameters.get(0)).length;
}
- throw new Exception("No public static parameters method on class "
- + getTestClass().getName());
- }
+ private List<Runner> createRunners() throws Exception {
+ if (runnerOverride != null) {
+ return Collections.singletonList(runnerOverride);
+ }
+ Parameters parameters = parametersMethod.getAnnotation(Parameters.class);
+ return Collections.unmodifiableList(createRunnersForParameters(
+ allParameters, parameters.name(),
+ getParametersRunnerFactory()));
+ }
- private List<Runner> createRunnersForParameters(
- Iterable<Object> allParameters, String namePattern,
- ParametersRunnerFactory runnerFactory)
- throws InitializationError,
- Exception {
- try {
- List<TestWithParameters> tests = createTestsForParameters(
- allParameters, namePattern);
- List<Runner> runners = new ArrayList<Runner>();
- for (TestWithParameters test : tests) {
- runners.add(runnerFactory
- .createRunnerForTestWithParameters(test));
+ private ParametersRunnerFactory getParametersRunnerFactory()
+ throws InstantiationException, IllegalAccessException {
+ UseParametersRunnerFactory annotation = testClass
+ .getAnnotation(UseParametersRunnerFactory.class);
+ if (annotation == null) {
+ return DEFAULT_FACTORY;
+ } else {
+ Class<? extends ParametersRunnerFactory> factoryClass = annotation
+ .value();
+ return factoryClass.newInstance();
}
- return runners;
- } catch (ClassCastException e) {
- throw parametersMethodReturnedWrongType();
}
- }
- private List<TestWithParameters> createTestsForParameters(
- Iterable<Object> allParameters, String namePattern)
- throws Exception {
- int i = 0;
- List<TestWithParameters> children = new ArrayList<TestWithParameters>();
- for (Object parametersOfSingleTest : allParameters) {
- children.add(createTestWithNotNormalizedParameters(namePattern,
- i++, parametersOfSingleTest));
+ private TestWithParameters createTestWithNotNormalizedParameters(
+ String pattern, int index, Object parametersOrSingleParameter) {
+ Object[] parameters = normalizeParameters(parametersOrSingleParameter);
+ return createTestWithParameters(testClass, pattern, index, parameters);
}
- return children;
- }
- private Exception parametersMethodReturnedWrongType() throws Exception {
- String className = getTestClass().getName();
- String methodName = getParametersMethod().getName();
- String message = MessageFormat.format(
- "{0}.{1}() must return an Iterable of arrays.",
- className, methodName);
- return new Exception(message);
- }
+ private static Object[] normalizeParameters(Object parametersOrSingleParameter) {
+ return (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter
+ : new Object[] { parametersOrSingleParameter };
+ }
- private static TestWithParameters createTestWithParameters(
- TestClass testClass, String pattern, int index, Object[] parameters) {
- String finalPattern = pattern.replaceAll("\\{index\\}",
- Integer.toString(index));
- String name = MessageFormat.format(finalPattern, parameters);
- return new TestWithParameters("[" + name + "]", testClass,
- Arrays.asList(parameters));
+ @SuppressWarnings("unchecked")
+ private static List<Object> allParameters(
+ TestClass testClass, FrameworkMethod parametersMethod) throws Throwable {
+ Object parameters = parametersMethod.invokeExplosively(null);
+ if (parameters instanceof List) {
+ return (List<Object>) parameters;
+ } else if (parameters instanceof Collection) {
+ return new ArrayList<Object>((Collection<Object>) parameters);
+ } else if (parameters instanceof Iterable) {
+ List<Object> result = new ArrayList<Object>();
+ for (Object entry : ((Iterable<Object>) parameters)) {
+ result.add(entry);
+ }
+ return result;
+ } else if (parameters instanceof Object[]) {
+ return Arrays.asList((Object[]) parameters);
+ } else {
+ throw parametersMethodReturnedWrongType(testClass, parametersMethod);
+ }
+ }
+
+ private static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception {
+ List<FrameworkMethod> methods = testClass
+ .getAnnotatedMethods(Parameters.class);
+ for (FrameworkMethod each : methods) {
+ if (each.isStatic() && each.isPublic()) {
+ return each;
+ }
+ }
+
+ throw new Exception("No public static parameters method on class "
+ + testClass.getName());
+ }
+
+ private List<Runner> createRunnersForParameters(
+ Iterable<Object> allParameters, String namePattern,
+ ParametersRunnerFactory runnerFactory) throws Exception {
+ try {
+ List<TestWithParameters> tests = createTestsForParameters(
+ allParameters, namePattern);
+ List<Runner> runners = new ArrayList<Runner>();
+ for (TestWithParameters test : tests) {
+ runners.add(runnerFactory
+ .createRunnerForTestWithParameters(test));
+ }
+ return runners;
+ } catch (ClassCastException e) {
+ throw parametersMethodReturnedWrongType(testClass, parametersMethod);
+ }
+ }
+
+ private List<TestWithParameters> createTestsForParameters(
+ Iterable<Object> allParameters, String namePattern)
+ throws Exception {
+ int i = 0;
+ List<TestWithParameters> children = new ArrayList<TestWithParameters>();
+ for (Object parametersOfSingleTest : allParameters) {
+ children.add(createTestWithNotNormalizedParameters(namePattern,
+ i++, parametersOfSingleTest));
+ }
+ return children;
+ }
+
+ private static Exception parametersMethodReturnedWrongType(
+ TestClass testClass, FrameworkMethod parametersMethod) throws Exception {
+ String className = testClass.getName();
+ String methodName = parametersMethod.getName();
+ String message = MessageFormat.format(
+ "{0}.{1}() must return an Iterable of arrays.", className,
+ methodName);
+ return new Exception(message);
+ }
+
+ private TestWithParameters createTestWithParameters(
+ TestClass testClass, String pattern, int index,
+ Object[] parameters) {
+ String finalPattern = pattern.replaceAll("\\{index\\}",
+ Integer.toString(index));
+ String name = MessageFormat.format(finalPattern, parameters);
+ return new TestWithParameters("[" + name + "]", testClass,
+ Arrays.asList(parameters));
+ }
}
}
diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java
index 92641bf..0a0e7cb 100755..100644
--- a/src/main/java/org/junit/runners/ParentRunner.java
+++ b/src/main/java/org/junit/runners/ParentRunner.java
@@ -1,21 +1,25 @@
package org.junit.runners;
+import static org.junit.internal.Checks.notNull;
import static org.junit.internal.runners.rules.RuleMemberValidator.CLASS_RULE_METHOD_VALIDATOR;
import static org.junit.internal.runners.rules.RuleMemberValidator.CLASS_RULE_VALIDATOR;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
+import org.junit.FixMethodOrder;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.internal.AssumptionViolatedException;
@@ -28,18 +32,22 @@ 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.Sortable;
+import org.junit.runner.manipulation.Orderable;
import org.junit.runner.manipulation.Sorter;
import org.junit.runner.notification.RunNotifier;
import org.junit.runner.notification.StoppedByUserException;
+import org.junit.runners.model.FrameworkMember;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.InvalidTestClassError;
+import org.junit.runners.model.MemberValueConsumer;
import org.junit.runners.model.RunnerScheduler;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.junit.validator.AnnotationsValidator;
-import org.junit.validator.PublicClassValidator;
import org.junit.validator.TestClassValidator;
/**
@@ -56,15 +64,15 @@ import org.junit.validator.TestClassValidator;
* @since 4.5
*/
public abstract class ParentRunner<T> extends Runner implements Filterable,
- Sortable {
- private static final List<TestClassValidator> VALIDATORS = Arrays.asList(
- new AnnotationsValidator(), new PublicClassValidator());
+ Orderable {
+ private static final List<TestClassValidator> VALIDATORS = Collections.<TestClassValidator>singletonList(
+ new AnnotationsValidator());
- private final Object childrenLock = new Object();
+ private final Lock childrenLock = new ReentrantLock();
private final TestClass testClass;
// Guarded by childrenLock
- private volatile Collection<T> filteredChildren = null;
+ private volatile List<T> filteredChildren = null;
private volatile RunnerScheduler scheduler = new RunnerScheduler() {
public void schedule(Runnable childStatement) {
@@ -84,6 +92,21 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
validate();
}
+ /**
+ * Constructs a new {@code ParentRunner} that will run the {@code TestClass}.
+ *
+ * @since 4.13
+ */
+ protected ParentRunner(TestClass testClass) throws InitializationError {
+ this.testClass = notNull(testClass);
+ validate();
+ }
+
+ /**
+ * @deprecated Please use {@link #ParentRunner(org.junit.runners.model.TestClass)}.
+ * @since 4.12
+ */
+ @Deprecated
protected TestClass createTestClass(Class<?> testClass) {
return new TestClass(testClass);
}
@@ -192,6 +215,7 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
+ statement = withInterruptIsolation(statement);
}
return statement;
}
@@ -219,7 +243,7 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
/**
* Returns a {@link Statement}: run all non-overridden {@code @AfterClass} methods on this class
- * and superclasses before executing {@code statement}; all AfterClass methods are
+ * and superclasses after executing {@code statement}; all AfterClass methods are
* always executed: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from AfterClass methods into a
* {@link org.junit.runners.model.MultipleFailureException}.
@@ -251,9 +275,10 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
* each method in the tested class.
*/
protected List<TestRule> classRules() {
- List<TestRule> result = testClass.getAnnotatedMethodValues(null, ClassRule.class, TestRule.class);
- result.addAll(testClass.getAnnotatedFieldValues(null, ClassRule.class, TestRule.class));
- return result;
+ ClassRuleCollector collector = new ClassRuleCollector();
+ testClass.collectAnnotatedMethodValues(null, ClassRule.class, TestRule.class, collector);
+ testClass.collectAnnotatedFieldValues(null, ClassRule.class, TestRule.class, collector);
+ return collector.getOrderedRules();
}
/**
@@ -271,6 +296,22 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
}
/**
+ * @return a {@link Statement}: clears interrupt status of current thread after execution of statement
+ */
+ protected final Statement withInterruptIsolation(final Statement statement) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ statement.evaluate();
+ } finally {
+ Thread.interrupted(); // clearing thread interrupted status for isolation
+ }
+ }
+ };
+ }
+
+ /**
* Evaluates whether a child is ignored. The default implementation always
* returns <code>false</code>.
*
@@ -346,8 +387,16 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
@Override
public Description getDescription() {
- Description description = Description.createSuiteDescription(getName(),
- getRunnerAnnotations());
+ Class<?> clazz = getTestClass().getJavaClass();
+ Description description;
+ // if subclass overrides `getName()` then we should use it
+ // to maintain backwards compatibility with JUnit 4.12
+ if (clazz == null || !clazz.getName().equals(getName())) {
+ description = Description.createSuiteDescription(getName(), getRunnerAnnotations());
+ } else {
+ description = Description.createSuiteDescription(clazz, getRunnerAnnotations());
+ }
+
for (T child : getFilteredChildren()) {
description.addChild(describeChild(child));
}
@@ -358,6 +407,7 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier = new EachTestNotifier(notifier,
getDescription());
+ testNotifier.fireTestSuiteStarted();
try {
Statement statement = classBlock(notifier);
statement.evaluate();
@@ -367,6 +417,8 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
throw e;
} catch (Throwable e) {
testNotifier.addFailure(e);
+ } finally {
+ testNotifier.fireTestSuiteFinished();
}
}
@@ -375,7 +427,8 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
//
public void filter(Filter filter) throws NoTestsRemainException {
- synchronized (childrenLock) {
+ childrenLock.lock();
+ try {
List<T> children = new ArrayList<T>(getFilteredChildren());
for (Iterator<T> iter = children.iterator(); iter.hasNext(); ) {
T each = iter.next();
@@ -389,21 +442,70 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
iter.remove();
}
}
- filteredChildren = Collections.unmodifiableCollection(children);
+ filteredChildren = Collections.unmodifiableList(children);
if (filteredChildren.isEmpty()) {
throw new NoTestsRemainException();
}
+ } finally {
+ childrenLock.unlock();
}
}
public void sort(Sorter sorter) {
- synchronized (childrenLock) {
+ if (shouldNotReorder()) {
+ return;
+ }
+
+ childrenLock.lock();
+ try {
for (T each : getFilteredChildren()) {
sorter.apply(each);
}
List<T> sortedChildren = new ArrayList<T>(getFilteredChildren());
Collections.sort(sortedChildren, comparator(sorter));
- filteredChildren = Collections.unmodifiableCollection(sortedChildren);
+ filteredChildren = Collections.unmodifiableList(sortedChildren);
+ } finally {
+ childrenLock.unlock();
+ }
+ }
+
+ /**
+ * Implementation of {@link Orderable#order(Orderer)}.
+ *
+ * @since 4.13
+ */
+ public void order(Orderer orderer) throws InvalidOrderingException {
+ if (shouldNotReorder()) {
+ return;
+ }
+
+ childrenLock.lock();
+ try {
+ List<T> children = getFilteredChildren();
+ // In theory, we could have duplicate Descriptions. De-dup them before ordering,
+ // and add them back at the end.
+ Map<Description, List<T>> childMap = new LinkedHashMap<Description, List<T>>(
+ children.size());
+ for (T child : children) {
+ Description description = describeChild(child);
+ List<T> childrenWithDescription = childMap.get(description);
+ if (childrenWithDescription == null) {
+ childrenWithDescription = new ArrayList<T>(1);
+ childMap.put(description, childrenWithDescription);
+ }
+ childrenWithDescription.add(child);
+ orderer.apply(child);
+ }
+
+ List<Description> inOrder = orderer.order(childMap.keySet());
+
+ children = new ArrayList<T>(children.size());
+ for (Description description : inOrder) {
+ children.addAll(childMap.get(description));
+ }
+ filteredChildren = Collections.unmodifiableList(children);
+ } finally {
+ childrenLock.unlock();
}
}
@@ -411,20 +513,29 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
// Private implementation
//
+ private boolean shouldNotReorder() {
+ // If the test specifies a specific order, do not reorder.
+ return getDescription().getAnnotation(FixMethodOrder.class) != null;
+ }
+
private void validate() throws InitializationError {
List<Throwable> errors = new ArrayList<Throwable>();
collectInitializationErrors(errors);
if (!errors.isEmpty()) {
- throw new InitializationError(errors);
+ throw new InvalidTestClassError(testClass.getJavaClass(), errors);
}
}
- private Collection<T> getFilteredChildren() {
+ private List<T> getFilteredChildren() {
if (filteredChildren == null) {
- synchronized (childrenLock) {
+ childrenLock.lock();
+ try {
if (filteredChildren == null) {
- filteredChildren = Collections.unmodifiableCollection(getChildren());
+ filteredChildren = Collections.unmodifiableList(
+ new ArrayList<T>(getChildren()));
}
+ } finally {
+ childrenLock.unlock();
}
}
return filteredChildren;
@@ -449,4 +560,23 @@ public abstract class ParentRunner<T> extends Runner implements Filterable,
public void setScheduler(RunnerScheduler scheduler) {
this.scheduler = scheduler;
}
+
+ private static class ClassRuleCollector implements MemberValueConsumer<TestRule> {
+ final List<RuleContainer.RuleEntry> entries = new ArrayList<RuleContainer.RuleEntry>();
+
+ public void accept(FrameworkMember<?> member, TestRule value) {
+ ClassRule rule = member.getAnnotation(ClassRule.class);
+ entries.add(new RuleContainer.RuleEntry(value, RuleContainer.RuleEntry.TYPE_TEST_RULE,
+ rule != null ? rule.order() : null));
+ }
+
+ public List<TestRule> getOrderedRules() {
+ Collections.sort(entries, RuleContainer.ENTRY_COMPARATOR);
+ List<TestRule> result = new ArrayList<TestRule>(entries.size());
+ for (RuleContainer.RuleEntry entry : entries) {
+ result.add((TestRule) entry.rule);
+ }
+ return result;
+ }
+ }
}
diff --git a/src/main/java/org/junit/runners/RuleContainer.java b/src/main/java/org/junit/runners/RuleContainer.java
new file mode 100644
index 0000000..30ddd8d
--- /dev/null
+++ b/src/main/java/org/junit/runners/RuleContainer.java
@@ -0,0 +1,113 @@
+package org.junit.runners;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.IdentityHashMap;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.MethodRule;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+/**
+ * Data structure for ordering of {@link TestRule}/{@link MethodRule} instances.
+ *
+ * @since 4.13
+ */
+class RuleContainer {
+ private final IdentityHashMap<Object, Integer> orderValues = new IdentityHashMap<Object, Integer>();
+ private final List<TestRule> testRules = new ArrayList<TestRule>();
+ private final List<MethodRule> methodRules = new ArrayList<MethodRule>();
+
+ /**
+ * Sets order value for the specified rule.
+ */
+ public void setOrder(Object rule, int order) {
+ orderValues.put(rule, order);
+ }
+
+ public void add(MethodRule methodRule) {
+ methodRules.add(methodRule);
+ }
+
+ public void add(TestRule testRule) {
+ testRules.add(testRule);
+ }
+
+ static final Comparator<RuleEntry> ENTRY_COMPARATOR = new Comparator<RuleEntry>() {
+ public int compare(RuleEntry o1, RuleEntry o2) {
+ int result = compareInt(o1.order, o2.order);
+ return result != 0 ? result : o1.type - o2.type;
+ }
+
+ private int compareInt(int a, int b) {
+ return (a < b) ? 1 : (a == b ? 0 : -1);
+ }
+ };
+
+ /**
+ * Returns entries in the order how they should be applied, i.e. inner-to-outer.
+ */
+ private List<RuleEntry> getSortedEntries() {
+ List<RuleEntry> ruleEntries = new ArrayList<RuleEntry>(
+ methodRules.size() + testRules.size());
+ for (MethodRule rule : methodRules) {
+ ruleEntries.add(new RuleEntry(rule, RuleEntry.TYPE_METHOD_RULE, orderValues.get(rule)));
+ }
+ for (TestRule rule : testRules) {
+ ruleEntries.add(new RuleEntry(rule, RuleEntry.TYPE_TEST_RULE, orderValues.get(rule)));
+ }
+ Collections.sort(ruleEntries, ENTRY_COMPARATOR);
+ return ruleEntries;
+ }
+
+ /**
+ * Applies all the rules ordered accordingly to the specified {@code statement}.
+ */
+ public Statement apply(FrameworkMethod method, Description description, Object target,
+ Statement statement) {
+ if (methodRules.isEmpty() && testRules.isEmpty()) {
+ return statement;
+ }
+ Statement result = statement;
+ for (RuleEntry ruleEntry : getSortedEntries()) {
+ if (ruleEntry.type == RuleEntry.TYPE_TEST_RULE) {
+ result = ((TestRule) ruleEntry.rule).apply(result, description);
+ } else {
+ result = ((MethodRule) ruleEntry.rule).apply(result, method, target);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns rule instances in the order how they should be applied, i.e. inner-to-outer.
+ * VisibleForTesting
+ */
+ List<Object> getSortedRules() {
+ List<Object> result = new ArrayList<Object>();
+ for (RuleEntry entry : getSortedEntries()) {
+ result.add(entry.rule);
+ }
+ return result;
+ }
+
+ static class RuleEntry {
+ static final int TYPE_TEST_RULE = 1;
+ static final int TYPE_METHOD_RULE = 0;
+
+ final Object rule;
+ final int type;
+ final int order;
+
+ RuleEntry(Object rule, int type, Integer order) {
+ this.rule = rule;
+ this.type = type;
+ this.order = order != null ? order.intValue() : Rule.DEFAULT_ORDER;
+ }
+ }
+}
diff --git a/src/main/java/org/junit/runners/Suite.java b/src/main/java/org/junit/runners/Suite.java
index b37179f..c2c8e58 100644
--- a/src/main/java/org/junit/runners/Suite.java
+++ b/src/main/java/org/junit/runners/Suite.java
@@ -47,7 +47,7 @@ public class Suite extends ParentRunner<Runner> {
/**
* @return the classes to be run
*/
- public Class<?>[] value();
+ Class<?>[] value();
}
private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError {
@@ -88,7 +88,7 @@ public class Suite extends ParentRunner<Runner> {
* @param suiteClasses the classes in the suite
*/
protected Suite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
- this(new AllDefaultPossibilitiesBuilder(true), klass, suiteClasses);
+ this(new AllDefaultPossibilitiesBuilder(), klass, suiteClasses);
}
/**
diff --git a/src/main/java/org/junit/runners/model/FrameworkField.java b/src/main/java/org/junit/runners/model/FrameworkField.java
index 945e389..ea2b16f 100644
--- a/src/main/java/org/junit/runners/model/FrameworkField.java
+++ b/src/main/java/org/junit/runners/model/FrameworkField.java
@@ -14,12 +14,26 @@ import org.junit.runners.BlockJUnit4ClassRunner;
public class FrameworkField extends FrameworkMember<FrameworkField> {
private final Field field;
- FrameworkField(Field field) {
+ /**
+ * Returns a new {@code FrameworkField} for {@code field}.
+ *
+ * <p>Access relaxed to {@code public} since version 4.13.1.
+ */
+ public FrameworkField(Field field) {
if (field == null) {
throw new NullPointerException(
"FrameworkField cannot be created without an underlying field.");
}
this.field = field;
+
+ if (isPublic()) {
+ // This field could be a public field in a package-scope base class
+ try {
+ field.setAccessible(true);
+ } catch (SecurityException e) {
+ // We may get an IllegalAccessException when we try to access the field
+ }
+ }
}
@Override
@@ -41,6 +55,11 @@ public class FrameworkField extends FrameworkMember<FrameworkField> {
}
@Override
+ boolean isBridgeMethod() {
+ return false;
+ }
+
+ @Override
protected int getModifiers() {
return field.getModifiers();
}
diff --git a/src/main/java/org/junit/runners/model/FrameworkMember.java b/src/main/java/org/junit/runners/model/FrameworkMember.java
index 724f096..5634b3f 100644
--- a/src/main/java/org/junit/runners/model/FrameworkMember.java
+++ b/src/main/java/org/junit/runners/model/FrameworkMember.java
@@ -12,15 +12,29 @@ public abstract class FrameworkMember<T extends FrameworkMember<T>> implements
Annotatable {
abstract boolean isShadowedBy(T otherMember);
- boolean isShadowedBy(List<T> members) {
- for (T each : members) {
- if (isShadowedBy(each)) {
- return true;
+ T handlePossibleBridgeMethod(List<T> members) {
+ for (int i = members.size() - 1; i >=0; i--) {
+ T otherMember = members.get(i);
+ if (isShadowedBy(otherMember)) {
+ if (otherMember.isBridgeMethod()) {
+ /*
+ * We need to return the previously-encountered bridge method
+ * because JUnit won't be able to call the parent method,
+ * because the parent class isn't public.
+ */
+ members.remove(i);
+ return otherMember;
+ }
+ // We found a shadowed member that isn't a bridge method. Ignore it.
+ return null;
}
}
- return false;
+ // No shadow or bridge method found. The caller should add *this* member.
+ return (T) this;
}
+ abstract boolean isBridgeMethod();
+
protected abstract int getModifiers();
/**
diff --git a/src/main/java/org/junit/runners/model/FrameworkMethod.java b/src/main/java/org/junit/runners/model/FrameworkMethod.java
index 3580052..4471407 100644
--- a/src/main/java/org/junit/runners/model/FrameworkMethod.java
+++ b/src/main/java/org/junit/runners/model/FrameworkMethod.java
@@ -28,6 +28,15 @@ public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
"FrameworkMethod cannot be created without an underlying method.");
}
this.method = method;
+
+ if (isPublic()) {
+ // This method could be a public method in a package-scope base class
+ try {
+ method.setAccessible(true);
+ } catch (SecurityException e) {
+ // We may get an IllegalAccessException when we try to call the method
+ }
+ }
}
/**
@@ -149,6 +158,11 @@ public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
}
@Override
+ boolean isBridgeMethod() {
+ return method.isBridge();
+ }
+
+ @Override
public boolean equals(Object obj) {
if (!FrameworkMethod.class.isInstance(obj)) {
return false;
diff --git a/src/main/java/org/junit/runners/model/InitializationError.java b/src/main/java/org/junit/runners/model/InitializationError.java
index 841b565..dd9c8b3 100644
--- a/src/main/java/org/junit/runners/model/InitializationError.java
+++ b/src/main/java/org/junit/runners/model/InitializationError.java
@@ -14,7 +14,7 @@ public class InitializationError extends Exception {
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
- * See https://github.com/junit-team/junit/issues/976
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final List<Throwable> fErrors;
diff --git a/src/main/java/org/junit/runners/model/InvalidTestClassError.java b/src/main/java/org/junit/runners/model/InvalidTestClassError.java
new file mode 100644
index 0000000..57be610
--- /dev/null
+++ b/src/main/java/org/junit/runners/model/InvalidTestClassError.java
@@ -0,0 +1,39 @@
+package org.junit.runners.model;
+
+import java.util.List;
+
+/**
+ * Thrown by {@link org.junit.runner.Runner}s in case the class under test is not valid.
+ * <p>
+ * Its message conveniently lists all of the validation errors.
+ *
+ * @since 4.13
+ */
+public class InvalidTestClassError extends InitializationError {
+ private static final long serialVersionUID = 1L;
+
+ private final String message;
+
+ public InvalidTestClassError(Class<?> offendingTestClass, List<Throwable> validationErrors) {
+ super(validationErrors);
+ this.message = createMessage(offendingTestClass, validationErrors);
+ }
+
+ private static String createMessage(Class<?> testClass, List<Throwable> validationErrors) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("Invalid test class '%s':", testClass.getName()));
+ int i = 1;
+ for (Throwable error : validationErrors) {
+ sb.append("\n " + (i++) + ". " + error.getMessage());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @return a message with a list of all of the validation errors
+ */
+ @Override
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/org/junit/runners/model/MemberValueConsumer.java b/src/main/java/org/junit/runners/model/MemberValueConsumer.java
new file mode 100644
index 0000000..a6157bf
--- /dev/null
+++ b/src/main/java/org/junit/runners/model/MemberValueConsumer.java
@@ -0,0 +1,18 @@
+package org.junit.runners.model;
+
+/**
+ * Represents a receiver for values of annotated fields/methods together with the declaring member.
+ *
+ * @see TestClass#collectAnnotatedFieldValues(Object, Class, Class, MemberValueConsumer)
+ * @see TestClass#collectAnnotatedMethodValues(Object, Class, Class, MemberValueConsumer)
+ * @since 4.13
+ */
+public interface MemberValueConsumer<T> {
+ /**
+ * Receives the next value and its declaring member.
+ *
+ * @param member declaring member ({@link FrameworkMethod} or {@link FrameworkField})
+ * @param value the value of the next member
+ */
+ void accept(FrameworkMember<?> member, T value);
+}
diff --git a/src/main/java/org/junit/runners/model/MultipleFailureException.java b/src/main/java/org/junit/runners/model/MultipleFailureException.java
index 325c645..8e355a7 100644
--- a/src/main/java/org/junit/runners/model/MultipleFailureException.java
+++ b/src/main/java/org/junit/runners/model/MultipleFailureException.java
@@ -1,9 +1,13 @@
package org.junit.runners.model;
+import java.io.PrintStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.junit.TestCouldNotBeSkippedException;
+import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.Throwables;
/**
@@ -17,12 +21,22 @@ public class MultipleFailureException extends Exception {
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
- * See https://github.com/junit-team/junit/issues/976
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final List<Throwable> fErrors;
public MultipleFailureException(List<Throwable> errors) {
- this.fErrors = new ArrayList<Throwable>(errors);
+ if (errors.isEmpty()) {
+ throw new IllegalArgumentException(
+ "List of Throwables must not be empty");
+ }
+ this.fErrors = new ArrayList<Throwable>(errors.size());
+ for (Throwable error : errors) {
+ if (error instanceof AssumptionViolatedException) {
+ error = new TestCouldNotBeSkippedException((AssumptionViolatedException) error);
+ }
+ fErrors.add(error);
+ }
}
public List<Throwable> getFailures() {
@@ -34,11 +48,32 @@ public class MultipleFailureException extends Exception {
StringBuilder sb = new StringBuilder(
String.format("There were %d errors:", fErrors.size()));
for (Throwable e : fErrors) {
- sb.append(String.format("\n %s(%s)", e.getClass().getName(), e.getMessage()));
+ sb.append(String.format("%n %s(%s)", e.getClass().getName(), e.getMessage()));
}
return sb.toString();
}
+ @Override
+ public void printStackTrace() {
+ for (Throwable e: fErrors) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintStream s) {
+ for (Throwable e: fErrors) {
+ e.printStackTrace(s);
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintWriter s) {
+ for (Throwable e: fErrors) {
+ e.printStackTrace(s);
+ }
+ }
+
/**
* Asserts that a list of throwables is empty. If it isn't empty,
* will throw {@link MultipleFailureException} (if there are
diff --git a/src/main/java/org/junit/runners/model/RunnerBuilder.java b/src/main/java/org/junit/runners/model/RunnerBuilder.java
index 7d3eee3..ba7c9e2 100644
--- a/src/main/java/org/junit/runners/model/RunnerBuilder.java
+++ b/src/main/java/org/junit/runners/model/RunnerBuilder.java
@@ -6,7 +6,11 @@ import java.util.List;
import java.util.Set;
import org.junit.internal.runners.ErrorReportingRunner;
+import org.junit.runner.Description;
+import org.junit.runner.OrderWith;
import org.junit.runner.Runner;
+import org.junit.runner.manipulation.InvalidOrderingException;
+import org.junit.runner.manipulation.Ordering;
/**
* A RunnerBuilder is a strategy for constructing runners for classes.
@@ -49,19 +53,39 @@ public abstract class RunnerBuilder {
public abstract Runner runnerForClass(Class<?> testClass) throws Throwable;
/**
- * Always returns a runner, even if it is just one that prints an error instead of running tests.
+ * Always returns a runner for the given test class.
+ *
+ * <p>In case of an exception a runner will be returned that prints an error instead of running
+ * tests.
+ *
+ * <p>Note that some of the internal JUnit implementations of RunnerBuilder will return
+ * {@code null} from this method, but no RunnerBuilder passed to a Runner constructor will
+ * return {@code null} from this method.
*
* @param testClass class to be run
* @return a Runner
*/
public Runner safeRunnerForClass(Class<?> testClass) {
try {
- return runnerForClass(testClass);
+ Runner runner = runnerForClass(testClass);
+ if (runner != null) {
+ configureRunner(runner);
+ }
+ return runner;
} catch (Throwable e) {
return new ErrorReportingRunner(testClass, e);
}
}
+ private void configureRunner(Runner runner) throws InvalidOrderingException {
+ Description description = runner.getDescription();
+ OrderWith orderWith = description.getAnnotation(OrderWith.class);
+ if (orderWith != null) {
+ Ordering ordering = Ordering.definedBy(orderWith.value(), description);
+ ordering.apply(runner);
+ }
+ }
+
Class<?> addParent(Class<?> parent) throws InitializationError {
if (!parents.add(parent)) {
throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
@@ -96,7 +120,7 @@ public abstract class RunnerBuilder {
}
private List<Runner> runners(Class<?>[] children) {
- ArrayList<Runner> runners = new ArrayList<Runner>();
+ List<Runner> runners = new ArrayList<Runner>();
for (Class<?> each : children) {
Runner childRunner = safeRunnerForClass(each);
if (childRunner != null) {
diff --git a/src/main/java/org/junit/runners/model/TestClass.java b/src/main/java/org/junit/runners/model/TestClass.java
index c8a544d..5962c2b 100755..100644
--- a/src/main/java/org/junit/runners/model/TestClass.java
+++ b/src/main/java/org/junit/runners/model/TestClass.java
@@ -84,20 +84,21 @@ public class TestClass implements Annotatable {
for (Annotation each : member.getAnnotations()) {
Class<? extends Annotation> type = each.annotationType();
List<T> members = getAnnotatedMembers(map, type, true);
- if (member.isShadowedBy(members)) {
+ T memberToAdd = member.handlePossibleBridgeMethod(members);
+ if (memberToAdd == null) {
return;
}
if (runsTopToBottom(type)) {
- members.add(0, member);
+ members.add(0, memberToAdd);
} else {
- members.add(member);
+ members.add(memberToAdd);
}
}
}
private static <T extends FrameworkMember<T>> Map<Class<? extends Annotation>, List<T>>
makeDeeplyUnmodifiable(Map<Class<? extends Annotation>, List<T>> source) {
- LinkedHashMap<Class<? extends Annotation>, List<T>> copy =
+ Map<Class<? extends Annotation>, List<T>> copy =
new LinkedHashMap<Class<? extends Annotation>, List<T>>();
for (Map.Entry<Class<? extends Annotation>, List<T>> entry : source.entrySet()) {
copy.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
@@ -168,7 +169,7 @@ public class TestClass implements Annotatable {
}
private static List<Class<?>> getSuperClasses(Class<?> testClass) {
- ArrayList<Class<?>> results = new ArrayList<Class<?>>();
+ List<Class<?>> results = new ArrayList<Class<?>>();
Class<?> current = testClass;
while (current != null) {
results.add(current);
@@ -224,24 +225,59 @@ public class TestClass implements Annotatable {
public <T> List<T> getAnnotatedFieldValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass) {
- List<T> results = new ArrayList<T>();
+ final List<T> results = new ArrayList<T>();
+ collectAnnotatedFieldValues(test, annotationClass, valueClass,
+ new MemberValueConsumer<T>() {
+ public void accept(FrameworkMember<?> member, T value) {
+ results.add(value);
+ }
+ });
+ return results;
+ }
+
+ /**
+ * Finds the fields annotated with the specified annotation and having the specified type,
+ * retrieves the values and passes those to the specified consumer.
+ *
+ * @since 4.13
+ */
+ public <T> void collectAnnotatedFieldValues(Object test,
+ Class<? extends Annotation> annotationClass, Class<T> valueClass,
+ MemberValueConsumer<T> consumer) {
for (FrameworkField each : getAnnotatedFields(annotationClass)) {
try {
Object fieldValue = each.get(test);
if (valueClass.isInstance(fieldValue)) {
- results.add(valueClass.cast(fieldValue));
+ consumer.accept(each, valueClass.cast(fieldValue));
}
} catch (IllegalAccessException e) {
throw new RuntimeException(
"How did getFields return a field we couldn't access?", e);
}
}
- return results;
}
public <T> List<T> getAnnotatedMethodValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass) {
- List<T> results = new ArrayList<T>();
+ final List<T> results = new ArrayList<T>();
+ collectAnnotatedMethodValues(test, annotationClass, valueClass,
+ new MemberValueConsumer<T>() {
+ public void accept(FrameworkMember<?> member, T value) {
+ results.add(value);
+ }
+ });
+ return results;
+ }
+
+ /**
+ * Finds the methods annotated with the specified annotation and returning the specified type,
+ * invokes it and pass the return value to the specified consumer.
+ *
+ * @since 4.13
+ */
+ public <T> void collectAnnotatedMethodValues(Object test,
+ Class<? extends Annotation> annotationClass, Class<T> valueClass,
+ MemberValueConsumer<T> consumer) {
for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) {
try {
/*
@@ -254,14 +290,13 @@ public class TestClass implements Annotatable {
*/
if (valueClass.isAssignableFrom(each.getReturnType())) {
Object fieldValue = each.invokeExplosively(test);
- results.add(valueClass.cast(fieldValue));
+ consumer.accept(each, valueClass.cast(fieldValue));
}
} catch (Throwable e) {
throw new RuntimeException(
"Exception in " + each.getName(), e);
}
}
- return results;
}
public boolean isPublic() {
diff --git a/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java
index 1c49f84..5c70a75 100644
--- a/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java
+++ b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java
@@ -4,8 +4,12 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
+import org.junit.internal.runners.statements.RunAfters;
+import org.junit.internal.runners.statements.RunBefores;
+import org.junit.runner.RunWith;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.FrameworkMethod;
@@ -18,13 +22,17 @@ import org.junit.runners.model.Statement;
*/
public class BlockJUnit4ClassRunnerWithParameters extends
BlockJUnit4ClassRunner {
+ private enum InjectionType {
+ CONSTRUCTOR, FIELD
+ }
+
private final Object[] parameters;
private final String name;
public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test)
throws InitializationError {
- super(test.getTestClass().getJavaClass());
+ super(test.getTestClass());
parameters = test.getParameters().toArray(
new Object[test.getParameters().size()]);
name = test.getName();
@@ -32,10 +40,15 @@ public class BlockJUnit4ClassRunnerWithParameters extends
@Override
public Object createTest() throws Exception {
- if (fieldsAreAnnotated()) {
- return createTestUsingFieldInjection();
- } else {
- return createTestUsingConstructorInjection();
+ InjectionType injectionType = getInjectionType();
+ switch (injectionType) {
+ case CONSTRUCTOR:
+ return createTestUsingConstructorInjection();
+ case FIELD:
+ return createTestUsingFieldInjection();
+ default:
+ throw new IllegalStateException("The injection type "
+ + injectionType + " is not supported.");
}
}
@@ -60,6 +73,13 @@ public class BlockJUnit4ClassRunnerWithParameters extends
int index = annotation.value();
try {
field.set(testClassInstance, parameters[index]);
+ } catch (IllegalAccessException e) {
+ IllegalAccessException wrappedException = new IllegalAccessException(
+ "Cannot set parameter '" + field.getName()
+ + "'. Ensure that the field '" + field.getName()
+ + "' is public.");
+ wrappedException.initCause(e);
+ throw wrappedException;
} catch (IllegalArgumentException iare) {
throw new Exception(getTestClass().getName()
+ ": Trying to set " + field.getName()
@@ -86,7 +106,7 @@ public class BlockJUnit4ClassRunnerWithParameters extends
@Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
- if (fieldsAreAnnotated()) {
+ if (getInjectionType() != InjectionType.CONSTRUCTOR) {
validateZeroArgConstructor(errors);
}
}
@@ -94,7 +114,7 @@ public class BlockJUnit4ClassRunnerWithParameters extends
@Override
protected void validateFields(List<Throwable> errors) {
super.validateFields(errors);
- if (fieldsAreAnnotated()) {
+ if (getInjectionType() == InjectionType.FIELD) {
List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
int[] usedIndices = new int[annotatedFieldsByParameter.size()];
for (FrameworkField each : annotatedFieldsByParameter) {
@@ -125,18 +145,74 @@ public class BlockJUnit4ClassRunnerWithParameters extends
@Override
protected Statement classBlock(RunNotifier notifier) {
- return childrenInvoker(notifier);
+ Statement statement = childrenInvoker(notifier);
+ statement = withBeforeParams(statement);
+ statement = withAfterParams(statement);
+ return statement;
+ }
+
+ private Statement withBeforeParams(Statement statement) {
+ List<FrameworkMethod> befores = getTestClass()
+ .getAnnotatedMethods(Parameterized.BeforeParam.class);
+ return befores.isEmpty() ? statement : new RunBeforeParams(statement, befores);
+ }
+
+ private class RunBeforeParams extends RunBefores {
+ RunBeforeParams(Statement next, List<FrameworkMethod> befores) {
+ super(next, befores, null);
+ }
+
+ @Override
+ protected void invokeMethod(FrameworkMethod method) throws Throwable {
+ int paramCount = method.getMethod().getParameterTypes().length;
+ method.invokeExplosively(null, paramCount == 0 ? (Object[]) null : parameters);
+ }
+ }
+
+ private Statement withAfterParams(Statement statement) {
+ List<FrameworkMethod> afters = getTestClass()
+ .getAnnotatedMethods(Parameterized.AfterParam.class);
+ return afters.isEmpty() ? statement : new RunAfterParams(statement, afters);
+ }
+
+ private class RunAfterParams extends RunAfters {
+ RunAfterParams(Statement next, List<FrameworkMethod> afters) {
+ super(next, afters, null);
+ }
+
+ @Override
+ protected void invokeMethod(FrameworkMethod method) throws Throwable {
+ int paramCount = method.getMethod().getParameterTypes().length;
+ method.invokeExplosively(null, paramCount == 0 ? (Object[]) null : parameters);
+ }
}
@Override
protected Annotation[] getRunnerAnnotations() {
- return new Annotation[0];
+ Annotation[] allAnnotations = super.getRunnerAnnotations();
+ Annotation[] annotationsWithoutRunWith = new Annotation[allAnnotations.length - 1];
+ int i = 0;
+ for (Annotation annotation: allAnnotations) {
+ if (!annotation.annotationType().equals(RunWith.class)) {
+ annotationsWithoutRunWith[i] = annotation;
+ ++i;
+ }
+ }
+ return annotationsWithoutRunWith;
}
private List<FrameworkField> getAnnotatedFieldsByParameter() {
return getTestClass().getAnnotatedFields(Parameter.class);
}
+ private InjectionType getInjectionType() {
+ if (fieldsAreAnnotated()) {
+ return InjectionType.FIELD;
+ } else {
+ return InjectionType.CONSTRUCTOR;
+ }
+ }
+
private boolean fieldsAreAnnotated() {
return !getAnnotatedFieldsByParameter().isEmpty();
}
diff --git a/src/main/java/org/junit/runners/parameterized/ParametersRunnerFactory.java b/src/main/java/org/junit/runners/parameterized/ParametersRunnerFactory.java
index 16ea1f3..8123e83 100644
--- a/src/main/java/org/junit/runners/parameterized/ParametersRunnerFactory.java
+++ b/src/main/java/org/junit/runners/parameterized/ParametersRunnerFactory.java
@@ -4,7 +4,7 @@ import org.junit.runner.Runner;
import org.junit.runners.model.InitializationError;
/**
- * A {@code ParameterizedRunnerFactory} creates a runner for a single
+ * A {@code ParametersRunnerFactory} creates a runner for a single
* {@link TestWithParameters}.
*
* @since 4.12
diff --git a/src/main/java/org/junit/runners/parameterized/TestWithParameters.java b/src/main/java/org/junit/runners/parameterized/TestWithParameters.java
index 1b86644..1c5abd9 100644
--- a/src/main/java/org/junit/runners/parameterized/TestWithParameters.java
+++ b/src/main/java/org/junit/runners/parameterized/TestWithParameters.java
@@ -1,6 +1,7 @@
package org.junit.runners.parameterized;
import static java.util.Collections.unmodifiableList;
+import static org.junit.internal.Checks.notNull;
import java.util.ArrayList;
import java.util.List;
@@ -73,10 +74,4 @@ public class TestWithParameters {
return testClass.getName() + " '" + name + "' with parameters "
+ parameters;
}
-
- private static void notNull(Object value, String message) {
- if (value == null) {
- throw new NullPointerException(message);
- }
- }
}
diff --git a/src/main/java/org/junit/validator/AnnotationValidatorFactory.java b/src/main/java/org/junit/validator/AnnotationValidatorFactory.java
index 7309fdd..fb2460d 100644
--- a/src/main/java/org/junit/validator/AnnotationValidatorFactory.java
+++ b/src/main/java/org/junit/validator/AnnotationValidatorFactory.java
@@ -27,9 +27,6 @@ public class AnnotationValidatorFactory {
}
Class<? extends AnnotationValidator> clazz = validateWithAnnotation.value();
- if (clazz == null) {
- throw new IllegalArgumentException("Can't create validator, value is null in annotation " + validateWithAnnotation.getClass().getName());
- }
try {
AnnotationValidator annotationValidator = clazz.newInstance();
VALIDATORS_FOR_ANNOTATION_TYPES.putIfAbsent(validateWithAnnotation, annotationValidator);
diff --git a/src/main/java/org/junit/validator/AnnotationsValidator.java b/src/main/java/org/junit/validator/AnnotationsValidator.java
index 30f54a6..d8b5840 100644
--- a/src/main/java/org/junit/validator/AnnotationsValidator.java
+++ b/src/main/java/org/junit/validator/AnnotationsValidator.java
@@ -40,7 +40,7 @@ public final class AnnotationsValidator implements TestClassValidator {
return validationErrors;
}
- private static abstract class AnnotatableValidator<T extends Annotatable> {
+ private abstract static class AnnotatableValidator<T extends Annotatable> {
private static final AnnotationValidatorFactory ANNOTATION_VALIDATOR_FACTORY = new AnnotationValidatorFactory();
abstract Iterable<T> getAnnotatablesForTestClass(TestClass testClass);
@@ -116,5 +116,5 @@ public final class AnnotationsValidator implements TestClassValidator {
AnnotationValidator validator, FrameworkField field) {
return validator.validateAnnotatedField(field);
}
- };
+ }
}
diff --git a/src/main/java/org/junit/validator/TestClassValidator.java b/src/main/java/org/junit/validator/TestClassValidator.java
index 43cb787..ba5e892 100644
--- a/src/main/java/org/junit/validator/TestClassValidator.java
+++ b/src/main/java/org/junit/validator/TestClassValidator.java
@@ -17,5 +17,5 @@ public interface TestClassValidator {
* the {@link TestClass} that is validated.
* @return the validation errors found by the validator.
*/
- public List<Exception> validateTestClass(TestClass testClass);
+ List<Exception> validateTestClass(TestClass testClass);
}
diff --git a/src/main/java/org/junit/validator/ValidateWith.java b/src/main/java/org/junit/validator/ValidateWith.java
index 03d7906..3725db8 100644
--- a/src/main/java/org/junit/validator/ValidateWith.java
+++ b/src/main/java/org/junit/validator/ValidateWith.java
@@ -1,8 +1,10 @@
package org.junit.validator;
+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;
/**
* Allows for an {@link AnnotationValidator} to be attached to an annotation.
@@ -13,6 +15,7 @@ import java.lang.annotation.RetentionPolicy;
* @since 4.12
*/
@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
@Inherited
public @interface ValidateWith {
Class<? extends AnnotationValidator> value();
diff --git a/version b/version
index 8d39259..650edfe 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-4.10
+4.13.2