Skip to content

Commit

Permalink
Prune causes and suppressed exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
juliette-derancourt committed Apr 28, 2023
1 parent b80a7a8 commit e3fa37b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;

Expand Down Expand Up @@ -129,4 +132,36 @@ else if (stackTraceElementFilter.test(name) || ALWAYS_INCLUDED_STACK_TRACE_ELEME
throwable.setStackTrace(prunedStackTrace.toArray(new StackTraceElement[0]));
}

/**
* Find all causes and suppressed exceptions in the backtrace of the
* supplied {@link Throwable}.
*
* @param rootThrowable the {@code Throwable} to explore; never {@code null}
* @return a list of all throwables found, including the supplied one;
* never {@code null}
*
* @since 5.10
*/
public static List<Throwable> findNestedThrowables(Throwable rootThrowable) {
Preconditions.notNull(rootThrowable, "Throwable must not be null");

HashSet<Throwable> visited = new HashSet<>();
Deque<Throwable> toVisit = new ArrayDeque<>();
toVisit.add(rootThrowable);

while (!toVisit.isEmpty()) {
Throwable current = toVisit.remove();
boolean isFirstVisit = visited.add(current);
if (isFirstVisit) {
Throwable cause = current.getCause();
if (cause != null) {
toVisit.add(cause);
}
toVisit.addAll(Arrays.asList(current.getSuppressed()));
}
}

return new ArrayList<>(visited);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,14 @@ public void executionFinished(TestDescriptor testDescriptor, TestExecutionResult
if (testExecutionResult.getThrowable().isPresent()) {
Throwable throwable = testExecutionResult.getThrowable().get();

ExceptionUtils.pruneStackTrace(throwable,
ClassNamePatternFilterUtils.excludeMatchingClassNames(pruningPattern));
ExceptionUtils.findNestedThrowables(throwable).forEach(this::pruneStackTrace);
}
super.executionFinished(testDescriptor, testExecutionResult);
}

private void pruneStackTrace(Throwable throwable) {
ExceptionUtils.pruneStackTrace(throwable,
ClassNamePatternFilterUtils.excludeMatchingClassNames(pruningPattern));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -172,20 +172,17 @@ private static void assertStackTraceDoesNotContain(List<StackTraceElement> stack
// -------------------------------------------------------------------

static class StackTracePruningTestCase {

@Test
void failingAssertion() {
Assertions.fail();
}

@Test
void multipleFailingAssertion() {
Assertions.assertAll(
Assertions::fail,
Assertions::fail
);
Assertions.assertAll(Assertions::fail, Assertions::fail);
}

@Test
void failingAssumption() {
Assumptions.assumeTrue(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertLinesMatch;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.platform.commons.util.ExceptionUtils.findNestedThrowables;
import static org.junit.platform.commons.util.ExceptionUtils.pruneStackTrace;
import static org.junit.platform.commons.util.ExceptionUtils.readStackTrace;
import static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;

Expand Down Expand Up @@ -75,7 +77,7 @@ void pruneStackTraceOfCallsFromSpecificPackage() {
throw new JUnitException("expected");
}
catch (JUnitException e) {
ExceptionUtils.pruneStackTrace(e, element -> !element.startsWith("org.junit."));
pruneStackTrace(e, element -> !element.startsWith("org.junit."));
assertThat(e.getStackTrace()) //
.noneMatch(element -> element.toString().contains("org.junit."));
}
Expand All @@ -87,7 +89,7 @@ void pruneStackTraceOfEverythingExceptJupiterAssertions() {
Assertions.fail();
}
catch (AssertionFailedError e) {
ExceptionUtils.pruneStackTrace(e, element -> false);
pruneStackTrace(e, element -> false);
assertStackTraceMatch(e.getStackTrace(), "\\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+");
}
}
Expand All @@ -100,7 +102,7 @@ void pruneStackTraceOfEverythingExceptJupiterAssumptions() {
});
}
catch (JUnitException e) {
ExceptionUtils.pruneStackTrace(e, element -> false);
pruneStackTrace(e, element -> false);
assertStackTraceMatch(e.getStackTrace(),
"\\Qorg.junit.jupiter.api.Assumptions.assumeTrue(Assumptions.java:\\E.+");
}
Expand All @@ -112,7 +114,7 @@ void pruneStackTraceOfAllLauncherCalls() {
throw new JUnitException("expected");
}
catch (JUnitException e) {
ExceptionUtils.pruneStackTrace(e, element -> true);
pruneStackTrace(e, element -> true);
assertThat(e.getStackTrace()) //
.noneMatch(element -> element.toString().contains("org.junit.platform.launcher."));
}
Expand All @@ -128,12 +130,41 @@ void pruneStackTraceOfEverythingPriorToFirstLauncherCall() {
stackTrace[stackTrace.length - 1] = new StackTraceElement("org.example.Class", "method", "file", 123);
e.setStackTrace(stackTrace);

ExceptionUtils.pruneStackTrace(e, element -> true);
pruneStackTrace(e, element -> true);
assertThat(e.getStackTrace()) //
.noneMatch(element -> element.toString().contains("org.example.Class.method(file:123)"));
}
}

@Test
void findSuppressedExceptionsAndCausesOfThrowable() {
Throwable t1 = new Throwable("#1");
Throwable t2 = new Throwable("#2");
Throwable t3 = new Throwable("#3");
Throwable t4 = new Throwable("#4");
Throwable t5 = new Throwable("#5");
t1.initCause(t2);
t2.initCause(t3);
t1.addSuppressed(t4);
t2.addSuppressed(t5);

assertThat(findNestedThrowables(t1)) //
.extracting(Throwable::getMessage) //
.containsExactlyInAnyOrder("#1", "#2", "#3", "#4", "#5");
}

@Test
void avoidCyclesWhileSearchingForNestedThrowables() {
Throwable t1 = new Throwable();
Throwable t2 = new Throwable(t1);
Throwable t3 = new Throwable(t1);
t1.initCause(t2);
t1.addSuppressed(t3);
t2.addSuppressed(t3);

assertThat(findNestedThrowables(t1)).hasSize(3);
}

private static void assertStackTraceMatch(StackTraceElement[] stackTrace, String expectedLines) {
List<String> stackStraceAsLines = Arrays.stream(stackTrace) //
.map(StackTraceElement::toString) //
Expand Down

0 comments on commit e3fa37b

Please sign in to comment.