Skip to content

Commit 3fab1a4

Browse files
committed
Merge pull request #556 from brasmusson/junit-formatter-fixes
Merge pull request #556 JUnitFormatter: Fix indentation, hook handling and support all-steps-first execution. Update the History.md
2 parents c082373 + 37d9bcb commit 3fab1a4

File tree

9 files changed

+593
-66
lines changed

9 files changed

+593
-66
lines changed

History.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* [Core] Improve readability with unanchored regular expressions ([#485](https://github.com/cucumber/cucumber-jvm/pull/485), [#466](https://github.com/cucumber/cucumber-jvm/issues/466) Anton)
1717
* [Core] Throw exception when unsupported command line options are used. ([#482](https://github.com/cucumber/cucumber-jvm/pull/482), [#463](https://github.com/cucumber/cucumber-jvm/issues/463) Klaus Bayrhammer)
1818
* [Scala] Release cucumber-scala for the two most recent minor releases (currently 2.10.2 and 2.9.3) ([#432](https://github.com/cucumber/cucumber-jvm/issues/432), [#462](https://github.com/cucumber/cucumber-jvm/pull/462) Chris Turner)
19+
* [Core] JUnitFormatter: Fix indentation, hook handling and support all-steps-first execution ([#556](https://github.com/cucumber/cucumber-jvm/pull/556) Björn Rasmusson)
1920

2021
## [1.1.3](https://github.com/cucumber/cucumber-jvm/compare/v1.1.2...v1.1.3) (2013-03-10)
2122

core/src/main/java/cucumber/runtime/Runtime.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,24 @@ public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, RuntimeOp
5151
}
5252

5353
public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collection<? extends Backend> backends, RuntimeOptions runtimeOptions) {
54-
this.resourceLoader = resourceLoader;
55-
this.classLoader = classLoader;
54+
this(resourceLoader, classLoader, backends, runtimeOptions, null);
55+
}
56+
57+
public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collection<? extends Backend> backends,
58+
RuntimeOptions runtimeOptions, RuntimeGlue optionalGlue) {
5659
if (backends.isEmpty()) {
5760
throw new CucumberException("No backends were found. Please make sure you have a backend module on your CLASSPATH.");
5861
}
62+
this.resourceLoader = resourceLoader;
63+
this.classLoader = classLoader;
5964
this.backends = backends;
60-
glue = new RuntimeGlue(undefinedStepsTracker, new LocalizedXStreams(classLoader));
65+
this.runtimeOptions = runtimeOptions;
66+
this.glue = optionalGlue != null ? optionalGlue : new RuntimeGlue(undefinedStepsTracker, new LocalizedXStreams(classLoader));
6167

6268
for (Backend backend : backends) {
6369
backend.loadGlue(glue, runtimeOptions.getGlue());
6470
backend.setUnreportedStepExecutor(this);
6571
}
66-
this.runtimeOptions = runtimeOptions;
6772
}
6873

6974
private static Collection<? extends Backend> loadBackends(ResourceLoader resourceLoader, ClassLoader classLoader) {

core/src/main/java/cucumber/runtime/formatter/JUnitFormatter.java

Lines changed: 77 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class JUnitFormatter implements Formatter, Reporter, StrictAware {
4646

4747
public JUnitFormatter(URL out) throws IOException {
4848
this.out = new UTF8OutputStreamWriter(new URLOutputStream(out));
49+
TestCase.treatSkippedAsFailure = false;
4950
try {
5051
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
5152
rootElement = doc.createElement("testsuite");
@@ -62,13 +63,15 @@ public void feature(Feature feature) {
6263

6364
@Override
6465
public void background(Background background) {
65-
testCase = new TestCase();
66-
root = testCase.createElement(doc);
66+
if (!isCurrentTestCaseCreatedNameless()) {
67+
testCase = new TestCase();
68+
root = testCase.createElement(doc);
69+
}
6770
}
6871

6972
@Override
7073
public void scenario(Scenario scenario) {
71-
if (testCase != null && testCase.scenario == null) {
74+
if (isCurrentTestCaseCreatedNameless()) {
7275
testCase.scenario = scenario;
7376
} else {
7477
testCase = new TestCase(scenario);
@@ -80,6 +83,10 @@ public void scenario(Scenario scenario) {
8083
increaseAttributeValue(rootElement, "tests");
8184
}
8285

86+
private boolean isCurrentTestCaseCreatedNameless() {
87+
return testCase != null && testCase.scenario == null;
88+
}
89+
8390
@Override
8491
public void step(Step step) {
8592
if (testCase != null) testCase.steps.add(step);
@@ -110,6 +117,10 @@ public void result(Result result) {
110117

111118
@Override
112119
public void before(Match match, Result result) {
120+
if (!isCurrentTestCaseCreatedNameless()) {
121+
testCase = new TestCase();
122+
root = testCase.createElement(doc);
123+
}
113124
handleHook(result);
114125
}
115126

@@ -119,10 +130,8 @@ public void after(Match match, Result result) {
119130
}
120131

121132
private void handleHook(Result result) {
122-
if (result.getStatus().equals(Result.FAILED)) {
123-
testCase.results.add(result);
124-
}
125-
133+
testCase.hookResults.add(result);
134+
testCase.updateElement(doc, root);
126135
}
127136

128137
private void increaseAttributeValue(Element element, String attribute) {
@@ -135,6 +144,7 @@ private void increaseAttributeValue(Element element, String attribute) {
135144

136145
@Override
137146
public void scenarioOutline(ScenarioOutline scenarioOutline) {
147+
testCase = null;
138148
}
139149

140150
@Override
@@ -195,6 +205,7 @@ private TestCase() {
195205
static boolean treatSkippedAsFailure = false;
196206
final List<Step> steps = new ArrayList<Step>();
197207
final List<Result> results = new ArrayList<Result>();
208+
final List<Result> hookResults = new ArrayList<Result>();
198209

199210
private Element createElement(Document doc) {
200211
return doc.createElement("testcase");
@@ -206,49 +217,31 @@ private void writeElement(Document doc, Element tc) {
206217
}
207218

208219
public void updateElement(Document doc, Element tc) {
209-
long totalDurationNanos = 0;
210-
for (Result r : results) {
211-
totalDurationNanos += r.getDuration() == null ? 0 : r.getDuration();
212-
}
213-
214-
double totalDurationSeconds = ((double) totalDurationNanos) / 1000000000;
215-
String time = NUMBER_FORMAT.format(totalDurationSeconds);
216-
tc.setAttribute("time", time);
220+
tc.setAttribute("time", calculateTotalDurationString());
217221

218222
StringBuilder sb = new StringBuilder();
223+
addStepAndResultListing(sb);
219224
Result skipped = null, failed = null;
220-
for (int i = 0; i < steps.size(); i++) {
221-
int length = sb.length();
222-
Result result = results.get(i);
225+
for (Result result : results) {
223226
if ("failed".equals(result.getStatus())) failed = result;
224227
if ("undefined".equals(result.getStatus()) || "pending".equals(result.getStatus())) skipped = result;
225-
sb.append(steps.get(i).getKeyword());
226-
sb.append(steps.get(i).getName());
227-
for (int j = 0; sb.length() - length + j < 140; j++) sb.append(".");
228-
sb.append(result.getStatus());
229-
sb.append("\n");
228+
}
229+
for (Result result : hookResults) {
230+
if (failed == null && "failed".equals(result.getStatus())) failed = result;
230231
}
231232
Element child;
232233
if (failed != null) {
233-
sb.append("\nStackTrace:\n");
234-
StringWriter sw = new StringWriter();
235-
failed.getError().printStackTrace(new PrintWriter(sw));
236-
sb.append(sw.toString());
237-
child = doc.createElement("failure");
238-
child.setAttribute("message", failed.getErrorMessage());
239-
child.appendChild(doc.createCDATASection(sb.toString()));
234+
addStackTrace(sb, failed);
235+
child = createElementWithMessage(doc, sb, "failure", failed.getErrorMessage());
240236
} else if (skipped != null) {
241237
if (treatSkippedAsFailure) {
242-
child = doc.createElement("failure");
243-
child.setAttribute("message", "The scenario has pending or undefined step(s)");
238+
child = createElementWithMessage(doc, sb, "failure", "The scenario has pending or undefined step(s)");
244239
}
245240
else {
246-
child = doc.createElement("skipped");
241+
child = createElement(doc, sb, "skipped");
247242
}
248-
child.appendChild(doc.createCDATASection(sb.toString()));
249243
} else {
250-
child = doc.createElement("system-out");
251-
child.appendChild(doc.createCDATASection(sb.toString()));
244+
child = createElement(doc, sb, "system-out");
252245
}
253246

254247
Node existingChild = tc.getFirstChild();
@@ -259,6 +252,54 @@ public void updateElement(Document doc, Element tc) {
259252
}
260253
}
261254

255+
private String calculateTotalDurationString() {
256+
long totalDurationNanos = 0;
257+
for (Result r : results) {
258+
totalDurationNanos += r.getDuration() == null ? 0 : r.getDuration();
259+
}
260+
for (Result r : hookResults) {
261+
totalDurationNanos += r.getDuration() == null ? 0 : r.getDuration();
262+
}
263+
double totalDurationSeconds = ((double) totalDurationNanos) / 1000000000;
264+
return NUMBER_FORMAT.format(totalDurationSeconds);
265+
}
266+
267+
private void addStepAndResultListing(StringBuilder sb) {
268+
for (int i = 0; i < steps.size(); i++) {
269+
int length = sb.length();
270+
String resultStatus = "not executed";
271+
if (i < results.size()) {
272+
resultStatus = results.get(i).getStatus();
273+
}
274+
sb.append(steps.get(i).getKeyword());
275+
sb.append(steps.get(i).getName());
276+
do {
277+
sb.append(".");
278+
} while (sb.length() - length < 76);
279+
sb.append(resultStatus);
280+
sb.append("\n");
281+
}
282+
}
283+
284+
private void addStackTrace(StringBuilder sb, Result failed) {
285+
sb.append("\nStackTrace:\n");
286+
StringWriter sw = new StringWriter();
287+
failed.getError().printStackTrace(new PrintWriter(sw));
288+
sb.append(sw.toString());
289+
}
290+
291+
private Element createElementWithMessage(Document doc, StringBuilder sb, String elementType, String message) {
292+
Element child = createElement(doc, sb, elementType);
293+
child.setAttribute("message", message);
294+
return child;
295+
}
296+
297+
private Element createElement(Document doc, StringBuilder sb, String elementType) {
298+
Element child = doc.createElement(elementType);
299+
child.appendChild(doc.createCDATASection(sb.toString()));
300+
return child;
301+
}
302+
262303
}
263304

264305
}

core/src/test/java/cucumber/runtime/RuntimeTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313

1414
import java.util.Arrays;
1515
import java.util.Collection;
16+
import java.util.Collections;
1617
import java.util.List;
1718
import java.util.Properties;
1819

1920
import static cucumber.runtime.TestHelper.feature;
2021
import static java.util.Arrays.asList;
2122
import static org.junit.Assert.assertEquals;
23+
import static org.junit.Assert.fail;
2224
import static org.mockito.Mockito.mock;
2325

2426
public class RuntimeTest {
@@ -163,6 +165,18 @@ public void strict_with_errors() {
163165
assertEquals(0x1, runtime.exitStatus());
164166
}
165167

168+
@Test
169+
public void should_throw_cucumer_exception_if_no_backends_are_found() throws Exception {
170+
try {
171+
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
172+
new Runtime(new ClasspathResourceLoader(classLoader), classLoader, Collections.<Backend>emptyList(),
173+
new RuntimeOptions(new Properties()));
174+
fail("A CucumberException should have been thrown");
175+
} catch (CucumberException e) {
176+
assertEquals("No backends were found. Please make sure you have a backend module on your CLASSPATH.", e.getMessage());
177+
}
178+
}
179+
166180
private Runtime createStrictRuntime() {
167181
return createRuntime("-g", "anything", "--strict");
168182
}

core/src/test/java/cucumber/runtime/TestHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
@Ignore
1414
public class TestHelper {
15-
static CucumberFeature feature(final String path, final String source) throws IOException {
15+
public static CucumberFeature feature(final String path, final String source) throws IOException {
1616
ArrayList<CucumberFeature> cucumberFeatures = new ArrayList<CucumberFeature>();
1717
FeatureBuilder featureBuilder = new FeatureBuilder(cucumberFeatures);
1818
featureBuilder.parse(new Resource() {

0 commit comments

Comments
 (0)