11package io .cucumber .core .plugin ;
22
33import io .cucumber .core .feature .FeatureWithLines ;
4+ import io .cucumber .messages .types .Envelope ;
5+ import io .cucumber .messages .types .TestCaseFinished ;
6+ import io .cucumber .messages .types .TestStepResult ;
7+ import io .cucumber .messages .types .TestStepResultStatus ;
48import io .cucumber .plugin .ConcurrentEventListener ;
59import io .cucumber .plugin .event .EventPublisher ;
6- import io .cucumber .plugin .event .TestCase ;
7- import io .cucumber .plugin .event .TestCaseFinished ;
8- import io .cucumber .plugin .event .TestRunFinished ;
10+ import io .cucumber .query .Query ;
911
1012import java .io .File ;
1113import java .io .OutputStream ;
14+ import java .io .OutputStreamWriter ;
15+ import java .io .PrintWriter ;
1216import java .net .URI ;
1317import java .net .URISyntaxException ;
14- import java .util . ArrayList ;
15- import java .util .Collection ;
18+ import java .nio . charset . StandardCharsets ;
19+ import java .util .HashSet ;
1620import java .util .LinkedHashMap ;
1721import java .util .Map ;
22+ import java .util .Set ;
1823
1924import static io .cucumber .core .feature .FeatureWithLines .create ;
25+ import static java .util .Objects .requireNonNull ;
2026
2127/**
2228 * Formatter for reporting all failed test cases and print their locations
2329 * Failed means: results that make the exit code non-zero.
2430 */
2531public final class RerunFormatter implements ConcurrentEventListener {
2632
27- private final UTF8PrintWriter out ;
28- private final Map <URI , Collection <Integer >> featureAndFailedLinesMapping = new LinkedHashMap <>();
33+ private final PrintWriter writer ;
34+ private final Map <String , Set <Integer >> featureAndFailedLinesMapping = new LinkedHashMap <>();
35+ private final Query query = new Query ();
2936
3037 public RerunFormatter (OutputStream out ) {
31- this .out = new UTF8PrintWriter (out );
38+ this .writer = createPrintWriter (out );
3239 }
3340
34- @ Override
35- public void setEventPublisher (EventPublisher publisher ) {
36- publisher .registerHandlerFor (TestCaseFinished .class , this ::handleTestCaseFinished );
37- publisher .registerHandlerFor (TestRunFinished .class , event -> finishReport ());
38- }
39-
40- private void handleTestCaseFinished (TestCaseFinished event ) {
41- if (!event .getResult ().getStatus ().isOk ()) {
42- recordTestFailed (event .getTestCase ());
43- }
44- }
45-
46- private void finishReport () {
47- for (Map .Entry <URI , Collection <Integer >> entry : featureAndFailedLinesMapping .entrySet ()) {
48- FeatureWithLines featureWithLines = create (relativize (entry .getKey ()), entry .getValue ());
49- out .println (featureWithLines .toString ());
50- }
51-
52- out .close ();
53- }
54-
55- private void recordTestFailed (TestCase testCase ) {
56- URI uri = testCase .getUri ();
57- Collection <Integer > failedTestCaseLines = getFailedTestCaseLines (uri );
58- failedTestCaseLines .add (testCase .getLocation ().getLine ());
59- }
60-
61- private Collection <Integer > getFailedTestCaseLines (URI uri ) {
62- return featureAndFailedLinesMapping .computeIfAbsent (uri , k -> new ArrayList <>());
41+ private static PrintWriter createPrintWriter (OutputStream out ) {
42+ return new PrintWriter (
43+ new OutputStreamWriter (
44+ requireNonNull (out ),
45+ StandardCharsets .UTF_8
46+ )
47+ );
6348 }
6449
6550 static URI relativize (URI uri ) {
@@ -79,4 +64,46 @@ static URI relativize(URI uri) {
7964 throw new IllegalArgumentException (e .getMessage (), e );
8065 }
8166 }
67+
68+ @ Override
69+ public void setEventPublisher (EventPublisher publisher ) {
70+ publisher .registerHandlerFor (Envelope .class , event -> {
71+ query .update (event );
72+ event .getTestCaseFinished ().ifPresent (this ::handleTestCaseFinished );
73+ event .getTestRunFinished ().ifPresent (testRunFinished -> finishReport ());
74+ });
75+ }
76+
77+ private void handleTestCaseFinished (TestCaseFinished event ) {
78+ TestStepResultStatus testStepResultStatus = query .findMostSevereTestStepResultBy (event )
79+ .map (TestStepResult ::getStatus )
80+ // By definition
81+ .orElse (TestStepResultStatus .PASSED );
82+
83+ if (testStepResultStatus == TestStepResultStatus .PASSED
84+ || testStepResultStatus == TestStepResultStatus .SKIPPED ) {
85+ return ;
86+ }
87+
88+ query .findPickleBy (event ).ifPresent (pickle -> {
89+ query .findLocationOf (pickle ).ifPresent (location -> {
90+ Set <Integer > lines = featureAndFailedLinesMapping .computeIfAbsent (pickle .getUri (),
91+ s -> new HashSet <>());
92+ // TODO: Messages are silly
93+ lines .add ((int ) (long ) location .getLine ());
94+ });
95+ });
96+ }
97+
98+ private void finishReport () {
99+ for (Map .Entry <String , Set <Integer >> entry : featureAndFailedLinesMapping .entrySet ()) {
100+ String key = entry .getKey ();
101+ // TODO: Should these be relative?
102+ FeatureWithLines featureWithLines = create (relativize (URI .create (key )), entry .getValue ());
103+ writer .println (featureWithLines .toString ());
104+ }
105+
106+ writer .close ();
107+ }
108+
82109}
0 commit comments