diff options
Diffstat (limited to 'src/main/java/org/junit/internal')
54 files changed, 2389 insertions, 1727 deletions
diff --git a/src/main/java/org/junit/internal/ArrayComparisonFailure.java b/src/main/java/org/junit/internal/ArrayComparisonFailure.java index 08851de..8627d6e 100644 --- a/src/main/java/org/junit/internal/ArrayComparisonFailure.java +++ b/src/main/java/org/junit/internal/ArrayComparisonFailure.java @@ -7,53 +7,61 @@ import org.junit.Assert; /** * Thrown when two array elements differ + * * @see Assert#assertArrayEquals(String, Object[], Object[]) */ public class ArrayComparisonFailure extends AssertionError { - private static final long serialVersionUID= 1L; - - private 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 - * dimension that was not equal - * @param cause the exception that caused the array's content to fail the assertion test - * @param index the array position of the objects that are not equal. - * @see Assert#assertArrayEquals(String, Object[], Object[]) - */ - public ArrayComparisonFailure(String message, AssertionError cause, int index) { - fMessage= message; - fCause= cause; - addDimension(index); - } - - public void addDimension(int index) { - fIndices.add(0, index); - } - - @Override - public String getMessage() { - StringBuilder builder= new StringBuilder(); - if (fMessage != null) - builder.append(fMessage); - builder.append("arrays first differed at element "); - for (int each : fIndices) { - builder.append("["); - builder.append(each); - builder.append("]"); - } - builder.append("; "); - builder.append(fCause.getMessage()); - return builder.toString(); - } - - /** - * {@inheritDoc} - */ - @Override public String toString() { - return getMessage(); - } + private static final long serialVersionUID = 1L; + + /* + * 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 + */ + private final List<Integer> fIndices = new ArrayList<Integer>(); + private final String fMessage; + + /** + * Construct a new <code>ArrayComparisonFailure</code> with an error text and the array's + * dimension that was not equal + * + * @param cause the exception that caused the array's content to fail the assertion test + * @param index the array position of the objects that are not equal. + * @see Assert#assertArrayEquals(String, Object[], Object[]) + */ + public ArrayComparisonFailure(String message, AssertionError cause, int index) { + this.fMessage = message; + initCause(cause); + addDimension(index); + } + + public void addDimension(int index) { + fIndices.add(0, index); + } + + @Override + public String getMessage() { + StringBuilder sb = new StringBuilder(); + if (fMessage != null) { + sb.append(fMessage); + } + sb.append("arrays first differed at element "); + for (int each : fIndices) { + sb.append("["); + sb.append(each); + sb.append("]"); + } + sb.append("; "); + sb.append(getCause().getMessage()); + return sb.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return getMessage(); + } } diff --git a/src/main/java/org/junit/internal/AssumptionViolatedException.java b/src/main/java/org/junit/internal/AssumptionViolatedException.java index 8e11268..880d73f 100644 --- a/src/main/java/org/junit/internal/AssumptionViolatedException.java +++ b/src/main/java/org/junit/internal/AssumptionViolatedException.java @@ -5,36 +5,107 @@ import org.hamcrest.Matcher; import org.hamcrest.SelfDescribing; import org.hamcrest.StringDescription; +/** + * An exception class used to implement <i>assumptions</i> (state in which a given test + * is meaningful and should or should not be executed). A test for which an assumption + * fails should not generate a test case failure. + * + * @see org.junit.Assume + */ public class AssumptionViolatedException extends RuntimeException implements SelfDescribing { - private static final long serialVersionUID= 1L; - - private final Object fValue; - - private final Matcher<?> fMatcher; - - public AssumptionViolatedException(Object value, Matcher<?> matcher) { - super(value instanceof Throwable ? (Throwable) value : null); - fValue= value; - fMatcher= matcher; - } - - public AssumptionViolatedException(String assumption) { - this(assumption, null); - } - - @Override - public String getMessage() { - return StringDescription.asString(this); - } - - public void describeTo(Description description) { - if (fMatcher != null) { - description.appendText("got: "); - description.appendValue(fValue); - description.appendText(", expected: "); - description.appendDescriptionOf(fMatcher); - } else { - description.appendText("failed assumption: " + fValue); - } - } -}
\ No newline at end of file + private static final long serialVersionUID = 2L; + + /* + * 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 + */ + private final String fAssumption; + private final boolean fValueMatcher; + private final Object fValue; + private final Matcher<?> fMatcher; + + /** + * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead. + */ + @Deprecated + public AssumptionViolatedException(String assumption, boolean hasValue, Object value, Matcher<?> matcher) { + this.fAssumption = assumption; + this.fValue = value; + this.fMatcher = matcher; + this.fValueMatcher = hasValue; + + if (value instanceof Throwable) { + initCause((Throwable) value); + } + } + + /** + * An assumption exception with the given <i>value</i> (String or + * Throwable) and an additional failing {@link Matcher}. + * + * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead. + */ + @Deprecated + public AssumptionViolatedException(Object value, Matcher<?> matcher) { + this(null, true, value, matcher); + } + + /** + * An assumption exception with the given <i>value</i> (String or + * Throwable) and an additional failing {@link Matcher}. + * + * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead. + */ + @Deprecated + public AssumptionViolatedException(String assumption, Object value, Matcher<?> matcher) { + this(assumption, true, value, matcher); + } + + /** + * An assumption exception with the given message only. + * + * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead. + */ + @Deprecated + public AssumptionViolatedException(String assumption) { + this(assumption, false, null, null); + } + + /** + * An assumption exception with the given message and a cause. + * + * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead. + */ + @Deprecated + public AssumptionViolatedException(String assumption, Throwable e) { + this(assumption, false, null, null); + initCause(e); + } + + @Override + public String getMessage() { + return StringDescription.asString(this); + } + + public void describeTo(Description description) { + if (fAssumption != null) { + description.appendText(fAssumption); + } + + if (fValueMatcher) { + // a value was passed in when this instance was constructed; print it + if (fAssumption != null) { + description.appendText(": "); + } + + description.appendText("got: "); + description.appendValue(fValue); + + if (fMatcher != null) { + description.appendText(", expected: "); + description.appendDescriptionOf(fMatcher); + } + } + } +} diff --git a/src/main/java/org/junit/internal/Classes.java b/src/main/java/org/junit/internal/Classes.java new file mode 100644 index 0000000..154603d --- /dev/null +++ b/src/main/java/org/junit/internal/Classes.java @@ -0,0 +1,18 @@ +package org.junit.internal;
+
+import static java.lang.Thread.currentThread;
+
+/**
+ * Miscellaneous functions dealing with classes.
+ */
+public class Classes {
+ /**
+ * Returns Class.forName for {@code className} using the current thread's class loader.
+ *
+ * @param className Name of the class.
+ * @throws ClassNotFoundException
+ */
+ public static Class<?> getClass(String className) throws ClassNotFoundException {
+ return Class.forName(className, true, currentThread().getContextClassLoader());
+ }
+}
diff --git a/src/main/java/org/junit/internal/ComparisonCriteria.java b/src/main/java/org/junit/internal/ComparisonCriteria.java index e97011d..e6d49a4 100644 --- a/src/main/java/org/junit/internal/ComparisonCriteria.java +++ b/src/main/java/org/junit/internal/ComparisonCriteria.java @@ -1,6 +1,7 @@ package org.junit.internal; import java.lang.reflect.Array; +import java.util.Arrays; import org.junit.Assert; @@ -9,68 +10,74 @@ import org.junit.Assert; * may demand exact equality, or, for example, equality within a given delta. */ public abstract class ComparisonCriteria { - /** - * Asserts that two arrays are equal, according to the criteria defined by - * the concrete subclass. If they are not, an {@link AssertionError} is - * thrown with the given message. If <code>expecteds</code> and - * <code>actuals</code> are <code>null</code>, they are considered equal. - * - * @param message - * the identifying message for the {@link AssertionError} ( - * <code>null</code> okay) - * @param expecteds - * Object array or array of arrays (multi-dimensional array) with - * expected values. - * @param actuals - * Object array or array of arrays (multi-dimensional array) with - * actual values - */ - public void arrayEquals(String message, Object expecteds, Object actuals) - throws ArrayComparisonFailure { - if (expecteds == actuals) - return; - String header= message == null ? "" : message + ": "; + /** + * Asserts that two arrays are equal, according to the criteria defined by + * the concrete subclass. If they are not, an {@link AssertionError} is + * thrown with the given message. If <code>expecteds</code> and + * <code>actuals</code> are <code>null</code>, they are considered equal. + * + * @param message the identifying message for the {@link AssertionError} ( + * <code>null</code> okay) + * @param expecteds Object array or array of arrays (multi-dimensional array) with + * expected values. + * @param actuals Object array or array of arrays (multi-dimensional array) with + * actual values + */ + public void arrayEquals(String message, Object expecteds, Object actuals) + 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 + // arrays. The deepEquals check allows us to circumvent it in the usual case where + // the arrays are exactly equal. + return; + } + String header = message == null ? "" : message + ": "; - int expectedsLength= assertArraysAreSameLength(expecteds, - actuals, header); + int expectedsLength = assertArraysAreSameLength(expecteds, + actuals, header); - for (int i= 0; i < expectedsLength; i++) { - Object expected= Array.get(expecteds, i); - Object actual= Array.get(actuals, i); + for (int i = 0; i < expectedsLength; i++) { + Object expected = Array.get(expecteds, i); + Object actual = Array.get(actuals, i); - if (isArray(expected) && isArray(actual)) { - try { - arrayEquals(message, expected, actual); - } catch (ArrayComparisonFailure e) { - e.addDimension(i); - throw e; - } - } else - try { - assertElementsEqual(expected, actual); - } catch (AssertionError e) { - throw new ArrayComparisonFailure(header, e, i); - } - } - } + if (isArray(expected) && isArray(actual)) { + try { + arrayEquals(message, expected, actual); + } catch (ArrayComparisonFailure e) { + e.addDimension(i); + throw e; + } + } else { + try { + assertElementsEqual(expected, actual); + } catch (AssertionError e) { + throw new ArrayComparisonFailure(header, e, i); + } + } + } + } - private boolean isArray(Object expected) { - return expected != null && expected.getClass().isArray(); - } + private boolean isArray(Object expected) { + return expected != null && expected.getClass().isArray(); + } - 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"); - 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); - return expectedsLength; - } + 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"); + } + 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); + } + return expectedsLength; + } - protected abstract void assertElementsEqual(Object expected, Object actual); + protected abstract void assertElementsEqual(Object expected, Object actual); } diff --git a/src/main/java/org/junit/internal/ExactComparisonCriteria.java b/src/main/java/org/junit/internal/ExactComparisonCriteria.java index 0a632ff..a267f7f 100644 --- a/src/main/java/org/junit/internal/ExactComparisonCriteria.java +++ b/src/main/java/org/junit/internal/ExactComparisonCriteria.java @@ -3,8 +3,8 @@ package org.junit.internal; import org.junit.Assert; public class ExactComparisonCriteria extends ComparisonCriteria { - @Override - protected void assertElementsEqual(Object expected, Object actual) { - Assert.assertEquals(expected, actual); - } + @Override + protected void assertElementsEqual(Object expected, Object actual) { + Assert.assertEquals(expected, actual); + } } diff --git a/src/main/java/org/junit/internal/InexactComparisonCriteria.java b/src/main/java/org/junit/internal/InexactComparisonCriteria.java index ef3d7ff..16e804b 100644 --- a/src/main/java/org/junit/internal/InexactComparisonCriteria.java +++ b/src/main/java/org/junit/internal/InexactComparisonCriteria.java @@ -3,17 +3,22 @@ package org.junit.internal; import org.junit.Assert; public class InexactComparisonCriteria extends ComparisonCriteria { - public double fDelta; + public Object fDelta; - public InexactComparisonCriteria(double delta) { - fDelta= delta; - } + public InexactComparisonCriteria(double delta) { + fDelta = delta; + } - @Override - protected void assertElementsEqual(Object expected, Object actual) { - if (expected instanceof Double) - Assert.assertEquals((Double)expected, (Double)actual, fDelta); - else - Assert.assertEquals((Float)expected, (Float)actual, fDelta); - } + public InexactComparisonCriteria(float delta) { + fDelta = delta; + } + + @Override + protected void assertElementsEqual(Object expected, Object actual) { + if (expected instanceof Double) { + Assert.assertEquals((Double) expected, (Double) actual, (Double) fDelta); + } else { + Assert.assertEquals((Float) expected, (Float) actual, (Float) fDelta); + } + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/JUnitSystem.java b/src/main/java/org/junit/internal/JUnitSystem.java index 6d9c242..cf0f2c0 100644 --- a/src/main/java/org/junit/internal/JUnitSystem.java +++ b/src/main/java/org/junit/internal/JUnitSystem.java @@ -3,6 +3,12 @@ package org.junit.internal; import java.io.PrintStream; public interface JUnitSystem { - void exit(int i); - PrintStream out(); + + /** + * Will be removed in the next major release + */ + @Deprecated + void exit(int code); + + PrintStream out(); } diff --git a/src/main/java/org/junit/internal/MethodSorter.java b/src/main/java/org/junit/internal/MethodSorter.java new file mode 100644 index 0000000..d8e661a --- /dev/null +++ b/src/main/java/org/junit/internal/MethodSorter.java @@ -0,0 +1,72 @@ +package org.junit.internal; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; + +import org.junit.FixMethodOrder; + +public class MethodSorter { + /** + * DEFAULT sort order + */ + public static final Comparator<Method> DEFAULT = new Comparator<Method>() { + public int compare(Method m1, Method m2) { + int i1 = m1.getName().hashCode(); + int i2 = m2.getName().hashCode(); + if (i1 != i2) { + return i1 < i2 ? -1 : 1; + } + return NAME_ASCENDING.compare(m1, m2); + } + }; + + /** + * Method name ascending lexicographic sort order, with {@link Method#toString()} as a tiebreaker + */ + public static final Comparator<Method> NAME_ASCENDING = new Comparator<Method>() { + public int compare(Method m1, Method m2) { + final int comparison = m1.getName().compareTo(m2.getName()); + if (comparison != 0) { + return comparison; + } + return m1.toString().compareTo(m2.toString()); + } + }; + + /** + * Gets declared methods of a class in a predictable order, unless @FixMethodOrder(MethodSorters.JVM) is specified. + * + * Using the JVM order is unwise since the Java platform does not + * specify any particular order, and in fact JDK 7 returns a more or less + * random order; well-written test code would not assume any order, but some + * does, and a predictable failure is better than a random failure on + * certain platforms. By default, uses an unspecified but deterministic order. + * + * @param clazz a class + * @return same as {@link Class#getDeclaredMethods} but sorted + * @see <a href="http://bugs.sun.com/view_bug.do?bug_id=7023180">JDK + * (non-)bug #7023180</a> + */ + public static Method[] getDeclaredMethods(Class<?> clazz) { + Comparator<Method> comparator = getSorter(clazz.getAnnotation(FixMethodOrder.class)); + + Method[] methods = clazz.getDeclaredMethods(); + if (comparator != null) { + Arrays.sort(methods, comparator); + } + + return methods; + } + + private MethodSorter() { + } + + private static Comparator<Method> getSorter(FixMethodOrder fixMethodOrder) { + if (fixMethodOrder == null) { + return DEFAULT; + } + + return fixMethodOrder.value().getComparator(); + } +} diff --git a/src/main/java/org/junit/internal/RealSystem.java b/src/main/java/org/junit/internal/RealSystem.java index 1067c6d..e64e1fe 100644 --- a/src/main/java/org/junit/internal/RealSystem.java +++ b/src/main/java/org/junit/internal/RealSystem.java @@ -4,12 +4,16 @@ import java.io.PrintStream; public class RealSystem implements JUnitSystem { - public void exit(int code) { - System.exit(code); - } + /** + * Will be removed in the next major release + */ + @Deprecated + public void exit(int code) { + System.exit(code); + } - public PrintStream out() { - return System.out; - } + public PrintStream out() { + return System.out; + } } diff --git a/src/main/java/org/junit/internal/TextListener.java b/src/main/java/org/junit/internal/TextListener.java index 2b1c679..9aa56c7 100644 --- a/src/main/java/org/junit/internal/TextListener.java +++ b/src/main/java/org/junit/internal/TextListener.java @@ -11,88 +11,91 @@ import org.junit.runner.notification.RunListener; public class TextListener extends RunListener { - private final PrintStream fWriter; - - public TextListener(JUnitSystem system) { - this(system.out()); - } - - public TextListener(PrintStream writer) { - this.fWriter= writer; - } - - @Override - public void testRunFinished(Result result) { - printHeader(result.getRunTime()); - printFailures(result); - printFooter(result); - } - - @Override - public void testStarted(Description description) { - fWriter.append('.'); - } - - @Override - public void testFailure(Failure failure) { - fWriter.append('E'); - } - - @Override - public void testIgnored(Description description) { - fWriter.append('I'); - } - - /* - * Internal methods - */ - - private PrintStream getWriter() { - return fWriter; - } - - protected void printHeader(long runTime) { - getWriter().println(); - getWriter().println("Time: " + elapsedTimeAsString(runTime)); - } - - protected void printFailures(Result result) { - List<Failure> failures= result.getFailures(); - if (failures.size() == 0) - return; - if (failures.size() == 1) - getWriter().println("There was " + failures.size() + " failure:"); - else - getWriter().println("There were " + failures.size() + " failures:"); - int i= 1; - for (Failure each : failures) - printFailure(each, "" + i++); - } - - protected void printFailure(Failure each, String prefix) { - getWriter().println(prefix + ") " + each.getTestHeader()); - getWriter().print(each.getTrace()); - } - - protected void printFooter(Result result) { - if (result.wasSuccessful()) { - getWriter().println(); - getWriter().print("OK"); - getWriter().println(" (" + result.getRunCount() + " test" + (result.getRunCount() == 1 ? "" : "s") + ")"); - - } else { - getWriter().println(); - getWriter().println("FAILURES!!!"); - getWriter().println("Tests run: " + result.getRunCount() + ", Failures: " + result.getFailureCount()); - } - getWriter().println(); - } - - /** - * Returns the formatted string of the elapsed time. Duplicated from - * BaseTestRunner. Fix it. - */ - protected String elapsedTimeAsString(long runTime) { - return NumberFormat.getInstance().format((double) runTime / 1000); - } + private final PrintStream writer; + + public TextListener(JUnitSystem system) { + this(system.out()); + } + + public TextListener(PrintStream writer) { + this.writer = writer; + } + + @Override + public void testRunFinished(Result result) { + printHeader(result.getRunTime()); + printFailures(result); + printFooter(result); + } + + @Override + public void testStarted(Description description) { + writer.append('.'); + } + + @Override + public void testFailure(Failure failure) { + writer.append('E'); + } + + @Override + public void testIgnored(Description description) { + writer.append('I'); + } + + /* + * Internal methods + */ + + private PrintStream getWriter() { + return writer; + } + + protected void printHeader(long runTime) { + getWriter().println(); + getWriter().println("Time: " + elapsedTimeAsString(runTime)); + } + + protected void printFailures(Result result) { + List<Failure> failures = result.getFailures(); + if (failures.size() == 0) { + return; + } + if (failures.size() == 1) { + getWriter().println("There was " + failures.size() + " failure:"); + } else { + getWriter().println("There were " + failures.size() + " failures:"); + } + int i = 1; + for (Failure each : failures) { + printFailure(each, "" + i++); + } + } + + protected void printFailure(Failure each, String prefix) { + getWriter().println(prefix + ") " + each.getTestHeader()); + getWriter().print(each.getTrace()); + } + + protected void printFooter(Result result) { + if (result.wasSuccessful()) { + getWriter().println(); + getWriter().print("OK"); + getWriter().println(" (" + result.getRunCount() + " test" + (result.getRunCount() == 1 ? "" : "s") + ")"); + + } else { + getWriter().println(); + getWriter().println("FAILURES!!!"); + getWriter().println("Tests run: " + result.getRunCount() + ", Failures: " + result.getFailureCount()); + } + getWriter().println(); + } + + /** + * Returns the formatted string of the elapsed time. Duplicated from + * BaseTestRunner. Fix it. + */ + protected String elapsedTimeAsString(long runTime) { + return NumberFormat.getInstance().format((double) runTime / 1000); + } } diff --git a/src/main/java/org/junit/internal/Throwables.java b/src/main/java/org/junit/internal/Throwables.java new file mode 100644 index 0000000..86dceef --- /dev/null +++ b/src/main/java/org/junit/internal/Throwables.java @@ -0,0 +1,42 @@ +package org.junit.internal; + +/** + * Miscellaneous functions dealing with {@code Throwable}. + * + * @author kcooney@google.com (Kevin Cooney) + * @since 4.12 + */ +public final class Throwables { + + private Throwables() { + } + + /** + * Rethrows the given {@code Throwable}, allowing the caller to + * declare that it throws {@code Exception}. This is useful when + * your callers have nothing reasonable they can do when a + * {@code Throwable} is thrown. This is declared to return {@code Exception} + * so it can be used in a {@code throw} clause: + * <pre> + * try { + * doSomething(); + * } catch (Throwable e} { + * throw Throwables.rethrowAsException(e); + * } + * doSomethingLater(); + * </pre> + * + * @param e exception to rethrow + * @return does not return anything + * @since 4.12 + */ + public static Exception rethrowAsException(Throwable e) throws Exception { + Throwables.<Exception>rethrow(e); + return null; // we never get here + } + + @SuppressWarnings("unchecked") + private static <T extends Throwable> void rethrow(Throwable e) throws T { + throw (T) e; + } +} diff --git a/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java b/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java index d3bd50a..d86ec95 100644 --- a/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java +++ b/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.builders; import java.util.Arrays; @@ -10,48 +7,50 @@ import org.junit.runner.Runner; import org.junit.runners.model.RunnerBuilder; public class AllDefaultPossibilitiesBuilder extends RunnerBuilder { - private final boolean fCanUseSuiteMethod; - - public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) { - fCanUseSuiteMethod= canUseSuiteMethod; - } - - @Override - public Runner runnerForClass(Class<?> testClass) throws Throwable { - List<RunnerBuilder> builders= Arrays.asList( - ignoredBuilder(), - annotatedBuilder(), - suiteMethodBuilder(), - junit3Builder(), - junit4Builder()); - - for (RunnerBuilder each : builders) { - Runner runner= each.safeRunnerForClass(testClass); - if (runner != null) - return runner; - } - return null; - } - - protected JUnit4Builder junit4Builder() { - return new JUnit4Builder(); - } - - protected JUnit3Builder junit3Builder() { - return new JUnit3Builder(); - } - - protected AnnotatedBuilder annotatedBuilder() { - return new AnnotatedBuilder(this); - } - - protected IgnoredBuilder ignoredBuilder() { - return new IgnoredBuilder(); - } - - protected RunnerBuilder suiteMethodBuilder() { - if (fCanUseSuiteMethod) - return new SuiteMethodBuilder(); - return new NullBuilder(); - } + private final boolean canUseSuiteMethod; + + public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) { + this.canUseSuiteMethod = canUseSuiteMethod; + } + + @Override + public Runner runnerForClass(Class<?> testClass) throws Throwable { + List<RunnerBuilder> builders = Arrays.asList( + ignoredBuilder(), + annotatedBuilder(), + suiteMethodBuilder(), + junit3Builder(), + junit4Builder()); + + for (RunnerBuilder each : builders) { + Runner runner = each.safeRunnerForClass(testClass); + if (runner != null) { + return runner; + } + } + return null; + } + + protected JUnit4Builder junit4Builder() { + return new JUnit4Builder(); + } + + protected JUnit3Builder junit3Builder() { + return new JUnit3Builder(); + } + + protected AnnotatedBuilder annotatedBuilder() { + return new AnnotatedBuilder(this); + } + + protected IgnoredBuilder ignoredBuilder() { + return new IgnoredBuilder(); + } + + protected RunnerBuilder suiteMethodBuilder() { + if (canUseSuiteMethod) { + return new SuiteMethodBuilder(); + } + return new NullBuilder(); + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java b/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java index 8ed9ca7..04d7a68 100644 --- a/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java +++ b/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.builders; import org.junit.runner.RunWith; @@ -8,38 +5,112 @@ import org.junit.runner.Runner; import org.junit.runners.model.InitializationError; import org.junit.runners.model.RunnerBuilder; +import java.lang.reflect.Modifier; + + +/** + * The {@code AnnotatedBuilder} is a strategy for constructing runners for test class that have been annotated with the + * {@code @RunWith} annotation. All tests within this class will be executed using the runner that was specified within + * the annotation. + * <p> + * If a runner supports inner member classes, the member classes will inherit the runner from the enclosing class, e.g.: + * <pre> + * @RunWith(MyRunner.class) + * public class MyTest { + * // some tests might go here + * + * public class MyMemberClass { + * @Test + * public void thisTestRunsWith_MyRunner() { + * // some test logic + * } + * + * // some more tests might go here + * } + * + * @RunWith(AnotherRunner.class) + * public class AnotherMemberClass { + * // some tests might go here + * + * public class DeepInnerClass { + * @Test + * public void thisTestRunsWith_AnotherRunner() { + * // some test logic + * } + * } + * + * public class DeepInheritedClass extends SuperTest { + * @Test + * public void thisTestRunsWith_SuperRunner() { + * // some test logic + * } + * } + * } + * } + * + * @RunWith(SuperRunner.class) + * public class SuperTest { + * // some tests might go here + * } + * </pre> + * The key points to note here are: + * <ul> + * <li>If there is no RunWith annotation, no runner will be created.</li> + * <li>The resolve step is inside-out, e.g. the closest RunWith annotation wins</li> + * <li>RunWith annotations are inherited and work as if the class was annotated itself.</li> + * <li>The default JUnit runner does not support inner member classes, + * so this is only valid for custom runners that support inner member classes.</li> + * <li>Custom runners with support for inner classes may or may not support RunWith annotations for member + * classes. Please refer to the custom runner documentation.</li> + * </ul> + * + * @see org.junit.runners.model.RunnerBuilder + * @see org.junit.runner.RunWith + * @since 4.0 + */ public class AnnotatedBuilder extends RunnerBuilder { - private static final String CONSTRUCTOR_ERROR_FORMAT= "Custom runner class %s should have a public constructor with signature %s(Class testClass)"; - - private RunnerBuilder fSuiteBuilder; - - public AnnotatedBuilder(RunnerBuilder suiteBuilder) { - fSuiteBuilder= suiteBuilder; - } - - @Override - public Runner runnerForClass(Class<?> testClass) throws Exception { - RunWith annotation= testClass.getAnnotation(RunWith.class); - if (annotation != null) - return buildRunner(annotation.value(), testClass); - return null; - } - - public Runner buildRunner(Class<? extends Runner> runnerClass, - Class<?> testClass) throws Exception { - try { - return runnerClass.getConstructor(Class.class).newInstance( - new Object[] { testClass }); - } catch (NoSuchMethodException e) { - try { - return runnerClass.getConstructor(Class.class, - RunnerBuilder.class).newInstance( - new Object[] { testClass, fSuiteBuilder }); - } catch (NoSuchMethodException e2) { - String simpleName= runnerClass.getSimpleName(); - throw new InitializationError(String.format( - CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName)); - } - } - } + private static final String CONSTRUCTOR_ERROR_FORMAT = "Custom runner class %s should have a public constructor with signature %s(Class testClass)"; + + private final RunnerBuilder suiteBuilder; + + public AnnotatedBuilder(RunnerBuilder suiteBuilder) { + this.suiteBuilder = suiteBuilder; + } + + @Override + public Runner runnerForClass(Class<?> testClass) throws Exception { + for (Class<?> currentTestClass = testClass; currentTestClass != null; + currentTestClass = getEnclosingClassForNonStaticMemberClass(currentTestClass)) { + RunWith annotation = currentTestClass.getAnnotation(RunWith.class); + if (annotation != null) { + return buildRunner(annotation.value(), testClass); + } + } + + return null; + } + + private Class<?> getEnclosingClassForNonStaticMemberClass(Class<?> currentTestClass) { + if (currentTestClass.isMemberClass() && !Modifier.isStatic(currentTestClass.getModifiers())) { + return currentTestClass.getEnclosingClass(); + } else { + return null; + } + } + + public Runner buildRunner(Class<? extends Runner> runnerClass, + Class<?> testClass) throws Exception { + try { + return runnerClass.getConstructor(Class.class).newInstance(testClass); + } catch (NoSuchMethodException e) { + try { + return runnerClass.getConstructor(Class.class, + RunnerBuilder.class).newInstance(testClass, suiteBuilder); + } catch (NoSuchMethodException e2) { + String simpleName = runnerClass.getSimpleName(); + throw new InitializationError(String.format( + CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName)); + } + } + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/builders/IgnoredBuilder.java b/src/main/java/org/junit/internal/builders/IgnoredBuilder.java index 6be342c..71940c8 100644 --- a/src/main/java/org/junit/internal/builders/IgnoredBuilder.java +++ b/src/main/java/org/junit/internal/builders/IgnoredBuilder.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.builders; import org.junit.Ignore; @@ -8,10 +5,11 @@ import org.junit.runner.Runner; import org.junit.runners.model.RunnerBuilder; public class IgnoredBuilder extends RunnerBuilder { - @Override - public Runner runnerForClass(Class<?> testClass) { - if (testClass.getAnnotation(Ignore.class) != null) - return new IgnoredClassRunner(testClass); - return null; - } + @Override + public Runner runnerForClass(Class<?> testClass) { + if (testClass.getAnnotation(Ignore.class) != null) { + return new IgnoredClassRunner(testClass); + } + return null; + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java b/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java index b4200a8..7c8926b 100644 --- a/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java +++ b/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.builders; import org.junit.runner.Description; @@ -8,19 +5,19 @@ import org.junit.runner.Runner; import org.junit.runner.notification.RunNotifier; public class IgnoredClassRunner extends Runner { - private final Class<?> fTestClass; + private final Class<?> clazz; - public IgnoredClassRunner(Class<?> testClass) { - fTestClass= testClass; - } + public IgnoredClassRunner(Class<?> testClass) { + clazz = testClass; + } - @Override - public void run(RunNotifier notifier) { - notifier.fireTestIgnored(getDescription()); - } + @Override + public void run(RunNotifier notifier) { + notifier.fireTestIgnored(getDescription()); + } - @Override - public Description getDescription() { - return Description.createSuiteDescription(fTestClass); - } + @Override + public Description getDescription() { + return Description.createSuiteDescription(clazz); + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/builders/JUnit3Builder.java b/src/main/java/org/junit/internal/builders/JUnit3Builder.java index ddb070b..8b6b371 100644 --- a/src/main/java/org/junit/internal/builders/JUnit3Builder.java +++ b/src/main/java/org/junit/internal/builders/JUnit3Builder.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.builders; import org.junit.internal.runners.JUnit38ClassRunner; @@ -8,14 +5,15 @@ import org.junit.runner.Runner; import org.junit.runners.model.RunnerBuilder; public class JUnit3Builder extends RunnerBuilder { - @Override - public Runner runnerForClass(Class<?> testClass) throws Throwable { - if (isPre4Test(testClass)) - return new JUnit38ClassRunner(testClass); - return null; - } + @Override + public Runner runnerForClass(Class<?> testClass) throws Throwable { + if (isPre4Test(testClass)) { + return new JUnit38ClassRunner(testClass); + } + return null; + } - boolean isPre4Test(Class<?> testClass) { - return junit.framework.TestCase.class.isAssignableFrom(testClass); - } + boolean isPre4Test(Class<?> testClass) { + return junit.framework.TestCase.class.isAssignableFrom(testClass); + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/builders/JUnit4Builder.java b/src/main/java/org/junit/internal/builders/JUnit4Builder.java index 4380db7..6a00678 100644 --- a/src/main/java/org/junit/internal/builders/JUnit4Builder.java +++ b/src/main/java/org/junit/internal/builders/JUnit4Builder.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.builders; import org.junit.runner.Runner; @@ -8,8 +5,8 @@ import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.RunnerBuilder; public class JUnit4Builder extends RunnerBuilder { - @Override - public Runner runnerForClass(Class<?> testClass) throws Throwable { - return new BlockJUnit4ClassRunner(testClass); - } + @Override + public Runner runnerForClass(Class<?> testClass) throws Throwable { + return new BlockJUnit4ClassRunner(testClass); + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/builders/NullBuilder.java b/src/main/java/org/junit/internal/builders/NullBuilder.java index 9d43d69..c8d306e 100644 --- a/src/main/java/org/junit/internal/builders/NullBuilder.java +++ b/src/main/java/org/junit/internal/builders/NullBuilder.java @@ -1,14 +1,11 @@ -/** - * - */ package org.junit.internal.builders; import org.junit.runner.Runner; import org.junit.runners.model.RunnerBuilder; public class NullBuilder extends RunnerBuilder { - @Override - public Runner runnerForClass(Class<?> each) throws Throwable { - return null; - } + @Override + public Runner runnerForClass(Class<?> each) throws Throwable { + return null; + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java b/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java index 659bf31..953e6cf 100644 --- a/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java +++ b/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.builders; import org.junit.internal.runners.SuiteMethod; @@ -8,19 +5,20 @@ import org.junit.runner.Runner; import org.junit.runners.model.RunnerBuilder; public class SuiteMethodBuilder extends RunnerBuilder { - @Override - public Runner runnerForClass(Class<?> each) throws Throwable { - if (hasSuiteMethod(each)) - return new SuiteMethod(each); - return null; - } + @Override + public Runner runnerForClass(Class<?> each) throws Throwable { + if (hasSuiteMethod(each)) { + return new SuiteMethod(each); + } + return null; + } - public boolean hasSuiteMethod(Class<?> testClass) { - try { - testClass.getMethod("suite"); - } catch (NoSuchMethodException e) { - return false; - } - return true; - } + public boolean hasSuiteMethod(Class<?> testClass) { + try { + testClass.getMethod("suite"); + } catch (NoSuchMethodException e) { + return false; + } + return true; + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/matchers/CombinableMatcher.java b/src/main/java/org/junit/internal/matchers/CombinableMatcher.java deleted file mode 100644 index e9e6947..0000000 --- a/src/main/java/org/junit/internal/matchers/CombinableMatcher.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.junit.internal.matchers; - -import static org.hamcrest.CoreMatchers.allOf; -import static org.hamcrest.CoreMatchers.anyOf; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.hamcrest.Matcher; - -public class CombinableMatcher<T> extends BaseMatcher<T> { - - private final Matcher<? extends T> fMatcher; - - public CombinableMatcher(Matcher<? extends T> matcher) { - fMatcher= matcher; - } - - public boolean matches(Object item) { - return fMatcher.matches(item); - } - - public void describeTo(Description description) { - description.appendDescriptionOf(fMatcher); - } - - @SuppressWarnings("unchecked") - public CombinableMatcher<T> and(Matcher<? extends T> matcher) { - return new CombinableMatcher<T>(allOf(matcher, fMatcher)); - } - - @SuppressWarnings("unchecked") - public CombinableMatcher<T> or(Matcher<? extends T> matcher) { - return new CombinableMatcher<T>(anyOf(matcher, fMatcher)); - } -}
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/matchers/Each.java b/src/main/java/org/junit/internal/matchers/Each.java deleted file mode 100644 index 527db3b..0000000 --- a/src/main/java/org/junit/internal/matchers/Each.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.junit.internal.matchers; - -import static org.hamcrest.CoreMatchers.not; -import static org.junit.internal.matchers.IsCollectionContaining.hasItem; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.hamcrest.Matcher; - -public class Each { - public static <T> Matcher<Iterable<T>> each(final Matcher<T> individual) { - final Matcher<Iterable<T>> allItemsAre = not(hasItem(not(individual))); - - return new BaseMatcher<Iterable<T>>() { - public boolean matches(Object item) { - return allItemsAre.matches(item); - } - - public void describeTo(Description description) { - description.appendText("each "); - individual.describeTo(description); - } - }; - } -} diff --git a/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java b/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java deleted file mode 100644 index 4436a83..0000000 --- a/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.junit.internal.matchers; - -import static org.hamcrest.core.AllOf.allOf; -import static org.hamcrest.core.IsEqual.equalTo; - -import java.util.ArrayList; -import java.util.Collection; - -import org.hamcrest.Description; -import org.hamcrest.Factory; -import org.hamcrest.Matcher; - -// Copied (hopefully temporarily) from hamcrest-library -public class IsCollectionContaining<T> extends TypeSafeMatcher<Iterable<T>> { - private final Matcher<? extends T> elementMatcher; - - public IsCollectionContaining(Matcher<? extends T> elementMatcher) { - this.elementMatcher = elementMatcher; - } - - @Override - public boolean matchesSafely(Iterable<T> collection) { - for (T item : collection) { - if (elementMatcher.matches(item)){ - return true; - } - } - return false; - } - - public void describeTo(Description description) { - description - .appendText("a collection containing ") - .appendDescriptionOf(elementMatcher); - } - - @Factory - public static <T> Matcher<Iterable<T>> hasItem(Matcher<? extends T> elementMatcher) { - return new IsCollectionContaining<T>(elementMatcher); - } - - @Factory - public static <T> Matcher<Iterable<T>> hasItem(T element) { - return hasItem(equalTo(element)); - } - - @Factory - public static <T> Matcher<Iterable<T>> hasItems(Matcher<? extends T>... elementMatchers) { - Collection<Matcher<? extends Iterable<T>>> all - = new ArrayList<Matcher<? extends Iterable<T>>>(elementMatchers.length); - for (Matcher<? extends T> elementMatcher : elementMatchers) { - all.add(hasItem(elementMatcher)); - } - return allOf(all); - } - - @Factory - public static <T> Matcher<Iterable<T>> hasItems(T... elements) { - Collection<Matcher<? extends Iterable<T>>> all - = new ArrayList<Matcher<? extends Iterable<T>>>(elements.length); - for (T element : elements) { - all.add(hasItem(element)); - } - return allOf(all); - } - -} diff --git a/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java b/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java new file mode 100644 index 0000000..5d45ba3 --- /dev/null +++ b/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java @@ -0,0 +1,56 @@ +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; + +/** + * A matcher that delegates to throwableMatcher and in addition appends the + * stacktrace of the actual Throwable in case of a mismatch. + */ +public class StacktracePrintingMatcher<T extends Throwable> extends + org.hamcrest.TypeSafeMatcher<T> { + + private final Matcher<T> throwableMatcher; + + public StacktracePrintingMatcher(Matcher<T> throwableMatcher) { + this.throwableMatcher = throwableMatcher; + } + + public void describeTo(Description description) { + throwableMatcher.describeTo(description); + } + + @Override + protected boolean matchesSafely(T item) { + return throwableMatcher.matches(item); + } + + @Override + protected void describeMismatchSafely(T item, Description description) { + throwableMatcher.describeMismatch(item, description); + description.appendText("\nStacktrace was: "); + description.appendText(readStacktrace(item)); + } + + private String readStacktrace(Throwable throwable) { + StringWriter stringWriter = new StringWriter(); + throwable.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } + + @Factory + public static <T extends Throwable> Matcher<T> isThrowable( + Matcher<T> throwableMatcher) { + return new StacktracePrintingMatcher<T>(throwableMatcher); + } + + @Factory + public static <T extends Exception> Matcher<T> isException( + Matcher<T> exceptionMatcher) { + return new StacktracePrintingMatcher<T>(exceptionMatcher); + } +} diff --git a/src/main/java/org/junit/internal/matchers/StringContains.java b/src/main/java/org/junit/internal/matchers/StringContains.java deleted file mode 100644 index e5f5334..0000000 --- a/src/main/java/org/junit/internal/matchers/StringContains.java +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ -package org.junit.internal.matchers; - -import org.hamcrest.Factory; -import org.hamcrest.Matcher; - -/** - * Tests if the argument is a string that contains a substring. - */ -public class StringContains extends SubstringMatcher { - public StringContains(String substring) { - super(substring); - } - - @Override - protected boolean evalSubstringOf(String s) { - return s.indexOf(substring) >= 0; - } - - @Override - protected String relationship() { - return "containing"; - } - - @Factory - public static Matcher<String> containsString(String substring) { - return new StringContains(substring); - } - -}
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/matchers/SubstringMatcher.java b/src/main/java/org/junit/internal/matchers/SubstringMatcher.java deleted file mode 100644 index 1c65240..0000000 --- a/src/main/java/org/junit/internal/matchers/SubstringMatcher.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.junit.internal.matchers; - -import org.hamcrest.Description; - -public abstract class SubstringMatcher extends TypeSafeMatcher<String> { - - protected final String substring; - - protected SubstringMatcher(final String substring) { - this.substring = substring; - } - - @Override - public boolean matchesSafely(String item) { - return evalSubstringOf(item); - } - - public void describeTo(Description description) { - description.appendText("a string ") - .appendText(relationship()) - .appendText(" ") - .appendValue(substring); - } - - protected abstract boolean evalSubstringOf(String string); - - protected abstract String relationship(); -}
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java b/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java new file mode 100644 index 0000000..22ce8bd --- /dev/null +++ b/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java @@ -0,0 +1,50 @@ +package org.junit.internal.matchers; + +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/** + * A matcher that applies a delegate matcher to the cause of the current Throwable, returning the result of that + * match. + * + * @param <T> the type of the throwable being matched + */ +public class ThrowableCauseMatcher<T extends Throwable> extends + TypeSafeMatcher<T> { + + private final Matcher<? extends Throwable> causeMatcher; + + public ThrowableCauseMatcher(Matcher<? extends Throwable> causeMatcher) { + this.causeMatcher = causeMatcher; + } + + public void describeTo(Description description) { + description.appendText("exception with cause "); + description.appendDescriptionOf(causeMatcher); + } + + @Override + protected boolean matchesSafely(T item) { + return causeMatcher.matches(item.getCause()); + } + + @Override + protected void describeMismatchSafely(T item, Description description) { + description.appendText("cause "); + causeMatcher.describeMismatch(item.getCause(), description); + } + + /** + * Returns a matcher that verifies that the outer exception has a cause for which the supplied matcher + * evaluates to true. + * + * @param matcher to apply to the cause of the outer exception + * @param <T> type of the outer exception + */ + @Factory + public static <T extends Throwable> Matcher<T> hasCause(final Matcher<? extends Throwable> matcher) { + return new ThrowableCauseMatcher<T>(matcher); + } +}
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/matchers/ThrowableMessageMatcher.java b/src/main/java/org/junit/internal/matchers/ThrowableMessageMatcher.java new file mode 100644 index 0000000..74386a8 --- /dev/null +++ b/src/main/java/org/junit/internal/matchers/ThrowableMessageMatcher.java @@ -0,0 +1,37 @@ +package org.junit.internal.matchers; + +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +public class ThrowableMessageMatcher<T extends Throwable> extends + TypeSafeMatcher<T> { + + private final Matcher<String> matcher; + + public ThrowableMessageMatcher(Matcher<String> matcher) { + this.matcher = matcher; + } + + public void describeTo(Description description) { + description.appendText("exception with message "); + description.appendDescriptionOf(matcher); + } + + @Override + protected boolean matchesSafely(T item) { + return matcher.matches(item.getMessage()); + } + + @Override + protected void describeMismatchSafely(T item, Description description) { + description.appendText("message "); + matcher.describeMismatch(item.getMessage(), description); + } + + @Factory + public static <T extends Throwable> Matcher<T> hasMessage(final Matcher<String> matcher) { + return new ThrowableMessageMatcher<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 794a174..4e2cc12 100644 --- a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java +++ b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java @@ -3,13 +3,16 @@ package org.junit.internal.matchers; import java.lang.reflect.Method; import org.hamcrest.BaseMatcher; +import org.junit.internal.MethodSorter; /** * Convenient base class for Matchers that require a non-null value of a specific type. * This simply implements the null check, checks the type and then casts. * * @author Joe Walnes + * @deprecated Please use {@link org.hamcrest.TypeSafeMatcher}. */ +@Deprecated public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> { private Class<?> expectedType; @@ -23,27 +26,27 @@ public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> { protected TypeSafeMatcher() { expectedType = findExpectedType(getClass()); } - + private static Class<?> findExpectedType(Class<?> fromClass) { for (Class<?> c = fromClass; c != Object.class; c = c.getSuperclass()) { - for (Method method : c.getDeclaredMethods()) { + for (Method method : MethodSorter.getDeclaredMethods(c)) { if (isMatchesSafelyMethod(method)) { return method.getParameterTypes()[0]; } } } - + throw new Error("Cannot determine correct type for matchesSafely() method."); } - + private static boolean isMatchesSafelyMethod(Method method) { - return method.getName().equals("matchesSafely") - && method.getParameterTypes().length == 1 - && !method.isSynthetic(); + return method.getName().equals("matchesSafely") + && method.getParameterTypes().length == 1 + && !method.isSynthetic(); } - + protected TypeSafeMatcher(Class<T> expectedType) { - this.expectedType = expectedType; + this.expectedType = expectedType; } /** diff --git a/src/main/java/org/junit/internal/requests/ClassRequest.java b/src/main/java/org/junit/internal/requests/ClassRequest.java index 53bf520..3d6b100 100644 --- a/src/main/java/org/junit/internal/requests/ClassRequest.java +++ b/src/main/java/org/junit/internal/requests/ClassRequest.java @@ -1,26 +1,39 @@ package org.junit.internal.requests; - import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; import org.junit.runner.Request; import org.junit.runner.Runner; public class ClassRequest extends Request { - private final Class<?> fTestClass; + private final Object runnerLock = new Object(); - private boolean fCanUseSuiteMethod; + /* + * 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 + */ + private final Class<?> fTestClass; + private final boolean canUseSuiteMethod; + private volatile Runner runner; - public ClassRequest(Class<?> testClass, boolean canUseSuiteMethod) { - fTestClass= testClass; - fCanUseSuiteMethod= canUseSuiteMethod; - } + public ClassRequest(Class<?> testClass, boolean canUseSuiteMethod) { + this.fTestClass = testClass; + this.canUseSuiteMethod = canUseSuiteMethod; + } - public ClassRequest(Class<?> testClass) { - this(testClass, true); - } + public ClassRequest(Class<?> testClass) { + this(testClass, true); + } - @Override - public Runner getRunner() { - return new AllDefaultPossibilitiesBuilder(fCanUseSuiteMethod).safeRunnerForClass(fTestClass); - } + @Override + public Runner getRunner() { + if (runner == null) { + synchronized (runnerLock) { + if (runner == null) { + runner = new AllDefaultPossibilitiesBuilder(canUseSuiteMethod).safeRunnerForClass(fTestClass); + } + } + } + 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 e5d98d1..066cba3 100644 --- a/src/main/java/org/junit/internal/requests/FilterRequest.java +++ b/src/main/java/org/junit/internal/requests/FilterRequest.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.requests; import org.junit.internal.runners.ErrorReportingRunner; @@ -13,30 +10,36 @@ import org.junit.runner.manipulation.NoTestsRemainException; * A filtered {@link Request}. */ public final class FilterRequest extends Request { - private final Request fRequest; - private final Filter fFilter; + private final Request 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 + */ + private final Filter fFilter; - /** - * Creates a filtered Request - * @param classRequest a {@link Request} describing your Tests - * @param filter {@link Filter} to apply to the Tests described in - * <code>classRequest</code> - */ - public FilterRequest(Request classRequest, Filter filter) { - fRequest= classRequest; - fFilter= filter; - } + /** + * Creates a filtered Request + * + * @param request a {@link Request} describing your Tests + * @param filter {@link Filter} to apply to the Tests described in + * <code>request</code> + */ + public FilterRequest(Request request, Filter filter) { + this.request = request; + this.fFilter = filter; + } - @Override - public Runner getRunner() { - try { - Runner runner= fRequest.getRunner(); - fFilter.apply(runner); - return runner; - } catch (NoTestsRemainException e) { - return new ErrorReportingRunner(Filter.class, new Exception(String - .format("No tests found matching %s from %s", fFilter - .describe(), fRequest.toString()))); - } - } + @Override + public Runner getRunner() { + try { + Runner runner = request.getRunner(); + fFilter.apply(runner); + return runner; + } catch (NoTestsRemainException e) { + return new ErrorReportingRunner(Filter.class, new Exception(String + .format("No tests found matching %s from %s", fFilter + .describe(), request.toString()))); + } + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/requests/SortingRequest.java b/src/main/java/org/junit/internal/requests/SortingRequest.java index 3c6f4f5..77061da 100644 --- a/src/main/java/org/junit/internal/requests/SortingRequest.java +++ b/src/main/java/org/junit/internal/requests/SortingRequest.java @@ -8,18 +8,18 @@ import org.junit.runner.Runner; import org.junit.runner.manipulation.Sorter; public class SortingRequest extends Request { - private final Request fRequest; - private final Comparator<Description> fComparator; + private final Request request; + private final Comparator<Description> comparator; - public SortingRequest(Request request, Comparator<Description> comparator) { - fRequest= request; - fComparator= comparator; - } + public SortingRequest(Request request, Comparator<Description> comparator) { + this.request = request; + this.comparator = comparator; + } - @Override - public Runner getRunner() { - Runner runner= fRequest.getRunner(); - new Sorter(fComparator).apply(runner); - return runner; - } + @Override + public Runner getRunner() { + Runner runner = request.getRunner(); + new Sorter(comparator).apply(runner); + return runner; + } } diff --git a/src/main/java/org/junit/internal/runners/ClassRoadie.java b/src/main/java/org/junit/internal/runners/ClassRoadie.java index 1f77d37..df1b453 100644 --- a/src/main/java/org/junit/internal/runners/ClassRoadie.java +++ b/src/main/java/org/junit/internal/runners/ClassRoadie.java @@ -4,6 +4,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; +import org.junit.internal.AssumptionViolatedException; import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; @@ -11,69 +12,70 @@ import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated -public -class ClassRoadie { - private RunNotifier fNotifier; - private TestClass fTestClass; - private Description fDescription; - private final Runnable fRunnable; - - public ClassRoadie(RunNotifier notifier, TestClass testClass, - Description description, Runnable runnable) { - fNotifier= notifier; - fTestClass= testClass; - fDescription= description; - fRunnable= runnable; - } +public class ClassRoadie { + private RunNotifier notifier; + private TestClass testClass; + private Description description; + private final Runnable runnable; - protected void runUnprotected() { - fRunnable.run(); - }; + public ClassRoadie(RunNotifier notifier, TestClass testClass, + Description description, Runnable runnable) { + this.notifier = notifier; + this.testClass = testClass; + this.description = description; + this.runnable = runnable; + } - protected void addFailure(Throwable targetException) { - fNotifier.fireTestFailure(new Failure(fDescription, targetException)); - } + protected void runUnprotected() { + runnable.run(); + } - public void runProtected() { - try { - runBefores(); - runUnprotected(); - } catch (FailedBefore e) { - } finally { - runAfters(); - } - } + protected void addFailure(Throwable targetException) { + notifier.fireTestFailure(new Failure(description, targetException)); + } - private void runBefores() throws FailedBefore { - try { - try { - List<Method> befores= fTestClass.getBefores(); - for (Method before : befores) - before.invoke(null); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } - } catch (org.junit.internal.AssumptionViolatedException e) { - throw new FailedBefore(); - } catch (Throwable e) { - addFailure(e); - throw new FailedBefore(); - } - } + public void runProtected() { + try { + runBefores(); + runUnprotected(); + } catch (FailedBefore e) { + } finally { + runAfters(); + } + } - private void runAfters() { - List<Method> afters= fTestClass.getAfters(); - for (Method after : afters) - try { - after.invoke(null); - } catch (InvocationTargetException e) { - addFailure(e.getTargetException()); - } catch (Throwable e) { - addFailure(e); // Untested, but seems impossible - } - } -}
\ No newline at end of file + private void runBefores() throws FailedBefore { + try { + try { + List<Method> befores = testClass.getBefores(); + for (Method before : befores) { + before.invoke(null); + } + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } catch (AssumptionViolatedException e) { + throw new FailedBefore(); + } catch (Throwable e) { + addFailure(e); + throw new FailedBefore(); + } + } + + private void runAfters() { + List<Method> afters = testClass.getAfters(); + for (Method after : afters) { + try { + after.invoke(null); + } catch (InvocationTargetException e) { + addFailure(e.getTargetException()); + } catch (Throwable e) { + addFailure(e); // Untested, but seems impossible + } + } + } +} diff --git a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java index 200b6f0..1d32beb 100644 --- a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java +++ b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java @@ -11,50 +11,58 @@ import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.InitializationError; public class ErrorReportingRunner extends Runner { - private final List<Throwable> fCauses; - - private final Class<?> fTestClass; - - public ErrorReportingRunner(Class<?> testClass, Throwable cause) { - fTestClass= testClass; - fCauses= getCauses(cause); - } - - @Override - public Description getDescription() { - Description description= Description.createSuiteDescription(fTestClass); - for (Throwable each : fCauses) - description.addChild(describeCause(each)); - return description; - } - - @Override - public void run(RunNotifier notifier) { - for (Throwable each : fCauses) - runCause(each, notifier); - } - - @SuppressWarnings("deprecation") - private List<Throwable> getCauses(Throwable cause) { - if (cause instanceof InvocationTargetException) - return getCauses(cause.getCause()); - if (cause instanceof InitializationError) - return ((InitializationError) cause).getCauses(); - if (cause instanceof org.junit.internal.runners.InitializationError) - return ((org.junit.internal.runners.InitializationError) cause) - .getCauses(); - return Arrays.asList(cause); - } - - private Description describeCause(Throwable child) { - return Description.createTestDescription(fTestClass, - "initializationError"); - } - - private void runCause(Throwable child, RunNotifier notifier) { - Description description= describeCause(child); - notifier.fireTestStarted(description); - notifier.fireTestFailure(new Failure(description, child)); - notifier.fireTestFinished(description); - } -}
\ No newline at end of file + private final List<Throwable> causes; + + private final Class<?> testClass; + + public ErrorReportingRunner(Class<?> testClass, Throwable cause) { + if (testClass == null) { + throw new NullPointerException("Test class cannot be null"); + } + this.testClass = testClass; + causes = getCauses(cause); + } + + @Override + public Description getDescription() { + Description description = Description.createSuiteDescription(testClass); + for (Throwable each : causes) { + description.addChild(describeCause(each)); + } + return description; + } + + @Override + public void run(RunNotifier notifier) { + for (Throwable each : causes) { + runCause(each, notifier); + } + } + + @SuppressWarnings("deprecation") + private List<Throwable> getCauses(Throwable cause) { + if (cause instanceof InvocationTargetException) { + return getCauses(cause.getCause()); + } + if (cause instanceof InitializationError) { + return ((InitializationError) cause).getCauses(); + } + if (cause instanceof org.junit.internal.runners.InitializationError) { + return ((org.junit.internal.runners.InitializationError) cause) + .getCauses(); + } + return Arrays.asList(cause); + } + + private Description describeCause(Throwable child) { + return Description.createTestDescription(testClass, + "initializationError"); + } + + private void runCause(Throwable child, RunNotifier notifier) { + Description description = describeCause(child); + notifier.fireTestStarted(description); + notifier.fireTestFailure(new Failure(description, child)); + notifier.fireTestFinished(description); + } +} diff --git a/src/main/java/org/junit/internal/runners/FailedBefore.java b/src/main/java/org/junit/internal/runners/FailedBefore.java index 29dcba4..1036cb6 100644 --- a/src/main/java/org/junit/internal/runners/FailedBefore.java +++ b/src/main/java/org/junit/internal/runners/FailedBefore.java @@ -2,13 +2,12 @@ package org.junit.internal.runners; import org.junit.runners.BlockJUnit4ClassRunner; - /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated class FailedBefore extends Exception { - private static final long serialVersionUID= 1L; + private static final long serialVersionUID = 1L; }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/InitializationError.java b/src/main/java/org/junit/internal/runners/InitializationError.java index 5715ec5..52065ec 100644 --- a/src/main/java/org/junit/internal/runners/InitializationError.java +++ b/src/main/java/org/junit/internal/runners/InitializationError.java @@ -3,28 +3,35 @@ package org.junit.internal.runners; import java.util.Arrays; import java.util.List; -@Deprecated /** - * Use the published version: {@link org.junit.runners.InitializationError} + * Use the published version: + * {@link org.junit.runners.model.InitializationError} * This may disappear as soon as 1 April 2009 */ +@Deprecated public class InitializationError extends Exception { - private static final long serialVersionUID= 1L; - private final List<Throwable> fErrors; + private static final long serialVersionUID = 1L; + + /* + * 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 + */ + private final List<Throwable> fErrors; + + public InitializationError(List<Throwable> errors) { + this.fErrors = errors; + } - public InitializationError(List<Throwable> errors) { - fErrors= errors; - } + public InitializationError(Throwable... errors) { + this(Arrays.asList(errors)); + } - public InitializationError(Throwable... errors) { - this(Arrays.asList(errors)); - } - - public InitializationError(String string) { - this(new Exception(string)); - } + public InitializationError(String string) { + this(new Exception(string)); + } - public List<Throwable> getCauses() { - return fErrors; - } + public List<Throwable> getCauses() { + return fErrors; + } } diff --git a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java index 0028d0c..631fcf2 100644 --- a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java +++ b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java @@ -17,142 +17,164 @@ 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 { - private final class OldTestClassAdaptingListener implements - TestListener { - private final RunNotifier fNotifier; - - private OldTestClassAdaptingListener(RunNotifier notifier) { - fNotifier= notifier; - } - - public void endTest(Test test) { - fNotifier.fireTestFinished(asDescription(test)); - } - - public void startTest(Test test) { - fNotifier.fireTestStarted(asDescription(test)); - } - - // Implement junit.framework.TestListener - public void addError(Test test, Throwable t) { - Failure failure= new Failure(asDescription(test), t); - fNotifier.fireTestFailure(failure); - } - - private Description asDescription(Test test) { - if (test instanceof Describable) { - Describable facade= (Describable) test; - return facade.getDescription(); - } - return Description.createTestDescription(getEffectiveClass(test), getName(test)); - } - - private Class<? extends Test> getEffectiveClass(Test test) { - return test.getClass(); - } - - private String getName(Test test) { - if (test instanceof TestCase) - return ((TestCase) test).getName(); - else - return test.toString(); - } - - public void addFailure(Test test, AssertionFailedError t) { - addError(test, t); - } - } - - private Test fTest; - - public JUnit38ClassRunner(Class<?> klass) { - this(new TestSuite(klass.asSubclass(TestCase.class))); - } - - public JUnit38ClassRunner(Test test) { - super(); - setTest(test); - } - - @Override - public void run(RunNotifier notifier) { - TestResult result= new TestResult(); - result.addListener(createAdaptingListener(notifier)); - getTest().run(result); - } - - public TestListener createAdaptingListener(final RunNotifier notifier) { - return new OldTestClassAdaptingListener(notifier); - } - - @Override - public Description getDescription() { - return makeDescription(getTest()); - } - - private static Description makeDescription(Test test) { - if (test instanceof TestCase) { - TestCase tc= (TestCase) test; - return Description.createTestDescription(tc.getClass(), tc.getName()); - } else if (test instanceof TestSuite) { - TestSuite ts= (TestSuite) test; - String name= ts.getName() == null ? createSuiteDescription(ts) : ts.getName(); - Description description= Description.createSuiteDescription(name); - int n= ts.testCount(); - for (int i= 0; i < n; i++) { - Description made= makeDescription(ts.testAt(i)); - description.addChild(made); - } - return description; - } else if (test instanceof Describable) { - Describable adapter= (Describable) test; - return adapter.getDescription(); - } else if (test instanceof TestDecorator) { - TestDecorator decorator= (TestDecorator) test; - return makeDescription(decorator.getTest()); - } else { - // This is the best we can do in this case - return Description.createSuiteDescription(test.getClass()); - } - } - - private static String createSuiteDescription(TestSuite ts) { - int count= ts.countTestCases(); - String example = count == 0 ? "" : String.format(" [example: %s]", ts.testAt(0)); - return String.format("TestSuite with %s tests%s", count, example); - } - - public void filter(Filter filter) throws NoTestsRemainException { - if (getTest() instanceof Filterable) { - Filterable adapter= (Filterable) getTest(); - adapter.filter(filter); - } else if (getTest() instanceof TestSuite) { - TestSuite suite= (TestSuite) getTest(); - TestSuite filtered= new TestSuite(suite.getName()); - int n= suite.testCount(); - for (int i= 0; i < n; i++) { - Test test= suite.testAt(i); - if (filter.shouldRun(makeDescription(test))) - filtered.addTest(test); - } - setTest(filtered); - } - } - - public void sort(Sorter sorter) { - if (getTest() instanceof Sortable) { - Sortable adapter= (Sortable) getTest(); - adapter.sort(sorter); - } - } - - private void setTest(Test test) { - fTest = test; - } - - private Test getTest() { - return fTest; - } + private static final class OldTestClassAdaptingListener implements + TestListener { + private final RunNotifier notifier; + + private OldTestClassAdaptingListener(RunNotifier notifier) { + this.notifier = notifier; + } + + public void endTest(Test test) { + notifier.fireTestFinished(asDescription(test)); + } + + public void startTest(Test test) { + notifier.fireTestStarted(asDescription(test)); + } + + // Implement junit.framework.TestListener + public void addError(Test test, Throwable e) { + Failure failure = new Failure(asDescription(test), e); + notifier.fireTestFailure(failure); + } + + private Description asDescription(Test test) { + if (test instanceof Describable) { + Describable facade = (Describable) test; + return facade.getDescription(); + } + return Description.createTestDescription(getEffectiveClass(test), getName(test)); + } + + private Class<? extends Test> getEffectiveClass(Test test) { + return test.getClass(); + } + + private String getName(Test test) { + if (test instanceof TestCase) { + return ((TestCase) test).getName(); + } else { + return test.toString(); + } + } + + public void addFailure(Test test, AssertionFailedError t) { + addError(test, t); + } + } + + private volatile Test test; + + public JUnit38ClassRunner(Class<?> klass) { + this(new TestSuite(klass.asSubclass(TestCase.class))); + } + + public JUnit38ClassRunner(Test test) { + super(); + setTest(test); + } + + @Override + public void run(RunNotifier notifier) { + TestResult result = new TestResult(); + result.addListener(createAdaptingListener(notifier)); + getTest().run(result); + } + + public TestListener createAdaptingListener(final RunNotifier notifier) { + return new OldTestClassAdaptingListener(notifier); + } + + @Override + public Description getDescription() { + return makeDescription(getTest()); + } + + private static Description makeDescription(Test test) { + if (test instanceof TestCase) { + TestCase tc = (TestCase) test; + return Description.createTestDescription(tc.getClass(), tc.getName(), + getAnnotations(tc)); + } else if (test instanceof TestSuite) { + TestSuite ts = (TestSuite) test; + String name = ts.getName() == null ? createSuiteDescription(ts) : ts.getName(); + Description description = Description.createSuiteDescription(name); + int n = ts.testCount(); + for (int i = 0; i < n; i++) { + Description made = makeDescription(ts.testAt(i)); + description.addChild(made); + } + return description; + } else if (test instanceof Describable) { + Describable adapter = (Describable) test; + return adapter.getDescription(); + } else if (test instanceof TestDecorator) { + TestDecorator decorator = (TestDecorator) test; + return makeDescription(decorator.getTest()); + } else { + // This is the best we can do in this case + return Description.createSuiteDescription(test.getClass()); + } + } + + /** + * Get the annotations associated with given TestCase. + * @param test the TestCase. + */ + private static Annotation[] getAnnotations(TestCase test) { + try { + Method m = test.getClass().getMethod(test.getName()); + return m.getDeclaredAnnotations(); + } catch (SecurityException e) { + } catch (NoSuchMethodException e) { + } + return new Annotation[0]; + } + + private static String createSuiteDescription(TestSuite ts) { + int count = ts.countTestCases(); + String example = count == 0 ? "" : String.format(" [example: %s]", ts.testAt(0)); + return String.format("TestSuite with %s tests%s", count, example); + } + + public void filter(Filter filter) throws NoTestsRemainException { + if (getTest() instanceof Filterable) { + Filterable adapter = (Filterable) getTest(); + adapter.filter(filter); + } else if (getTest() instanceof TestSuite) { + TestSuite suite = (TestSuite) getTest(); + TestSuite filtered = new TestSuite(suite.getName()); + int n = suite.testCount(); + for (int i = 0; i < n; i++) { + Test test = suite.testAt(i); + if (filter.shouldRun(makeDescription(test))) { + filtered.addTest(test); + } + } + setTest(filtered); + if (filtered.testCount() == 0) { + throw new NoTestsRemainException(); + } + } + } + + public void sort(Sorter sorter) { + if (getTest() instanceof Sortable) { + Sortable adapter = (Sortable) getTest(); + adapter.sort(sorter); + } + } + + private void setTest(Test test) { + this.test = test; + } + + private Test getTest() { + return test; + } } diff --git a/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java b/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java index d732880..69a23c4 100644 --- a/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java +++ b/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java @@ -21,125 +21,127 @@ import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. - * - * This may disappear as soon as 1 April 2009 */ @Deprecated public class JUnit4ClassRunner extends Runner implements Filterable, Sortable { - private final List<Method> fTestMethods; - private TestClass fTestClass; - - public JUnit4ClassRunner(Class<?> klass) throws InitializationError { - fTestClass= new TestClass(klass); - fTestMethods= getTestMethods(); - validate(); - } - - protected List<Method> getTestMethods() { - return fTestClass.getTestMethods(); - } - - protected void validate() throws InitializationError { - MethodValidator methodValidator= new MethodValidator(fTestClass); - methodValidator.validateMethodsForDefaultRunner(); - methodValidator.assertValid(); - } - - @Override - public void run(final RunNotifier notifier) { - new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() { - public void run() { - runMethods(notifier); - } - }).runProtected(); - } - - protected void runMethods(final RunNotifier notifier) { - for (Method method : fTestMethods) - invokeTestMethod(method, notifier); - } - - @Override - public Description getDescription() { - Description spec= Description.createSuiteDescription(getName(), classAnnotations()); - List<Method> testMethods= fTestMethods; - for (Method method : testMethods) - spec.addChild(methodDescription(method)); - return spec; - } - - protected Annotation[] classAnnotations() { - return fTestClass.getJavaClass().getAnnotations(); - } - - protected String getName() { - return getTestClass().getName(); - } - - protected Object createTest() throws Exception { - return getTestClass().getConstructor().newInstance(); - } - - protected void invokeTestMethod(Method method, RunNotifier notifier) { - Description description= methodDescription(method); - Object test; - try { - test= createTest(); - } catch (InvocationTargetException e) { - testAborted(notifier, description, e.getCause()); - return; - } catch (Exception e) { - testAborted(notifier, description, e); - return; - } - TestMethod testMethod= wrapMethod(method); - new MethodRoadie(test, testMethod, notifier, description).run(); - } - - private void testAborted(RunNotifier notifier, Description description, - Throwable e) { - notifier.fireTestStarted(description); - notifier.fireTestFailure(new Failure(description, e)); - notifier.fireTestFinished(description); - } - - protected TestMethod wrapMethod(Method method) { - return new TestMethod(method, fTestClass); - } - - protected String testName(Method method) { - return method.getName(); - } - - protected Description methodDescription(Method method) { - return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method)); - } - - protected Annotation[] testAnnotations(Method method) { - return method.getAnnotations(); - } - - public void filter(Filter filter) throws NoTestsRemainException { - for (Iterator<Method> iter= fTestMethods.iterator(); iter.hasNext();) { - Method method= iter.next(); - if (!filter.shouldRun(methodDescription(method))) - iter.remove(); - } - if (fTestMethods.isEmpty()) - throw new NoTestsRemainException(); - } - - public void sort(final Sorter sorter) { - Collections.sort(fTestMethods, new Comparator<Method>() { - public int compare(Method o1, Method o2) { - return sorter.compare(methodDescription(o1), methodDescription(o2)); - } - }); - } - - protected TestClass getTestClass() { - return fTestClass; - } + private final List<Method> testMethods; + private TestClass testClass; + + public JUnit4ClassRunner(Class<?> klass) throws InitializationError { + testClass = new TestClass(klass); + testMethods = getTestMethods(); + validate(); + } + + protected List<Method> getTestMethods() { + return testClass.getTestMethods(); + } + + protected void validate() throws InitializationError { + MethodValidator methodValidator = new MethodValidator(testClass); + methodValidator.validateMethodsForDefaultRunner(); + methodValidator.assertValid(); + } + + @Override + public void run(final RunNotifier notifier) { + new ClassRoadie(notifier, testClass, getDescription(), new Runnable() { + public void run() { + runMethods(notifier); + } + }).runProtected(); + } + + protected void runMethods(final RunNotifier notifier) { + for (Method method : testMethods) { + invokeTestMethod(method, notifier); + } + } + + @Override + public Description getDescription() { + Description spec = Description.createSuiteDescription(getName(), classAnnotations()); + List<Method> testMethods = this.testMethods; + for (Method method : testMethods) { + spec.addChild(methodDescription(method)); + } + return spec; + } + + protected Annotation[] classAnnotations() { + return testClass.getJavaClass().getAnnotations(); + } + + protected String getName() { + return getTestClass().getName(); + } + + protected Object createTest() throws Exception { + return getTestClass().getConstructor().newInstance(); + } + + protected void invokeTestMethod(Method method, RunNotifier notifier) { + Description description = methodDescription(method); + Object test; + try { + test = createTest(); + } catch (InvocationTargetException e) { + testAborted(notifier, description, e.getCause()); + return; + } catch (Exception e) { + testAborted(notifier, description, e); + return; + } + TestMethod testMethod = wrapMethod(method); + new MethodRoadie(test, testMethod, notifier, description).run(); + } + + private void testAborted(RunNotifier notifier, Description description, + Throwable e) { + notifier.fireTestStarted(description); + notifier.fireTestFailure(new Failure(description, e)); + notifier.fireTestFinished(description); + } + + protected TestMethod wrapMethod(Method method) { + return new TestMethod(method, testClass); + } + + protected String testName(Method method) { + return method.getName(); + } + + protected Description methodDescription(Method method) { + return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method)); + } + + protected Annotation[] testAnnotations(Method method) { + return method.getAnnotations(); + } + + public void filter(Filter filter) throws NoTestsRemainException { + for (Iterator<Method> iter = testMethods.iterator(); iter.hasNext(); ) { + Method method = iter.next(); + if (!filter.shouldRun(methodDescription(method))) { + iter.remove(); + } + } + if (testMethods.isEmpty()) { + throw new NoTestsRemainException(); + } + } + + public void sort(final Sorter sorter) { + Collections.sort(testMethods, new Comparator<Method>() { + public int compare(Method o1, Method o2) { + return sorter.compare(methodDescription(o1), methodDescription(o2)); + } + }); + } + + protected TestClass getTestClass() { + return testClass; + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/MethodRoadie.java b/src/main/java/org/junit/internal/runners/MethodRoadie.java index 4407821..01a476b 100644 --- a/src/main/java/org/junit/internal/runners/MethodRoadie.java +++ b/src/main/java/org/junit/internal/runners/MethodRoadie.java @@ -15,143 +15,149 @@ import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.TestTimedOutException; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class MethodRoadie { - private final Object fTest; - private final RunNotifier fNotifier; - private final Description fDescription; - private TestMethod fTestMethod; + private final Object test; + private final RunNotifier notifier; + private final Description description; + private TestMethod testMethod; - public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) { - fTest= test; - fNotifier= notifier; - fDescription= description; - fTestMethod= method; - } + public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) { + this.test = test; + this.notifier = notifier; + this.description = description; + testMethod = method; + } - public void run() { - if (fTestMethod.isIgnored()) { - fNotifier.fireTestIgnored(fDescription); - return; - } - fNotifier.fireTestStarted(fDescription); - try { - long timeout= fTestMethod.getTimeout(); - if (timeout > 0) - runWithTimeout(timeout); - else - runTest(); - } finally { - fNotifier.fireTestFinished(fDescription); - } - } + public void run() { + if (testMethod.isIgnored()) { + notifier.fireTestIgnored(description); + return; + } + notifier.fireTestStarted(description); + try { + long timeout = testMethod.getTimeout(); + if (timeout > 0) { + runWithTimeout(timeout); + } else { + runTest(); + } + } finally { + notifier.fireTestFinished(description); + } + } - private void runWithTimeout(final long timeout) { - runBeforesThenTestThenAfters(new Runnable() { - - public void run() { - ExecutorService service= Executors.newSingleThreadExecutor(); - Callable<Object> callable= new Callable<Object>() { - public Object call() throws Exception { - runTestMethod(); - return null; - } - }; - Future<Object> result= service.submit(callable); - service.shutdown(); - try { - boolean terminated= service.awaitTermination(timeout, - TimeUnit.MILLISECONDS); - if (!terminated) - service.shutdownNow(); - result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation - } catch (TimeoutException e) { - addFailure(new Exception(String.format("test timed out after %d milliseconds", timeout))); - } catch (Exception e) { - addFailure(e); - } - } - }); - } - - public void runTest() { - runBeforesThenTestThenAfters(new Runnable() { - public void run() { - runTestMethod(); - } - }); - } + private void runWithTimeout(final long timeout) { + runBeforesThenTestThenAfters(new Runnable() { - public void runBeforesThenTestThenAfters(Runnable test) { - try { - runBefores(); - test.run(); - } catch (FailedBefore e) { - } catch (Exception e) { - throw new RuntimeException("test should never throw an exception to this level"); - } finally { - runAfters(); - } - } - - protected void runTestMethod() { - try { - fTestMethod.invoke(fTest); - if (fTestMethod.expectsException()) - addFailure(new AssertionError("Expected exception: " + fTestMethod.getExpectedException().getName())); - } catch (InvocationTargetException e) { - Throwable actual= e.getTargetException(); - if (actual instanceof AssumptionViolatedException) - return; - else if (!fTestMethod.expectsException()) - addFailure(actual); - else if (fTestMethod.isUnexpected(actual)) { - String message= "Unexpected exception, expected<" + fTestMethod.getExpectedException().getName() + "> but was<" - + actual.getClass().getName() + ">"; - addFailure(new Exception(message, actual)); - } - } catch (Throwable e) { - addFailure(e); - } - } - - private void runBefores() throws FailedBefore { - try { - try { - List<Method> befores= fTestMethod.getBefores(); - for (Method before : befores) - before.invoke(fTest); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } - } catch (AssumptionViolatedException e) { - throw new FailedBefore(); - } catch (Throwable e) { - addFailure(e); - throw new FailedBefore(); - } - } + public void run() { + ExecutorService service = Executors.newSingleThreadExecutor(); + Callable<Object> callable = new Callable<Object>() { + public Object call() throws Exception { + runTestMethod(); + return null; + } + }; + Future<Object> result = service.submit(callable); + service.shutdown(); + try { + boolean terminated = service.awaitTermination(timeout, + TimeUnit.MILLISECONDS); + if (!terminated) { + service.shutdownNow(); + } + result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation + } catch (TimeoutException e) { + addFailure(new TestTimedOutException(timeout, TimeUnit.MILLISECONDS)); + } catch (Exception e) { + addFailure(e); + } + } + }); + } - private void runAfters() { - List<Method> afters= fTestMethod.getAfters(); - for (Method after : afters) - try { - after.invoke(fTest); - } catch (InvocationTargetException e) { - addFailure(e.getTargetException()); - } catch (Throwable e) { - addFailure(e); // Untested, but seems impossible - } - } + public void runTest() { + runBeforesThenTestThenAfters(new Runnable() { + public void run() { + runTestMethod(); + } + }); + } - protected void addFailure(Throwable e) { - fNotifier.fireTestFailure(new Failure(fDescription, e)); - } + public void runBeforesThenTestThenAfters(Runnable test) { + try { + runBefores(); + test.run(); + } catch (FailedBefore e) { + } catch (Exception e) { + throw new RuntimeException("test should never throw an exception to this level"); + } finally { + runAfters(); + } + } + + protected void runTestMethod() { + try { + testMethod.invoke(test); + if (testMethod.expectsException()) { + addFailure(new AssertionError("Expected exception: " + testMethod.getExpectedException().getName())); + } + } catch (InvocationTargetException e) { + Throwable actual = e.getTargetException(); + if (actual instanceof AssumptionViolatedException) { + return; + } else if (!testMethod.expectsException()) { + addFailure(actual); + } else if (testMethod.isUnexpected(actual)) { + String message = "Unexpected exception, expected<" + testMethod.getExpectedException().getName() + "> but was<" + + actual.getClass().getName() + ">"; + addFailure(new Exception(message, actual)); + } + } catch (Throwable e) { + addFailure(e); + } + } + + private void runBefores() throws FailedBefore { + try { + try { + List<Method> befores = testMethod.getBefores(); + for (Method before : befores) { + before.invoke(test); + } + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } catch (AssumptionViolatedException e) { + throw new FailedBefore(); + } catch (Throwable e) { + addFailure(e); + throw new FailedBefore(); + } + } + + private void runAfters() { + List<Method> afters = testMethod.getAfters(); + for (Method after : afters) { + try { + after.invoke(test); + } catch (InvocationTargetException e) { + addFailure(e.getTargetException()); + } catch (Throwable e) { + addFailure(e); // Untested, but seems impossible + } + } + } + + protected void addFailure(Throwable e) { + notifier.fireTestFailure(new Failure(description, e)); + } } diff --git a/src/main/java/org/junit/internal/runners/MethodValidator.java b/src/main/java/org/junit/internal/runners/MethodValidator.java index cadc93f..ba9c9d1 100644 --- a/src/main/java/org/junit/internal/runners/MethodValidator.java +++ b/src/main/java/org/junit/internal/runners/MethodValidator.java @@ -15,77 +15,83 @@ import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class MethodValidator { - private final List<Throwable> fErrors= new ArrayList<Throwable>(); + private final List<Throwable> errors = new ArrayList<Throwable>(); - private TestClass fTestClass; + private TestClass testClass; - public MethodValidator(TestClass testClass) { - fTestClass = testClass; - } + public MethodValidator(TestClass testClass) { + this.testClass = testClass; + } - public void validateInstanceMethods() { - validateTestMethods(After.class, false); - validateTestMethods(Before.class, false); - validateTestMethods(Test.class, false); - - List<Method> methods= fTestClass.getAnnotatedMethods(Test.class); - if (methods.size() == 0) - fErrors.add(new Exception("No runnable methods")); - } + public void validateInstanceMethods() { + validateTestMethods(After.class, false); + validateTestMethods(Before.class, false); + validateTestMethods(Test.class, false); - public void validateStaticMethods() { - validateTestMethods(BeforeClass.class, true); - validateTestMethods(AfterClass.class, true); - } - - public List<Throwable> validateMethodsForDefaultRunner() { - validateNoArgConstructor(); - validateStaticMethods(); - validateInstanceMethods(); - return fErrors; - } - - public void assertValid() throws InitializationError { - if (!fErrors.isEmpty()) - throw new InitializationError(fErrors); - } + List<Method> methods = testClass.getAnnotatedMethods(Test.class); + if (methods.size() == 0) { + errors.add(new Exception("No runnable methods")); + } + } - public void validateNoArgConstructor() { - try { - fTestClass.getConstructor(); - } catch (Exception e) { - fErrors.add(new Exception("Test class should have public zero-argument constructor", e)); - } - } + public void validateStaticMethods() { + validateTestMethods(BeforeClass.class, true); + validateTestMethods(AfterClass.class, true); + } - private void validateTestMethods(Class<? extends Annotation> annotation, - boolean isStatic) { - List<Method> methods= fTestClass.getAnnotatedMethods(annotation); - - for (Method each : methods) { - if (Modifier.isStatic(each.getModifiers()) != isStatic) { - String state= isStatic ? "should" : "should not"; - fErrors.add(new Exception("Method " + each.getName() + "() " + public List<Throwable> validateMethodsForDefaultRunner() { + validateNoArgConstructor(); + validateStaticMethods(); + validateInstanceMethods(); + return errors; + } + + public void assertValid() throws InitializationError { + if (!errors.isEmpty()) { + throw new InitializationError(errors); + } + } + + public void validateNoArgConstructor() { + try { + testClass.getConstructor(); + } catch (Exception e) { + errors.add(new Exception("Test class should have public zero-argument constructor", e)); + } + } + + private void validateTestMethods(Class<? extends Annotation> annotation, + boolean isStatic) { + List<Method> methods = testClass.getAnnotatedMethods(annotation); + + for (Method each : methods) { + if (Modifier.isStatic(each.getModifiers()) != isStatic) { + String state = isStatic ? "should" : "should not"; + errors.add(new Exception("Method " + each.getName() + "() " + state + " be static")); - } - if (!Modifier.isPublic(each.getDeclaringClass().getModifiers())) - fErrors.add(new Exception("Class " + each.getDeclaringClass().getName() + } + if (!Modifier.isPublic(each.getDeclaringClass().getModifiers())) { + errors.add(new Exception("Class " + each.getDeclaringClass().getName() + " should be public")); - if (!Modifier.isPublic(each.getModifiers())) - fErrors.add(new Exception("Method " + each.getName() + } + if (!Modifier.isPublic(each.getModifiers())) { + errors.add(new Exception("Method " + each.getName() + " should be public")); - if (each.getReturnType() != Void.TYPE) - fErrors.add(new Exception("Method " + each.getName() + } + if (each.getReturnType() != Void.TYPE) { + errors.add(new Exception("Method " + each.getName() + " should be void")); - if (each.getParameterTypes().length != 0) - fErrors.add(new Exception("Method " + each.getName() + } + if (each.getParameterTypes().length != 0) { + errors.add(new Exception("Method " + each.getName() + " should have no parameters")); - } - } + } + } + } } diff --git a/src/main/java/org/junit/internal/runners/SuiteMethod.java b/src/main/java/org/junit/internal/runners/SuiteMethod.java index 4e8bebc..e336983 100644 --- a/src/main/java/org/junit/internal/runners/SuiteMethod.java +++ b/src/main/java/org/junit/internal/runners/SuiteMethod.java @@ -6,7 +6,8 @@ import java.lang.reflect.Modifier; import junit.framework.Test; -/** Runner for use with JUnit 3.8.x-style AllTests classes +/** + * Runner for use with JUnit 3.8.x-style AllTests classes * (those that only implement a static <code>suite()</code> * method). For example: * <pre> @@ -19,22 +20,22 @@ import junit.framework.Test; * </pre> */ public class SuiteMethod extends JUnit38ClassRunner { - public SuiteMethod(Class<?> klass) throws Throwable { - super(testFromSuiteMethod(klass)); - } + public SuiteMethod(Class<?> klass) throws Throwable { + super(testFromSuiteMethod(klass)); + } - public static Test testFromSuiteMethod(Class<?> klass) throws Throwable { - Method suiteMethod= null; - Test suite= null; - try { - suiteMethod= klass.getMethod("suite"); - if (! Modifier.isStatic(suiteMethod.getModifiers())) { - throw new Exception(klass.getName() + ".suite() must be static"); - } - suite= (Test) suiteMethod.invoke(null); // static method - } catch (InvocationTargetException e) { - throw e.getCause(); - } - return suite; - } + public static Test testFromSuiteMethod(Class<?> klass) throws Throwable { + Method suiteMethod = null; + Test suite = null; + try { + suiteMethod = klass.getMethod("suite"); + if (!Modifier.isStatic(suiteMethod.getModifiers())) { + throw new Exception(klass.getName() + ".suite() must be static"); + } + suite = (Test) suiteMethod.invoke(null); // static method + } catch (InvocationTargetException e) { + throw e.getCause(); + } + return suite; + } } diff --git a/src/main/java/org/junit/internal/runners/TestClass.java b/src/main/java/org/junit/internal/runners/TestClass.java index 1ca2b9d..1abaeea 100644 --- a/src/main/java/org/junit/internal/runners/TestClass.java +++ b/src/main/java/org/junit/internal/runners/TestClass.java @@ -11,92 +11,99 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.internal.MethodSorter; import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class TestClass { - private final Class<?> fClass; - - public TestClass(Class<?> klass) { - fClass= klass; - } - - public List<Method> getTestMethods() { - return getAnnotatedMethods(Test.class); - } - - List<Method> getBefores() { - return getAnnotatedMethods(BeforeClass.class); - } - - List<Method> getAfters() { - return getAnnotatedMethods(AfterClass.class); - } - - public List<Method> getAnnotatedMethods(Class<? extends Annotation> annotationClass) { - List<Method> results= new ArrayList<Method>(); - for (Class<?> eachClass : getSuperClasses(fClass)) { - Method[] methods= eachClass.getDeclaredMethods(); - for (Method eachMethod : methods) { - Annotation annotation= eachMethod.getAnnotation(annotationClass); - if (annotation != null && ! isShadowed(eachMethod, results)) - results.add(eachMethod); - } - } - if (runsTopToBottom(annotationClass)) - Collections.reverse(results); - return results; - } - - private boolean runsTopToBottom(Class< ? extends Annotation> annotation) { - return annotation.equals(Before.class) || annotation.equals(BeforeClass.class); - } - - private boolean isShadowed(Method method, List<Method> results) { - for (Method each : results) { - if (isShadowed(method, each)) - return true; - } - return false; - } - - private boolean isShadowed(Method current, Method previous) { - if (! previous.getName().equals(current.getName())) - return false; - if (previous.getParameterTypes().length != current.getParameterTypes().length) - return false; - for (int i= 0; i < previous.getParameterTypes().length; i++) { - if (! previous.getParameterTypes()[i].equals(current.getParameterTypes()[i])) - return false; - } - return true; - } - - private List<Class<?>> getSuperClasses(Class< ?> testClass) { - ArrayList<Class<?>> results= new ArrayList<Class<?>>(); - Class<?> current= testClass; - while (current != null) { - results.add(current); - current= current.getSuperclass(); - } - return results; - } - - public Constructor<?> getConstructor() throws SecurityException, NoSuchMethodException { - return fClass.getConstructor(); - } - - public Class<?> getJavaClass() { - return fClass; - } - - public String getName() { - return fClass.getName(); - } + private final Class<?> klass; + + public TestClass(Class<?> klass) { + this.klass = klass; + } + + public List<Method> getTestMethods() { + return getAnnotatedMethods(Test.class); + } + + List<Method> getBefores() { + return getAnnotatedMethods(BeforeClass.class); + } + + List<Method> getAfters() { + return getAnnotatedMethods(AfterClass.class); + } + + public List<Method> getAnnotatedMethods(Class<? extends Annotation> annotationClass) { + List<Method> results = new ArrayList<Method>(); + for (Class<?> eachClass : getSuperClasses(klass)) { + Method[] methods = MethodSorter.getDeclaredMethods(eachClass); + for (Method eachMethod : methods) { + Annotation annotation = eachMethod.getAnnotation(annotationClass); + if (annotation != null && !isShadowed(eachMethod, results)) { + results.add(eachMethod); + } + } + } + if (runsTopToBottom(annotationClass)) { + Collections.reverse(results); + } + return results; + } + + private boolean runsTopToBottom(Class<? extends Annotation> annotation) { + return annotation.equals(Before.class) || annotation.equals(BeforeClass.class); + } + + private boolean isShadowed(Method method, List<Method> results) { + for (Method each : results) { + if (isShadowed(method, each)) { + return true; + } + } + return false; + } + + private boolean isShadowed(Method current, Method previous) { + if (!previous.getName().equals(current.getName())) { + return false; + } + if (previous.getParameterTypes().length != current.getParameterTypes().length) { + return false; + } + for (int i = 0; i < previous.getParameterTypes().length; i++) { + if (!previous.getParameterTypes()[i].equals(current.getParameterTypes()[i])) { + return false; + } + } + return true; + } + + private List<Class<?>> getSuperClasses(Class<?> testClass) { + ArrayList<Class<?>> results = new ArrayList<Class<?>>(); + Class<?> current = testClass; + while (current != null) { + results.add(current); + current = current.getSuperclass(); + } + return results; + } + + public Constructor<?> getConstructor() throws SecurityException, NoSuchMethodException { + return klass.getConstructor(); + } + + public Class<?> getJavaClass() { + return klass; + } + + public String getName() { + return klass.getName(); + } } diff --git a/src/main/java/org/junit/internal/runners/TestMethod.java b/src/main/java/org/junit/internal/runners/TestMethod.java index a06213c..821e193 100644 --- a/src/main/java/org/junit/internal/runners/TestMethod.java +++ b/src/main/java/org/junit/internal/runners/TestMethod.java @@ -13,57 +13,59 @@ import org.junit.runners.BlockJUnit4ClassRunner; /** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be - * removed in the next release. Please use + * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class TestMethod { - private final Method fMethod; - private TestClass fTestClass; + private final Method method; + private TestClass testClass; - public TestMethod(Method method, TestClass testClass) { - fMethod= method; - fTestClass= testClass; - } + public TestMethod(Method method, TestClass testClass) { + this.method = method; + this.testClass = testClass; + } - public boolean isIgnored() { - return fMethod.getAnnotation(Ignore.class) != null; - } + public boolean isIgnored() { + return method.getAnnotation(Ignore.class) != null; + } - public long getTimeout() { - Test annotation= fMethod.getAnnotation(Test.class); - if (annotation == null) - return 0; - long timeout= annotation.timeout(); - return timeout; - } + public long getTimeout() { + Test annotation = method.getAnnotation(Test.class); + if (annotation == null) { + return 0; + } + long timeout = annotation.timeout(); + return timeout; + } - protected Class<? extends Throwable> getExpectedException() { - Test annotation= fMethod.getAnnotation(Test.class); - if (annotation == null || annotation.expected() == None.class) - return null; - else - return annotation.expected(); - } + protected Class<? extends Throwable> getExpectedException() { + Test annotation = method.getAnnotation(Test.class); + if (annotation == null || annotation.expected() == None.class) { + return null; + } else { + return annotation.expected(); + } + } - boolean isUnexpected(Throwable exception) { - return ! getExpectedException().isAssignableFrom(exception.getClass()); - } + boolean isUnexpected(Throwable exception) { + return !getExpectedException().isAssignableFrom(exception.getClass()); + } - boolean expectsException() { - return getExpectedException() != null; - } + boolean expectsException() { + return getExpectedException() != null; + } - List<Method> getBefores() { - return fTestClass.getAnnotatedMethods(Before.class); - } + List<Method> getBefores() { + return testClass.getAnnotatedMethods(Before.class); + } - List<Method> getAfters() { - return fTestClass.getAnnotatedMethods(After.class); - } + List<Method> getAfters() { + return testClass.getAnnotatedMethods(After.class); + } - public void invoke(Object test) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { - fMethod.invoke(test); - } + public void invoke(Object test) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + method.invoke(test); + } } 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 a7d534c..e094809 100644 --- a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java +++ b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.runners.model; import org.junit.internal.AssumptionViolatedException; @@ -10,42 +7,42 @@ import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.MultipleFailureException; public class EachTestNotifier { - private final RunNotifier fNotifier; - - private final Description fDescription; - - public EachTestNotifier(RunNotifier notifier, Description description) { - fNotifier= notifier; - fDescription= description; - } - - public void addFailure(Throwable targetException) { - if (targetException instanceof MultipleFailureException) { - addMultipleFailureException((MultipleFailureException) targetException); - } else { - fNotifier - .fireTestFailure(new Failure(fDescription, targetException)); - } - } - - private void addMultipleFailureException(MultipleFailureException mfe) { - for (Throwable each : mfe.getFailures()) - addFailure(each); - } - - public void addFailedAssumption(AssumptionViolatedException e) { - fNotifier.fireTestAssumptionFailed(new Failure(fDescription, e)); - } - - public void fireTestFinished() { - fNotifier.fireTestFinished(fDescription); - } - - public void fireTestStarted() { - fNotifier.fireTestStarted(fDescription); - } - - public void fireTestIgnored() { - fNotifier.fireTestIgnored(fDescription); - } + private final RunNotifier notifier; + + private final Description description; + + public EachTestNotifier(RunNotifier notifier, Description description) { + this.notifier = notifier; + this.description = description; + } + + public void addFailure(Throwable targetException) { + if (targetException instanceof MultipleFailureException) { + addMultipleFailureException((MultipleFailureException) targetException); + } else { + notifier.fireTestFailure(new Failure(description, targetException)); + } + } + + private void addMultipleFailureException(MultipleFailureException mfe) { + for (Throwable each : mfe.getFailures()) { + addFailure(each); + } + } + + public void addFailedAssumption(AssumptionViolatedException e) { + notifier.fireTestAssumptionFailed(new Failure(description, e)); + } + + public void fireTestFinished() { + notifier.fireTestFinished(description); + } + + public void fireTestStarted() { + notifier.fireTestStarted(description); + } + + public void fireTestIgnored() { + notifier.fireTestIgnored(description); + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java b/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java index 3316806..054f042 100644 --- a/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java +++ b/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java @@ -4,9 +4,9 @@ import java.util.List; @Deprecated public class MultipleFailureException extends org.junit.runners.model.MultipleFailureException { - private static final long serialVersionUID= 1L; + private static final long serialVersionUID = 1L; - public MultipleFailureException(List<Throwable> errors) { - super(errors); - } + public MultipleFailureException(List<Throwable> errors) { + super(errors); + } } diff --git a/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java b/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java index 9150d90..79d5c05 100644 --- a/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java +++ b/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.runners.model; import java.lang.reflect.InvocationTargetException; @@ -10,13 +7,13 @@ import java.lang.reflect.InvocationTargetException; * wrapping it in an InvocationTargetException. */ public abstract class ReflectiveCallable { - public Object run() throws Throwable { - try { - return runReflectiveCall(); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } - } + public Object run() throws Throwable { + try { + return runReflectiveCall(); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } - protected abstract Object runReflectiveCall() throws Throwable; + protected abstract Object runReflectiveCall() throws Throwable; }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java b/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java deleted file mode 100644 index e7df8bf..0000000 --- a/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.junit.internal.runners.rules; - -import java.lang.annotation.Annotation; -import java.util.List; - -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.rules.TestRule; -import org.junit.runners.model.FrameworkField; -import org.junit.runners.model.TestClass; - -/** - * A RuleFieldValidator validates the rule fields of a - * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the - * {@code TestClass} are written to a list of errors. - * - * There are two slightly different validators. The {@link #CLASS_RULE_VALIDATOR} - * validates fields with a {@link ClassRule} annotation and the - * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation. - */ -public enum RuleFieldValidator { - /** - * Validates fields with a {@link ClassRule} annotation. - */ - CLASS_RULE_VALIDATOR(ClassRule.class, true), - /** - * Validates fields with a {@link Rule} annotation. - */ - RULE_VALIDATOR(Rule.class, false); - - private final Class<? extends Annotation> fAnnotation; - - private final boolean fOnlyStaticFields; - - private RuleFieldValidator(Class<? extends Annotation> annotation, - boolean onlyStaticFields) { - this.fAnnotation= annotation; - this.fOnlyStaticFields= onlyStaticFields; - } - - /** - * Validate the {@link org.junit.runners.model.TestClass} and adds reasons - * for rejecting the class to a list of errors. - * @param target the {@code TestClass} to validate. - * @param errors the list of errors. - */ - public void validate(TestClass target, List<Throwable> errors) { - List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation); - for (FrameworkField each : fields) - validateField(each, errors); - } - - private void validateField(FrameworkField field, List<Throwable> errors) { - optionallyValidateStatic(field, errors); - validatePublic(field, errors); - validateTestRuleOrMethodRule(field, errors); - } - - private void optionallyValidateStatic(FrameworkField field, - List<Throwable> errors) { - if (fOnlyStaticFields && !field.isStatic()) - addError(errors, field, "must be static."); - } - - private void validatePublic(FrameworkField field, List<Throwable> errors) { - if (!field.isPublic()) - addError(errors, field, "must be public."); - } - - private void validateTestRuleOrMethodRule(FrameworkField field, - List<Throwable> errors) { - if (!isMethodRule(field) && !isTestRule(field)) - addError(errors, field, "must implement MethodRule or TestRule."); - } - - private boolean isTestRule(FrameworkField target) { - return TestRule.class.isAssignableFrom(target.getType()); - } - - @SuppressWarnings("deprecation") - private boolean isMethodRule(FrameworkField target) { - return org.junit.rules.MethodRule.class.isAssignableFrom(target - .getType()); - } - - private void addError(List<Throwable> errors, FrameworkField field, - String suffix) { - String message= "The @" + fAnnotation.getSimpleName() + " '" - + field.getName() + "' " + suffix; - errors.add(new Exception(message)); - } -} diff --git a/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java b/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java index 23f921b..36de4f1 100644 --- a/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java +++ b/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java @@ -1,91 +1,279 @@ package org.junit.internal.runners.rules; -import java.lang.annotation.Annotation; -import java.util.List; import org.junit.ClassRule; import org.junit.Rule; +import org.junit.rules.MethodRule; import org.junit.rules.TestRule; -import org.junit.runners.model.FrameworkField; +import org.junit.runners.model.FrameworkMember; import org.junit.runners.model.TestClass; +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + /** - * A RuleFieldValidator validates the rule fields of a - * {@link TestClass}. All reasons for rejecting the + * A RuleMemberValidator validates the rule fields/methods of a + * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the * {@code TestClass} are written to a list of errors. * - * There are two slightly different validators. The {@link #CLASS_RULE_VALIDATOR} + * <p>There are four slightly different validators. The {@link #CLASS_RULE_VALIDATOR} * validates fields with a {@link ClassRule} annotation and the - * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation. + * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation.</p> + * + * <p>The {@link #CLASS_RULE_METHOD_VALIDATOR} + * validates methods with a {@link ClassRule} annotation and the + * {@link #RULE_METHOD_VALIDATOR} validates methods with a {@link Rule} annotation.</p> */ -public enum RuleMemberValidator { - /** - * Validates fields with a {@link ClassRule} annotation. - */ - CLASS_RULE_VALIDATOR(ClassRule.class, true), - /** - * Validates fields with a {@link Rule} annotation. - */ - RULE_VALIDATOR(Rule.class, false); - - private final Class<? extends Annotation> fAnnotation; - - private final boolean fOnlyStaticFields; - - private RuleMemberValidator(Class<? extends Annotation> annotation, - boolean onlyStaticFields) { - this.fAnnotation= annotation; - this.fOnlyStaticFields= onlyStaticFields; - } - - /** - * Validate the {@link TestClass} and adds reasons - * for rejecting the class to a list of errors. - * @param target the {@code TestClass} to validate. - * @param errors the list of errors. - */ - public void validate(TestClass target, List<Throwable> errors) { - List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation); - for (FrameworkField each : fields) - validateField(each, errors); - } - - private void validateField(FrameworkField field, List<Throwable> errors) { - optionallyValidateStatic(field, errors); - validatePublic(field, errors); - validateTestRuleOrMethodRule(field, errors); - } - - private void optionallyValidateStatic(FrameworkField field, - List<Throwable> errors) { - if (fOnlyStaticFields && !field.isStatic()) - addError(errors, field, "must be static."); - } - - private void validatePublic(FrameworkField field, List<Throwable> errors) { - if (!field.isPublic()) - addError(errors, field, "must be public."); - } - - private void validateTestRuleOrMethodRule(FrameworkField field, - List<Throwable> errors) { - if (!isMethodRule(field) && !isTestRule(field)) - addError(errors, field, "must implement MethodRule or TestRule."); - } - - private boolean isTestRule(FrameworkField target) { - return TestRule.class.isAssignableFrom(target.getType()); - } - - @SuppressWarnings("deprecation") - private boolean isMethodRule(FrameworkField target) { - return org.junit.rules.MethodRule.class.isAssignableFrom(target - .getType()); - } - - private void addError(List<Throwable> errors, FrameworkField field, - String suffix) { - String message= "The @" + fAnnotation.getSimpleName() + " '" - + field.getName() + "' " + suffix; - errors.add(new Exception(message)); - } +public class RuleMemberValidator { + /** + * Validates fields with a {@link ClassRule} annotation. + */ + public static final RuleMemberValidator CLASS_RULE_VALIDATOR = + classRuleValidatorBuilder() + .withValidator(new DeclaringClassMustBePublic()) + .withValidator(new MemberMustBeStatic()) + .withValidator(new MemberMustBePublic()) + .withValidator(new FieldMustBeATestRule()) + .build(); + /** + * Validates fields with a {@link Rule} annotation. + */ + public static final RuleMemberValidator RULE_VALIDATOR = + testRuleValidatorBuilder() + .withValidator(new MemberMustBeNonStaticOrAlsoClassRule()) + .withValidator(new MemberMustBePublic()) + .withValidator(new FieldMustBeARule()) + .build(); + /** + * Validates methods with a {@link ClassRule} annotation. + */ + public static final RuleMemberValidator CLASS_RULE_METHOD_VALIDATOR = + classRuleValidatorBuilder() + .forMethods() + .withValidator(new DeclaringClassMustBePublic()) + .withValidator(new MemberMustBeStatic()) + .withValidator(new MemberMustBePublic()) + .withValidator(new MethodMustBeATestRule()) + .build(); + + /** + * Validates methods with a {@link Rule} annotation. + */ + public static final RuleMemberValidator RULE_METHOD_VALIDATOR = + testRuleValidatorBuilder() + .forMethods() + .withValidator(new MemberMustBeNonStaticOrAlsoClassRule()) + .withValidator(new MemberMustBePublic()) + .withValidator(new MethodMustBeARule()) + .build(); + + private final Class<? extends Annotation> annotation; + private final boolean methods; + private final List<RuleValidator> validatorStrategies; + + RuleMemberValidator(Builder builder) { + this.annotation = builder.annotation; + this.methods = builder.methods; + this.validatorStrategies = builder.validators; + } + + /** + * Validate the {@link org.junit.runners.model.TestClass} and adds reasons + * for rejecting the class to a list of errors. + * + * @param target the {@code TestClass} to validate. + * @param errors the list of errors. + */ + public void validate(TestClass target, List<Throwable> errors) { + List<? extends FrameworkMember<?>> members = methods ? target.getAnnotatedMethods(annotation) + : target.getAnnotatedFields(annotation); + + for (FrameworkMember<?> each : members) { + validateMember(each, errors); + } + } + + private void validateMember(FrameworkMember<?> member, List<Throwable> errors) { + for (RuleValidator strategy : validatorStrategies) { + strategy.validate(member, annotation, errors); + } + } + + private static Builder classRuleValidatorBuilder() { + return new Builder(ClassRule.class); + } + + private static Builder testRuleValidatorBuilder() { + return new Builder(Rule.class); + } + + private static class Builder { + private final Class<? extends Annotation> annotation; + private boolean methods; + private final List<RuleValidator> validators; + + private Builder(Class<? extends Annotation> annotation) { + this.annotation = annotation; + this.methods = false; + this.validators = new ArrayList<RuleValidator>(); + } + + Builder forMethods() { + methods = true; + return this; + } + + Builder withValidator(RuleValidator validator) { + validators.add(validator); + return this; + } + + RuleMemberValidator build() { + return new RuleMemberValidator(this); + } + } + + private static boolean isRuleType(FrameworkMember<?> member) { + return isMethodRule(member) || isTestRule(member); + } + + private static boolean isTestRule(FrameworkMember<?> member) { + return TestRule.class.isAssignableFrom(member.getType()); + } + + private static boolean isMethodRule(FrameworkMember<?> member) { + return MethodRule.class.isAssignableFrom(member.getType()); + } + + /** + * Encapsulates a single piece of validation logic, used to determine if {@link org.junit.Rule} and + * {@link org.junit.ClassRule} annotations have been used correctly + */ + interface RuleValidator { + /** + * Examine the given member and add any violations of the strategy's validation logic to the given list of errors + * @param member The member (field or member) to examine + * @param annotation The type of rule annotation on the member + * @param errors The list of errors to add validation violations to + */ + void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors); + } + + /** + * Requires the validated member to be non-static + */ + private static final class MemberMustBeNonStaticOrAlsoClassRule implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + boolean isMethodRuleMember = isMethodRule(member); + boolean isClassRuleAnnotated = (member.getAnnotation(ClassRule.class) != null); + + // We disallow: + // - static MethodRule members + // - static @Rule annotated members + // - UNLESS they're also @ClassRule annotated + // Note that MethodRule cannot be annotated with @ClassRule + if (member.isStatic() && (isMethodRuleMember || !isClassRuleAnnotated)) { + String message; + if (isMethodRule(member)) { + message = "must not be static."; + } else { + message = "must not be static or it must be annotated with @ClassRule."; + } + errors.add(new ValidationError(member, annotation, message)); + } + } + } + + /** + * Requires the member to be static + */ + private static final class MemberMustBeStatic implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!member.isStatic()) { + errors.add(new ValidationError(member, annotation, + "must be static.")); + } + } + } + + /** + * Requires the member's declaring class to be public + */ + private static final class DeclaringClassMustBePublic implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isDeclaringClassPublic(member)) { + errors.add(new ValidationError(member, annotation, + "must be declared in a public class.")); + } + } + + private boolean isDeclaringClassPublic(FrameworkMember<?> member) { + return Modifier.isPublic(member.getDeclaringClass().getModifiers()); + } + } + + /** + * Requires the member to be public + */ + private static final class MemberMustBePublic implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!member.isPublic()) { + errors.add(new ValidationError(member, annotation, + "must be public.")); + } + } + } + + /** + * Requires the member is a field implementing {@link org.junit.rules.MethodRule} or {@link org.junit.rules.TestRule} + */ + private static final class FieldMustBeARule implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isRuleType(member)) { + errors.add(new ValidationError(member, annotation, + "must implement MethodRule or TestRule.")); + } + } + } + + /** + * Require the member to return an implementation of {@link org.junit.rules.MethodRule} or + * {@link org.junit.rules.TestRule} + */ + private static final class MethodMustBeARule implements RuleValidator { + public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isRuleType(member)) { + errors.add(new ValidationError(member, annotation, + "must return an implementation of MethodRule or TestRule.")); + } + } + } + + /** + * Require the member to return an implementation of {@link org.junit.rules.TestRule} + */ + private static final class MethodMustBeATestRule implements RuleValidator { + public void validate(FrameworkMember<?> member, + Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isTestRule(member)) { + errors.add(new ValidationError(member, annotation, + "must return an implementation of TestRule.")); + } + } + } + + /** + * Requires the member is a field implementing {@link org.junit.rules.TestRule} + */ + private static final class FieldMustBeATestRule implements RuleValidator { + + public void validate(FrameworkMember<?> member, + Class<? extends Annotation> annotation, List<Throwable> errors) { + if (!isTestRule(member)) { + errors.add(new ValidationError(member, annotation, + "must implement TestRule.")); + } + } + } } diff --git a/src/main/java/org/junit/internal/runners/rules/ValidationError.java b/src/main/java/org/junit/internal/runners/rules/ValidationError.java new file mode 100644 index 0000000..d1af8ae --- /dev/null +++ b/src/main/java/org/junit/internal/runners/rules/ValidationError.java @@ -0,0 +1,11 @@ +package org.junit.internal.runners.rules; + +import org.junit.runners.model.FrameworkMember; + +import java.lang.annotation.Annotation; + +class ValidationError extends Exception { + 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 ddfef07..d0636bd 100644 --- a/src/main/java/org/junit/internal/runners/statements/ExpectException.java +++ b/src/main/java/org/junit/internal/runners/statements/ExpectException.java @@ -1,38 +1,36 @@ -/** - * - */ package org.junit.internal.runners.statements; import org.junit.internal.AssumptionViolatedException; import org.junit.runners.model.Statement; public class ExpectException extends Statement { - private Statement fNext; - private final Class<? extends Throwable> fExpected; - - public ExpectException(Statement next, Class<? extends Throwable> expected) { - fNext= next; - fExpected= expected; - } - - @Override - public void evaluate() throws Exception { - boolean complete = false; - try { - fNext.evaluate(); - complete = true; - } catch (AssumptionViolatedException e) { - throw e; - } catch (Throwable e) { - if (!fExpected.isAssignableFrom(e.getClass())) { - String message= "Unexpected exception, expected<" - + fExpected.getName() + "> but was<" - + e.getClass().getName() + ">"; - throw new Exception(message, e); - } - } - if (complete) - throw new AssertionError("Expected exception: " - + fExpected.getName()); - } + private final Statement next; + private final Class<? extends Throwable> expected; + + public ExpectException(Statement next, Class<? extends Throwable> expected) { + this.next = next; + this.expected = expected; + } + + @Override + public void evaluate() throws Exception { + boolean complete = false; + try { + next.evaluate(); + complete = true; + } catch (AssumptionViolatedException e) { + throw e; + } catch (Throwable e) { + if (!expected.isAssignableFrom(e.getClass())) { + String message = "Unexpected exception, expected<" + + expected.getName() + "> but was<" + + e.getClass().getName() + ">"; + throw new Exception(message, e); + } + } + if (complete) { + throw new AssertionError("Expected exception: " + + expected.getName()); + } + } }
\ No newline at end of file diff --git a/src/main/java/org/junit/internal/runners/statements/Fail.java b/src/main/java/org/junit/internal/runners/statements/Fail.java index e7d0d5c..e55875c 100644 --- a/src/main/java/org/junit/internal/runners/statements/Fail.java +++ b/src/main/java/org/junit/internal/runners/statements/Fail.java @@ -2,16 +2,15 @@ package org.junit.internal.runners.statements; import org.junit.runners.model.Statement; - public class Fail extends Statement { - private final Throwable fError; + private final Throwable error; - public Fail(Throwable e) { - fError= e; - } + public Fail(Throwable e) { + error = e; + } - @Override - public void evaluate() throws Throwable { - throw fError; - } + @Override + public void evaluate() throws Throwable { + throw error; + } } diff --git a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java index bff7c72..7f4f0d5 100644 --- a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java +++ b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java @@ -1,71 +1,311 @@ -/** - * - */ package org.junit.internal.runners.statements; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.runners.model.MultipleFailureException; import org.junit.runners.model.Statement; +import org.junit.runners.model.TestTimedOutException; public class FailOnTimeout extends Statement { - private final Statement fOriginalStatement; - - private final long fTimeout; - - public FailOnTimeout(Statement originalStatement, long timeout) { - fOriginalStatement= originalStatement; - fTimeout= timeout; - } - - @Override - public void evaluate() throws Throwable { - StatementThread thread= evaluateStatement(); - if (!thread.fFinished) - throwExceptionForUnfinishedThread(thread); - } - - private StatementThread evaluateStatement() throws InterruptedException { - StatementThread thread= new StatementThread(fOriginalStatement); - thread.start(); - thread.join(fTimeout); - thread.interrupt(); - return thread; - } - - private void throwExceptionForUnfinishedThread(StatementThread thread) - throws Throwable { - if (thread.fExceptionThrownByOriginalStatement != null) - throw thread.fExceptionThrownByOriginalStatement; - else - throwTimeoutException(thread); - } - - private void throwTimeoutException(StatementThread thread) throws Exception { - Exception exception= new Exception(String.format( - "test timed out after %d milliseconds", fTimeout)); - exception.setStackTrace(thread.getStackTrace()); - throw exception; - } - - private static class StatementThread extends Thread { - private final Statement fStatement; - - private boolean fFinished= false; - - private Throwable fExceptionThrownByOriginalStatement= null; - - public StatementThread(Statement statement) { - fStatement= statement; - } - - @Override - public void run() { - try { - fStatement.evaluate(); - fFinished= true; - } catch (InterruptedException e) { - //don't log the InterruptedException - } catch (Throwable e) { - fExceptionThrownByOriginalStatement= e; - } - } - } -}
\ No newline at end of file + private final Statement originalStatement; + private final TimeUnit timeUnit; + private final long timeout; + private final boolean lookForStuckThread; + private volatile ThreadGroup threadGroup = null; + + /** + * Returns a new builder for building an instance. + * + * @since 4.12 + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Creates an instance wrapping the given statement with the given timeout in milliseconds. + * + * @param statement the statement to wrap + * @param timeoutMillis the timeout in milliseconds + * @deprecated use {@link #builder()} instead. + */ + @Deprecated + public FailOnTimeout(Statement statement, long timeoutMillis) { + this(builder().withTimeout(timeoutMillis, TimeUnit.MILLISECONDS), statement); + } + + private FailOnTimeout(Builder builder, Statement statement) { + originalStatement = statement; + timeout = builder.timeout; + timeUnit = builder.unit; + lookForStuckThread = builder.lookForStuckThread; + } + + /** + * Builder for {@link FailOnTimeout}. + * + * @since 4.12 + */ + public static class Builder { + private boolean lookForStuckThread = false; + private long timeout = 0; + private TimeUnit unit = TimeUnit.SECONDS; + + private Builder() { + } + + /** + * Specifies the time to wait before timing out the test. + * + * <p>If this is not called, or is called with a {@code timeout} of + * {@code 0}, the returned {@code Statement} will wait forever for the + * test to complete, however the test will still launch from a separate + * thread. This can be useful for disabling timeouts in environments + * where they are dynamically set based on some property. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the {@code timeout} argument + * @return {@code this} for method chaining. + */ + public Builder withTimeout(long timeout, TimeUnit unit) { + if (timeout < 0) { + throw new IllegalArgumentException("timeout must be non-negative"); + } + if (unit == null) { + throw new NullPointerException("TimeUnit cannot be null"); + } + this.timeout = timeout; + this.unit = unit; + return this; + } + + /** + * Specifies whether to look for a stuck thread. If a timeout occurs and this + * feature is enabled, the test will look for a thread that appears to be stuck + * and dump its backtrace. This feature is experimental. Behavior may change + * after the 4.12 release in response to feedback. + * + * @param enable {@code true} to enable the feature + * @return {@code this} for method chaining. + */ + public Builder withLookingForStuckThread(boolean enable) { + this.lookForStuckThread = enable; + return this; + } + + /** + * Builds a {@link FailOnTimeout} instance using the values in this builder, + * wrapping the given statement. + * + * @param statement + */ + public FailOnTimeout build(Statement statement) { + if (statement == null) { + throw new NullPointerException("statement cannot be null"); + } + return new FailOnTimeout(this, statement); + } + } + + @Override + public void evaluate() throws Throwable { + CallableStatement callable = new CallableStatement(); + FutureTask<Throwable> task = new FutureTask<Throwable>(callable); + threadGroup = new ThreadGroup("FailOnTimeoutGroup"); + Thread thread = new Thread(threadGroup, task, "Time-limited test"); + thread.setDaemon(true); + thread.start(); + callable.awaitStarted(); + Throwable throwable = getResult(task, thread); + if (throwable != null) { + throw throwable; + } + } + + /** + * Wait for the test task, returning the exception thrown by the test if the + * test failed, an exception indicating a timeout if the test timed out, or + * {@code null} if the test passed. + */ + private Throwable getResult(FutureTask<Throwable> task, Thread thread) { + try { + if (timeout > 0) { + return task.get(timeout, timeUnit); + } else { + return task.get(); + } + } catch (InterruptedException e) { + return e; // caller will re-throw; no need to call Thread.interrupt() + } catch (ExecutionException e) { + // test failed; have caller re-throw the exception thrown by the test + return e.getCause(); + } catch (TimeoutException e) { + return createTimeoutException(thread); + } + } + + private Exception createTimeoutException(Thread thread) { + StackTraceElement[] stackTrace = thread.getStackTrace(); + final Thread stuckThread = lookForStuckThread ? getStuckThread(thread) : null; + Exception currThreadException = new TestTimedOutException(timeout, timeUnit); + if (stackTrace != null) { + currThreadException.setStackTrace(stackTrace); + thread.interrupt(); + } + if (stuckThread != null) { + Exception stuckThreadException = + new Exception ("Appears to be stuck in thread " + + stuckThread.getName()); + stuckThreadException.setStackTrace(getStackTrace(stuckThread)); + return new MultipleFailureException( + Arrays.<Throwable>asList(currThreadException, stuckThreadException)); + } else { + return currThreadException; + } + } + + /** + * Retrieves the stack trace for a given thread. + * @param thread The thread whose stack is to be retrieved. + * @return The stack trace; returns a zero-length array if the thread has + * terminated or the stack cannot be retrieved for some other reason. + */ + private StackTraceElement[] getStackTrace(Thread thread) { + try { + return thread.getStackTrace(); + } catch (SecurityException e) { + return new StackTraceElement[0]; + } + } + + /** + * Determines whether the test appears to be stuck in some thread other than + * the "main thread" (the one created to run the test). This feature is experimental. + * Behavior may change after the 4.12 release in response to feedback. + * @param mainThread The main thread created by {@code evaluate()} + * @return The thread which appears to be causing the problem, if different from + * {@code mainThread}, or {@code null} if the main thread appears to be the + * problem or if the thread cannot be determined. The return value is never equal + * to {@code mainThread}. + */ + private Thread getStuckThread(Thread mainThread) { + if (threadGroup == null) { + return null; + } + Thread[] threadsInGroup = getThreadArray(threadGroup); + if (threadsInGroup == null) { + return null; + } + + // Now that we have all the threads in the test's thread group: Assume that + // any thread we're "stuck" in is RUNNABLE. Look for all RUNNABLE threads. + // If just one, we return that (unless it equals threadMain). If there's more + // than one, pick the one that's using the most CPU time, if this feature is + // supported. + Thread stuckThread = null; + long maxCpuTime = 0; + for (Thread thread : threadsInGroup) { + if (thread.getState() == Thread.State.RUNNABLE) { + long threadCpuTime = cpuTime(thread); + if (stuckThread == null || threadCpuTime > maxCpuTime) { + stuckThread = thread; + maxCpuTime = threadCpuTime; + } + } + } + return (stuckThread == mainThread) ? null : stuckThread; + } + + /** + * Returns all active threads belonging to a thread group. + * @param group The thread group. + * @return The active threads in the thread group. The result should be a + * complete list of the active threads at some point in time. Returns {@code null} + * if this cannot be determined, e.g. because new threads are being created at an + * extremely fast rate. + */ + private Thread[] getThreadArray(ThreadGroup group) { + final int count = group.activeCount(); // this is just an estimate + int enumSize = Math.max(count * 2, 100); + int enumCount; + Thread[] threads; + int loopCount = 0; + while (true) { + threads = new Thread[enumSize]; + enumCount = group.enumerate(threads); + if (enumCount < enumSize) { + break; + } + // if there are too many threads to fit into the array, enumerate's result + // is >= the array's length; therefore we can't trust that it returned all + // the threads. Try again. + enumSize += 100; + if (++loopCount >= 5) { + return null; + } + // threads are proliferating too fast for us. Bail before we get into + // trouble. + } + return copyThreads(threads, enumCount); + } + + /** + * Returns an array of the first {@code count} Threads in {@code threads}. + * (Use instead of Arrays.copyOf to maintain compatibility with Java 1.5.) + * @param threads The source array. + * @param count The maximum length of the result array. + * @return The first {@count} (at most) elements of {@code threads}. + */ + private Thread[] copyThreads(Thread[] threads, int count) { + int length = Math.min(count, threads.length); + Thread[] result = new Thread[length]; + for (int i = 0; i < length; i++) { + result[i] = threads[i]; + } + return result; + } + + /** + * Returns the CPU time used by a thread, if possible. + * @param thr The thread to query. + * @return The CPU time used by {@code thr}, or 0 if it cannot be determined. + */ + private long cpuTime (Thread thr) { + ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); + if (mxBean.isThreadCpuTimeSupported()) { + try { + return mxBean.getThreadCpuTime(thr.getId()); + } catch (UnsupportedOperationException e) { + } + } + return 0; + } + + private class CallableStatement implements Callable<Throwable> { + private final CountDownLatch startLatch = new CountDownLatch(1); + + public Throwable call() throws Exception { + try { + startLatch.countDown(); + originalStatement.evaluate(); + } catch (Exception e) { + throw e; + } catch (Throwable e) { + return e; + } + return null; + } + + public void awaitStarted() throws InterruptedException { + startLatch.await(); + } + } +} diff --git a/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java b/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java index e2e81e1..68c0545 100644 --- a/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java +++ b/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java @@ -1,22 +1,19 @@ -/** - * - */ package org.junit.internal.runners.statements; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; public class InvokeMethod extends Statement { - private final FrameworkMethod fTestMethod; - private Object fTarget; - - public InvokeMethod(FrameworkMethod testMethod, Object target) { - fTestMethod= testMethod; - fTarget= target; - } - - @Override - public void evaluate() throws Throwable { - fTestMethod.invokeExplosively(fTarget); - } + private final FrameworkMethod testMethod; + private final Object target; + + public InvokeMethod(FrameworkMethod testMethod, Object target) { + this.testMethod = testMethod; + this.target = target; + } + + @Override + public void evaluate() throws Throwable { + testMethod.invokeExplosively(target); + } }
\ No newline at end of file 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 475ec72..7512a7d 100644 --- a/src/main/java/org/junit/internal/runners/statements/RunAfters.java +++ b/src/main/java/org/junit/internal/runners/statements/RunAfters.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.runners.statements; import java.util.ArrayList; @@ -11,33 +8,34 @@ import org.junit.runners.model.MultipleFailureException; import org.junit.runners.model.Statement; public class RunAfters extends Statement { - private final Statement fNext; + private final Statement next; - private final Object fTarget; + private final Object target; - private final List<FrameworkMethod> fAfters; - - public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) { - fNext= next; - fAfters= afters; - fTarget= target; - } + private final List<FrameworkMethod> afters; - @Override - public void evaluate() throws Throwable { - List<Throwable> errors = new ArrayList<Throwable>(); - try { - fNext.evaluate(); - } catch (Throwable e) { - errors.add(e); - } finally { - for (FrameworkMethod each : fAfters) - try { - each.invokeExplosively(fTarget); - } catch (Throwable e) { - errors.add(e); - } - } - MultipleFailureException.assertEmpty(errors); - } + public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) { + this.next = next; + this.afters = afters; + this.target = target; + } + + @Override + public void evaluate() throws Throwable { + List<Throwable> errors = new ArrayList<Throwable>(); + try { + next.evaluate(); + } catch (Throwable e) { + errors.add(e); + } finally { + for (FrameworkMethod each : afters) { + try { + each.invokeExplosively(target); + } catch (Throwable e) { + errors.add(e); + } + } + } + MultipleFailureException.assertEmpty(errors); + } }
\ 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 66a34e1..238fbe7 100644 --- a/src/main/java/org/junit/internal/runners/statements/RunBefores.java +++ b/src/main/java/org/junit/internal/runners/statements/RunBefores.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.runners.statements; import java.util.List; @@ -9,22 +6,23 @@ import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; public class RunBefores extends Statement { - private final Statement fNext; + private final Statement next; - private final Object fTarget; + private final Object target; - private final List<FrameworkMethod> fBefores; + private final List<FrameworkMethod> befores; - public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) { - fNext= next; - fBefores= befores; - fTarget= target; - } + public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) { + this.next = next; + this.befores = befores; + this.target = target; + } - @Override - public void evaluate() throws Throwable { - for (FrameworkMethod before : fBefores) - before.invokeExplosively(fTarget); - fNext.evaluate(); - } + @Override + public void evaluate() throws Throwable { + for (FrameworkMethod before : befores) { + before.invokeExplosively(target); + } + next.evaluate(); + } }
\ No newline at end of file |