diff --git a/src/main/java/org/junit/internal/TextListener.java b/src/main/java/org/junit/internal/TextListener.java index a75e80d50618..d548aeb26e99 100644 --- a/src/main/java/org/junit/internal/TextListener.java +++ b/src/main/java/org/junit/internal/TextListener.java @@ -74,7 +74,7 @@ protected void printFailures(Result result) { protected void printFailure(Failure each, String prefix) { getWriter().println(prefix + ") " + each.getTestHeader()); - getWriter().print(each.getTrace()); + getWriter().print(each.getTrimmedTrace()); } protected void printFooter(Result result) { diff --git a/src/main/java/org/junit/internal/Throwables.java b/src/main/java/org/junit/internal/Throwables.java index 92b8d2f8d467..9d67d9612ff9 100644 --- a/src/main/java/org/junit/internal/Throwables.java +++ b/src/main/java/org/junit/internal/Throwables.java @@ -1,7 +1,15 @@ package org.junit.internal; +import java.io.BufferedReader; +import java.io.IOException; import java.io.PrintWriter; +import java.io.StringReader; import java.io.StringWriter; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Miscellaneous functions dealing with {@code Throwable}. @@ -54,4 +62,185 @@ public static String getStacktrace(Throwable exception) { exception.printStackTrace(writer); return stringWriter.toString(); } + + /** + * Gets a trimmed version of the stack trace of the given exception. Stack trace + * elements that are below the test method are filtered out. + * + * @return a trimmed stack trace, or the original trace if trimming wasn't possible + */ + public static String getTrimmedStackTrace(Throwable exception) { + List trimmedStackTraceLines = getTrimmedStackTraceLines(exception); + if (trimmedStackTraceLines.isEmpty()) { + return getFullStackTrace(exception); + } + + StringBuilder result = new StringBuilder(exception.toString()); + appendStackTraceLines(trimmedStackTraceLines, result); + appendStackTraceLines(getCauseStackTraceLines(exception), result); + return result.toString(); + } + + private static List getTrimmedStackTraceLines(Throwable exception) { + List stackTraceElements = Arrays.asList(exception.getStackTrace()); + int linesToInclude = stackTraceElements.size(); + + State state = State.PROCESSING_OTHER_CODE; + for (StackTraceElement stackTraceElement : asReversedList(stackTraceElements)) { + state = state.processStackTraceElement(stackTraceElement); + if (state == State.DONE) { + List trimmedLines = new ArrayList(linesToInclude + 2); + trimmedLines.add(""); + for (StackTraceElement each : stackTraceElements.subList(0, linesToInclude)) { + trimmedLines.add("\tat " + each); + } + if (exception.getCause() != null) { + trimmedLines.add("\t... " + (stackTraceElements.size() - trimmedLines.size()) + " trimmed"); + } + return trimmedLines; + } + linesToInclude--; + } + return Collections.emptyList(); + } + + private static List getCauseStackTraceLines(Throwable exception) { + if (exception.getCause() != null) { + String fullTrace = getFullStackTrace(exception); + BufferedReader reader = new BufferedReader( + new StringReader(fullTrace.substring(exception.toString().length()))); + List causedByLines = new ArrayList(); + + try { + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("Caused by: ")) { + causedByLines.add(line); + while ((line = reader.readLine()) != null) { + causedByLines.add(line); + } + return causedByLines; + } + } + } catch (IOException e) { + // We should never get here, because we are reading from a StringReader + } + } + + return Collections.emptyList(); + } + + private static String getFullStackTrace(Throwable exception) { + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + exception.printStackTrace(writer); + return stringWriter.toString(); + } + + private static void appendStackTraceLines( + List stackTraceLines, StringBuilder destBuilder) { + for (String stackTraceLine : stackTraceLines) { + destBuilder.append(String.format("%s%n", stackTraceLine)); + } + } + + private static List asReversedList(final List list) { + return new AbstractList() { + + @Override + public T get(int index) { + return list.get(list.size() - index - 1); + } + + @Override + public int size() { + return list.size(); + } + }; + } + + private enum State { + PROCESSING_OTHER_CODE { + @Override public State processLine(String methodName) { + if (isTestFrameworkMethod(methodName)) { + return PROCESSING_TEST_FRAMEWORK_CODE; + } + return this; + } + }, + PROCESSING_TEST_FRAMEWORK_CODE { + @Override public State processLine(String methodName) { + if (isReflectionMethod(methodName)) { + return PROCESSING_REFLECTION_CODE; + } else if (isTestFrameworkMethod(methodName)) { + return this; + } + return PROCESSING_OTHER_CODE; + } + }, + PROCESSING_REFLECTION_CODE { + @Override public State processLine(String methodName) { + if (isReflectionMethod(methodName)) { + return this; + } else if (isTestFrameworkMethod(methodName)) { + // This is here to handle TestCase.runBare() calling TestCase.runTest(). + return PROCESSING_TEST_FRAMEWORK_CODE; + } + return DONE; + } + }, + DONE { + @Override public State processLine(String methodName) { + return this; + } + }; + + /** Processes a stack trace element method name, possibly moving to a new state. */ + protected abstract State processLine(String methodName); + + /** Processes a stack trace element, possibly moving to a new state. */ + public final State processStackTraceElement(StackTraceElement element) { + return processLine(element.getClassName() + "." + element.getMethodName() + "()"); + } + } + + private static final String[] TEST_FRAMEWORK_METHOD_NAME_PREFIXES = { + "org.junit.runner.", + "org.junit.runners.", + "org.junit.experimental.runners.", + "org.junit.internal.", + "junit.", + }; + + private static final String[] TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES = { + "org.junit.internal.StackTracesTest", + }; + + private static boolean isTestFrameworkMethod(String methodName) { + return isMatchingMethod(methodName, TEST_FRAMEWORK_METHOD_NAME_PREFIXES) && + !isMatchingMethod(methodName, TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES); + } + + private static final String[] REFLECTION_METHOD_NAME_PREFIXES = { + "sun.reflect.", + "java.lang.reflect.", + "org.junit.rules.RunRules.(", + "org.junit.rules.RunRules.applyAll(", // calls TestRules + "org.junit.runners.BlockJUnit4ClassRunner.withMethodRules(", // calls MethodRules + "junit.framework.TestCase.runBare(", // runBare() directly calls setUp() and tearDown() + }; + + private static boolean isReflectionMethod(String methodName) { + return isMatchingMethod(methodName, REFLECTION_METHOD_NAME_PREFIXES); + } + + private static boolean isMatchingMethod(String methodName, String[] methodNamePrefixes) { + for (String methodNamePrefix : methodNamePrefixes) { + if (methodName.startsWith(methodNamePrefix)) { + return true; + } + } + + return false; + } } diff --git a/src/main/java/org/junit/runner/notification/Failure.java b/src/main/java/org/junit/runner/notification/Failure.java index 267d157d7069..4551302807b3 100644 --- a/src/main/java/org/junit/runner/notification/Failure.java +++ b/src/main/java/org/junit/runner/notification/Failure.java @@ -64,14 +64,21 @@ public String toString() { } /** - * Convenience method - * - * @return the printed form of the exception + * Gets the printed form of the exception and its stack trace. */ public String getTrace() { return Throwables.getStacktrace(getException()); } + /** + * Gets a the printed form of the exception, with a trimmed version of the stack trace. + * This method will attempt to filter out frames of the stack trace that are below + * the test method call. + */ + public String getTrimmedTrace() { + return Throwables.getTrimmedStackTrace(getException()); + } + /** * Convenience method * diff --git a/src/test/java/junit/tests/runner/StackFilterTest.java b/src/test/java/junit/tests/runner/StackFilterTest.java index 741fc3484612..6aca9ebf1053 100644 --- a/src/test/java/junit/tests/runner/StackFilterTest.java +++ b/src/test/java/junit/tests/runner/StackFilterTest.java @@ -15,28 +15,28 @@ protected void setUp() { StringWriter swin = new StringWriter(); PrintWriter pwin = new PrintWriter(swin); pwin.println("junit.framework.AssertionFailedError"); - pwin.println(" at junit.framework.Assert.fail(Assert.java:144)"); - pwin.println(" at junit.framework.Assert.assert(Assert.java:19)"); - pwin.println(" at junit.framework.Assert.assert(Assert.java:26)"); - pwin.println(" at MyTest.f(MyTest.java:13)"); - pwin.println(" at MyTest.testStackTrace(MyTest.java:8)"); - pwin.println(" at java.lang.reflect.Method.invoke(Native Method)"); - pwin.println(" at junit.framework.TestCase.runTest(TestCase.java:156)"); - pwin.println(" at junit.framework.TestCase.runBare(TestCase.java:130)"); - pwin.println(" at junit.framework.TestResult$1.protect(TestResult.java:100)"); - pwin.println(" at junit.framework.TestResult.runProtected(TestResult.java:118)"); - pwin.println(" at junit.framework.TestResult.run(TestResult.java:103)"); - pwin.println(" at junit.framework.TestCase.run(TestCase.java:121)"); - pwin.println(" at junit.framework.TestSuite.runTest(TestSuite.java:157)"); - pwin.println(" at junit.framework.TestSuite.run(TestSuite.java, Compiled Code)"); - pwin.println(" at junit.swingui.TestRunner$17.run(TestRunner.java:669)"); + pwin.println("\tat junit.framework.Assert.fail(Assert.java:144)"); + pwin.println("\tat junit.framework.Assert.assert(Assert.java:19)"); + pwin.println("\tat junit.framework.Assert.assert(Assert.java:26)"); + pwin.println("\tat MyTest.f(MyTest.java:13)"); + pwin.println("\tat MyTest.testStackTrace(MyTest.java:8)"); + pwin.println("\tat java.lang.reflect.Method.invoke(Native Method)"); + pwin.println("\tat junit.framework.TestCase.runTest(TestCase.java:156)"); + pwin.println("\tat junit.framework.TestCase.runBare(TestCase.java:130)"); + pwin.println("\tat junit.framework.TestResult$1.protect(TestResult.java:100)"); + pwin.println("\tat junit.framework.TestResult.runProtected(TestResult.java:118)"); + pwin.println("\tat junit.framework.TestResult.run(TestResult.java:103)"); + pwin.println("\tat junit.framework.TestCase.run(TestCase.java:121)"); + pwin.println("\tat junit.framework.TestSuite.runTest(TestSuite.java:157)"); + pwin.println("\tat junit.framework.TestSuite.run(TestSuite.java, Compiled Code)"); + pwin.println("\tat junit.swingui.TestRunner$17.run(TestRunner.java:669)"); fUnfiltered = swin.toString(); StringWriter swout = new StringWriter(); PrintWriter pwout = new PrintWriter(swout); pwout.println("junit.framework.AssertionFailedError"); - pwout.println(" at MyTest.f(MyTest.java:13)"); - pwout.println(" at MyTest.testStackTrace(MyTest.java:8)"); + pwout.println("\tat MyTest.f(MyTest.java:13)"); + pwout.println("\tat MyTest.testStackTrace(MyTest.java:8)"); fFiltered = swout.toString(); } diff --git a/src/test/java/org/junit/internal/AllInternalTests.java b/src/test/java/org/junit/internal/AllInternalTests.java index 04cef67c73ab..e74b6fa83f11 100644 --- a/src/test/java/org/junit/internal/AllInternalTests.java +++ b/src/test/java/org/junit/internal/AllInternalTests.java @@ -16,6 +16,7 @@ FailOnTimeoutTest.class, MethodSorterTest.class, StacktracePrintingMatcherTest.class, + StackTracesTest.class, ThrowableCauseMatcherTest.class, ArrayComparisonFailureTest.class }) diff --git a/src/test/java/org/junit/internal/StackTracesTest.java b/src/test/java/org/junit/internal/StackTracesTest.java new file mode 100644 index 000000000000..c9498b7c079a --- /dev/null +++ b/src/test/java/org/junit/internal/StackTracesTest.java @@ -0,0 +1,412 @@ +package org.junit.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.regex.Pattern; + +import junit.framework.TestCase; +import org.hamcrest.CoreMatchers; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.hamcrest.TypeSafeMatcher; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.MethodRule; +import org.junit.rules.TestRule; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +public class StackTracesTest { + private static final String EOL = System.getProperty("line.separator", "\n"); + private static ExecutorService executorService; + + @BeforeClass + public static void startExecutorService() { + executorService = Executors.newFixedThreadPool(1); + } + + @AfterClass + public static void shutDownExecutorService() { + executorService.shutdown(); + executorService = null; + } + + @Test + public void getTrimmedStackForJUnit4TestFailingInTestMethod() { + Result result = runTest(TestWithOneThrowingTestMethod.class); + assertEquals("Should run the test", 1, result.getRunCount()); + assertEquals("One test should fail", 1, result.getFailureCount()); + Failure failure = result.getFailures().get(0); + + assertHasTrimmedTrace(failure, + message("java.lang.RuntimeException: cause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.throwsExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$TestWithOneThrowingTestMethod.alwaysThrows")); + assertNotEquals(failure.getTrace(), failure.getTrimmedTrace()); + } + + @Test + public void getTrimmedStackForJUnit4TestFailingInTestMethodWithCause() { + Result result = runTest(TestWithOneThrowingTestMethodWithCause.class); + assertEquals("Should run the test", 1, result.getRunCount()); + assertEquals("One test should fail", 1, result.getFailureCount()); + Failure failure = result.getFailures().get(0); + + assertHasTrimmedTrace(failure, + message("java.lang.RuntimeException: outer"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.throwsExceptionWithCause"), + at("org.junit.internal.StackTracesTest$TestWithOneThrowingTestMethodWithCause.alwaysThrows"), + framesTrimmed(), + message("Caused by: java.lang.RuntimeException: cause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.throwsExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithCause"), + framesInCommon()); + assertNotEquals(failure.getTrace(), failure.getTrimmedTrace()); + } + + @Test + public void getTrimmedStackForJUnit4TestFailingInBeforeMethod() { + Result result = runTest(TestWithThrowingBeforeMethod.class); + assertEquals("Should run the test", 1, result.getRunCount()); + assertEquals("One test should fail", 1, result.getFailureCount()); + Failure failure = result.getFailures().get(0); + + assertHasTrimmedTrace(failure, + message("java.lang.RuntimeException: cause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.throwsExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$TestWithThrowingBeforeMethod.alwaysThrows")); + assertNotEquals(failure.getTrace(), failure.getTrimmedTrace()); + } + + @Test + public void getTrimmedStackForJUnit3TestFailingInTestMethod() { + Result result = runTest(JUnit3TestWithOneThrowingTestMethod.class); + assertEquals("Should run the test", 1, result.getRunCount()); + assertEquals("One test should fail", 1, result.getFailureCount()); + Failure failure = result.getFailures().get(0); + + assertHasTrimmedTrace(failure, + message("java.lang.RuntimeException: cause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.throwsExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$JUnit3TestWithOneThrowingTestMethod.testAlwaysThrows")); + assertNotEquals(failure.getTrace(), failure.getTrimmedTrace()); + } + + @Test + public void getTrimmedStackForJUnit3TestFailingInSetupMethod() { + Result result = runTest(JUnit3TestWithThrowingSetUpMethod.class); + assertEquals("Should run the test", 1, result.getRunCount()); + assertEquals("One test should fail", 1, result.getFailureCount()); + Failure failure = result.getFailures().get(0); + + assertHasTrimmedTrace(failure, + message("java.lang.RuntimeException: cause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.throwsExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$JUnit3TestWithThrowingSetUpMethod.setUp")); + assertNotEquals(failure.getTrace(), failure.getTrimmedTrace()); + } + + @Test + public void getTrimmedStackForJUnit4TestFailingInTestRule() { + Result result = runTest(TestWithThrowingTestRule.class); + assertEquals("Should run the test", 1, result.getRunCount()); + assertEquals("One test should fail", 1, result.getFailureCount()); + Failure failure = result.getFailures().get(0); + + assertHasTrimmedTrace(failure, + message("java.lang.RuntimeException: cause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.throwsExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$ThrowingTestRule.apply")); + assertNotEquals(failure.getTrace(), failure.getTrimmedTrace()); + } + + @Test + public void getTrimmedStackForJUnit4TestFailingInMethodRule() { + Result result = runTest(TestWithThrowingMethodRule.class); + assertEquals("Should run the test", 1, result.getRunCount()); + assertEquals("One test should fail", 1, result.getFailureCount()); + Failure failure = result.getFailures().get(0); + + assertHasTrimmedTrace(failure, + message("java.lang.RuntimeException: cause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.doThrowExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$FakeClassUnderTest.throwsExceptionWithoutCause"), + at("org.junit.internal.StackTracesTest$ThrowingMethodRule.apply")); + assertNotEquals(failure.getTrace(), failure.getTrimmedTrace()); + } + + private abstract static class StringMatcher extends TypeSafeMatcher { + } + + /** + * A matcher that matches the exception message in a stack trace. + */ + private static class ExceptionMessageMatcher extends StringMatcher { + private final Matcher matcher; + + public ExceptionMessageMatcher(String message) { + matcher = CoreMatchers.equalTo(message); + } + + public void describeTo(Description description) { + matcher.describeTo(description); + } + + @Override + protected boolean matchesSafely(String line) { + return matcher.matches(line); + } + } + + /** Returns a matcher that matches the message line in a stack trace. */ + private static StringMatcher message(String message) { + return new ExceptionMessageMatcher(message); + } + + /** + * A matcher that matches the "at ..." line in a stack trace. + */ + private static class StackTraceLineMatcher extends StringMatcher { + private static final Pattern PATTERN + = Pattern.compile("at ([a-zA-Z0-9.$]+)\\([a-zA-Z0-9]+\\.java:[0-9]+\\)"); + + private final String method; + + public StackTraceLineMatcher(String method) { + this.method = method; + } + + public void describeTo(Description description) { + description.appendText("A stack trace line for method " + method); + } + + @Override + protected boolean matchesSafely(String line) { + if (!line.startsWith("\t")) { + return false; + } + + line = line.substring(1); + java.util.regex.Matcher matcher = PATTERN.matcher(line); + if (!matcher.matches()) { + fail("Line does not look like a stack trace line: " + line); + } + String matchedMethod = matcher.group(1); + return method.equals(matchedMethod); + } + } + + /** Returns a matcher that matches the "at ..." line in a stack trace. */ + private static StringMatcher at(String method) { + return new StackTraceLineMatcher(method); + } + + /** + * A matcher that matches the line printed when frames were removed from a stack trace. + */ + private static class FramesRemovedMatcher extends StringMatcher { + private static final Pattern PATTERN + = Pattern.compile("\\.\\.\\. [0-9]+ ([a-z]+)"); + + private final String suffix; + + public FramesRemovedMatcher(String suffix) { + this.suffix = suffix; + } + + public void describeTo(Description description) { + description.appendText("A line matching \"..x " + suffix + "\""); + } + + @Override + protected boolean matchesSafely(String line) { + if (!line.startsWith("\t")) { + return false; + } + line = line.substring(1); + + java.util.regex.Matcher matcher = PATTERN.matcher(line); + if (!matcher.matches()) { + fail("Line does not look like a stack trace line: " + line); + } + return suffix.equals(matcher.group(1)); + } + } + + /** Returns a matcher that matches the "\t...x more" line in a stack trace. */ + private static StringMatcher framesInCommon() { + return new FramesRemovedMatcher("more"); + } + + /** Returns a matcher that matches the "\t...x trimmed" line in a stack trace. */ + private static StringMatcher framesTrimmed() { + return new FramesRemovedMatcher("trimmed"); + } + + private static Result runTest(final Class testClass) { + Future future = executorService.submit(new Callable() { + public Result call() throws Exception { + JUnitCore core = new JUnitCore(); + return core.run(testClass); + } + }); + + try { + return future.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Could not run test " + testClass, e); + } catch (ExecutionException e) { + throw new RuntimeException("Could not run test " + testClass, e); + } + } + + private static void assertHasTrimmedTrace(Failure failure, StringMatcher... matchers) { + String trimmedTrace = failure.getTrimmedTrace(); + String[] lines = trimmedTrace.split(EOL); + + int index = 0; + for (; index < lines.length && index < matchers.length; index++) { + String line = lines[index]; + StringMatcher matcher = matchers[index]; + assertThat(line, matcher); + } + if (index < lines.length) { + String extraLine = lines[index]; + fail("Extra line in trimmed trace: " + extraLine); + } else if (index < matchers.length) { + StringDescription description = new StringDescription(); + matchers[index].describeTo(description); + fail("Missing line in trimmed trace: " + description.toString()); + } + } + + public static class TestWithOneThrowingTestMethod { + + @Test + public void alwaysThrows() { + new FakeClassUnderTest().throwsExceptionWithoutCause(); + } + } + + public static class JUnit3TestWithOneThrowingTestMethod extends TestCase { + + public void testAlwaysThrows() { + new FakeClassUnderTest().throwsExceptionWithoutCause(); + } + } + + public static class TestWithOneThrowingTestMethodWithCause { + + @Test + public void alwaysThrows() { + new FakeClassUnderTest().throwsExceptionWithCause(); + } + } + + public static class TestWithThrowingBeforeMethod { + + @Before + public void alwaysThrows() { + new FakeClassUnderTest().throwsExceptionWithoutCause(); + } + + @Test + public void alwaysPasses() { + } + } + + public static class JUnit3TestWithThrowingSetUpMethod extends TestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + new FakeClassUnderTest().throwsExceptionWithoutCause(); + } + + public void testAlwaysPasses() { + } + } + + public static class ThrowingTestRule implements TestRule { + + public Statement apply( + Statement base, org.junit.runner.Description description) { + new FakeClassUnderTest().throwsExceptionWithoutCause(); + return base; + } + } + + public static class TestWithThrowingTestRule { + + @Rule + public final TestRule rule = new ThrowingTestRule(); + + @Test + public void alwaysPasses() { + } + } + + public static class ThrowingMethodRule implements MethodRule { + + public Statement apply( + Statement base, FrameworkMethod method, Object target) { + new FakeClassUnderTest().throwsExceptionWithoutCause(); + return base; + } + } + + public static class TestWithThrowingMethodRule { + + @Rule + public final ThrowingMethodRule rule = new ThrowingMethodRule(); + + @Test + public void alwaysPasses() { + } + } + + private static class FakeClassUnderTest { + + public void throwsExceptionWithCause() { + doThrowExceptionWithCause(); + } + + public void throwsExceptionWithoutCause() { + doThrowExceptionWithoutCause(); + } + + private void doThrowExceptionWithCause() { + try { + throwsExceptionWithoutCause(); + } catch (Exception e) { + throw new RuntimeException("outer", e); + } + } + + private void doThrowExceptionWithoutCause() { + throw new RuntimeException("cause"); + } + } +} diff --git a/src/test/java/org/junit/tests/SampleJUnit4Tests.java b/src/test/java/org/junit/tests/SampleJUnit4Tests.java new file mode 100644 index 000000000000..c42026f2f79f --- /dev/null +++ b/src/test/java/org/junit/tests/SampleJUnit4Tests.java @@ -0,0 +1,48 @@ +package org.junit.tests; + +import org.junit.Test; + +/** + * Container for sample JUnit4-style tests used in integration tests. + */ +public class SampleJUnit4Tests { + + public static class TestWithOneThrowingTestMethod { + + @Test + public void alwaysThrows() { + new FakeClassUnderTest().throwsExceptionWithoutCause(); + } + } + + public static class TestWithOneThrowingTestMethodWithCause { + + @Test + public void alwaysThrows() { + new FakeClassUnderTest().throwsExceptionWithCause(); + } + } + + private static class FakeClassUnderTest { + + public void throwsExceptionWithCause() { + doThrowExceptionWithCause(); + } + + public void throwsExceptionWithoutCause() { + doThrowExceptionWithoutCause(); + } + + private void doThrowExceptionWithCause() { + try { + throwsExceptionWithoutCause(); + } catch (Exception e) { + throw new RuntimeException("outer", e); + } + } + + private void doThrowExceptionWithoutCause() { + throw new RuntimeException("cause"); + } + } +}