aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Bentley <prb@google.com>2021-03-03 11:24:18 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-03-03 11:24:18 +0000
commitb6446bec0ab6e17c7e047f5063201f66c7eaf8e2 (patch)
tree9d0ef66d108d76d38341cb002d5a46f74299983f
parent23e304a839c5924c7ac7399076318adc511e9985 (diff)
parentd3857e49835c77c9f8000af18904d05b43b24ce1 (diff)
downloadjunit-b6446bec0ab6e17c7e047f5063201f66c7eaf8e2.tar.gz
Merge changes I578a2676,I4b37c2d0,Id1e2d638,I1ebe37da,I6135799c
* changes: Don't convert assumption failures into errors in ErrorCollector.addError(). Remove support for stuck threads Remove DisableOnDebug (new in 4.12) as it is not supported on Android Extra generic type information to aid certain javacs. Upgrade external/junit to 4.13.2
-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/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.java2
-rw-r--r--src/main/java/org/junit/rules/ErrorCollector.java42
-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.java4
-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
121 files changed, 3475 insertions, 784 deletions
diff --git a/README.version b/README.version
index fd9d421..430e5e8 100644
--- a/README.version
+++ b/README.version
@@ -1,8 +1,9 @@
-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:
+ Extra generic type information to aid certain javacs.
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.
+ Don't convert assumption failures into errors in rules/ErrorCollector
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/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..37e3d68 100644
--- a/src/main/java/org/junit/matchers/JUnitMatchers.java
+++ b/src/main/java/org/junit/matchers/JUnitMatchers.java
@@ -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((Matcher) elementMatcher);
}
/**
diff --git a/src/main/java/org/junit/rules/ErrorCollector.java b/src/main/java/org/junit/rules/ErrorCollector.java
index 8c6600e..18d94b8 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,6 +46,21 @@ 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) {
+ if (error == null) {
+ throw new NullPointerException("Error cannot be null");
+ }
+ // BEGIN Android-changed: Don't convert assumption failures to errors. b/181123057
+ // Submitted upstream: https://github.com/junit-team/junit4/issues/1703
+ /*
+ if (error instanceof AssumptionViolatedException) {
+ AssertionError e = new AssertionError(error.getMessage());
+ e.initCause(error);
+ errors.add(e);
+ } else {
+ errors.add(error);
+ }
+ */
+ // END Android-changed: Don't convert assumption failures to errors. b/181123057
errors.add(error);
}
@@ -76,9 +94,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..5cf905a 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 {
@@ -82,7 +82,7 @@ public class Timeout implements TestRule {
}
/**
- * Create a {@code Timeout} instance initialized with values form
+ * Create a {@code Timeout} instance initialized with values from
* a builder.
*
* @since 4.12
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