diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc index 50a7b18e32c3..be55dad70ca9 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc @@ -31,6 +31,8 @@ repository on GitHub. inside Spring Boot executable jars for Spring Boot 3.2 and later. * The `junit-platform-suite-engine` now includes configuration provided via `@ConfigurationParameter` when selecting tests by `UniqueId`. +* In order to support using `@EnabledInNativeImage` on test classes, + `UniqueIdTrackingListener` now tracks descendants of skipped test containers. [[release-notes-5.11.0-M1-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java index 80d06ad9c27e..72f7e3228577 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java @@ -130,6 +130,7 @@ public class UniqueIdTrackingListener implements TestExecutionListener { private final List uniqueIds = new ArrayList<>(); private boolean enabled; + private TestPlan testPlan; public UniqueIdTrackingListener() { // to avoid missing-explicit-ctor warning @@ -138,22 +139,38 @@ public UniqueIdTrackingListener() { @Override public void testPlanExecutionStarted(TestPlan testPlan) { this.enabled = testPlan.getConfigurationParameters().getBoolean(LISTENER_ENABLED_PROPERTY_NAME).orElse(false); + this.testPlan = testPlan; } @Override public void executionSkipped(TestIdentifier testIdentifier, String reason) { - trackTestUid(testIdentifier); + if (this.enabled) { + // When a container is skipped, there are no events for its children. + // Therefore, in order to track them, we need to traverse the subtree. + trackTestUidRecursively(testIdentifier); + } } @Override public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { - trackTestUid(testIdentifier); + if (this.enabled) { + trackTestUid(testIdentifier); + } + } + + private void trackTestUidRecursively(TestIdentifier testIdentifier) { + boolean tracked = trackTestUid(testIdentifier); + if (!tracked) { + this.testPlan.getChildren(testIdentifier).forEach(this::trackTestUidRecursively); + } } - private void trackTestUid(TestIdentifier testIdentifier) { - if (this.enabled && testIdentifier.isTest()) { + private boolean trackTestUid(TestIdentifier testIdentifier) { + if (testIdentifier.isTest()) { this.uniqueIds.add(testIdentifier.getUniqueId()); + return true; } + return false; } @Override @@ -178,6 +195,7 @@ public void testPlanExecutionFinished(TestPlan testPlan) { logger.error(ex, () -> "Failed to write unique IDs to output file " + outputFile.toAbsolutePath()); } } + this.testPlan = null; } private Path createOutputFile(ConfigurationParameters configurationParameters) { diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java index 3149fd21e5da..9d56bddc5761 100644 --- a/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java @@ -46,6 +46,7 @@ import org.assertj.core.api.Condition; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import org.junit.platform.engine.TestDescriptor; @@ -54,6 +55,7 @@ import org.junit.platform.launcher.LauncherDiscoveryRequest; import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestIdentifier; +import org.junit.platform.launcher.TestPlan; import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.testkit.engine.EngineTestKit; import org.junit.platform.testkit.engine.Event; @@ -79,9 +81,10 @@ class UniqueIdTrackingListenerIntegrationTests { private static final String testD = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase3]/[method:testD()]"; private static final String testE = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase4]/[method:testE()]"; private static final String testF = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase4]/[method:testF()]"; + private static final String testG = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$DisabledTestCase]/[nested-class:Inner]/[method:testG()]"; private static final String[] expectedUniqueIds = { passingTest, skippedTest, abortedTest, failingTest, - dynamicTest1, dynamicTest2, testA, testB }; + dynamicTest1, dynamicTest2, testA, testB, testG }; private static final String[] expectedConcurrentUniqueIds = { testA, testB, testC, testD, testE, testF }; @@ -225,11 +228,21 @@ private static List executeTests(Map configurationParame .build(); LauncherFactory.create().execute(request, new TestExecutionListener() { + private TestPlan testPlan; + + @Override + public void testPlanExecutionStarted(TestPlan testPlan) { + this.testPlan = testPlan; + } + @Override public void executionSkipped(TestIdentifier testIdentifier, String reason) { if (testIdentifier.isTest()) { uniqueIds.add(testIdentifier.getUniqueId()); } + else { + this.testPlan.getChildren(testIdentifier).forEach(child -> executionSkipped(child, reason)); + } } @Override @@ -243,7 +256,8 @@ public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult } private static ClassSelector[] selectClasses() { - return new ClassSelector[] { selectClass(TestCase1.class), selectClass(TestCase2.class) }; + return new ClassSelector[] { selectClass(TestCase1.class), selectClass(TestCase2.class), + selectClass(DisabledTestCase.class) }; } private static Stream findFiles(String dir, String prefix) throws IOException { @@ -340,4 +354,16 @@ void testF() { } } + @Disabled + static class DisabledTestCase { + + @Nested + class Inner { + + @Test + void testG() { + } + } + } + } diff --git a/platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts b/platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts index 26f76f8b874a..c83d5d840ada 100644 --- a/platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts +++ b/platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts @@ -31,8 +31,6 @@ tasks.test { graalvmNative { binaries { named("test") { - buildArgs.add("--initialize-at-build-time=org.junit.platform.launcher.core.LauncherConfig") - buildArgs.add("--initialize-at-build-time=org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter") buildArgs.add("-H:+ReportExceptionStackTraces") } } diff --git a/platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts b/platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts index c928064e495a..1399f18e3201 100644 --- a/platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts +++ b/platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts @@ -1,6 +1,6 @@ pluginManagement { plugins { - id("org.graalvm.buildtools.native") version "0.9.13" + id("org.graalvm.buildtools.native") version "0.10.1" } repositories { mavenCentral() diff --git a/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/ClassLevelAnnotationTests.java b/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/ClassLevelAnnotationTests.java new file mode 100644 index 000000000000..582b46f7cde1 --- /dev/null +++ b/platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/ClassLevelAnnotationTests.java @@ -0,0 +1,25 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package com.example.project; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledInNativeImage; + +@EnabledInNativeImage +class ClassLevelAnnotationTests { + @Nested + class Inner { + @Test + void test() { + } + } +} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java index 191b64626511..c0fdcb8bc2cc 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java @@ -10,11 +10,9 @@ package platform.tooling.support.tests; +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.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeFalse; -import static platform.tooling.support.tests.XmlAssertions.verifyContainsExpectedStartedOpenTestReport; import java.nio.file.Paths; import java.time.Duration; @@ -22,6 +20,7 @@ import de.sormuras.bartholdy.tool.GradleWrapper; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.junit.jupiter.api.extension.DisabledOnOpenJ9; import platform.tooling.support.MavenRepo; @@ -31,6 +30,7 @@ * @since 1.9.1 */ @DisabledOnOpenJ9 +@EnabledIfEnvironmentVariable(named = "GRAALVM_HOME", matches = ".+") class GraalVmStarterTests { @Test @@ -40,7 +40,6 @@ void runsTestsInNativeImage() { .setProject("graalvm-starter") // .addArguments("-Dmaven.repo=" + MavenRepo.dir()) // .addArguments("javaToolchains", "nativeTest", "--no-daemon", "--stacktrace") // - .addArguments("-Porg.gradle.java.installations.fromEnv=GRAALVM_HOME") // .setTimeout(Duration.ofMinutes(10)) // .build(); @@ -48,15 +47,11 @@ void runsTestsInNativeImage() { assertFalse(result.isTimedOut(), () -> "tool timed out: " + result); - assumeFalse( - result.getOutputLines("err").stream().anyMatch( - line -> line.contains("No locally installed toolchains match")), - "Abort test if GraalVM is not installed"); - assertEquals(0, result.getExitCode()); - assertTrue(result.getOutputLines("out").stream().anyMatch(line -> line.contains("BUILD SUCCESSFUL"))); - - var testResultsDir = Request.WORKSPACE.resolve(request.getWorkspace()).resolve("build/test-results/test"); - verifyContainsExpectedStartedOpenTestReport(testResultsDir); + assertThat(result.getOutputLines("out")) // + .anyMatch(line -> line.contains("CalculatorTests > 1 + 1 = 2 SUCCESSFUL")) // + .anyMatch(line -> line.contains("CalculatorTests > 1 + 100 = 101 SUCCESSFUL")) // + .anyMatch(line -> line.contains("ClassLevelAnnotationTests$Inner > test() SUCCESSFUL")) // + .anyMatch(line -> line.contains("BUILD SUCCESSFUL")); } }