diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java index a98f42c66d42..7b108da8070f 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java @@ -42,7 +42,8 @@ public final class ExceptionUtils { private static final String JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX = "org.junit.platform.launcher."; - private static final String STACK_TRACE_ELEMENTS_TO_EXCLUDE = "org.junit.*,jdk.internal.reflect.*,sun.reflect.*"; + private static final Predicate STACK_TRACE_ELEMENT_FILTER = ClassNamePatternFilterUtils // + .excludeMatchingClassNames("org.junit.*,jdk.internal.reflect.*,sun.reflect.*"); private ExceptionUtils() { /* no-op */ @@ -114,9 +115,6 @@ public static void pruneStackTrace(Throwable throwable, List testClassNa Preconditions.notNull(throwable, "Throwable must not be null"); Preconditions.notNull(testClassNames, "List of test class names must not be null"); - Predicate stackTraceElementFilter = ClassNamePatternFilterUtils // - .excludeMatchingClassNames(STACK_TRACE_ELEMENTS_TO_EXCLUDE); - List stackTrace = Arrays.asList(throwable.getStackTrace()); List prunedStackTrace = new ArrayList<>(); @@ -134,7 +132,7 @@ public static void pruneStackTrace(Throwable throwable, List testClassNa else if (className.startsWith(JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX)) { prunedStackTrace.clear(); } - else if (stackTraceElementFilter.test(className)) { + else if (STACK_TRACE_ELEMENT_FILTER.test(className)) { prunedStackTrace.add(element); } } diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java index b6ba60af8001..06d8b6a97207 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java @@ -10,11 +10,12 @@ package org.junit.platform.launcher.core; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.engine.EngineExecutionListener; @@ -48,8 +49,9 @@ public void executionFinished(TestDescriptor testDescriptor, TestExecutionResult } private static List getTestClassNames(TestDescriptor testDescriptor) { - return Stream.of(testDescriptor.getSource(), testDescriptor.getParent().flatMap(TestDescriptor::getSource)) // - .filter(Optional::isPresent) // + return findAllParentDescriptors(testDescriptor) // + .stream() // + .map(TestDescriptor::getSource).filter(Optional::isPresent) // .map(Optional::get) // .map(source -> { if (source instanceof ClassSource) { @@ -66,4 +68,18 @@ else if (source instanceof MethodSource) { .collect(Collectors.toList()); } + private static Set findAllParentDescriptors(TestDescriptor testDescriptor) { + return findParentDescriptors(testDescriptor, new HashSet<>()); + } + + private static Set findParentDescriptors(TestDescriptor testDescriptor, + Set visitedDescriptors) { + if (testDescriptor.isRoot()) { + return visitedDescriptors; + } + TestDescriptor parent = testDescriptor.getParent().get(); + visitedDescriptors.add(parent); + return findParentDescriptors(parent, visitedDescriptors); + } + } diff --git a/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java b/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java index cc849fa215ed..d8a612bef2bc 100644 --- a/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java +++ b/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java @@ -20,7 +20,11 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.platform.engine.TestExecutionResult; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; @@ -39,7 +43,7 @@ class StackTracePruningTests { @Test void shouldPruneStackTraceByDefault() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssertion")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // .execute(); List stackTrace = extractStackTrace(results); @@ -52,7 +56,7 @@ void shouldPruneStackTraceByDefault() { void shouldPruneStackTraceWhenEnabled() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssertion")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // .execute(); List stackTrace = extractStackTrace(results); @@ -65,7 +69,7 @@ void shouldPruneStackTraceWhenEnabled() { void shouldNotPruneStackTraceWhenDisabled() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "false") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssertion")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // .execute(); List stackTrace = extractStackTrace(results); @@ -82,7 +86,7 @@ void shouldNotPruneStackTraceWhenDisabled() { void shouldAlwaysKeepJupiterAssertionStackTraceElement() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssertion")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // .execute(); List stackTrace = extractStackTrace(results); @@ -98,7 +102,7 @@ void shouldAlwaysKeepJupiterAssertionStackTraceElement() { void shouldAlwaysKeepJupiterAssumptionStackTraceElement() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssumption")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssumption")) // .execute(); List stackTrace = extractStackTrace(results); @@ -114,7 +118,7 @@ void shouldAlwaysKeepJupiterAssumptionStackTraceElement() { void shouldKeepEverythingAfterTestCall() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssertion")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // .execute(); List stackTrace = extractStackTrace(results); @@ -123,7 +127,28 @@ void shouldKeepEverythingAfterTestCall() { """ \\Qorg.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:\\E.+ \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ - \\Qorg.junit.platform.StackTracePruningTests$StackTracePruningTestCase.failingAssertion(StackTracePruningTests.java:\\E.+ + \\Qorg.junit.platform.StackTracePruningTests$FailingTestTestCase.failingAssertion(StackTracePruningTests.java:\\E.+ + >>>> + """); + } + + @ParameterizedTest + @ValueSource(strings = { "org.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase", + "org.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase$NestedTestCase", + "org.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase$NestedTestCase$NestedNestedTestCase" }) + void shouldKeepEverythingAfterLifecycleMethodCall(Class methodClass) { + EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // + .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // + .selectors(selectMethod(methodClass, "test")) // + .execute(); + + List stackTrace = extractStackTrace(results); + + assertStackTraceMatch(stackTrace, + """ + \\Qorg.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:\\E.+ + \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ + \\Qorg.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase.setUp(StackTracePruningTests.java:\\E.+ >>>> """); } @@ -132,7 +157,7 @@ void shouldKeepEverythingAfterTestCall() { void shouldPruneStackTracesOfSuppressedExceptions() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // - .selectors(selectMethod(StackTracePruningTestCase.class, "multipleFailingAssertions")) // + .selectors(selectMethod(FailingTestTestCase.class, "multipleFailingAssertions")) // .execute(); Throwable throwable = getThrowable(results); @@ -170,7 +195,7 @@ private static void assertStackTraceDoesNotContain(List stack // ------------------------------------------------------------------- - static class StackTracePruningTestCase { + static class FailingTestTestCase { @Test void failingAssertion() { @@ -191,4 +216,35 @@ void failingAssumption() { } + static class FailingBeforeEachTestCase { + + @BeforeEach + void setUp() { + Assertions.fail(); + } + + @Test + void test() { + } + + @Nested + class NestedTestCase { + + @Test + void test() { + } + + @Nested + class NestedNestedTestCase { + + @Test + void test() { + } + + } + + } + + } + }