Skip to content

Commit 3817e2d

Browse files
committed
WIP on DefaultSummaryPrinter
1 parent 792503f commit 3817e2d

File tree

3 files changed

+381
-450
lines changed

3 files changed

+381
-450
lines changed

cucumber-core/src/main/java/io/cucumber/core/plugin/DefaultSummaryPrinter.java

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,37 @@
11
package io.cucumber.core.plugin;
22

3+
import io.cucumber.messages.types.Envelope;
4+
import io.cucumber.messages.types.Snippet;
5+
import io.cucumber.messages.types.Suggestion;
36
import io.cucumber.plugin.ColorAware;
47
import io.cucumber.plugin.ConcurrentEventListener;
58
import io.cucumber.plugin.event.EventPublisher;
69
import io.cucumber.plugin.event.SnippetsSuggestedEvent;
710
import io.cucumber.plugin.event.TestRunFinished;
11+
import io.cucumber.query.Query;
12+
import io.cucumber.query.Repository;
813

914
import java.io.OutputStream;
1015
import java.io.PrintStream;
16+
import java.util.Collection;
1117
import java.util.LinkedHashSet;
1218
import java.util.List;
1319
import java.util.Locale;
20+
import java.util.Optional;
1421
import java.util.Set;
22+
import java.util.stream.Collectors;
23+
24+
import static io.cucumber.query.Repository.RepositoryFeature.INCLUDE_GHERKIN_DOCUMENT;
25+
import static io.cucumber.query.Repository.RepositoryFeature.INCLUDE_SUGGESTIONS;
1526

