Skip to content

Commit 9d09898

Browse files
Add more queries (#76)
* Added several new methods to find pickles, testcases, test cast started and test steps by `TestStepStarted` events. * Made lineage API public. While Converting the TeamCity plugin to the message protocol[1] I found that. Unlike the JUnit XML formatter this plugin also responds to test steps and so needs queries that involve test steps. The plugin also needs to transform the elements in the Pickle tree to something more complex than a name so the lineage api is made public. 1. cucumber/cucumber-jvm#3007 Co-authored-by: David Goss <[email protected]>
1 parent fadb358 commit 9d09898

26 files changed

+712
-310
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
9+
### Added
10+
- Made `Lineage` APIs public ([#76](https://github.com/cucumber/query/pull/76))
11+
- New method `findPickleBy(TestStepStarted)` ([#76](https://github.com/cucumber/query/pull/76))
12+
- New method `findTestCaseBy(TestStepStarted)` ([#76](https://github.com/cucumber/query/pull/76))
13+
- New method `findTestCaseStartedBy(TestStepStarted)` ([#76](https://github.com/cucumber/query/pull/76))
14+
- New method `findTestStepBy(TestStepStarted)` ([#76](https://github.com/cucumber/query/pull/76))
15+
- New method `findTestStepsStartedBy(TestCaseStarted)` ([#76](https://github.com/cucumber/query/pull/76))
16+
17+
### Fixed
18+
- `Query.findAllTestCaseStarted` orders events by `timestamp` and `id` ([#76](https://github.com/cucumber/query/pull/76))
919

1020
## [13.2.0] - 2025-02-02
1121
### Changed

README.md

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,44 +32,9 @@ using `id` fields. It's a bit similar to rows in a relational database, with
3232
primary and foreign keys.
3333

3434
Consumers of these messages may want to *query* the messages for certain information.
35-
For example, [cucumber-react](https://github.com/cucumber/cucumber-react) needs to know the status of
36-
a [Step](../cucumber-messages/messages.md#io.cucumber.messages.GherkinDocument.Feature.Step) as it
37-
is rendering the [GherkinDocument](../cucumber-messages/messages.md#io.cucumber.messages.GherkinDocument)
35+
For example, [@cucumber/react-components](https://github.com/cucumber/react-components) needs to know the status of
36+
a [Step](https://github.com/cucumber/messages/blob/main/messages.md#step) as it
37+
is rendering the [GherkinDocument](https://github.com/cucumber/messages/blob/main/messages.md#gherkindocument)
3838

3939
The Query library makes this easy by providing a function to look up the
4040
status of a step, a scenario or an entire file.
41-
42-
## API
43-
44-
| Query function | .NET | Go | Java | Ruby | TypeScript |
45-
|-----------------------------------------------------------------------------------------------|------|-----|------|------|------------|
46-
| `getStepResults(uri: string, lineNumber: number): messages.ITestResult[]` | | | | ||
47-
| `getScenarioResults(uri: string, lineNumber: number): messages.ITestResult[]` | | | | ||
48-
| `getDocumentResults(uri: string): messages.ITestResult[]` | | | | ||
49-
| `getStepMatchArguments(uri: string, lineNumber: number): messages.IStepMatchArgument[]` | | | | ||
50-
| `getGherkinStep(gherkinStepId: string): messages.GherkinDocument.Feature.IStep` | | | | ||
51-
| `countMostSevereTestStepResultStatus(): Map<TestStepResultStatus, Long>` | | || ||
52-
| `countTestCasesStarted(): int` | | || ||
53-
| `findAllPickles(): List<Pickle>` | | || ||
54-
| `findAllPickleSteps(): List<PickleStep>` | | || ||
55-
| `findAllTestCaseStarted(): List<TestCaseStarted>` | | || ||
56-
| `findAllTestCaseStartedGroupedByFeature(): Map<Optional<Feature>, List<TestCaseStarted>>` | | || ||
57-
| `findAllTestSteps(): List<TestStep>` | | || ||
58-
| `findAttachmentsBy(TestStepFinished): List<Attachment>` | | || ||
59-
| `findFeatureBy(TestCaseStarted): Optional<Feature>` | | || ||
60-
| `findHookBy(TestStep): Optional<Hook>` | | || ||
61-
| `findMeta(): Optional<Meta>` | | || ||
62-
| `findMostSevereTestStepResulBy(TestCaseStarted): Optional<TestStepResult>` | | || ||
63-
| `findNameOf(Pickle, NamingStrategy): String` | | || ||
64-
| `findPickleBy(TestCaseStarted): Optional<Pickle>` | | || ||
65-
| `findPickleStepBy(TestStep testStep): Optional<PickleStep>` | | || ||
66-
| `findStepBy(PickleStep pickleStep): Optional<Step>` | | || ||
67-
| `findTestCaseBy(TestCaseStarted): Optional<TestCase>` | | || ||
68-
| `findTestCaseDurationBy(TestCaseStarted): Optional<Duration>` | | || ||
69-
| `findTestCaseFinishedBy(TestCaseStarted): Optional<TestCaseFinished>` | | || ||
70-
| `findTestRunDuration(): Optional<Duration>` | | || ||
71-
| `findTestRunFinished(): Optional<TestRunFinished>` | | || ||
72-
| `findTestRunStarted(): Optional<TestRunStarted>` | | || ||
73-
| `findTestStepBy(TestStepFinished): Optional<TestStep>` | | || ||
74-
| `findTestStepsFinishedBy(TestCaseStarted): List<TestStepFinished>` | | || ||
75-
| `findTestStepFinishedAndTestStepBy(TestCaseStarted): List<Entry<TestStepFinished, TestStep>>` | | || ||

java/src/main/java/io/cucumber/query/Lineage.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
import java.util.Objects;
1111
import java.util.Optional;
12-
import java.util.function.Supplier;
1312

1413
import static java.util.Objects.requireNonNull;
1514

@@ -21,7 +20,7 @@
2120
*
2221
* @see LineageReducer
2322
*/
24-
class Lineage {
23+
public final class Lineage {
2524

2625
private final GherkinDocument document;
2726
private final Feature feature;
@@ -67,42 +66,38 @@ private Lineage(GherkinDocument document, Feature feature, Rule rule, Scenario s
6766
this.exampleIndex = exampleIndex;
6867
}
6968

70-
GherkinDocument document() {
69+
public GherkinDocument document() {
7170
return document;
7271
}
7372

74-
Optional<Feature> feature() {
73+
public Optional<Feature> feature() {
7574
return Optional.ofNullable(feature);
7675
}
7776

78-
Optional<Rule> rule() {
77+
public Optional<Rule> rule() {
7978
return Optional.ofNullable(rule);
8079
}
8180

82-
Optional<Scenario> scenario() {
81+
public Optional<Scenario> scenario() {
8382
return Optional.ofNullable(scenario);
8483
}
8584

86-
Optional<Examples> examples() {
85+
public Optional<Examples> examples() {
8786
return Optional.ofNullable(examples);
8887
}
8988

90-
Optional<TableRow> example() {
89+
public Optional<TableRow> example() {
9190
return Optional.ofNullable(example);
9291
}
9392

94-
Optional<Integer> examplesIndex() {
93+
public Optional<Integer> examplesIndex() {
9594
return Optional.ofNullable(examplesIndex);
9695
}
9796

98-
Optional<Integer> exampleIndex() {
97+
public Optional<Integer> exampleIndex() {
9998
return Optional.ofNullable(exampleIndex);
10099
}
101100

102-
<T> LineageReducer reduce(Supplier<LineageCollector<T>> collector) {
103-
return new LineageReducerDescending(collector);
104-
}
105-
106101
@Override
107102
public boolean equals(Object o) {
108103
if (this == o) return true;

java/src/main/java/io/cucumber/query/LineageCollector.java

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package io.cucumber.query;
22

3+
import io.cucumber.messages.types.Examples;
4+
import io.cucumber.messages.types.Feature;
5+
import io.cucumber.messages.types.GherkinDocument;
36
import io.cucumber.messages.types.Pickle;
7+
import io.cucumber.messages.types.Rule;
8+
import io.cucumber.messages.types.Scenario;
9+
import io.cucumber.messages.types.TableRow;
410

511
import java.util.function.Supplier;
612

7-
import static java.util.Objects.requireNonNull;
8-
913
/**
1014
* Visit the {@link Lineage} of a {@linkplain io.cucumber.messages.types.GherkinDocument GherkinDocument element}
1115
* or {@link Pickle} and reduce it.
@@ -17,14 +21,53 @@
1721
*
1822
* @param <T> the type reduced to.
1923
*/
20-
interface LineageReducer<T> {
24+
public interface LineageReducer<T> {
2125

22-
static <T> LineageReducer<T> descending(Supplier<? extends LineageCollector<T>> collector) {
26+
static <T> LineageReducer<T> descending(Supplier<? extends Collector<T>> collector) {
2327
return new LineageReducerDescending<>(collector);
2428
}
29+
30+
static <T> LineageReducer<T> ascending(Supplier<? extends Collector<T>> collector) {
31+
return new LineageReducerAscending<>(collector);
32+
}
2533

2634
T reduce(Lineage lineage);
2735

2836
T reduce(Lineage lineage, Pickle pickle);
2937

38+
/**
39+
* Collect the {@link Lineage} of a
40+
* {@linkplain io.cucumber.messages.types.GherkinDocument GherkinDocument element}
41+
* or {@link Pickle} and reduce it to a single result.
42+
*
43+
* @param <T> the type reduced to.
44+
*/
45+
interface Collector<T> {
46+
default void add(GherkinDocument document) {
47+
48+
}
49+
50+
default void add(Feature feature) {
51+
52+
}
53+
54+
default void add(Rule rule) {
55+
56+
}
57+
58+
default void add(Scenario scenario) {
59+
60+
}
61+
62+
default void add(Examples examples, int index) {
63+
}
64+
65+
default void add(TableRow example, int index) {
66+
}
67+
68+
default void add(Pickle pickle) {
69+
}
70+
71+
T finish();
72+
}
3073
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.cucumber.query;
2+
3+
import io.cucumber.messages.types.Pickle;
4+
5+
import java.util.function.Supplier;
6+
7+
import static java.util.Objects.requireNonNull;
8+
9+
/**
10+
* Reduces the lineage of a Gherkin document element in ascending order.
11+
*
12+
* @param <T> type to which the lineage is reduced.
13+
*/
14+
class LineageReducerAscending<T> implements LineageReducer<T> {
15+
16+
private final Supplier<? extends Collector<T>> collectorSupplier;
17+
18+
LineageReducerAscending(Supplier<? extends Collector<T>> collectorSupplier) {
19+
this.collectorSupplier = requireNonNull(collectorSupplier);
20+
}
21+
22+
@Override
23+
public T reduce(Lineage lineage) {
24+
Collector<T> collector = collectorSupplier.get();
25+
reduceAddLineage(collector, lineage);
26+
return collector.finish();
27+
}
28+
29+
@Override
30+
public T reduce(Lineage lineage, Pickle pickle) {
31+
Collector<T> collector = collectorSupplier.get();
32+
collector.add(pickle);
33+
reduceAddLineage(collector, lineage);
34+
return collector.finish();
35+
}
36+
37+
private static <T> void reduceAddLineage(Collector<T> reducer, Lineage lineage) {
38+
lineage.example().ifPresent(example -> reducer.add(example, lineage.exampleIndex().orElse(0)));
39+
lineage.examples().ifPresent(examples -> reducer.add(examples, lineage.examplesIndex().orElse(0)));
40+
lineage.scenario().ifPresent(reducer::add);
41+
lineage.rule().ifPresent(reducer::add);
42+
lineage.feature().ifPresent(reducer::add);
43+
reducer.add(lineage.document());
44+
}
45+
}

java/src/main/java/io/cucumber/query/LineageReducerDescending.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,33 @@
1313
*/
1414
class LineageReducerDescending<T> implements LineageReducer<T> {
1515

16-
private final Supplier<? extends LineageCollector<T>> reducerSupplier;
16+
private final Supplier<? extends Collector<T>> collectorSupplier;
1717

18-
LineageReducerDescending(Supplier<? extends LineageCollector<T>> reducerSupplier) {
19-
this.reducerSupplier = requireNonNull(reducerSupplier);
18+
LineageReducerDescending(Supplier<? extends Collector<T>> collectorSupplier) {
19+
this.collectorSupplier = requireNonNull(collectorSupplier);
2020
}
2121

2222
@Override
2323
public T reduce(Lineage lineage) {
24-
LineageCollector<T> reducer = reducerSupplier.get();
25-
reduceAddLineage(reducer, lineage);
26-
return reducer.finish();
24+
Collector<T> collector = collectorSupplier.get();
25+
reduceAddLineage(collector, lineage);
26+
return collector.finish();
2727
}
2828

2929
@Override
3030
public T reduce(Lineage lineage, Pickle pickle) {
31-
LineageCollector<T> reducer = reducerSupplier.get();
32-
reduceAddLineage(reducer, lineage);
33-
reducer.add(pickle);
34-
return reducer.finish();
31+
Collector<T> collector = collectorSupplier.get();
32+
reduceAddLineage(collector, lineage);
33+
collector.add(pickle);
34+
return collector.finish();
3535
}
3636

37-
private static <T> void reduceAddLineage(LineageCollector<T> reducer, Lineage lineage) {
38-
reducer.add(lineage.document());
39-
lineage.feature().ifPresent(reducer::add);
40-
lineage.rule().ifPresent(reducer::add);
41-
lineage.scenario().ifPresent(reducer::add);
42-
lineage.examples().ifPresent(examples -> reducer.add(examples, lineage.examplesIndex().orElse(0)));
43-
lineage.example().ifPresent(example -> reducer.add(example, lineage.exampleIndex().orElse(0)));
37+
private static <T> void reduceAddLineage(Collector<T> collector, Lineage lineage) {
38+
collector.add(lineage.document());
39+
lineage.feature().ifPresent(collector::add);
40+
lineage.rule().ifPresent(collector::add);
41+
lineage.scenario().ifPresent(collector::add);
42+
lineage.examples().ifPresent(examples -> collector.add(examples, lineage.examplesIndex().orElse(0)));
43+
lineage.example().ifPresent(example -> collector.add(example, lineage.exampleIndex().orElse(0)));
4444
}
4545
}

java/src/main/java/io/cucumber/query/NamingCollector.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.cucumber.messages.types.Rule;
77
import io.cucumber.messages.types.Scenario;
88
import io.cucumber.messages.types.TableRow;
9+
import io.cucumber.query.LineageReducer.Collector;
910
import io.cucumber.query.NamingStrategy.ExampleName;
1011
import io.cucumber.query.NamingStrategy.FeatureName;
1112
import io.cucumber.query.NamingStrategy.Strategy;
@@ -24,9 +25,10 @@
2425
*
2526
* @see NamingStrategy
2627
*/
27-
class NamingCollector implements LineageCollector<String> {
28+
class NamingCollector implements Collector<String> {
2829

29-
private final Deque<String> parts = new ArrayDeque<>();
30+
// There are at most 5 levels to a feature file.
31+
private final Deque<String> parts = new ArrayDeque<>(5);
3032
private final CharSequence delimiter = " - ";
3133
private final Strategy strategy;
3234
private final FeatureName featureName;

0 commit comments

Comments
 (0)