diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc index 5f8f864a4a6b..c9a3569af431 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc @@ -31,6 +31,8 @@ JUnit repository on GitHub. calling the internal `ReflectionUtils.makeAccessible(Field)` method directly. * Support both the primitive type `void` and the wrapper type `Void` in the internal `ReflectionUtils` to allow `String` to `Class` conversion in parameterized tests. +* Add support for passing line and column number to `ConsoleLauncher` via + `--select-file` and `--select-resource`. [[release-notes-5.12.0-M1-junit-jupiter]] diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ResourceUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ResourceUtils.java similarity index 74% rename from junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ResourceUtils.java rename to junit-platform-commons/src/main/java/org/junit/platform/commons/util/ResourceUtils.java index b61598d59b8e..fd6b78e3223e 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ResourceUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ResourceUtils.java @@ -8,19 +8,21 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.platform.engine.support.descriptor; +package org.junit.platform.commons.util; + +import static org.apiguardian.api.API.Status.INTERNAL; import java.net.URI; -import org.junit.platform.commons.util.Preconditions; -import org.junit.platform.commons.util.StringUtils; +import org.apiguardian.api.API; /** * Collection of static utility methods for working with resources. * - * @since 1.3 + * @since 1.3 (originally in org.junit.platform.engine.support.descriptor) */ -final class ResourceUtils { +@API(status = INTERNAL, since = "1.12") +public final class ResourceUtils { private ResourceUtils() { /* no-op */ @@ -33,8 +35,10 @@ private ResourceUtils() { * @param uri the {@code URI} from which to strip the query component * @return a new {@code URI} with the query component removed, or the * original {@code URI} unmodified if it does not have a query component + * + * @since 1.3 */ - static URI stripQueryComponent(URI uri) { + public static URI stripQueryComponent(URI uri) { Preconditions.notNull(uri, "URI must not be null"); if (StringUtils.isBlank(uri.getQuery())) { diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java index 7c18320eba67..6f1803185706 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java @@ -19,12 +19,16 @@ import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUri; +import java.net.URI; + import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.util.ResourceUtils; import org.junit.platform.engine.DiscoverySelectorIdentifier; import org.junit.platform.engine.discovery.ClassSelector; import org.junit.platform.engine.discovery.ClasspathResourceSelector; import org.junit.platform.engine.discovery.DirectorySelector; import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.engine.discovery.FilePosition; import org.junit.platform.engine.discovery.FileSelector; import org.junit.platform.engine.discovery.IterationSelector; import org.junit.platform.engine.discovery.MethodSelector; @@ -53,8 +57,12 @@ public UriSelector convert(String value) { static class File implements ITypeConverter { @Override public FileSelector convert(String value) { - return selectFile(value); + URI uri = URI.create(value); + String path = ResourceUtils.stripQueryComponent(uri).getPath(); + FilePosition filePosition = FilePosition.fromQuery(uri.getQuery()).orElse(null); + return selectFile(path, filePosition); } + } static class Directory implements ITypeConverter { @@ -88,7 +96,10 @@ public MethodSelector convert(String value) { static class ClasspathResource implements ITypeConverter { @Override public ClasspathResourceSelector convert(String value) { - return selectClasspathResource(value); + URI uri = URI.create(value); + String path = ResourceUtils.stripQueryComponent(uri).getPath(); + FilePosition filePosition = FilePosition.fromQuery(uri.getQuery()).orElse(null); + return selectClasspathResource(path, filePosition); } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java index d71b1f0a6e2f..46298abb9d11 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java @@ -76,7 +76,10 @@ static class SelectorOptions { private final List selectedUris2 = new ArrayList<>(); @Option(names = { "-f", - "--select-file" }, paramLabel = "FILE", arity = "1", converter = SelectorConverter.File.class, description = "Select a file for test discovery. This option can be repeated.") + "--select-file" }, paramLabel = "FILE", arity = "1", converter = SelectorConverter.File.class, // + description = "Select a file for test discovery. " + + "The line and column numbers can be provided as URI query parameters (e.g. foo.txt?line=12&column=34). " + + "This option can be repeated.") private final List selectedFiles = new ArrayList<>(); @Option(names = { "--f", "-select-file" }, arity = "1", hidden = true, converter = SelectorConverter.File.class) @@ -123,7 +126,10 @@ static class SelectorOptions { private final List selectedMethods2 = new ArrayList<>(); @Option(names = { "-r", - "--select-resource" }, paramLabel = "RESOURCE", arity = "1", converter = SelectorConverter.ClasspathResource.class, description = "Select a classpath resource for test discovery. This option can be repeated.") + "--select-resource" }, paramLabel = "RESOURCE", arity = "1", converter = SelectorConverter.ClasspathResource.class, // + description = "Select a classpath resource for test discovery. " + + "The line and column numbers can be provided as URI query parameters (e.g. foo.csv?line=12&column=34). " + + "This option can be repeated.") private final List selectedClasspathResources = new ArrayList<>(); @Option(names = { "--r", diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java index 595dd33cd307..e326a3343a58 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java @@ -19,6 +19,7 @@ import org.apiguardian.api.API; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.ResourceUtils; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.TestSource; diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java index bd97bbe609e1..fef3e61b5db7 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java @@ -20,6 +20,7 @@ import org.apiguardian.api.API; import org.junit.platform.commons.logging.LoggerFactory; import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.ResourceUtils; import org.junit.platform.engine.TestSource; /** diff --git a/platform-tests/src/test/java/org/junit/platform/console/options/CommandLineOptionsParsingTests.java b/platform-tests/src/test/java/org/junit/platform/console/options/CommandLineOptionsParsingTests.java index 3b9b10935c9f..acb491afa48f 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/options/CommandLineOptionsParsingTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/options/CommandLineOptionsParsingTests.java @@ -44,6 +44,7 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.platform.engine.DiscoverySelector; import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.engine.discovery.FilePosition; /** * @since 1.10 @@ -382,7 +383,9 @@ void parseValidFileSelectors(ArgsType type) { () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("-select-file=foo.txt").discovery.getSelectedFiles()), () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("--select-file foo.txt").discovery.getSelectedFiles()), () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("--select-file=foo.txt").discovery.getSelectedFiles()), - () -> assertEquals(List.of(selectFile("foo.txt"), selectFile("bar.csv")), type.parseArgLine("-f foo.txt -f bar.csv").discovery.getSelectedFiles()) + () -> assertEquals(List.of(selectFile("foo.txt"), selectFile("bar.csv")), type.parseArgLine("-f foo.txt -f bar.csv").discovery.getSelectedFiles()), + () -> assertEquals(List.of(selectFile("foo.txt", FilePosition.from(5))), type.parseArgLine("-f foo.txt?line=5").discovery.getSelectedFiles()), + () -> assertEquals(List.of(selectFile("foo.txt", FilePosition.from(12, 34))), type.parseArgLine("-f foo.txt?line=12&column=34").discovery.getSelectedFiles()) ); // @formatter:on } @@ -509,7 +512,9 @@ void parseValidClasspathResourceSelectors(ArgsType type) { () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("-select-resource=/foo.csv").discovery.getSelectedClasspathResources()), () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("--select-resource /foo.csv").discovery.getSelectedClasspathResources()), () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("--select-resource=/foo.csv").discovery.getSelectedClasspathResources()), - () -> assertEquals(List.of(selectClasspathResource("/foo.csv"), selectClasspathResource("bar.json")), type.parseArgLine("-r /foo.csv -r bar.json").discovery.getSelectedClasspathResources()) + () -> assertEquals(List.of(selectClasspathResource("/foo.csv"), selectClasspathResource("bar.json")), type.parseArgLine("-r /foo.csv -r bar.json").discovery.getSelectedClasspathResources()), + () -> assertEquals(List.of(selectClasspathResource("/foo.csv", FilePosition.from(5))), type.parseArgLine("-r /foo.csv?line=5").discovery.getSelectedClasspathResources()), + () -> assertEquals(List.of(selectClasspathResource("/foo.csv", FilePosition.from(12, 34))), type.parseArgLine("-r /foo.csv?line=12&column=34").discovery.getSelectedClasspathResources()) ); // @formatter:on }