diff --git a/CHANGELOG.md b/CHANGELOG.md index db9eb53bf6..873dd13f33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] (In Git) ### Added + * [JUnit Platform] Support discovery selectors with FilePosition ([2121](https://github.com/cucumber/cucumber-jvm/pull/2121) M.P. Korstanje) ### Changed + * [JUnit Platform] Update dependency org.junit.platform:junit-platform-engine to v1.7.0 ### Deprecated diff --git a/junit-platform-engine/README.md b/junit-platform-engine/README.md index a08b2912ed..4b9752586c 100644 --- a/junit-platform-engine/README.md +++ b/junit-platform-engine/README.md @@ -237,12 +237,20 @@ Supported `DiscoverySelector`s are: The only supported `DiscoveryFilter` is the `PackageNameFilter` and only when features are selected from the classpath. +### Selecting individual scenarios, rules and examples + +The `FileSelector` and `ClasspathResourceSelector` support a `FilePosition`. + + * `DiscoverySelectors.selectClasspathResource("rule.feature", FilePosition.from(5))` + * `DiscoverySelectors.selectFile("rule.feature", FilePosition.from(5))` + The `UriSelector` supports URI's with a `line` query parameter: - `classpath:/com/example/example.feature?line=20` - `file:/path/to/com/example/example.feature?line=20` - + Any `TestDescriptor` that matches the line *and* its descendants will be -included in the discovery result. +included in the discovery result. So for example selecting a `Rule` will +execute all scenarios contained within. ## Tags ## diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java index 0ddbcbbd92..39133b5f73 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java @@ -18,20 +18,14 @@ import org.junit.platform.engine.discovery.PackageSelector; import org.junit.platform.engine.discovery.UniqueIdSelector; import org.junit.platform.engine.discovery.UriSelector; -import org.junit.platform.engine.support.descriptor.ClasspathResourceSource; -import org.junit.platform.engine.support.descriptor.FilePosition; -import org.junit.platform.engine.support.descriptor.FileSource; import java.net.URI; -import java.nio.file.Path; -import java.util.Optional; import java.util.UUID; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; import static java.util.Comparator.comparing; -import static org.junit.platform.engine.support.descriptor.FilePosition.fromQuery; final class FeatureResolver { @@ -62,16 +56,15 @@ static FeatureResolver createFeatureResolver( } void resolveFile(FileSelector selector) { - resolvePath(selector.getPath()); - } - - private void resolvePath(Path path) { featureScanner - .scanForResourcesPath(path) + .scanForResourcesPath(selector.getPath()) .stream() .sorted(comparing(Feature::getUri)) .map(this::createFeatureDescriptor) - .forEach(engineDescriptor::mergeFeature); + .forEach(featureDescriptor -> { + featureDescriptor.prune(TestDescriptorOnLine.from(selector)); + engineDescriptor.mergeFeature(featureDescriptor); + }); } private FeatureDescriptor createFeatureDescriptor(Feature feature) { @@ -137,7 +130,12 @@ private String getNameOrKeyWord(Node node) { } void resolveDirectory(DirectorySelector selector) { - resolvePath(selector.getPath()); + featureScanner + .scanForResourcesPath(selector.getPath()) + .stream() + .sorted(comparing(Feature::getUri)) + .map(this::createFeatureDescriptor) + .forEach(engineDescriptor::mergeFeature); } void resolvePackageResource(PackageSelector selector) { @@ -163,12 +161,16 @@ void resolveClass(ClassSelector classSelector) { void resolveClasspathResource(ClasspathResourceSelector selector) { String classpathResourceName = selector.getClasspathResourceName(); + featureScanner .scanForClasspathResource(classpathResourceName, packageFilter) .stream() .sorted(comparing(Feature::getUri)) .map(this::createFeatureDescriptor) - .forEach(engineDescriptor::mergeFeature); + .forEach(featureDescriptor -> { + featureDescriptor.prune(TestDescriptorOnLine.from(selector)); + engineDescriptor.mergeFeature(featureDescriptor); + }); } void resolveClasspathRoot(ClasspathRootSelector selector) { @@ -211,38 +213,13 @@ private Stream resolveUri(URI uri) { } void resolveUri(UriSelector selector) { - URI uri = selector.getUri(); - - Predicate keepTestOnSelectedLine = fromQuery(uri.getQuery()) - .map(FilePosition::getLine) - .map(FeatureResolver::testDescriptorOnLine) - .orElse(testDescriptor -> true); - - resolveUri(stripQuery(uri)) + resolveUri(stripQuery(selector.getUri())) .forEach(featureDescriptor -> { - featureDescriptor.prune(keepTestOnSelectedLine); + featureDescriptor.prune(TestDescriptorOnLine.from(selector)); engineDescriptor.mergeFeature(featureDescriptor); }); } - private static Predicate testDescriptorOnLine(Integer line) { - return descriptor -> descriptor.getSource() - .flatMap(testSource -> { - if (testSource instanceof FileSource) { - FileSource fileSystemSource = (FileSource) testSource; - return fileSystemSource.getPosition(); - } - if (testSource instanceof ClasspathResourceSource) { - ClasspathResourceSource classpathResourceSource = (ClasspathResourceSource) testSource; - return classpathResourceSource.getPosition(); - } - return Optional.empty(); - }) - .map(FilePosition::getLine) - .map(line::equals) - .orElse(false); - } - private static URI stripQuery(URI uri) { if (uri.getQuery() == null) { return uri; diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/TestDescriptorOnLine.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/TestDescriptorOnLine.java new file mode 100644 index 0000000000..ae1d1de4b6 --- /dev/null +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/TestDescriptorOnLine.java @@ -0,0 +1,62 @@ +package io.cucumber.junit.platform.engine; + +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.discovery.ClasspathResourceSelector; +import org.junit.platform.engine.discovery.FileSelector; +import org.junit.platform.engine.discovery.UriSelector; +import org.junit.platform.engine.support.descriptor.ClasspathResourceSource; +import org.junit.platform.engine.support.descriptor.FileSource; + +import java.util.Optional; +import java.util.function.Predicate; + +import static org.junit.platform.engine.support.descriptor.FilePosition.fromQuery; + +@SuppressWarnings("Convert2MethodRef") +class TestDescriptorOnLine { + + static Predicate testDescriptorOnLine(int line) { + return descriptor -> descriptor.getSource() + .flatMap(testSource -> { + if (testSource instanceof FileSource) { + FileSource fileSystemSource = (FileSource) testSource; + return fileSystemSource.getPosition(); + } + if (testSource instanceof ClasspathResourceSource) { + ClasspathResourceSource classpathResourceSource = (ClasspathResourceSource) testSource; + return classpathResourceSource.getPosition(); + } + return Optional.empty(); + }) + .map(filePosition -> filePosition.getLine()) + .map(testSourceLine -> line == testSourceLine) + .orElse(false); + } + + private static boolean anyTestDescriptor(TestDescriptor testDescriptor) { + return true; + } + + static Predicate from(UriSelector selector) { + String query = selector.getUri().getQuery(); + return fromQuery(query) + .map(filePosition -> filePosition.getLine()) + .map(TestDescriptorOnLine::testDescriptorOnLine) + .orElse(TestDescriptorOnLine::anyTestDescriptor); + } + + static Predicate from(ClasspathResourceSelector selector) { + return selector.getPosition() + .map(filePosition -> filePosition.getLine()) + .map(TestDescriptorOnLine::testDescriptorOnLine) + .orElse(TestDescriptorOnLine::anyTestDescriptor); + } + + static Predicate from(FileSelector selector) { + return selector.getPosition() + .map(filePosition -> filePosition.getLine()) + .map(TestDescriptorOnLine::testDescriptorOnLine) + .orElse(TestDescriptorOnLine::anyTestDescriptor); + } + +} diff --git a/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/DiscoverySelectorResolverTest.java b/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/DiscoverySelectorResolverTest.java index 28fbdd33f7..1a64fe1d89 100644 --- a/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/DiscoverySelectorResolverTest.java +++ b/junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/DiscoverySelectorResolverTest.java @@ -11,6 +11,7 @@ import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.engine.discovery.FilePosition; import org.junit.platform.engine.discovery.UniqueIdSelector; import java.io.File; @@ -62,6 +63,32 @@ void resolveRequestWithClasspathResourceSelector() { assertEquals(1, testDescriptor.getChildren().size()); } + @Test + void resolveRequestWithClasspathResourceSelectorAndFilePosition() { + String feature = "io/cucumber/junit/platform/engine/rule.feature"; + FilePosition line = FilePosition.from(5); + DiscoverySelector resource = selectClasspathResource(feature, line); + EngineDiscoveryRequest discoveryRequest = new SelectorRequest(resource); + resolver.resolveSelectors(discoveryRequest, testDescriptor); + assertEquals(1L, testDescriptor.getDescendants() + .stream() + .filter(TestDescriptor::isTest) + .count()); + } + + @Test + void resolveRequestWithClasspathResourceSelectorAndFilePositionOfContainer() { + String feature = "io/cucumber/junit/platform/engine/rule.feature"; + FilePosition line = FilePosition.from(3); + DiscoverySelector resource = selectClasspathResource(feature, line); + EngineDiscoveryRequest discoveryRequest = new SelectorRequest(resource); + resolver.resolveSelectors(discoveryRequest, testDescriptor); + assertEquals(2L, testDescriptor.getDescendants() + .stream() + .filter(TestDescriptor::isTest) + .count()); + } + @Test void resolveRequestWithMultipleClasspathResourceSelector() { DiscoverySelector resource1 = selectClasspathResource("io/cucumber/junit/platform/engine/single.feature"); @@ -155,6 +182,32 @@ void resolveRequestWithFileSelector() { assertEquals(1, testDescriptor.getChildren().size()); } + @Test + void resolveRequestWithFileSelectorAndPosition() { + String feature = "src/test/resources/io/cucumber/junit/platform/engine/rule.feature"; + FilePosition line = FilePosition.from(5); + DiscoverySelector resource = selectFile(feature, line); + EngineDiscoveryRequest discoveryRequest = new SelectorRequest(resource); + resolver.resolveSelectors(discoveryRequest, testDescriptor); + assertEquals(1L, testDescriptor.getDescendants() + .stream() + .filter(TestDescriptor::isTest) + .count()); + } + + @Test + void resolveRequestWithFileSelectorAndPositionOfContainer() { + String feature = "src/test/resources/io/cucumber/junit/platform/engine/rule.feature"; + FilePosition line = FilePosition.from(3); + DiscoverySelector resource = selectFile(feature, line); + EngineDiscoveryRequest discoveryRequest = new SelectorRequest(resource); + resolver.resolveSelectors(discoveryRequest, testDescriptor); + assertEquals(2L, testDescriptor.getDescendants() + .stream() + .filter(TestDescriptor::isTest) + .count()); + } + @Test void resolveRequestWithDirectorySelector() { DiscoverySelector resource = selectDirectory("src/test/resources/io/cucumber/junit/platform/engine"); diff --git a/pom.xml b/pom.xml index 68e4969f2b..25219a3a9c 100644 --- a/pom.xml +++ b/pom.xml @@ -34,8 +34,8 @@ 4.13 - 5.6.2 - 1.6.2 + 5.7.0 + 1.7.0 2.2 3.5.10