From 309a696f4f7a2cda191521b43b0f2482832a6123 Mon Sep 17 00:00:00 2001 From: Juliette de Rancourt Date: Wed, 28 Jun 2023 20:41:23 +0200 Subject: [PATCH 01/10] Revise stacktrace pruning (#3364) Resolves #3299. --- .../release-notes-5.10.0-RC1.adoc | 5 +- .../asciidoc/user-guide/running-tests.adoc | 19 ++- .../junitbuild.testing-conventions.gradle.kts | 1 - .../platform/commons/util/ExceptionUtils.java | 33 +++-- .../junit/platform/engine/TestDescriptor.java | 20 +++ .../platform/launcher/LauncherConstants.java | 44 ------- .../core/EngineExecutionOrchestrator.java | 6 +- ...ckTracePruningEngineExecutionListener.java | 46 ++++--- .../platform/StackTracePruningTests.java | 117 ++++++++++++------ .../commons/util/ExceptionUtilsTests.java | 55 ++++---- .../AbstractTestDescriptorTests.java | 32 ++++- .../launcher/core/LauncherFactoryTests.java | 4 +- ...egacyXmlReportGeneratingListenerTests.java | 4 +- 13 files changed, 224 insertions(+), 162 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc index c3a9bdaea88f..4524496a0b36 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc @@ -33,7 +33,10 @@ JUnit repository on GitHub. * New `selectMethod()` and `selectNestedMethod()` variants in `DiscoverySelectors` that accept a `Class...` argument of parameter types as a type-safe alternative to providing the names of parameter types as a comma-delimited string. - +* Stack trace pruning has been revised and now only removes calls from the `org.junit`, + `jdk.internal.reflect`, and `sun.reflect` packages. Please refer to the + <<../user-guide/index.adoc#stacktrace-pruning, User Guide>> for details. +* New `getAncestors()` method in `TestDescriptor`. [[release-notes-5.10.0-RC1-junit-jupiter]] === JUnit Jupiter diff --git a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc index 8461b9bc7c1f..35915254fd95 100644 --- a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc @@ -1138,18 +1138,13 @@ Since version 1.10, the JUnit Platform provides built-in support for pruning sta produced by failing tests. This feature can be enabled or disabled via the `junit.platform.stacktrace.pruning.enabled` _configuration parameter_. -By default, all calls from the `org.junit`, `java`, and `jdk` packages are removed from the -stack trace. You can also configure the JUnit Platform to exclude different or additional -calls. To do this, provide a pattern for the `junit.platform.stacktrace.pruning.pattern` -_configuration parameter_ to specify which fully qualified class names should be excluded -from the stack traces. - -In addition, and independently of the provided pattern, all elements prior to and -including the first call from the JUnit Platform Launcher will be removed. - -NOTE: Since they provide necessary insights to understand a test failure, calls to -`{Assertions}` or `{Assumptions}` will never be excluded from stack traces even though -they are part of the `org.junit` package. +If enabled, all calls from the `org.junit`, `jdk.internal.reflect`, and `sun.reflect` +packages are removed from the stack trace, unless they are subsequent to the test itself +or any of its ancestors. For that reason, calls to `{Assertions}` or `{Assumptions}` will +never be excluded. + +In addition, all elements prior to and including the first call from the JUnit Platform +Launcher will be removed. [[stacktrace-pruning-pattern]] ==== Pattern Matching Syntax diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts index 122db6b5b5af..8a1cdd89955f 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts @@ -41,7 +41,6 @@ tasks.withType().configureEach { server.set(uri("https://ge.junit.org")) } systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager") - systemProperty("junit.platform.stacktrace.pruning.enabled", false) // Required until ASM officially supports the JDK 14 systemProperty("net.bytebuddy.experimental", true) if (buildParameters.testing.enableJFR) { 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 64f59d12d5fb..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,6 +42,9 @@ public final class ExceptionUtils { private static final String JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX = "org.junit.platform.launcher."; + private static final Predicate STACK_TRACE_ELEMENT_FILTER = ClassNamePatternFilterUtils // + .excludeMatchingClassNames("org.junit.*,jdk.internal.reflect.*,sun.reflect.*"); + private ExceptionUtils() { /* no-op */ } @@ -92,38 +95,44 @@ public static String readStackTrace(Throwable throwable) { } /** - * Prune the stack trace of the supplied {@link Throwable} by filtering its - * elements using the supplied {@link Predicate}, except for - * {@code org.junit.jupiter.api.Assertions} and - * {@code org.junit.jupiter.api.Assumptions} that will always remain - * present. + * Prune the stack trace of the supplied {@link Throwable} by removing + * elements from the {@code org.junit}, {@code jdk.internal.reflect} and + * {@code sun.reflect} packages. If an element matching one of the supplied + * class names is encountered, all following elements will be kept regardless. * *

Additionally, all elements prior to and including the first * JUnit Launcher call will be removed. * * @param throwable the {@code Throwable} whose stack trace should be * pruned; never {@code null} - * @param stackTraceElementFilter the {@code Predicate} used to filter - * elements of the stack trace; never {@code null} + * @param testClassNames the test class names that should stop the pruning + * if encountered; never {@code null} * * @since 5.10 */ @API(status = INTERNAL, since = "5.10") - public static void pruneStackTrace(Throwable throwable, Predicate stackTraceElementFilter) { + public static void pruneStackTrace(Throwable throwable, List testClassNames) { Preconditions.notNull(throwable, "Throwable must not be null"); - Preconditions.notNull(stackTraceElementFilter, "Predicate must not be null"); + Preconditions.notNull(testClassNames, "List of test class names must not be null"); List stackTrace = Arrays.asList(throwable.getStackTrace()); List prunedStackTrace = new ArrayList<>(); Collections.reverse(stackTrace); - for (StackTraceElement element : stackTrace) { + for (int i = 0; i < stackTrace.size(); i++) { + StackTraceElement element = stackTrace.get(i); String className = element.getClassName(); - if (className.startsWith(JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX)) { + + if (testClassNames.contains(className)) { + // Include all elements called by the test + prunedStackTrace.addAll(stackTrace.subList(i, stackTrace.size())); + break; + } + 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-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java index b16a8d4319e2..7dfcecc644ca 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java @@ -106,6 +106,26 @@ default String getLegacyReportingName() { */ Set getChildren(); + /** + * Get the immutable set of all ancestors of this descriptor. + * + *

An ancestors is the parent of this descriptor or the parent + * of one of its parents, recursively. + * + * @see #getParent() + */ + @API(status = STABLE, since = "5.10") + default Set getAncestors() { + if (!getParent().isPresent()) { + return Collections.emptySet(); + } + TestDescriptor parent = getParent().get(); + Set ancestors = new LinkedHashSet<>(); + ancestors.add(parent); + ancestors.addAll(parent.getAncestors()); + return Collections.unmodifiableSet(ancestors); + } + /** * Get the immutable set of all descendants of this descriptor. * diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java index bf16677f933c..af05c4ae86ee 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java @@ -174,50 +174,6 @@ public class LauncherConstants { @API(status = EXPERIMENTAL, since = "1.10") public static final String STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME = "junit.platform.stacktrace.pruning.enabled"; - /** - * Property name used to provide patterns to remove elements from stack traces. - * - *

Pattern Matching Syntax

- * - *

If the property value consists solely of an asterisk ({@code *}), all - * elements will be removed. Otherwise, the property value will be treated - * as a comma-separated list of patterns where each individual pattern will - * be matched against the fully qualified class name (FQCN) of the - * stack trace element. Any dot ({@code .}) in a pattern will match against - * a dot ({@code .}) or a dollar sign ({@code $}) in a FQCN. Any asterisk - * ({@code *}) will match against one or more characters in a FQCN. All - * other characters in a pattern will be matched one-to-one against a FQCN. - * - *

Examples

- * - *
    - *
  • {@code *}: remove all elements. - *
  • {@code org.junit.*}: remove every element with the {@code org.junit} - * base package and any of its subpackages. - *
  • {@code *.MyClass}: remove every element whose simple class name is - * exactly {@code MyClass}. - *
  • {@code *System*, *Dev*}: exclude every element whose FQCN contains - * {@code System} or {@code Dev}. - *
  • {@code org.example.MyClass, org.example.TheirClass}: remove - * elements whose FQCN is exactly {@code org.example.MyClass} or - * {@code org.example.TheirClass}. - *
- * - * @see #STACKTRACE_PRUNING_DEFAULT_PATTERN - */ - @API(status = EXPERIMENTAL, since = "1.10") - public static final String STACKTRACE_PRUNING_PATTERN_PROPERTY_NAME = "junit.platform.stacktrace.pruning.pattern"; - - /** - * Default pattern for stack trace pruning which matches the - * {@code org.junit}, {@code java}, and {@code jdk} base packages as well - * as any of their subpackages. - * - * @see #STACKTRACE_PRUNING_PATTERN_PROPERTY_NAME - */ - @API(status = EXPERIMENTAL, since = "1.10") - public static final String STACKTRACE_PRUNING_DEFAULT_PATTERN = "org.junit.*,java.*,jdk.*"; - private LauncherConstants() { /* no-op */ } diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java index ab1a8e94b503..ed764199a3f6 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java @@ -12,9 +12,7 @@ import static org.apiguardian.api.API.Status.INTERNAL; import static org.junit.platform.launcher.LauncherConstants.DRY_RUN_PROPERTY_NAME; -import static org.junit.platform.launcher.LauncherConstants.STACKTRACE_PRUNING_DEFAULT_PATTERN; import static org.junit.platform.launcher.LauncherConstants.STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME; -import static org.junit.platform.launcher.LauncherConstants.STACKTRACE_PRUNING_PATTERN_PROPERTY_NAME; import static org.junit.platform.launcher.core.ListenerRegistry.forEngineExecutionListeners; import java.util.Optional; @@ -179,9 +177,7 @@ private static EngineExecutionListener selectExecutionListener(EngineExecutionLi boolean stackTracePruningEnabled = configurationParameters.getBoolean(STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME) // .orElse(true); if (stackTracePruningEnabled) { - String pruningPattern = configurationParameters.get(STACKTRACE_PRUNING_PATTERN_PROPERTY_NAME) // - .orElse(STACKTRACE_PRUNING_DEFAULT_PATTERN); - return new StackTracePruningEngineExecutionListener(engineExecutionListener, pruningPattern); + return new StackTracePruningEngineExecutionListener(engineExecutionListener); } return engineExecutionListener; } 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 574ce4827d47..c183fdb683b8 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,49 +10,61 @@ package org.junit.platform.launcher.core; -import java.util.Arrays; import java.util.List; -import java.util.function.Predicate; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; -import org.junit.platform.commons.util.ClassNamePatternFilterUtils; import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.engine.EngineExecutionListener; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.support.descriptor.ClassSource; +import org.junit.platform.engine.support.descriptor.MethodSource; /** * Prunes the stack trace in case of a failed event. * * @since 1.10 - * @see org.junit.platform.commons.util.ExceptionUtils#pruneStackTrace(Throwable, Predicate) + * @see org.junit.platform.commons.util.ExceptionUtils#pruneStackTrace(Throwable, List) */ class StackTracePruningEngineExecutionListener extends DelegatingEngineExecutionListener { - private static final List ALWAYS_INCLUDED_STACK_TRACE_ELEMENTS = Arrays.asList( // - "org.junit.jupiter.api.Assertions", // - "org.junit.jupiter.api.Assumptions" // - ); - - private final Predicate stackTraceElementFilter; - - StackTracePruningEngineExecutionListener(EngineExecutionListener delegate, String pruningPattern) { + StackTracePruningEngineExecutionListener(EngineExecutionListener delegate) { super(delegate); - this.stackTraceElementFilter = ClassNamePatternFilterUtils.excludeMatchingClassNames(pruningPattern) // - .or(ALWAYS_INCLUDED_STACK_TRACE_ELEMENTS::contains); } @Override public void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) { + List testClassNames = getTestClassNames(testDescriptor); if (testExecutionResult.getThrowable().isPresent()) { Throwable throwable = testExecutionResult.getThrowable().get(); - ExceptionUtils.findNestedThrowables(throwable).forEach(this::pruneStackTrace); + ExceptionUtils.findNestedThrowables(throwable).forEach( + t -> ExceptionUtils.pruneStackTrace(t, testClassNames)); } super.executionFinished(testDescriptor, testExecutionResult); } - private void pruneStackTrace(Throwable throwable) { - ExceptionUtils.pruneStackTrace(throwable, stackTraceElementFilter); + private static List getTestClassNames(TestDescriptor testDescriptor) { + return testDescriptor.getAncestors() // + .stream() // + .map(TestDescriptor::getSource) // + .filter(Optional::isPresent) // + .map(Optional::get) // + .map(source -> { + if (source instanceof ClassSource) { + return ((ClassSource) source).getClassName(); + } + else if (source instanceof MethodSource) { + return ((MethodSource) source).getClassName(); + } + else { + return null; + } + }) // + .filter(Objects::nonNull) // + .collect(Collectors.toList()); } } 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 397dacc3172d..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,16 +43,11 @@ 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); - assertStackTraceMatch(stackTrace, """ - \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ - """); - - assertStackTraceDoesNotContain(stackTrace, "java.util.ArrayList.forEach(ArrayList.java:"); assertStackTraceDoesNotContain(stackTrace, "jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:"); } @@ -57,16 +56,11 @@ 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); - assertStackTraceMatch(stackTrace, """ - \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ - """); - - assertStackTraceDoesNotContain(stackTrace, "java.util.ArrayList.forEach(ArrayList.java:"); assertStackTraceDoesNotContain(stackTrace, "jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:"); } @@ -75,83 +69,103 @@ 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); assertStackTraceMatch(stackTrace, """ \\Qorg.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:\\E.+ - \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ >>>> - \\Qjava.base/java.util.ArrayList.forEach(ArrayList.java:\\E.+ + \\Qorg.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:\\E.+ >>>> """); } @Test - void shouldPruneStackTraceAccordingToPattern() { + void shouldAlwaysKeepJupiterAssertionStackTraceElement() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // - .configurationParameter("junit.platform.stacktrace.pruning.pattern", "jdk.*") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssertion")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // .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.+ >>>> - \\Qjava.base/java.util.ArrayList.forEach(ArrayList.java:\\E.+ + \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ >>>> """); - - assertStackTraceDoesNotContain(stackTrace, "jdk."); } @Test - void shouldAlwaysKeepJupiterAssertionStackTraceElement() { + void shouldAlwaysKeepJupiterAssumptionStackTraceElement() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // - .configurationParameter("junit.platform.stacktrace.pruning.pattern", "*") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssertion")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssumption")) // .execute(); List stackTrace = extractStackTrace(results); assertStackTraceMatch(stackTrace, """ - \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ + >>>> + \\Qorg.junit.jupiter.api.Assumptions.assumeTrue(Assumptions.java:\\E.+ + >>>> """); } @Test - void shouldAlwaysKeepJupiterAssumptionStackTraceElement() { + void shouldKeepEverythingAfterTestCall() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // - .configurationParameter("junit.platform.stacktrace.pruning.pattern", "*") // - .selectors(selectMethod(StackTracePruningTestCase.class, "failingAssumption")) // + .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // .execute(); List stackTrace = extractStackTrace(results); - assertStackTraceMatch(stackTrace, """ - \\Qorg.junit.jupiter.api.Assumptions.assumeTrue(Assumptions.java:\\E.+ - """); + assertStackTraceMatch(stackTrace, + """ + \\Qorg.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:\\E.+ + \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.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.+ + >>>> + """); } @Test 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); for (Throwable suppressed : throwable.getSuppressed()) { List stackTrace = Arrays.asList(suppressed.getStackTrace()); - assertStackTraceDoesNotContain(stackTrace, "java.util.ArrayList.forEach(ArrayList.java:"); + assertStackTraceDoesNotContain(stackTrace, + "jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:"); } } @@ -181,7 +195,7 @@ private static void assertStackTraceDoesNotContain(List stack // ------------------------------------------------------------------- - static class StackTracePruningTestCase { + static class FailingTestTestCase { @Test void failingAssertion() { @@ -202,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() { + } + + } + + } + + } + } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java index 1c96ca1b7e9b..edd11a17ded4 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java @@ -18,8 +18,11 @@ import static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException; import java.io.IOException; +import java.util.List; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.PreconditionViolationException; @@ -64,44 +67,38 @@ void readStackTraceForLocalJUnitException() { } } - @Test - void pruneStackTraceOfCallsFromSpecificPackage() { - try { - throw new JUnitException("expected"); - } - catch (JUnitException e) { - pruneStackTrace(e, element -> !element.startsWith("org.junit.")); - assertThat(e.getStackTrace()) // - .noneMatch(element -> element.toString().contains("org.junit.")); - } + @ParameterizedTest + @ValueSource(strings = { "org.junit.", "jdk.internal.reflect.", "sun.reflect." }) + void pruneStackTraceOfCallsFromSpecificPackage(String shouldBePruned) { + JUnitException exception = new JUnitException("expected"); + + pruneStackTrace(exception, List.of()); + + assertThat(exception.getStackTrace()) // + .noneMatch(element -> element.toString().contains(shouldBePruned)); } @Test void pruneStackTraceOfAllLauncherCalls() { - try { - throw new JUnitException("expected"); - } - catch (JUnitException e) { - pruneStackTrace(e, element -> true); - assertThat(e.getStackTrace()) // - .noneMatch(element -> element.toString().contains("org.junit.platform.launcher.")); - } + JUnitException exception = new JUnitException("expected"); + + pruneStackTrace(exception, List.of()); + + assertThat(exception.getStackTrace()) // + .noneMatch(element -> element.toString().contains("org.junit.platform.launcher.")); } @Test void pruneStackTraceOfEverythingPriorToFirstLauncherCall() { - try { - throw new JUnitException("expected"); - } - catch (JUnitException e) { - StackTraceElement[] stackTrace = e.getStackTrace(); - stackTrace[stackTrace.length - 1] = new StackTraceElement("org.example.Class", "method", "file", 123); - e.setStackTrace(stackTrace); + JUnitException exception = new JUnitException("expected"); + StackTraceElement[] stackTrace = exception.getStackTrace(); + stackTrace[stackTrace.length - 1] = new StackTraceElement("org.example.Class", "method", "file", 123); + exception.setStackTrace(stackTrace); - pruneStackTrace(e, element -> true); - assertThat(e.getStackTrace()) // - .noneMatch(element -> element.toString().contains("org.example.Class.method(file:123)")); - } + pruneStackTrace(exception, List.of()); + + assertThat(exception.getStackTrace()) // + .noneMatch(element -> element.toString().contains("org.example.Class.method(file:123)")); } @Test diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java index abbc0f1e9f69..97d5614470d7 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java @@ -10,6 +10,7 @@ package org.junit.platform.engine.support.descriptor; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; @@ -34,15 +35,18 @@ class AbstractTestDescriptorTests { private EngineDescriptor engineDescriptor; + private GroupDescriptor group1; + private GroupDescriptor group11; + private LeafDescriptor leaf111; @BeforeEach void initTree() { engineDescriptor = new EngineDescriptor(UniqueId.forEngine("testEngine"), "testEngine"); - var group1 = new GroupDescriptor(UniqueId.root("group", "group1")); + group1 = new GroupDescriptor(UniqueId.root("group", "group1")); engineDescriptor.addChild(group1); var group2 = new GroupDescriptor(UniqueId.root("group", "group2")); engineDescriptor.addChild(group2); - var group11 = new GroupDescriptor(UniqueId.root("group", "group1-1")); + group11 = new GroupDescriptor(UniqueId.root("group", "group1-1")); group1.addChild(group11); group1.addChild(new LeafDescriptor(UniqueId.root("leaf", "leaf1-1"))); @@ -50,7 +54,8 @@ void initTree() { group2.addChild(new LeafDescriptor(UniqueId.root("leaf", "leaf2-1"))); - group11.addChild(new LeafDescriptor(UniqueId.root("leaf", "leaf11-1"))); + leaf111 = new LeafDescriptor(UniqueId.root("leaf", "leaf11-1")); + group11.addChild(leaf111); } @Test @@ -133,6 +138,27 @@ void pruneGroup() { assertFalse(visited.contains(UniqueId.root("group", "group1"))); } + @Test + void getAncestors() { + assertThat(getAncestorsUniqueIds(engineDescriptor)).isEmpty(); + + assertThat(getAncestorsUniqueIds(group1)).containsExactly( // + UniqueId.forEngine("testEngine")); + + assertThat(getAncestorsUniqueIds(group11)).containsExactly( // + UniqueId.root("group", "group1"), // + UniqueId.forEngine("testEngine")); + + assertThat(getAncestorsUniqueIds(leaf111)).containsExactly( // + UniqueId.root("group", "group1-1"), // + UniqueId.root("group", "group1"), // + UniqueId.forEngine("testEngine")); + } + + private List getAncestorsUniqueIds(TestDescriptor descriptor) { + return descriptor.getAncestors().stream().map(TestDescriptor::getUniqueId).toList(); + } + } class GroupDescriptor extends AbstractTestDescriptor { diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java index e769149edfff..d1432692d6d1 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java @@ -38,6 +38,7 @@ import org.junit.platform.fakes.TestEngineSpy; import org.junit.platform.launcher.InterceptedTestEngine; import org.junit.platform.launcher.InterceptorInjectedLauncherSessionListener; +import org.junit.platform.launcher.LauncherConstants; import org.junit.platform.launcher.LauncherDiscoveryRequest; import org.junit.platform.launcher.LauncherSessionListener; import org.junit.platform.launcher.TagFilter; @@ -298,7 +299,8 @@ public void execute(ExecutionRequest request) { .addTestEngines(engine) // .build(); var launcher = LauncherFactory.create(config); - var request = request().build(); + var request = request().configurationParameter(LauncherConstants.STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME, + "false").build(); AtomicReference result = new AtomicReference<>(); launcher.execute(request, new TestExecutionListener() { diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java index 1ae505f8ba54..4e4c14cabece 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java @@ -51,6 +51,7 @@ import org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestDescriptor; import org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestEngine; import org.junit.platform.fakes.TestDescriptorStub; +import org.junit.platform.launcher.LauncherConstants; import org.junit.platform.launcher.TestIdentifier; import org.junit.platform.launcher.TestPlan; import org.opentest4j.AssertionFailedError; @@ -429,7 +430,8 @@ private void executeTests(TestEngine engine, Clock clock) { var reportListener = new LegacyXmlReportGeneratingListener(tempDirectory.toString(), out, clock); var launcher = createLauncher(engine); launcher.registerTestExecutionListeners(reportListener); - launcher.execute(request().selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))).build()); + launcher.execute(request().configurationParameter(LauncherConstants.STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME, + "false").selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))).build()); } private Match readValidXmlFile(Path xmlFile) throws Exception { From 607e028efca30fdca1e1cb0739c58857183a0165 Mon Sep 17 00:00:00 2001 From: Juliette de Rancourt Date: Thu, 29 Jun 2023 20:07:40 +0200 Subject: [PATCH 02/10] Fix version in `@API` annotation for `TestDescriptor.getAncestors()` --- .../src/main/java/org/junit/platform/engine/TestDescriptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java index 7dfcecc644ca..16fafe5fda30 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java @@ -114,7 +114,7 @@ default String getLegacyReportingName() { * * @see #getParent() */ - @API(status = STABLE, since = "5.10") + @API(status = STABLE, since = "1.10") default Set getAncestors() { if (!getParent().isPresent()) { return Collections.emptySet(); From 1f36bc8a4a4c14f7b69ef1eaa61cb534f48f55ae Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 30 Jun 2023 11:50:07 +0200 Subject: [PATCH 03/10] Polishing --- .../release-notes-5.10.0-RC1.adoc | 1 + .../asciidoc/user-guide/running-tests.adoc | 11 +++--- .../asciidoc/user-guide/writing-tests.adoc | 10 +++--- .../platform/commons/util/ExceptionUtils.java | 35 ++++++++++--------- .../junit/platform/engine/TestDescriptor.java | 9 +++-- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc index 4524496a0b36..de5d8d10c2a7 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc @@ -38,6 +38,7 @@ JUnit repository on GitHub. <<../user-guide/index.adoc#stacktrace-pruning, User Guide>> for details. * New `getAncestors()` method in `TestDescriptor`. + [[release-notes-5.10.0-RC1-junit-jupiter]] === JUnit Jupiter diff --git a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc index 35915254fd95..3507f5892c6a 100644 --- a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc @@ -1132,14 +1132,15 @@ give it a try and provide feedback to the JUnit team so they can improve and eve <> this feature. [[stacktrace-pruning]] -=== Stack trace pruning +=== Stack Trace Pruning Since version 1.10, the JUnit Platform provides built-in support for pruning stack traces -produced by failing tests. This feature can be enabled or disabled via the -`junit.platform.stacktrace.pruning.enabled` _configuration parameter_. +produced by failing tests. This feature is enabled by default but can be disabled by +setting the `junit.platform.stacktrace.pruning.enabled` _configuration parameter_ to +`false`. -If enabled, all calls from the `org.junit`, `jdk.internal.reflect`, and `sun.reflect` -packages are removed from the stack trace, unless they are subsequent to the test itself +When enabled, all calls from the `org.junit`, `jdk.internal.reflect`, and `sun.reflect` +packages are removed from the stack trace, unless the calls occur after the test itself or any of its ancestors. For that reason, calls to `{Assertions}` or `{Assumptions}` will never be excluded. diff --git a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc index 97991db4fb86..e4dee9ca8339 100644 --- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc @@ -2302,11 +2302,11 @@ might conflict with the configured execution order. Thus, in both cases, test me such test classes are only executed concurrently if the `@Execution(CONCURRENT)` annotation is present on the test class or method. -When parallel execution is enabled and a default `{ClassOrderer}` (see -<> for details) is registered, top-level test -classes will initially be sorted accordingly and scheduled in that order. However, they -are not guaranteed to be started in exactly that order since the threads they are executed -on are not controlled directly by JUnit. +When parallel execution is enabled and a default `{ClassOrderer}` is registered (see +<> for details), top-level test classes will +initially be sorted accordingly and scheduled in that order. However, they are not +guaranteed to be started in exactly that order since the threads they are executed on are +not controlled directly by JUnit. All nodes of the test tree that are configured with the `CONCURRENT` execution mode will be executed fully in parallel according to the provided 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 7b108da8070f..ce698dbbdcd6 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 @@ -96,24 +96,25 @@ public static String readStackTrace(Throwable throwable) { /** * Prune the stack trace of the supplied {@link Throwable} by removing - * elements from the {@code org.junit}, {@code jdk.internal.reflect} and - * {@code sun.reflect} packages. If an element matching one of the supplied - * class names is encountered, all following elements will be kept regardless. + * {@linkplain StackTraceElement stack trace elements} from the {@code org.junit}, + * {@code jdk.internal.reflect}, and {@code sun.reflect} packages. If a + * {@code StackTraceElement} matching one of the supplied {@code classNames} + * is encountered, all subsequent elements in the stack trace will be retained. * - *

Additionally, all elements prior to and including the first - * JUnit Launcher call will be removed. + *

Additionally, all elements prior to and including the first JUnit Platform + * Launcher call will be removed. * - * @param throwable the {@code Throwable} whose stack trace should be - * pruned; never {@code null} - * @param testClassNames the test class names that should stop the pruning - * if encountered; never {@code null} + * @param throwable the {@code Throwable} whose stack trace should be pruned; + * never {@code null} + * @param classNames the class names that should stop the pruning if encountered; + * never {@code null} * - * @since 5.10 + * @since 1.10 */ - @API(status = INTERNAL, since = "5.10") - public static void pruneStackTrace(Throwable throwable, List testClassNames) { + @API(status = INTERNAL, since = "1.10") + public static void pruneStackTrace(Throwable throwable, List classNames) { Preconditions.notNull(throwable, "Throwable must not be null"); - Preconditions.notNull(testClassNames, "List of test class names must not be null"); + Preconditions.notNull(classNames, "List of class names must not be null"); List stackTrace = Arrays.asList(throwable.getStackTrace()); List prunedStackTrace = new ArrayList<>(); @@ -124,7 +125,7 @@ public static void pruneStackTrace(Throwable throwable, List testClassNa StackTraceElement element = stackTrace.get(i); String className = element.getClassName(); - if (testClassNames.contains(className)) { + if (classNames.contains(className)) { // Include all elements called by the test prunedStackTrace.addAll(stackTrace.subList(i, stackTrace.size())); break; @@ -142,16 +143,16 @@ else if (STACK_TRACE_ELEMENT_FILTER.test(className)) { } /** - * Find all causes and suppressed exceptions in the backtrace of the + * Find all causes and suppressed exceptions in the stack trace of the * supplied {@link Throwable}. * * @param rootThrowable the {@code Throwable} to explore; never {@code null} * @return an immutable list of all throwables found, including the supplied * one; never {@code null} * - * @since 5.10 + * @since 1.10 */ - @API(status = INTERNAL, since = "5.10") + @API(status = INTERNAL, since = "1.10") public static List findNestedThrowables(Throwable rootThrowable) { Preconditions.notNull(rootThrowable, "Throwable must not be null"); diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java index 16fafe5fda30..0f7f6f20ea49 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java @@ -109,8 +109,8 @@ default String getLegacyReportingName() { /** * Get the immutable set of all ancestors of this descriptor. * - *

An ancestors is the parent of this descriptor or the parent - * of one of its parents, recursively. + *

An ancestor is the parent of this descriptor or the parent of + * one of its parents, recursively. * * @see #getParent() */ @@ -122,7 +122,10 @@ default Set getAncestors() { TestDescriptor parent = getParent().get(); Set ancestors = new LinkedHashSet<>(); ancestors.add(parent); - ancestors.addAll(parent.getAncestors()); + // Need to recurse? + if (parent.getParent().isPresent()) { + ancestors.addAll(parent.getAncestors()); + } return Collections.unmodifiableSet(ancestors); } From 85141561529dd85ae245c088662c826152adcf45 Mon Sep 17 00:00:00 2001 From: JUnit Team Date: Sun, 2 Jul 2023 15:18:29 +0000 Subject: [PATCH 04/10] Bump Gradle Wrapper from 8.2-rc-2 to 8.2 --- gradle/wrapper/gradle-wrapper.jar | Bin 63282 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f03f475efe8cbb781d8a1f9dfcd7fbfc89bcef5a..033e24c4cdf41af1ab109bc7f253b2b887023340 100644 GIT binary patch delta 4535 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 Xi8D5$8u-zfBqfRg0GR*r-#`8rZG^#i delta 4433 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 Date: Sun, 2 Jul 2023 17:56:19 +0200 Subject: [PATCH 05/10] Set readonly attribute to false if `DosFileAttributeView` is supported Fixes #3352. --- .../release-notes-5.10.0-RC1.adoc | 2 ++ .../engine/extension/TempDirectory.java | 10 ++++++++ .../TempDirectoryPerContextTests.java | 24 ++++++++++++------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc index de5d8d10c2a7..41467f50ac58 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc @@ -64,6 +64,8 @@ JUnit repository on GitHub. the documentation for extension registration via `@ExtendWith` on fields. * The scope of applicability for `TestWatcher` implementations is now more extensively documented in the User Guide and Javadoc. +* `@TempDir` now successfully cleans up files and directories on Windows that are set to + read-only. [[release-notes-5.10.0-RC1-junit-vintage]] diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java index d8dbdd3776a3..0c41baa4724b 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java @@ -33,6 +33,7 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.DosFileAttributeView; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -390,6 +391,15 @@ private static void tryToResetPermissions(Path path) { if (Files.isDirectory(path)) { file.setExecutable(true); } + DosFileAttributeView dos = Files.getFileAttributeView(path, DosFileAttributeView.class); + if (dos != null) { + try { + dos.setReadOnly(false); + } + catch (IOException ignore) { + // nothing we can do + } + } } private IOException createIOExceptionWithAttachedFailures(SortedMap failures) { diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java index 84addd7dd905..4515d33b27fb 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java +++ b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.DosFileAttributeView; import java.util.Deque; import java.util.LinkedList; import java.util.function.Supplier; @@ -47,8 +48,6 @@ import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterResolutionException; @@ -126,7 +125,6 @@ void nonMintPermissionsDoNotCauseFailure() { } @Test - @DisabledOnOs(OS.WINDOWS) @DisplayName("is capable of removing a read-only file in a read-only dir") void readOnlyFileInReadOnlyDirDoesNotCauseFailure() { executeTestsForClass(ReadOnlyFileInReadOnlyDirDoesNotCauseFailureTestCase.class).testEvents()// @@ -134,7 +132,6 @@ void readOnlyFileInReadOnlyDirDoesNotCauseFailure() { } @Test - @DisabledOnOs(OS.WINDOWS) @DisplayName("is capable of removing a read-only file in a dir in a read-only dir") void readOnlyFileInNestedReadOnlyDirDoesNotCauseFailure() { executeTestsForClass(ReadOnlyFileInDirInReadOnlyDirDoesNotCauseFailureTestCase.class).testEvents()// @@ -942,8 +939,8 @@ static class ReadOnlyFileInReadOnlyDirDoesNotCauseFailureTestCase { void createReadOnlyFileInReadOnlyDir(@TempDir File tempDir) throws IOException { File file = tempDir.toPath().resolve("file").toFile(); assumeTrue(file.createNewFile()); - assumeTrue(tempDir.setReadOnly()); - assumeTrue(file.setReadOnly()); + assumeTrue(makeReadOnly(tempDir)); + assumeTrue(makeReadOnly(file)); } } @@ -956,13 +953,22 @@ void createReadOnlyFileInReadOnlyDir(@TempDir File tempDir) throws IOException { File file = tempDir.toPath().resolve("dir").resolve("file").toFile(); assumeTrue(file.getParentFile().mkdirs()); assumeTrue(file.createNewFile()); - assumeTrue(tempDir.setReadOnly()); - assumeTrue(file.getParentFile().setReadOnly()); - assumeTrue(file.setReadOnly()); + assumeTrue(makeReadOnly(tempDir)); + assumeTrue(makeReadOnly(file.getParentFile())); + assumeTrue(makeReadOnly(file)); } } + private static boolean makeReadOnly(File file) throws IOException { + var dos = Files.getFileAttributeView(file.toPath(), DosFileAttributeView.class); + if (dos != null) { + dos.setReadOnly(true); + return true; + } + return file.setReadOnly(); + } + // https://github.com/junit-team/junit5/issues/2609 @SuppressWarnings("ResultOfMethodCallIgnored") static class NonMintPermissionContentInTempDirectoryDoesNotCauseFailureTestCase { From f82d93faea69ff1617399d1881cd90ee55403bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Bodn=C3=A1r?= Date: Sat, 10 Jun 2023 09:33:11 +0200 Subject: [PATCH 06/10] Document replacement for @Test(expected=...) in JUnit 4 migration tips Closes #3348 --- .../src/docs/asciidoc/user-guide/migration-from-junit4.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc b/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc index 09d351fbfec6..1bea652ce7b1 100644 --- a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc +++ b/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc @@ -63,6 +63,10 @@ tests to JUnit Jupiter. * `@Rule` and `@ClassRule` no longer exist; superseded by `@ExtendWith` and `@RegisterExtension`. - See also <>. +* `@Test(expected = ...)` and the `ExpectedException` rule no longer exist; use + `Assertions.assertThrows(...)` instead. + See <> if you still do need to use + `ExpectedException`. * Assertions and assumptions in JUnit Jupiter accept the failure message as their last argument instead of the first one. - See <> for details. From 9d1f6e110d9dd94c8b63a33e06810a4bc13e92c1 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 3 Jul 2023 16:04:26 +0200 Subject: [PATCH 07/10] Polish formatting See #3348 --- .../src/docs/asciidoc/user-guide/migration-from-junit4.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc b/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc index 1bea652ce7b1..98e0b056af93 100644 --- a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc +++ b/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc @@ -65,8 +65,8 @@ tests to JUnit Jupiter. - See also <>. * `@Test(expected = ...)` and the `ExpectedException` rule no longer exist; use `Assertions.assertThrows(...)` instead. - See <> if you still do need to use - `ExpectedException`. + - See <> if you still need to use + `ExpectedException`. * Assertions and assumptions in JUnit Jupiter accept the failure message as their last argument instead of the first one. - See <> for details. From 791446460919fb034c6d86d5004d87cd97a4afd0 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 3 Jul 2023 16:19:34 +0200 Subject: [PATCH 08/10] Prepare release notes for 5.10 RC1 --- .../release-notes-5.10.0-RC1.adoc | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc index 41467f50ac58..a27df6037dd6 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-RC1.adoc @@ -1,9 +1,17 @@ [[release-notes-5.10.0-RC1]] == 5.10.0-RC1 -*Date of Release:* ❓ +*Date of Release:* July ❓, 2023 -*Scope:* ❓ +*Scope:* + +* New `@SelectMethod` support in test `@Suite` classes. +* Various enhancements for discovery selectors for classes and methods, including + additional support for custom ClassLoader arrangements. +* Improved `@TempDir` support for cleaning up files and directories on Windows. +* Revised stack trace pruning support. +* Various documentation improvements. +* Minor changes and enhancements since 5.10 M1. For a complete list of all _closed_ issues and pull requests for this release, consult the link:{junit5-repo}+/milestone/69?closed=1+[5.10.0-RC1] milestone page in the @@ -13,10 +21,6 @@ JUnit repository on GitHub. [[release-notes-5.10.0-RC1-junit-platform]] === JUnit Platform -==== Bug Fixes - -* ❓ - ==== Deprecations and Breaking Changes * The `getMethodParameterTypes()` methods in `MethodSelector` and `NestedMethodSelector` @@ -42,43 +46,25 @@ JUnit repository on GitHub. [[release-notes-5.10.0-RC1-junit-jupiter]] === JUnit Jupiter -==== Bug Fixes - -* ❓ - -==== Deprecations and Breaking Changes - -* ❓ - ==== New Features and Improvements * `@TempDir` can now be used as a meta-annotation in order to create custom _composed annotations_. See the `@JimfsTempDir` example in the <<../user-guide/index.adoc#writing-tests-built-in-extensions-TempDirectory, User Guide>> for details. -* Lifecycle and thread-safety semantics are now documented for the `TempDirFactory` SPI. +* `@TempDir` now successfully cleans up files and directories on Windows that are set to + read-only. * New `reason` attribute in `@Execution` which can be used to document the reason for using the selected execution mode. * The <<../user-guide/index.adoc#extensions-RandomNumberExtension, User Guide>> now includes an example implementation of the `RandomNumberExtension` in order to improve the documentation for extension registration via `@ExtendWith` on fields. +* Lifecycle and thread-safety semantics are now documented for the `TempDirFactory` SPI. * The scope of applicability for `TestWatcher` implementations is now more extensively documented in the User Guide and Javadoc. -* `@TempDir` now successfully cleans up files and directories on Windows that are set to - read-only. [[release-notes-5.10.0-RC1-junit-vintage]] === JUnit Vintage -==== Bug Fixes - -* ❓ - -==== Deprecations and Breaking Changes - -* ❓ - -==== New Features and Improvements - -* ❓ +No changes. From b72d1f64f37c5ed7fe0dc38785ab45bbad8cb747 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 3 Jul 2023 20:02:50 +0200 Subject: [PATCH 09/10] Fix copy-n-paste error in JRE enum Javadoc --- .../src/main/java/org/junit/jupiter/api/condition/JRE.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java index b42bc90e90c3..50679310e9fa 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java @@ -139,7 +139,7 @@ public enum JRE { JAVA_20, /** - * Java 20. + * Java 21. * * @since 5.9.2 */ From ed8038cfe9ebaad2002a4af8cf4c64de3d7c0dbb Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 3 Jul 2023 20:03:24 +0200 Subject: [PATCH 10/10] Polish Javadoc for JRE enum --- .../java/org/junit/jupiter/api/condition/JRE.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java index 50679310e9fa..fc6c980271b0 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/JRE.java @@ -37,6 +37,13 @@ * @see #JAVA_12 * @see #JAVA_13 * @see #JAVA_14 + * @see #JAVA_15 + * @see #JAVA_16 + * @see #JAVA_17 + * @see #JAVA_18 + * @see #JAVA_19 + * @see #JAVA_20 + * @see #JAVA_21 * @see #OTHER * @see EnabledOnJre * @see DisabledOnJre @@ -221,14 +228,16 @@ private static JRE determineCurrentVersion() { /** * @return {@code true} if this {@code JRE} is known to be the - * Java Runtime Environment version for the currently executing JVM + * Java Runtime Environment version for the currently executing JVM or if + * the version is {@link #OTHER} */ public boolean isCurrentVersion() { return this == CURRENT_VERSION; } /** - * @return the {@link JRE} for the currently executing JVM + * @return the {@link JRE} for the currently executing JVM, potentially + * {@link #OTHER} * * @since 5.7 */