1
1
package io .cucumber .core .plugin ;
2
2
3
- import io .cucumber .plugin .event .PickleStepTestStep ;
3
+ import io .cucumber .core .exception .CucumberException ;
4
+ import io .cucumber .plugin .EventListener ;
5
+ import io .cucumber .plugin .StrictAware ;
4
6
import io .cucumber .plugin .event .EventPublisher ;
7
+ import io .cucumber .plugin .event .PickleStepTestStep ;
5
8
import io .cucumber .plugin .event .Result ;
6
9
import io .cucumber .plugin .event .Status ;
7
10
import io .cucumber .plugin .event .TestCaseFinished ;
8
11
import io .cucumber .plugin .event .TestCaseStarted ;
9
12
import io .cucumber .plugin .event .TestRunFinished ;
13
+ import io .cucumber .plugin .event .TestRunStarted ;
10
14
import io .cucumber .plugin .event .TestSourceRead ;
11
15
import io .cucumber .plugin .event .TestStepFinished ;
12
- import io .cucumber .core .exception .CucumberException ;
13
-
14
- import io .cucumber .plugin .EventListener ;
15
- import io .cucumber .plugin .StrictAware ;
16
16
import org .w3c .dom .Document ;
17
17
import org .w3c .dom .Element ;
18
- import org .w3c .dom .NodeList ;
19
18
20
19
import javax .xml .XMLConstants ;
21
20
import javax .xml .parsers .DocumentBuilderFactory ;
26
25
import javax .xml .transform .TransformerFactory ;
27
26
import javax .xml .transform .dom .DOMSource ;
28
27
import javax .xml .transform .stream .StreamResult ;
29
-
30
- import static java .util .Locale .ROOT ;
31
- import static java .util .concurrent .TimeUnit .SECONDS ;
32
-
33
28
import java .io .Closeable ;
34
29
import java .io .IOException ;
35
30
import java .io .PrintWriter ;
38
33
import java .net .URL ;
39
34
import java .text .DecimalFormat ;
40
35
import java .text .NumberFormat ;
36
+ import java .time .Duration ;
37
+ import java .time .Instant ;
41
38
import java .util .ArrayList ;
42
39
import java .util .List ;
43
40
import java .util .Locale ;
44
41
42
+ import static java .util .Locale .ROOT ;
43
+ import static java .util .concurrent .TimeUnit .SECONDS ;
44
+
45
45
public final class JUnitFormatter implements EventListener , StrictAware {
46
46
47
- private static final long NANOS_PER_SECONDS = SECONDS .toNanos (1L );
47
+ private static final long MILLIS_PER_SECOND = SECONDS .toMillis (1L );
48
48
private final Writer writer ;
49
49
private final Document document ;
50
50
private final Element rootElement ;
@@ -55,6 +55,7 @@ public final class JUnitFormatter implements EventListener, StrictAware {
55
55
private String currentFeatureFile = null ;
56
56
private String previousTestCaseName ;
57
57
private int exampleNumber ;
58
+ private Instant started ;
58
59
59
60
@ SuppressWarnings ("WeakerAccess" ) // Used by plugin factory
60
61
public JUnitFormatter (URL writer ) throws IOException {
@@ -72,13 +73,24 @@ private static String getUniqueTestNameForScenarioExample(String testCaseName, i
72
73
return testCaseName + (testCaseName .contains (" " ) ? " " : "_" ) + exampleNumber ;
73
74
}
74
75
76
+ private static String calculateTotalDurationString (Duration result ) {
77
+ DecimalFormat numberFormat = (DecimalFormat ) NumberFormat .getNumberInstance (Locale .US );
78
+ double duration = (double ) result .toMillis () / MILLIS_PER_SECOND ;
79
+ return numberFormat .format (duration );
80
+ }
81
+
75
82
@ Override
76
83
public void setEventPublisher (EventPublisher publisher ) {
84
+ publisher .registerHandlerFor (TestRunStarted .class , this ::handleTestRunStarted );
77
85
publisher .registerHandlerFor (TestSourceRead .class , this ::handleTestSourceRead );
78
86
publisher .registerHandlerFor (TestCaseStarted .class , this ::handleTestCaseStarted );
79
87
publisher .registerHandlerFor (TestCaseFinished .class , this ::handleTestCaseFinished );
80
88
publisher .registerHandlerFor (TestStepFinished .class , this ::handleTestStepFinished );
81
- publisher .registerHandlerFor (TestRunFinished .class , event -> finishReport ());
89
+ publisher .registerHandlerFor (TestRunFinished .class , this ::handleTestRunFinished );
90
+ }
91
+
92
+ private void handleTestRunStarted (TestRunStarted event ) {
93
+ this .started = event .getInstant ();
82
94
}
83
95
84
96
@ Override
@@ -119,13 +131,15 @@ private void handleTestCaseFinished(TestCaseFinished event) {
119
131
}
120
132
}
121
133
122
- private void finishReport ( ) {
134
+ private void handleTestRunFinished ( TestRunFinished event ) {
123
135
try {
136
+ Instant finished = event .getInstant ();
124
137
// set up a transformer
125
138
rootElement .setAttribute ("name" , JUnitFormatter .class .getName ());
126
139
rootElement .setAttribute ("failures" , String .valueOf (rootElement .getElementsByTagName ("failure" ).getLength ()));
127
140
rootElement .setAttribute ("skipped" , String .valueOf (rootElement .getElementsByTagName ("skipped" ).getLength ()));
128
- rootElement .setAttribute ("time" , getTotalDuration (rootElement .getElementsByTagName ("testcase" )));
141
+ rootElement .setAttribute ("errors" , "0" );
142
+ rootElement .setAttribute ("time" , calculateTotalDurationString (Duration .between (started , finished )));
129
143
130
144
TransformerFactory factory = TransformerFactory .newInstance ();
131
145
factory .setFeature (XMLConstants .FEATURE_SECURE_PROCESSING , true );
@@ -148,22 +162,6 @@ private void closeQuietly(Closeable out) {
148
162
}
149
163
}
150
164
151
- private String getTotalDuration (NodeList testCaseNodes ) {
152
- double totalDurationSecondsForAllTimes = 0.0d ;
153
- for (int i = 0 ; i < testCaseNodes .getLength (); i ++) {
154
- try {
155
- double testCaseTime =
156
- Double .parseDouble (testCaseNodes .item (i ).getAttributes ().getNamedItem ("time" ).getNodeValue ());
157
- totalDurationSecondsForAllTimes += testCaseTime ;
158
- } catch (NumberFormatException | NullPointerException e ) {
159
- throw new CucumberException (e );
160
- }
161
- }
162
- DecimalFormat nfmt = (DecimalFormat ) NumberFormat .getNumberInstance (Locale .US );
163
- nfmt .applyPattern ("0.######" );
164
- return nfmt .format (totalDurationSecondsForAllTimes );
165
- }
166
-
167
165
private void increaseAttributeValue (Element element , String attribute ) {
168
166
int value = 0 ;
169
167
if (element .hasAttribute (attribute )) {
@@ -203,7 +201,7 @@ private String calculateElementName(io.cucumber.plugin.event.TestCase testCase)
203
201
}
204
202
205
203
void addTestCaseElement (Document doc , Element tc , Result result ) {
206
- tc .setAttribute ("time" , calculateTotalDurationString (result ));
204
+ tc .setAttribute ("time" , calculateTotalDurationString (result . getDuration () ));
207
205
208
206
StringBuilder sb = new StringBuilder ();
209
207
addStepAndResultListing (sb );
@@ -229,20 +227,14 @@ void addTestCaseElement(Document doc, Element tc, Result result) {
229
227
}
230
228
231
229
void handleEmptyTestCase (Document doc , Element tc , Result result ) {
232
- tc .setAttribute ("time" , calculateTotalDurationString (result ));
230
+ tc .setAttribute ("time" , calculateTotalDurationString (result . getDuration () ));
233
231
234
232
String resultType = strict ? "failure" : "skipped" ;
235
233
Element child = createElementWithMessage (doc , new StringBuilder (), resultType , "The scenario has no steps" );
236
234
237
235
tc .appendChild (child );
238
236
}
239
237
240
- private String calculateTotalDurationString (Result result ) {
241
- DecimalFormat numberFormat = (DecimalFormat ) NumberFormat .getNumberInstance (Locale .US );
242
- numberFormat .applyPattern ("0.######" );
243
- return numberFormat .format (((double ) result .getDuration ().toNanos () / NANOS_PER_SECONDS ));
244
- }
245
-
246
238
private void addStepAndResultListing (StringBuilder sb ) {
247
239
for (int i = 0 ; i < steps .size (); i ++) {
248
240
int length = sb .length ();
0 commit comments