1627
public final class DefaultSummaryPrinter implements ColorAware, ConcurrentEventListener {
1728

18-
private final Set<String> snippets = new LinkedHashSet<>();
29+
private final Repository repository = Repository.builder()
30+
.feature(INCLUDE_GHERKIN_DOCUMENT, true)
31+
.feature(INCLUDE_SUGGESTIONS, true)
32+
.build();
33+
private final Query query = new Query(repository);
34+
1935
private final Stats stats;
2036
private final PrintStream out;
2137

@@ -25,20 +41,15 @@ public DefaultSummaryPrinter() {
2541

2642
DefaultSummaryPrinter(OutputStream out, Locale locale) {
2743
this.out = new PrintStream(out);
28-
this.stats = new Stats(locale);
44+
this.stats = new Stats(query, locale);
2945
}
3046

3147
@Override
3248
public void setEventPublisher(EventPublisher publisher) {
33-
stats.setEventPublisher(publisher);
34-
publisher.registerHandlerFor(SnippetsSuggestedEvent.class, this::handleSnippetsSuggestedEvent);
49+
publisher.registerHandlerFor(Envelope.class, repository::update);
3550
publisher.registerHandlerFor(TestRunFinished.class, event -> print());
3651
}
3752

38-
private void handleSnippetsSuggestedEvent(SnippetsSuggestedEvent event) {
39-
this.snippets.addAll(event.getSuggestion().getSnippets());
40-
}
41-
4253
private void print() {
4354
out.println();
4455
printStats();
@@ -65,15 +76,25 @@ private void printErrors() {
6576
}
6677

6778
private void printSnippets() {
79+
Set<Snippet> snippets = query.findAllTestCaseFinished().stream()
80+
.map(query::findPickleBy)
81+
.filter(Optional::isPresent)
82+
.map(Optional::get)
83+
.map(query::findSuggestionsBy)
84+
.flatMap(Collection::stream)
85+
.map(Suggestion::getSnippets)
86+
.flatMap(Collection::stream)
87+
.collect(Collectors.toCollection(LinkedHashSet::new));
88+
6889
if (snippets.isEmpty()) {
6990
return;
7091
}
7192

7293
out.println();
7394
out.println("You can implement missing steps with the snippets below:");
7495
out.println();
75-
for (String snippet : snippets) {
76-
out.println(snippet);
96+
for (Snippet snippet : snippets) {
97+
out.println(snippet.getCode());
7798
out.println();
7899
}
79100
}
Lines changed: 61 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,40 @@
11
package io.cucumber.core.plugin;
22

3+
import io.cucumber.messages.types.Location;
4+
import io.cucumber.messages.types.TestStepFinished;
5+
import io.cucumber.messages.types.TestStepResult;
6+
import io.cucumber.messages.types.TestStepResultStatus;
37
import io.cucumber.plugin.ColorAware;
4-
import io.cucumber.plugin.ConcurrentEventListener;
5-
import io.cucumber.plugin.event.EventPublisher;
6-
import io.cucumber.plugin.event.PickleStepTestStep;
7-
import io.cucumber.plugin.event.Result;
8-
import io.cucumber.plugin.event.Status;
9-
import io.cucumber.plugin.event.TestCase;
10-
import io.cucumber.plugin.event.TestCaseFinished;
11-
import io.cucumber.plugin.event.TestRunFinished;
12-
import io.cucumber.plugin.event.TestRunStarted;
13-
import io.cucumber.plugin.event.TestStepFinished;
8+
import io.cucumber.query.Query;
149

1510
import java.io.PrintStream;
1611
import java.text.DecimalFormat;
1712
import java.text.DecimalFormatSymbols;
18-
import java.time.Duration;
19-
import java.time.Instant;
2013
import java.util.ArrayList;
14+
import java.util.Collections;
2115
import java.util.List;
2216
import java.util.Locale;
17+
import java.util.Map;
18+
import java.util.Optional;
19+
import java.util.function.Function;
20+
import java.util.stream.Collectors;
2321

2422
import static io.cucumber.core.plugin.Formats.ansi;
2523
import static io.cucumber.core.plugin.Formats.monochrome;
2624
import static java.util.Locale.ROOT;
2725
import static java.util.concurrent.TimeUnit.SECONDS;
2826

29-
class Stats implements ConcurrentEventListener, ColorAware {
27+
class Stats implements ColorAware {
3028

3129
private static final long ONE_SECOND = SECONDS.toNanos(1);
3230
private static final long ONE_MINUTE = 60 * ONE_SECOND;
33-
private final SubCounts scenarioSubCounts = new SubCounts();
34-
private final SubCounts stepSubCounts = new SubCounts();
31+
private final Query query;
3532
private final Locale locale;
36-
private final List<TestCase> failedScenarios = new ArrayList<>();
37-
private final List<TestCase> ambiguousScenarios = new ArrayList<>();
38-
private final List<TestCase> pendingScenarios = new ArrayList<>();
39-
private final List<TestCase> undefinedScenarios = new ArrayList<>();
4033
private final List<Throwable> errors = new ArrayList<>();
41-
private Instant startTime = Instant.EPOCH;
42-
private Duration totalDuration = Duration.ZERO;
4334
private Formats formats = ansi();
4435

45-
Stats(Locale locale) {
36+
Stats(Query query, Locale locale) {
37+
this.query = query;
4638
this.locale = locale;
4739
}
4840

@@ -51,102 +43,14 @@ public void setMonochrome(boolean monochrome) {
5143
formats = monochrome ? monochrome() : ansi();
5244
}
5345

54-
@Override
55-
public void setEventPublisher(EventPublisher publisher) {
56-
publisher.registerHandlerFor(TestRunStarted.class, this::setStartTime);
57-
publisher.registerHandlerFor(TestStepFinished.class, this::addStepResult);
58-
publisher.registerHandlerFor(TestCaseFinished.class, this::addScenario);
59-
publisher.registerHandlerFor(TestRunFinished.class, this::setFinishTime);
60-
}
61-
62-
private void setStartTime(TestRunStarted event) {
63-
setStartTime(event.getInstant());
64-
}
65-
66-
private void addStepResult(TestStepFinished event) {
67-
Result result = event.getResult();
68-
if (result.getError() != null) {
69-
addError(result.getError());
70-
}
71-
if (event.getTestStep() instanceof PickleStepTestStep) {
72-
addStep(result.getStatus());
73-
}
74-
}
75-
76-
private void addScenario(TestCaseFinished event) {
77-
TestCase testCase = event.getTestCase();
78-
addScenario(event.getResult().getStatus(), testCase);
79-
}
80-
81-
private void setFinishTime(TestRunFinished event) {
82-
setFinishTime(event.getInstant());
83-
}
84-
85-
void setStartTime(Instant startTime) {
86-
this.startTime = startTime;
87-
}
88-
89-
private void addError(Throwable error) {
90-
errors.add(error);
91-
}
92-
93-
void addStep(Status resultStatus) {
94-
addResultToSubCount(stepSubCounts, resultStatus);
95-
}
96-
97-
void addScenario(Status resultStatus, TestCase testCase) {
98-
addResultToSubCount(scenarioSubCounts, resultStatus);
99-
switch (resultStatus) {
100-
case FAILED:
101-
failedScenarios.add(testCase);
102-
break;
103-
case AMBIGUOUS:
104-
ambiguousScenarios.add(testCase);
105-
break;
106-
case PENDING:
107-
pendingScenarios.add(testCase);
108-
break;
109-
case UNDEFINED:
110-
undefinedScenarios.add(testCase);
111-
break;
112-
default:
113-
// intentionally left blank
114-
}
115-
}
116-
117-
void setFinishTime(Instant finishTime) {
118-
this.totalDuration = Duration.between(startTime, finishTime);
119-
}
120-
121-
private void addResultToSubCount(SubCounts subCounts, Status resultStatus) {
122-
switch (resultStatus) {
123-
case FAILED:
124-
subCounts.failed++;
125-
break;
126-
case AMBIGUOUS:
127-
subCounts.ambiguous++;
128-
break;
129-
case PENDING:
130-
subCounts.pending++;
131-
break;
132-
case UNDEFINED:
133-
subCounts.undefined++;
134-
break;
135-
case SKIPPED:
136-
subCounts.skipped++;
137-
break;
138-
default:
139-
subCounts.passed++;
140-
}
141-
}
142-
14346
public List<Throwable> getErrors() {
14447
return errors;
14548
}
14649

14750
void printStats(PrintStream out) {
14851
printNonZeroResultScenarios(out);
149-
if (stepSubCounts.getTotal() == 0) {
52+
List<TestStepFinished> testStepFinished = query.findAllTestStepFinished();
53+
if (testStepFinished.isEmpty()) {
15054
out.println("0 Scenarios");
15155
out.println("0 Steps");
15256
} else {
@@ -157,30 +61,42 @@ void printStats(PrintStream out) {
15761
}
15862

15963
private void printStepCounts(PrintStream out) {
160-
out.print(stepSubCounts.getTotal());
64+
List<io.cucumber.messages.types.TestStepFinished> testStepsFinished = query.findAllTestStepFinished();
65+
Map<TestStepResultStatus, Long> testStepResultStatus = query.countMostSevereTestStepResultStatus();
66+
67+
out.print(testStepsFinished.size());
16168
out.print(" Steps (");
162-
printSubCounts(out, stepSubCounts);
69+
printSubCounts(out, testStepResultStatus);
16370
out.println(")");
16471
}
16572

16673
private void printScenarioCounts(PrintStream out) {
167-
out.print(scenarioSubCounts.getTotal());
74+
Map<TestStepResultStatus, Long> scenarioSubCounts = query.findAllTestCaseFinished()
75+
.stream()
76+
.map(query::findMostSevereTestStepResultBy)
77+
.filter(Optional::isPresent)
78+
.map(Optional::get)
79+
.map(TestStepResult::getStatus)
80+
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
81+
82+
out.print(query.findAllTestCaseFinished().size());
16883
out.print(" Scenarios (");
16984
printSubCounts(out, scenarioSubCounts);
17085
out.println(")");
17186
}
17287

173-
private void printSubCounts(PrintStream out, SubCounts subCounts) {
88+
private void printSubCounts(PrintStream out, Map<TestStepResultStatus, Long> subCounts) {
17489
boolean addComma = false;
175-
addComma = printSubCount(out, subCounts.failed, Status.FAILED, addComma);
176-
addComma = printSubCount(out, subCounts.ambiguous, Status.AMBIGUOUS, addComma);
177-
addComma = printSubCount(out, subCounts.skipped, Status.SKIPPED, addComma);
178-
addComma = printSubCount(out, subCounts.pending, Status.PENDING, addComma);
179-
addComma = printSubCount(out, subCounts.undefined, Status.UNDEFINED, addComma);
180-
addComma = printSubCount(out, subCounts.passed, Status.PASSED, addComma);
90+
addComma = printSubCount(out, subCounts, TestStepResultStatus.FAILED, addComma);
91+
addComma = printSubCount(out, subCounts, TestStepResultStatus.AMBIGUOUS, addComma);
92+
addComma = printSubCount(out, subCounts, TestStepResultStatus.SKIPPED, addComma);
93+
addComma = printSubCount(out, subCounts, TestStepResultStatus.PENDING, addComma);
94+
addComma = printSubCount(out, subCounts, TestStepResultStatus.UNDEFINED, addComma);
95+
addComma = printSubCount(out, subCounts, TestStepResultStatus.PASSED, addComma);
18196
}
18297

183-
private boolean printSubCount(PrintStream out, int count, Status type, boolean addComma) {
98+
private boolean printSubCount(PrintStream out, Map<TestStepResultStatus, Long> subCounts, TestStepResultStatus type, boolean addComma) {
99+
long count = subCounts.getOrDefault(type, 0L);
184100
if (count != 0) {
185101
if (addComma) {
186102
out.print(", ");
@@ -193,50 +109,44 @@ private boolean printSubCount(PrintStream out, int count, Status type, boolean a
193109
}
194110

195111
private void printDuration(PrintStream out) {
196-
out.print(String.format("%dm", (totalDuration.toNanos() / ONE_MINUTE)));
197-
DecimalFormat format = new DecimalFormat("0.000", new DecimalFormatSymbols(locale));
198-
out.println(format.format(((double) (totalDuration.toNanos() % ONE_MINUTE) / ONE_SECOND)) + "s");
112+
query.findTestRunDuration().ifPresent(duration -> {
113+
out.printf("%dm", (duration.toNanos() / ONE_MINUTE));
114+
DecimalFormat format = new DecimalFormat("0.000", new DecimalFormatSymbols(locale));
115+
out.println(format.format(((double) (duration.toNanos() % ONE_MINUTE) / ONE_SECOND)) + "s");
116+
});
199117
}
200118

201119
private void printNonZeroResultScenarios(PrintStream out) {
202-
printScenarios(out, failedScenarios, Status.FAILED);
203-
printScenarios(out, ambiguousScenarios, Status.AMBIGUOUS);
204-
printScenarios(out, pendingScenarios, Status.PENDING);
205-
printScenarios(out, undefinedScenarios, Status.UNDEFINED);
120+
Map<TestStepResultStatus, List<io.cucumber.messages.types.TestCaseFinished>> testCaseFinishedByStatus = query.findAllTestCaseFinished()
121+
.stream()
122+
.collect(Collectors.groupingBy(testCaseFinished -> query.findMostSevereTestStepResultBy(testCaseFinished).map(TestStepResult::getStatus).orElse(TestStepResultStatus.UNKNOWN)));
123+
124+
printScenarios(out, testCaseFinishedByStatus, TestStepResultStatus.FAILED);
125+
printScenarios(out, testCaseFinishedByStatus, TestStepResultStatus.AMBIGUOUS);
126+
printScenarios(out, testCaseFinishedByStatus, TestStepResultStatus.PENDING);
127+
printScenarios(out, testCaseFinishedByStatus, TestStepResultStatus.UNDEFINED);
206128
}
207129

208-
private void printScenarios(PrintStream out, List<TestCase> scenarios, Status type) {
130+
private void printScenarios(PrintStream out, Map<TestStepResultStatus, List<io.cucumber.messages.types.TestCaseFinished>> testCaseFinishedByStatus, TestStepResultStatus type) {
131+
List<io.cucumber.messages.types.TestCaseFinished> scenarios = testCaseFinishedByStatus.getOrDefault(type, Collections.emptyList());
209132
Format format = formats.get(type.name().toLowerCase(ROOT));
210133
if (!scenarios.isEmpty()) {
211134
out.println(format.text(firstLetterCapitalizedName(type) + " scenarios:"));
212135
}
213-
for (TestCase scenario : scenarios) {
214-
String location = scenario.getUri() + ":" + scenario.getLocation().getLine();
215-
out.println(location + " # " + scenario.getName());
136+
for (io.cucumber.messages.types.TestCaseFinished testCaseFinished : scenarios) {
137+
query.findPickleBy(testCaseFinished).ifPresent(pickle -> {
138+
String location = pickle.getUri() + query.findLocationOf(pickle).map(Location::getLine).map(line -> ":" + line).orElse("");
139+
out.println(location + " # " + pickle.getName());
140+
});
216141
}
217142
if (!scenarios.isEmpty()) {
218143
out.println();
219144
}
220145
}
221146

222-
private String firstLetterCapitalizedName(Status status) {
147+
private String firstLetterCapitalizedName(TestStepResultStatus status) {
223148
String name = status.name();
224-
return name.substring(0, 1) + name.substring(1).toLowerCase(ROOT);
225-
}
226-
227-
static class SubCounts {
228-
229-
public int passed = 0;
230-
public int failed = 0;
231-
public int ambiguous = 0;
232-
public int skipped = 0;
233-
public int pending = 0;
234-
public int undefined = 0;
235-
236-
int getTotal() {
237-
return passed + failed + ambiguous + skipped + pending + undefined;
238-
}
239-
149+
return name.charAt(0) + name.substring(1).toLowerCase(ROOT);
240150
}
241151

242152
}

0 commit comments

Comments
 (0)