22
33import io .cucumber .core .feature .FeatureWithLines ;
44import io .cucumber .messages .types .Envelope ;
5+ import io .cucumber .messages .types .Pickle ;
6+ import io .cucumber .messages .types .TestCase ;
57import io .cucumber .messages .types .TestCaseFinished ;
6- import io .cucumber .messages .types .TestStepResult ;
8+ import io .cucumber .messages .types .TestCaseStarted ;
9+ import io .cucumber .messages .types .TestRunFinished ;
10+ import io .cucumber .messages .types .TestStepFinished ;
711import io .cucumber .messages .types .TestStepResultStatus ;
812import io .cucumber .plugin .ConcurrentEventListener ;
913import io .cucumber .plugin .event .EventPublisher ;
10- import io .cucumber .query .Query ;
1114
1215import java .io .File ;
1316import java .io .OutputStream ;
1619import java .net .URI ;
1720import java .net .URISyntaxException ;
1821import java .nio .charset .StandardCharsets ;
22+ import java .util .ArrayList ;
23+ import java .util .Collections ;
24+ import java .util .Comparator ;
25+ import java .util .HashMap ;
1926import java .util .HashSet ;
20- import java .util .LinkedHashMap ;
27+ import java .util .List ;
2128import java .util .Map ;
29+ import java .util .Optional ;
2230import java .util .Set ;
2331
2432import static io .cucumber .core .feature .FeatureWithLines .create ;
33+ import static io .cucumber .messages .types .TestStepResultStatus .PASSED ;
34+ import static io .cucumber .messages .types .TestStepResultStatus .SKIPPED ;
35+ import static java .util .Collections .emptyList ;
36+ import static java .util .Comparator .comparing ;
2537import static java .util .Objects .requireNonNull ;
2638
2739/**
3042 */
3143public final class RerunFormatter implements ConcurrentEventListener {
3244
33- private final PrintWriter writer ;
34- private final Map <String , Set <Integer >> featureAndFailedLinesMapping = new LinkedHashMap <>();
3545 private final Query query = new Query ();
46+ private final Map <String , Set <Integer >> featureAndFailedLinesMapping = new HashMap <>();
47+ private final PrintWriter writer ;
3648
3749 public RerunFormatter (OutputStream out ) {
3850 this .writer = createPrintWriter (out );
@@ -68,32 +80,30 @@ public void setEventPublisher(EventPublisher publisher) {
6880 publisher .registerHandlerFor (Envelope .class , event -> {
6981 query .update (event );
7082 event .getTestCaseFinished ().ifPresent (this ::handleTestCaseFinished );
71- event .getTestRunFinished ().ifPresent (testRunFinished -> finishReport () );
83+ event .getTestRunFinished ().ifPresent (this :: handleTestRunFinished );
7284 });
7385 }
7486
87+
7588 private void handleTestCaseFinished (TestCaseFinished event ) {
76- TestStepResultStatus testStepResultStatus = query .findMostSevereTestStepResultBy (event )
77- .map (TestStepResult ::getStatus )
89+ TestStepResultStatus status = query .findMostSevereTestStepResultBy (event )
7890 // By definition
79- .orElse (TestStepResultStatus .PASSED );
80-
81- if (testStepResultStatus == TestStepResultStatus .PASSED
82- || testStepResultStatus == TestStepResultStatus .SKIPPED ) {
91+ .orElse (PASSED );
92+ if (status == PASSED || status == SKIPPED ) {
8393 return ;
8494 }
85-
8695 query .findPickleBy (event ).ifPresent (pickle -> {
87- Set <Integer > lines = featureAndFailedLinesMapping
88- .computeIfAbsent (pickle .getUri (), s -> new HashSet <>());
89- query .findLocationOf (pickle ).ifPresent (location -> {
96+ // Adds the entire feature for rerunning
97+ Set <Integer > lines = featureAndFailedLinesMapping .computeIfAbsent (pickle .getUri (), s -> new HashSet <>());
98+ pickle .getLocation ().ifPresent (location -> {
99+ // Adds the specific scenarios
90100 // TODO: Messages are silly
91101 lines .add ((int ) (long ) location .getLine ());
92102 });
93103 });
94104 }
95105
96- private void finishReport ( ) {
106+ private void handleTestRunFinished ( TestRunFinished testRunFinished ) {
97107 for (Map .Entry <String , Set <Integer >> entry : featureAndFailedLinesMapping .entrySet ()) {
98108 String key = entry .getKey ();
99109 // TODO: Should these be relative?
@@ -104,4 +114,65 @@ private void finishReport() {
104114 writer .close ();
105115 }
106116
117+ /**
118+ * Miniaturized version of Cucumber Query.
119+ * <p>
120+ * The rerun plugin only needs a few things.
121+ */
122+ private static class Query {
123+
124+ private final Map <String , TestCase > testCaseById = new HashMap <>();
125+ private final Map <String , List <TestStepResultStatus >> testStepsResultStatusByTestCaseStartedId = new HashMap <>();
126+ private final Map <String , TestCaseStarted > testCaseStartedById = new HashMap <>();
127+ private final Map <String , Pickle > pickleById = new HashMap <>();
128+
129+ void update (Envelope envelope ) {
130+ envelope .getPickle ().ifPresent (this ::updatePickle );
131+ envelope .getTestCase ().ifPresent (this ::updateTestCase );
132+ envelope .getTestCaseStarted ().ifPresent (this ::updateTestCaseStarted );
133+ envelope .getTestStepFinished ().ifPresent (this ::updateTestStepFinished );
134+ }
135+
136+ private void updatePickle (Pickle event ) {
137+ pickleById .put (event .getId (), event );
138+ }
139+
140+ private void updateTestCase (TestCase event ) {
141+ testCaseById .put (event .getId (), event );
142+ }
143+
144+ private void updateTestCaseStarted (TestCaseStarted testCaseStarted ) {
145+ testCaseStartedById .put (testCaseStarted .getId (), testCaseStarted );
146+ }
147+
148+ private void updateTestStepFinished (TestStepFinished event ) {
149+ String testCaseStartedId = event .getTestCaseStartedId ();
150+ testStepsResultStatusByTestCaseStartedId .computeIfAbsent (testCaseStartedId , s -> new ArrayList <>())
151+ .add (event .getTestStepResult ().getStatus ());
152+ }
153+
154+ public Optional <TestStepResultStatus > findMostSevereTestStepResultBy (TestCaseFinished testCaseFinished ) {
155+ List <TestStepResultStatus > statuses = testStepsResultStatusByTestCaseStartedId
156+ .getOrDefault (testCaseFinished .getTestCaseStartedId (), emptyList ());
157+ if (statuses .isEmpty ()) {
158+ return Optional .empty ();
159+ }
160+ return Optional .of (Collections .max (statuses , comparing (Enum ::ordinal )));
161+ }
162+
163+ public Optional <Pickle > findPickleBy (TestCaseFinished testCaseFinished ) {
164+ String testCaseStartedId = testCaseFinished .getTestCaseStartedId ();
165+ TestCaseStarted testCaseStarted = testCaseStartedById .get (testCaseStartedId );
166+ if (testCaseStarted == null ) {
167+ return Optional .empty ();
168+ }
169+ TestCase testCase = testCaseById .get (testCaseStarted .getTestCaseId ());
170+ if (testCase == null ) {
171+ return Optional .empty ();
172+ }
173+ return Optional .ofNullable (pickleById .get (testCase .getPickleId ()));
174+ }
175+
176+ }
177+
107178}
0 commit comments