Skip to content

[JUnit Platform] Support discovery selectors with FilePosition #2121

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 1 commit into from
Sep 14, 2020
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
12 changes: 10 additions & 2 deletions junit-platform-engine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ##

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -211,38 +213,13 @@ private Stream<FeatureDescriptor> resolveUri(URI uri) {
}

void resolveUri(UriSelector selector) {
URI uri = selector.getUri();

Predicate<TestDescriptor> 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<TestDescriptor> 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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<TestDescriptor> 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<TestDescriptor> from(UriSelector selector) {
String query = selector.getUri().getQuery();
return fromQuery(query)
.map(filePosition -> filePosition.getLine())
.map(TestDescriptorOnLine::testDescriptorOnLine)
.orElse(TestDescriptorOnLine::anyTestDescriptor);
}

static Predicate<TestDescriptor> from(ClasspathResourceSelector selector) {
return selector.getPosition()
.map(filePosition -> filePosition.getLine())
.map(TestDescriptorOnLine::testDescriptorOnLine)
.orElse(TestDescriptorOnLine::anyTestDescriptor);
}

static Predicate<TestDescriptor> from(FileSelector selector) {
return selector.getPosition()
.map(filePosition -> filePosition.getLine())
.map(TestDescriptorOnLine::testDescriptorOnLine)
.orElse(TestDescriptorOnLine::anyTestDescriptor);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@

<!--Test Dependencies-->
<junit.version>4.13</junit.version>
<junit-jupiter.version>5.6.2</junit-jupiter.version>
<junit-platform.version>1.6.2</junit-platform.version>
<junit-jupiter.version>5.7.0</junit-jupiter.version>
<junit-platform.version>1.7.0</junit-platform.version>
<hamcrest.version>2.2</hamcrest.version>
<mockito.version>3.5.10</mockito.version>
<!--Maven plugins-->
Expand Down