diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0-RC1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0-RC1.adoc index bceaa8c724c6..c37bdeac7ee1 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0-RC1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0-RC1.adoc @@ -90,4 +90,7 @@ repository on GitHub. [[release-notes-6.0.0-RC1-junit-vintage-new-features-and-improvements]] ==== New Features and Improvements +* Improved validation of additional classpath roots in the Console Launcher. +Now logs and skips only non-existent entries, ensuring more consistent behavior. + * ❓ diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java index 67a74a9179b7..ba3951f783a8 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java @@ -24,7 +24,9 @@ import static org.junit.platform.launcher.TagFilter.includeTags; import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; @@ -32,6 +34,8 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; import org.junit.platform.commons.util.ModuleUtils; import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ReflectionUtils; @@ -49,6 +53,8 @@ */ class DiscoveryRequestCreator { + private static final Logger logger = LoggerFactory.getLogger(DiscoveryRequestCreator.class); + static LauncherDiscoveryRequestBuilder toDiscoveryRequestBuilder(TestDiscoveryOptions options) { LauncherDiscoveryRequestBuilder requestBuilder = request(); List selectors = createDiscoverySelectors(options); @@ -77,7 +83,7 @@ private static List createDiscoverySelectors(TestDi } private static List createClasspathRootSelectors(TestDiscoveryOptions options) { - Set classpathRoots = determineClasspathRoots(options); + Set classpathRoots = validateAndLogInvalidRoots(determineClasspathRoots(options)); return selectClasspathRoots(classpathRoots); } @@ -86,12 +92,30 @@ private static Set determineClasspathRoots(TestDiscoveryOptions options) { () -> "No classpath entries selected"); if (selectedClasspathEntries.isEmpty()) { Set rootDirs = new LinkedHashSet<>(ReflectionUtils.getAllClasspathRootDirectories()); - rootDirs.addAll(options.getExistingAdditionalClasspathEntries()); + rootDirs.addAll(options.getAdditionalClasspathEntries()); return rootDirs; } return new LinkedHashSet<>(selectedClasspathEntries); } + private static Set validateAndLogInvalidRoots(Set roots) { + LinkedHashSet valid = new LinkedHashSet<>(); + HashSet seen = new HashSet<>(); + + for (Path root : roots) { + if (!seen.add(root)) { + continue; + } + if (Files.exists(root)) { + valid.add(root); + } else { + logger.warn(() -> "Ignoring non-existing classpath root: %s".formatted(root)); + } + } + + return valid; + } + private static void addFilters(LauncherDiscoveryRequestBuilder requestBuilder, TestDiscoveryOptions options, List selectors) { requestBuilder.filters(includedClassNamePatterns(options, selectors)); diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java index fdef039fe467..5c6f9e35f317 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java @@ -28,13 +28,17 @@ import java.io.File; import java.net.URI; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.logging.LogRecord; import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.fixtures.TrackLogRecords; import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.logging.LogRecordListener; import org.junit.platform.console.options.TestDiscoveryOptions; import org.junit.platform.engine.Filter; import org.junit.platform.engine.UniqueId; @@ -372,6 +376,34 @@ void convertsConfigurationParametersResources() { assertThat(configurationParameters.get("com.example.prop.second")).contains("second value"); } + @Test + void logsInvalidSearchPathRoots(@TrackLogRecords LogRecordListener listener) { + var opts = new TestDiscoveryOptions(); + opts.setScanClasspath(true); + opts.setSelectedClasspathEntries(List.of(Paths.get("/does/not/exist"))); + + DiscoveryRequestCreator.toDiscoveryRequestBuilder(opts); + + assertThat(listener.stream(DiscoveryRequestCreator.class)) // + .map(LogRecord::getMessage) // + .filteredOn(message -> message.contains("/does/not/exist")) // + .hasSize(1); + } + + @Test + void logsInvalidAdditionalClasspathRoots(@TrackLogRecords LogRecordListener listener) { + var opts = new TestDiscoveryOptions(); + opts.setScanClasspath(true); + opts.setAdditionalClasspathEntries(List.of(Paths.get("/also/does/not/exist"))); + + DiscoveryRequestCreator.toDiscoveryRequestBuilder(opts); + + assertThat(listener.stream(DiscoveryRequestCreator.class)) // + .map(LogRecord::getMessage) // + .filteredOn(message -> message.contains("/also/does/not/exist")) // + .hasSize(1); + } + private LauncherDiscoveryRequest convert() { return DiscoveryRequestCreator.toDiscoveryRequestBuilder(options).build(); } diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java index e2c2bbc1f85b..e8120109e0ac 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java @@ -405,6 +405,7 @@ void execute(@FilePrefix("console-launcher") OutputFiles outputFiles) throws Exc .addArguments("-enableassertions") // .addArguments("-Djava.util.logging.config.file=logging.properties") // .addArguments("-Djunit.platform.launcher.interceptors.enabled=true") // + .addArguments("-Duser.language=en", "-Duser.country=US") // .addArguments("-jar", MavenRepo.jar("junit-platform-console-standalone").toString()) // .addArguments("execute") // .addArguments("--scan-class-path") // @@ -517,6 +518,7 @@ void executeWithJarredTestClasses(@FilePrefix("jar") OutputFiles jarOutputFiles, .addArguments("-enableassertions") // .addArguments("-Djava.util.logging.config.file=logging.properties") // .addArguments("-Djunit.platform.launcher.interceptors.enabled=true") // + .addArguments("-Duser.language=en", "-Duser.country=US") // .addArguments("-jar", MavenRepo.jar("junit-platform-console-standalone").toString()) // .addArguments("execute") // .addArguments("--scan-class-path") //