Skip to content

Fail on classpath resource names that are blank after removing leading slash #4761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ repository on GitHub.
[[release-notes-5.13.4-junit-platform-bug-fixes]]
==== Bug Fixes

* ❓
* `ClasspathResourceSelector` no longer allows to be constructed with a resource name that
is blank after removing the leading slash.
* `PackageSource.from(String)` now allows to be constructed with an empty string to
indicate the default package.

[[release-notes-5.13.4-junit-platform-deprecations-and-breaking-changes]]
==== Deprecations and Breaking Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.junit.platform.commons.PreconditionViolationException;
import org.junit.platform.commons.function.Try;
import org.junit.platform.commons.support.Resource;
import org.junit.platform.commons.util.Preconditions;
import org.junit.platform.commons.util.ReflectionUtils;
import org.junit.platform.commons.util.StringUtils;
import org.junit.platform.commons.util.ToStringBuilder;
Expand Down Expand Up @@ -64,6 +65,8 @@ public final class ClasspathResourceSelector implements DiscoverySelector {
ClasspathResourceSelector(String classpathResourceName, @Nullable FilePosition position) {
boolean startsWithSlash = classpathResourceName.startsWith("/");
this.classpathResourceName = (startsWithSlash ? classpathResourceName.substring(1) : classpathResourceName);
Preconditions.notBlank(this.classpathResourceName,
"classpath resource name must not be blank after removing leading slash");
this.position = position;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ private PackageSource(Package javaPackage) {
}

private PackageSource(String packageName) {
this.packageName = Preconditions.notBlank(packageName, "package name must not be null or blank");
Preconditions.notNull(packageName, "package name must not be null");
Preconditions.condition(packageName.isEmpty() || !packageName.isBlank(),
"package name must not contain only whitespace");
this.packageName = packageName;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

package org.junit.platform.launcher.core;

import static org.junit.platform.commons.util.UnrecoverableExceptions.rethrowIfUnrecoverable;
import static org.junit.platform.engine.SelectorResolutionResult.Status.FAILED;
import static org.junit.platform.engine.SelectorResolutionResult.Status.UNRESOLVED;

Expand Down Expand Up @@ -78,7 +79,7 @@ else if (result.getStatus() == UNRESOLVED && selector instanceof UniqueIdSelecto
}
}

static @Nullable TestSource toSource(DiscoverySelector selector) {
private static @Nullable TestSource toSource(DiscoverySelector selector) {
if (selector instanceof ClassSelector classSelector) {
return ClassSource.from(classSelector.getClassName());
}
Expand All @@ -96,17 +97,26 @@ else if (result.getStatus() == UNRESOLVED && selector instanceof UniqueIdSelecto
if (selector instanceof PackageSelector packageSelector) {
return PackageSource.from(packageSelector.getPackageName());
}
if (selector instanceof FileSelector fileSelector) {
return fileSelector.getPosition() //
.map(DiscoveryIssueCollector::convert) //
.map(position -> FileSource.from(fileSelector.getFile(), position)) //
.orElseGet(() -> FileSource.from(fileSelector.getFile()));
}
if (selector instanceof DirectorySelector directorySelector) {
return DirectorySource.from(directorySelector.getDirectory());
try {
// Both FileSource and DirectorySource call File.getCanonicalFile() to normalize the reported file which
// can throw an exception for certain file names on certain file systems. UriSource.from(...) is affected
// as well because it may return a FileSource or DirectorySource
if (selector instanceof FileSelector fileSelector) {
return fileSelector.getPosition() //
.map(DiscoveryIssueCollector::convert) //
.map(position -> FileSource.from(fileSelector.getFile(), position)) //
.orElseGet(() -> FileSource.from(fileSelector.getFile()));
}
if (selector instanceof DirectorySelector directorySelector) {
return DirectorySource.from(directorySelector.getDirectory());
}
if (selector instanceof UriSelector uriSelector) {
return UriSource.from(uriSelector.getUri());
}
}
if (selector instanceof UriSelector uriSelector) {
return UriSource.from(uriSelector.getUri());
catch (Exception ex) {
rethrowIfUnrecoverable(ex);
logger.warn(ex, () -> "Failed to convert DiscoverySelector [%s] into TestSource".formatted(selector));
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,11 @@ void parseDirectorySelectorWithAbsolutePath() {
void selectClasspathResourcesPreconditions() {
assertViolatesPrecondition(() -> selectClasspathResource((String) null));
assertViolatesPrecondition(() -> selectClasspathResource(""));
assertViolatesPrecondition(() -> selectClasspathResource("/"));
assertViolatesPrecondition(() -> selectClasspathResource(" "));
assertViolatesPrecondition(() -> selectClasspathResource("/ "));
assertViolatesPrecondition(() -> selectClasspathResource("\t"));
assertViolatesPrecondition(() -> selectClasspathResource("/\t"));
assertViolatesPrecondition(() -> selectClasspathResource((Set<Resource>) null));
assertViolatesPrecondition(() -> selectClasspathResource(Collections.emptySet()));
assertViolatesPrecondition(() -> selectClasspathResource(Collections.singleton(null)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.platform.commons.PreconditionViolationException;

/**
Expand Down Expand Up @@ -49,9 +51,11 @@ void packageSourceFromNullPackageReference() {
assertThrows(PreconditionViolationException.class, () -> PackageSource.from((Package) null));
}

@Test
void packageSourceFromPackageName() {
var testPackage = getClass().getPackage().getName();
@ParameterizedTest
@ValueSource(classes = PackageSourceTests.class)
@ValueSource(strings = "DefaultPackageTestCase")
void packageSourceFromPackageName(Class<?> testClass) {
var testPackage = testClass.getPackage().getName();
var source = PackageSource.from(testPackage);

assertThat(source.getPackageName()).isEqualTo(testPackage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public static Stream<Pair> pairs() {
new Pair(selectClasspathResource("someResource", FilePosition.from(42, 23)),
ClasspathResourceSource.from("someResource",
org.junit.platform.engine.support.descriptor.FilePosition.from(42, 23))), //
new Pair(selectPackage(""), PackageSource.from("")), //
new Pair(selectPackage("some.package"), PackageSource.from("some.package")), //
new Pair(selectFile("someFile"), FileSource.from(new File("someFile"))), //
new Pair(selectFile("someFile", FilePosition.from(42)),
Expand Down
Loading