Skip to content

Commit

Permalink
Merge branch 'main' into origin/issues/3107-method-filters
Browse files Browse the repository at this point in the history
  • Loading branch information
yhkuo41 committed Jul 3, 2023
2 parents c58bebd + ed8038c commit 3fdbe6d
Show file tree
Hide file tree
Showing 20 changed files with 302 additions and 217 deletions.
@@ -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
Expand All @@ -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`
Expand All @@ -33,46 +37,34 @@ 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

==== 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.


[[release-notes-5.10.0-RC1-junit-vintage]]
=== JUnit Vintage

==== Bug Fixes

* ❓

==== Deprecations and Breaking Changes

* ❓

==== New Features and Improvements

* ❓
No changes.
Expand Up @@ -63,6 +63,10 @@ tests to JUnit Jupiter.
* `@Rule` and `@ClassRule` no longer exist; superseded by `@ExtendWith` and
`@RegisterExtension`.
- See also <<migrating-from-junit4-rule-support>>.
* `@Test(expected = ...)` and the `ExpectedException` rule no longer exist; use
`Assertions.assertThrows(...)` instead.
- See <<migrating-from-junit4-rule-support>> 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 <<migrating-from-junit4-failure-message-arguments>> for details.
Expand Down
24 changes: 10 additions & 14 deletions documentation/src/docs/asciidoc/user-guide/running-tests.adoc
Expand Up @@ -1132,24 +1132,20 @@ give it a try and provide feedback to the JUnit team so they can improve and eve
<<api-evolution, promote>> 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`.

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.
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.

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.
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
Expand Down
10 changes: 5 additions & 5 deletions documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
Expand Up @@ -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
<<writing-tests-test-execution-order-classes>> 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
<<writing-tests-test-execution-order-classes>> 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
Expand Down
Expand Up @@ -41,7 +41,6 @@ tasks.withType<Test>().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) {
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=0906569bf96e8ebefbc1aa56318c74aeafd9710455b2817c70d709c5d77785c4
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-rc-2-bin.zip
distributionSha256Sum=38f66cd6eef217b4c35855bb11ea4e9fbc53594ccccb5fb82dfd317ef8c2c5a3
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -139,7 +146,7 @@ public enum JRE {
JAVA_20,

/**
* Java 20.
* Java 21.
*
* @since 5.9.2
*/
Expand Down Expand Up @@ -221,14 +228,16 @@ private static JRE determineCurrentVersion() {

/**
* @return {@code true} if <em>this</em> {@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
*/
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Path, IOException> failures) {
Expand Down
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -126,15 +125,13 @@ 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()//
.assertStatistics(stats -> stats.started(1).succeeded(1));
}

@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()//
Expand Down Expand Up @@ -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));
}

}
Expand All @@ -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 {
Expand Down
Expand Up @@ -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<String> STACK_TRACE_ELEMENT_FILTER = ClassNamePatternFilterUtils //
.excludeMatchingClassNames("org.junit.*,jdk.internal.reflect.*,sun.reflect.*");

private ExceptionUtils() {
/* no-op */
}
Expand Down Expand Up @@ -92,38 +95,45 @@ 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
* {@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.
*
* <p>Additionally, all elements prior to and including the first
* JUnit Launcher call will be removed.
* <p>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 stackTraceElementFilter the {@code Predicate} used to filter
* elements of the stack trace; 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, Predicate<String> stackTraceElementFilter) {
@API(status = INTERNAL, since = "1.10")
public static void pruneStackTrace(Throwable throwable, List<String> classNames) {
Preconditions.notNull(throwable, "Throwable must not be null");
Preconditions.notNull(stackTraceElementFilter, "Predicate must not be null");
Preconditions.notNull(classNames, "List of class names must not be null");

List<StackTraceElement> stackTrace = Arrays.asList(throwable.getStackTrace());
List<StackTraceElement> 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 (classNames.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);
}
}
Expand All @@ -133,16 +143,16 @@ else if (stackTraceElementFilter.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<Throwable> findNestedThrowables(Throwable rootThrowable) {
Preconditions.notNull(rootThrowable, "Throwable must not be null");

Expand Down

0 comments on commit 3fdbe6d

Please sign in to comment.