diff --git a/README.md b/README.md index 5b61d030dd0a..f3a477103dc8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This repository is the home of _JUnit 5_. ## Latest Releases -- General Availability (GA): [JUnit 5.12.1](https://github.com/junit-team/junit5/releases/tag/r5.12.1) (March 14, 2025) +- General Availability (GA): [JUnit 5.12.2](https://github.com/junit-team/junit5/releases/tag/r5.12.2) (April 11, 2025) - Preview (Milestone/Release Candidate): N/A ## Documentation diff --git a/documentation/src/docs/asciidoc/release-notes/index.adoc b/documentation/src/docs/asciidoc/release-notes/index.adoc index 12c1eb68b7d8..e58d49ed8971 100644 --- a/documentation/src/docs/asciidoc/release-notes/index.adoc +++ b/documentation/src/docs/asciidoc/release-notes/index.adoc @@ -17,6 +17,8 @@ authors as well as build tool and IDE vendors. include::{includedir}/link-attributes.adoc[] +include::{basedir}/release-notes-5.12.2.adoc[] + include::{basedir}/release-notes-5.12.1.adoc[] include::{basedir}/release-notes-5.12.0.adoc[] diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.2.adoc new file mode 100644 index 000000000000..224078ba645e --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.2.adoc @@ -0,0 +1,33 @@ +[[release-notes-5.12.2]] +== 5.12.2 + +*Date of Release:* April 11, 2025 + +*Scope:* Bug fixes and enhancements since 5.12.1 + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/95?closed=1+[5.12.2] milestone page in the JUnit repository +on GitHub. + + +[[release-notes-5.12.2-junit-platform]] +=== JUnit Platform + +No changes. + + +[[release-notes-5.12.2-junit-jupiter]] +=== JUnit Jupiter + +[[release-notes-5.12.2-junit-jupiter-bug-fixes]] +==== Bug Fixes + +* Fix handling of `CleanupMode.ON_SUCCESS` with `@TempDir` that caused no temporary + directories (using that mode) to be deleted after the first failure even if the + corresponding tests passed. + + +[[release-notes-5.12.2-junit-vintage]] +=== JUnit Vintage + +No changes. diff --git a/gradle.properties b/gradle.properties index 493b1f3d62e5..51fe52ab05d9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,13 +1,13 @@ group = org.junit -version = 5.12.1 +version = 5.12.2 jupiterGroup = org.junit.jupiter platformGroup = org.junit.platform -platformVersion = 1.12.1 +platformVersion = 1.12.2 vintageGroup = org.junit.vintage -vintageVersion = 5.12.1 +vintageVersion = 5.12.2 # We need more metaspace due to apparent memory leak in Asciidoctor/JRuby # The exports are needed due to https://github.com/diffplug/spotless/issues/834 diff --git a/gradle/plugins/build-parameters/build.gradle.kts b/gradle/plugins/build-parameters/build.gradle.kts index 5d647a81a7de..8a1276886d14 100644 --- a/gradle/plugins/build-parameters/build.gradle.kts +++ b/gradle/plugins/build-parameters/build.gradle.kts @@ -65,6 +65,10 @@ buildParameters { } group("testing") { description = "Testing related parameters" + bool("dryRun") { + description = "Enables dry run mode for tests" + defaultValue = false + } bool("enableJaCoCo") { description = "Enables JaCoCo test coverage reporting" defaultValue = true 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 5ec321f5f5d4..2985c59bf1f9 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 @@ -113,6 +113,7 @@ tasks.withType().configureEach { "-XX:FlightRecorderOptions=stackdepth=1024" ) } + systemProperty("junit.platform.execution.dryRun.enabled", buildParameters.testing.dryRun) // Track OS as input so that tests are executed on all configured operating systems on CI trackOperationSystemAsInput() 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 5349499d4f09..7d00f097e345 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 @@ -129,12 +129,17 @@ public void beforeEach(ExtensionContext context) { } private static void installFailureTracker(ExtensionContext context) { - context.getStore(NAMESPACE).put(FAILURE_TRACKER, (CloseableResource) () -> context.getParent() // - .ifPresent(parentContext -> { - if (selfOrChildFailed(context)) { - parentContext.getStore(NAMESPACE).put(CHILD_FAILED, true); - } - })); + context.getParent() // + .filter(parentContext -> !context.getRoot().equals(parentContext)) // + .ifPresent(parentContext -> installFailureTracker(context, parentContext)); + } + + private static void installFailureTracker(ExtensionContext context, ExtensionContext parentContext) { + context.getStore(NAMESPACE).put(FAILURE_TRACKER, (CloseableResource) () -> { + if (selfOrChildFailed(context)) { + getContextSpecificStore(parentContext).put(CHILD_FAILED, true); + } + }); } private void injectStaticFields(ExtensionContext context, Class testClass) { @@ -286,7 +291,11 @@ static CloseablePath createTempDir(TempDirFactory factory, CleanupMode cleanupMo private static boolean selfOrChildFailed(ExtensionContext context) { return context.getExecutionException().isPresent() // - || context.getStore(NAMESPACE).getOrDefault(CHILD_FAILED, Boolean.class, false); + || getContextSpecificStore(context).getOrDefault(CHILD_FAILED, Boolean.class, false); + } + + private static ExtensionContext.Store getContextSpecificStore(ExtensionContext context) { + return context.getStore(NAMESPACE.append(context)); } static class CloseablePath implements CloseableResource { diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java index d6d161db3381..b8d0b90468aa 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java @@ -27,7 +27,7 @@ import java.util.logging.Level; import java.util.logging.LogRecord; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; @@ -60,6 +60,7 @@ class TempDirFieldTests { private static Path alwaysFieldDir; private static Path onSuccessFailingFieldDir; private static Path onSuccessPassingFieldDir; + private static Path onSuccessPassingParameterDir; /** * Ensure the cleanup mode defaults to ALWAYS for fields. @@ -152,6 +153,14 @@ void cleanupModeOnSuccessFailingField() { assertThat(onSuccessFailingFieldDir).exists(); } + @Test + void cleanupModeOnSuccessFailingThenPassingField() { + executeTests(selectClass(OnSuccessFailingFieldCase.class), selectClass(OnSuccessPassingFieldCase.class)); + + assertThat(onSuccessFailingFieldDir).exists(); + assertThat(onSuccessPassingFieldDir).doesNotExist(); + } + /** * Ensure that ON_SUCCESS cleanup modes are obeyed for static fields when tests are failing. *

