Skip to content

Commit b3e18b5

Browse files
committed
[JUnit Platform] Support discovery selectors with FilePosition
There was no good way to discover and execute a single test when using a file based test system like Cucumber. Recently JUnit added support for file position in the `FileSelector` and `ClasspathResourceSelector`. See: junit-team/junit5#2146 This implements a filter that prunes all scenarios that do not match the `FilePosition` from the discovery selector. This feature itself is not new. It was already implemented for the `UriSelector`.
1 parent c03e1d4 commit b3e18b5

File tree

6 files changed

+139
-45
lines changed

6 files changed

+139
-45
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88
## [Unreleased] (In Git)
99

1010
### Added
11+
* [JUnit Platform] Support discovery selectors with FilePosition ([2121](https://github.com/cucumber/cucumber-jvm/pull/2121) M.P. Korstanje)
1112

1213
### Changed
14+
* [JUnit Platform] Update dependency org.junit.platform:junit-platform-engine to v1.7.0
1315

1416
### Deprecated
1517

junit-platform-engine/README.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,20 @@ Supported `DiscoverySelector`s are:
237237
The only supported `DiscoveryFilter` is the `PackageNameFilter` and only when
238238
features are selected from the classpath.
239239

240+
### Selecting individual scenarios, rules and examples
241+
242+
The `FileSelector` and `ClasspathResourceSelector` support a `FilePosition`.
243+
244+
* `DiscoverySelectors.selectClasspathResource("rule.feature", FilePosition.from(5))`
245+
* `DiscoverySelectors.selectFile("rule.feature", FilePosition.from(5))`
246+
240247
The `UriSelector` supports URI's with a `line` query parameter:
241248
- `classpath:/com/example/example.feature?line=20`
242249
- `file:/path/to/com/example/example.feature?line=20`
243-
250+
244251
Any `TestDescriptor` that matches the line *and* its descendants will be
245-
included in the discovery result.
252+
included in the discovery result. So for example selecting a `Rule` will
253+
execute all scenarios contained within.
246254

247255
## Tags ##
248256

junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/FeatureResolver.java

+18-41
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,14 @@
1818
import org.junit.platform.engine.discovery.PackageSelector;
1919
import org.junit.platform.engine.discovery.UniqueIdSelector;
2020
import org.junit.platform.engine.discovery.UriSelector;
21-
import org.junit.platform.engine.support.descriptor.ClasspathResourceSource;
22-
import org.junit.platform.engine.support.descriptor.FilePosition;
23-
import org.junit.platform.engine.support.descriptor.FileSource;
2421

2522
import java.net.URI;
26-
import java.nio.file.Path;
27-
import java.util.Optional;
2823
import java.util.UUID;
2924
import java.util.function.Predicate;
3025
import java.util.function.Supplier;
3126
import java.util.stream.Stream;
3227

3328
import static java.util.Comparator.comparing;
34-
import static org.junit.platform.engine.support.descriptor.FilePosition.fromQuery;
3529

3630
final class FeatureResolver {
3731

@@ -62,16 +56,15 @@ static FeatureResolver createFeatureResolver(
6256
}
6357

6458
void resolveFile(FileSelector selector) {
65-
resolvePath(selector.getPath());
66-
}
67-
68-
private void resolvePath(Path path) {
6959
featureScanner
70-
.scanForResourcesPath(path)
60+
.scanForResourcesPath(selector.getPath())
7161
.stream()
7262
.sorted(comparing(Feature::getUri))
7363
.map(this::createFeatureDescriptor)
74-
.forEach(engineDescriptor::mergeFeature);
64+
.forEach(featureDescriptor -> {
65+
featureDescriptor.prune(TestDescriptorOnLine.from(selector));
66+
engineDescriptor.mergeFeature(featureDescriptor);
67+
});
7568
}
7669

7770
private FeatureDescriptor createFeatureDescriptor(Feature feature) {
@@ -137,7 +130,12 @@ private String getNameOrKeyWord(Node node) {
137130
}
138131

139132
void resolveDirectory(DirectorySelector selector) {
140-
resolvePath(selector.getPath());
133+
featureScanner
134+
.scanForResourcesPath(selector.getPath())
135+
.stream()
136+
.sorted(comparing(Feature::getUri))
137+
.map(this::createFeatureDescriptor)
138+
.forEach(engineDescriptor::mergeFeature);
141139
}
142140

143141
void resolvePackageResource(PackageSelector selector) {
@@ -163,12 +161,16 @@ void resolveClass(ClassSelector classSelector) {
163161

164162
void resolveClasspathResource(ClasspathResourceSelector selector) {
165163
String classpathResourceName = selector.getClasspathResourceName();
164+
166165
featureScanner
167166
.scanForClasspathResource(classpathResourceName, packageFilter)
168167
.stream()
169168
.sorted(comparing(Feature::getUri))
170169
.map(this::createFeatureDescriptor)
171-
.forEach(engineDescriptor::mergeFeature);
170+
.forEach(featureDescriptor -> {
171+
featureDescriptor.prune(TestDescriptorOnLine.from(selector));
172+
engineDescriptor.mergeFeature(featureDescriptor);
173+
});
172174
}
173175

174176
void resolveClasspathRoot(ClasspathRootSelector selector) {
@@ -211,38 +213,13 @@ private Stream<FeatureDescriptor> resolveUri(URI uri) {
211213
}
212214

213215
void resolveUri(UriSelector selector) {
214-
URI uri = selector.getUri();
215-
216-
Predicate<TestDescriptor> keepTestOnSelectedLine = fromQuery(uri.getQuery())
217-
.map(FilePosition::getLine)
218-
.map(FeatureResolver::testDescriptorOnLine)
219-
.orElse(testDescriptor -> true);
220-
221-
resolveUri(stripQuery(uri))
216+
resolveUri(stripQuery(selector.getUri()))
222217
.forEach(featureDescriptor -> {
223-
featureDescriptor.prune(keepTestOnSelectedLine);
218+
featureDescriptor.prune(TestDescriptorOnLine.from(selector));
224219
engineDescriptor.mergeFeature(featureDescriptor);
225220
});
226221
}
227222

228-
private static Predicate<TestDescriptor> testDescriptorOnLine(Integer line) {
229-
return descriptor -> descriptor.getSource()
230-
.flatMap(testSource -> {
231-
if (testSource instanceof FileSource) {
232-
FileSource fileSystemSource = (FileSource) testSource;
233-
return fileSystemSource.getPosition();
234-
}
235-
if (testSource instanceof ClasspathResourceSource) {
236-
ClasspathResourceSource classpathResourceSource = (ClasspathResourceSource) testSource;
237-
return classpathResourceSource.getPosition();
238-
}
239-
return Optional.empty();
240-
})
241-
.map(FilePosition::getLine)
242-
.map(line::equals)
243-
.orElse(false);
244-
}
245-
246223
private static URI stripQuery(URI uri) {
247224
if (uri.getQuery() == null) {
248225
return uri;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.cucumber.junit.platform.engine;
2+
3+
import org.junit.platform.engine.TestDescriptor;
4+
import org.junit.platform.engine.discovery.ClasspathResourceSelector;
5+
import org.junit.platform.engine.discovery.FileSelector;
6+
import org.junit.platform.engine.discovery.UriSelector;
7+
import org.junit.platform.engine.support.descriptor.ClasspathResourceSource;
8+
import org.junit.platform.engine.support.descriptor.FileSource;
9+
10+
import java.util.Optional;
11+
import java.util.function.Predicate;
12+
13+
import static org.junit.platform.engine.support.descriptor.FilePosition.fromQuery;
14+
15+
@SuppressWarnings("Convert2MethodRef")
16+
class TestDescriptorOnLine {
17+
18+
static Predicate<TestDescriptor> testDescriptorOnLine(int line) {
19+
return descriptor -> descriptor.getSource()
20+
.flatMap(testSource -> {
21+
if (testSource instanceof FileSource) {
22+
FileSource fileSystemSource = (FileSource) testSource;
23+
return fileSystemSource.getPosition();
24+
}
25+
if (testSource instanceof ClasspathResourceSource) {
26+
ClasspathResourceSource classpathResourceSource = (ClasspathResourceSource) testSource;
27+
return classpathResourceSource.getPosition();
28+
}
29+
return Optional.empty();
30+
})
31+
.map(filePosition -> filePosition.getLine())
32+
.map(testSourceLine -> line == testSourceLine)
33+
.orElse(false);
34+
}
35+
36+
private static boolean anyTestDescriptor(TestDescriptor testDescriptor) {
37+
return true;
38+
}
39+
40+
static Predicate<TestDescriptor> from(UriSelector selector) {
41+
String query = selector.getUri().getQuery();
42+
return fromQuery(query)
43+
.map(filePosition -> filePosition.getLine())
44+
.map(TestDescriptorOnLine::testDescriptorOnLine)
45+
.orElse(TestDescriptorOnLine::anyTestDescriptor);
46+
}
47+
48+
static Predicate<TestDescriptor> from(ClasspathResourceSelector selector) {
49+
return selector.getPosition()
50+
.map(filePosition -> filePosition.getLine())
51+
.map(TestDescriptorOnLine::testDescriptorOnLine)
52+
.orElse(TestDescriptorOnLine::anyTestDescriptor);
53+
}
54+
55+
static Predicate<TestDescriptor> from(FileSelector selector) {
56+
return selector.getPosition()
57+
.map(filePosition -> filePosition.getLine())
58+
.map(TestDescriptorOnLine::testDescriptorOnLine)
59+
.orElse(TestDescriptorOnLine::anyTestDescriptor);
60+
}
61+
62+
}

junit-platform-engine/src/test/java/io/cucumber/junit/platform/engine/DiscoverySelectorResolverTest.java

+45
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.junit.platform.engine.TestDescriptor;
1212
import org.junit.platform.engine.UniqueId;
1313
import org.junit.platform.engine.discovery.DiscoverySelectors;
14+
import org.junit.platform.engine.discovery.FilePosition;
1415
import org.junit.platform.engine.discovery.UniqueIdSelector;
1516

1617
import java.io.File;
@@ -62,6 +63,28 @@ void resolveRequestWithClasspathResourceSelector() {
6263
assertEquals(1, testDescriptor.getChildren().size());
6364
}
6465

66+
@Test
67+
void resolveRequestWithClasspathResourceSelectorAndFilePosition() {
68+
DiscoverySelector resource = selectClasspathResource("io/cucumber/junit/platform/engine/rule.feature", FilePosition.from(5));
69+
EngineDiscoveryRequest discoveryRequest = new SelectorRequest(resource);
70+
resolver.resolveSelectors(discoveryRequest, testDescriptor);
71+
assertEquals(1L, testDescriptor.getDescendants()
72+
.stream()
73+
.filter(TestDescriptor::isTest)
74+
.count());
75+
}
76+
77+
@Test
78+
void resolveRequestWithClasspathResourceSelectorAndFilePositionOfContainer() {
79+
DiscoverySelector resource = selectClasspathResource("io/cucumber/junit/platform/engine/rule.feature", FilePosition.from(3));
80+
EngineDiscoveryRequest discoveryRequest = new SelectorRequest(resource);
81+
resolver.resolveSelectors(discoveryRequest, testDescriptor);
82+
assertEquals(2L, testDescriptor.getDescendants()
83+
.stream()
84+
.filter(TestDescriptor::isTest)
85+
.count());
86+
}
87+
6588
@Test
6689
void resolveRequestWithMultipleClasspathResourceSelector() {
6790
DiscoverySelector resource1 = selectClasspathResource("io/cucumber/junit/platform/engine/single.feature");
@@ -155,6 +178,28 @@ void resolveRequestWithFileSelector() {
155178
assertEquals(1, testDescriptor.getChildren().size());
156179
}
157180

181+
@Test
182+
void resolveRequestWithFileSelectorAndPosition() {
183+
DiscoverySelector resource = selectFile("src/test/resources/io/cucumber/junit/platform/engine/rule.feature", FilePosition.from(5));
184+
EngineDiscoveryRequest discoveryRequest = new SelectorRequest(resource);
185+
resolver.resolveSelectors(discoveryRequest, testDescriptor);
186+
assertEquals(1L, testDescriptor.getDescendants()
187+
.stream()
188+
.filter(TestDescriptor::isTest)
189+
.count());
190+
}
191+
192+
@Test
193+
void resolveRequestWithFileSelectorAndPositionOfContainer() {
194+
DiscoverySelector resource = selectFile("src/test/resources/io/cucumber/junit/platform/engine/rule.feature", FilePosition.from(3));
195+
EngineDiscoveryRequest discoveryRequest = new SelectorRequest(resource);
196+
resolver.resolveSelectors(discoveryRequest, testDescriptor);
197+
assertEquals(2L, testDescriptor.getDescendants()
198+
.stream()
199+
.filter(TestDescriptor::isTest)
200+
.count());
201+
}
202+
158203
@Test
159204
void resolveRequestWithDirectorySelector() {
160205
DiscoverySelector resource = selectDirectory("src/test/resources/io/cucumber/junit/platform/engine");

pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434

3535
<!--Test Dependencies-->
3636
<junit.version>4.13</junit.version>
37-
<junit-jupiter.version>5.6.2</junit-jupiter.version>
38-
<junit-platform.version>1.6.2</junit-platform.version>
37+
<junit-jupiter.version>5.7.0</junit-jupiter.version>
38+
<junit-platform.version>1.7.0</junit-platform.version>
3939
<hamcrest.version>2.2</hamcrest.version>
4040
<mockito.version>3.5.10</mockito.version>
4141
<!--Maven plugins-->

0 commit comments

Comments
 (0)