@@ -174,21 +183,20 @@ void cleanupModeOnSuccessFailingStaticField() { */ @Test void cleanupModeOnSuccessFailingStaticFieldWithNesting() { - LauncherDiscoveryRequest request = request()// - .selectors(selectClass(OnSuccessFailingStaticFieldWithNestingCase.class))// - .build(); - executeTests(request); + executeTestsForClass(OnSuccessFailingStaticFieldWithNestingCase.class); assertThat(onSuccessFailingFieldDir).exists(); + assertThat(onSuccessPassingParameterDir).doesNotExist(); } - @AfterAll - static void afterAll() throws IOException { + @AfterEach + void deleteTempDirs() throws IOException { deleteIfNotNullAndExists(defaultFieldDir); deleteIfNotNullAndExists(neverFieldDir); deleteIfNotNullAndExists(alwaysFieldDir); deleteIfNotNullAndExists(onSuccessFailingFieldDir); deleteIfNotNullAndExists(onSuccessPassingFieldDir); + deleteIfNotNullAndExists(onSuccessPassingParameterDir); } static void deleteIfNotNullAndExists(Path dir) throws IOException { @@ -286,13 +294,21 @@ static class OnSuccessFailingStaticFieldWithNestingCase { static Path onSuccessFailingFieldDir; @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class NestedTestCase { @Test - void test() { + @Order(1) + void failingTest() { TempDirFieldTests.onSuccessFailingFieldDir = onSuccessFailingFieldDir; fail(); } + + @Test + @Order(2) + void passingTest(@TempDir(cleanup = ON_SUCCESS) Path tempDir) { + TempDirFieldTests.onSuccessPassingParameterDir = tempDir; + } } } @@ -400,8 +416,16 @@ void cleanupModeOnSuccessFailingParameter() { assertThat(onSuccessFailingParameterDir).exists(); } - @AfterAll - static void afterAll() throws IOException { + @Test + void cleanupModeOnSuccessFailingThenPassingParameter() { + executeTestsForClass(OnSuccessFailingThenPassingParameterCase.class); + + assertThat(onSuccessFailingParameterDir).exists(); + assertThat(onSuccessPassingParameterDir).doesNotExist(); + } + + @AfterEach + void deleteTempDirs() throws IOException { TempDirFieldTests.deleteIfNotNullAndExists(defaultParameterDir); TempDirFieldTests.deleteIfNotNullAndExists(neverParameterDir); TempDirFieldTests.deleteIfNotNullAndExists(alwaysParameterDir); @@ -457,6 +481,24 @@ void testOnSuccessFailingParameter(@TempDir(cleanup = ON_SUCCESS) Path onSuccess } } + @SuppressWarnings({ "JUnitMalformedDeclaration", "NewClassNamingConvention" }) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + static class OnSuccessFailingThenPassingParameterCase { + + @Test + @Order(1) + void testOnSuccessFailingParameter(@TempDir(cleanup = ON_SUCCESS) Path onSuccessFailingParameterDir) { + TempDirParameterTests.onSuccessFailingParameterDir = onSuccessFailingParameterDir; + fail(); + } + + @Test + @Order(2) + void testOnSuccessPassingParameter(@TempDir(cleanup = ON_SUCCESS) Path onSuccessPassingParameterDir) { + TempDirParameterTests.onSuccessPassingParameterDir = onSuccessPassingParameterDir; + } + } + } @Nested