Skip to content

Commit ab20f66

Browse files
committed
[Core] Indent pretty formatter stacktrace
Renders the stack trace in the pretty formatter with the same indent as the step that failed it. Before ``` [INFO] Running io.cucumber.skeleton.RunCucumberTest Scenario: a few cukes # io/cucumber/skeleton/belly.feature:3 Given I have 42 cukes in my belly # io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(int) org.opentest4j.AssertionFailedError: expected: "b" but was: "a" at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) at io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(StepDefinitions.java:12) at ✽.I have 42 cukes in my belly(classpath:io/cucumber/skeleton/belly.feature:4) ``` After ``` [INFO] Running io.cucumber.skeleton.RunCucumberTest Scenario: a few cukes # io/cucumber/skeleton/belly.feature:3 Given I have 42 cukes in my belly # io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(int) org.opentest4j.AssertionFailedError: expected: "b" but was: "a" at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) at io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(StepDefinitions.java:12) at ✽.I have 42 cukes in my belly(classpath:io/cucumber/skeleton/belly.feature:4) ```
1 parent daeb1e4 commit ab20f66

12 files changed

+198
-70
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
## [Unreleased]
1313
### Fixed
1414
- [Core] Include root cause when using DataTable.asList and friends ([#2949](https://github.com/cucumber/cucumber-jvm/pull/2949) M.P. Korstanje)
15+
- [Core] Indent stacktrace in pretty formatter ([#2970](https://github.com/cucumber/cucumber-jvm/pull/2970) M.P. Korstanje)
1516
- [JUnit Platform Engine] Set Engine-Version-cucumber attribute ([#2963](https://github.com/cucumber/cucumber-jvm/pull/2963) M.P. Korstanje)
1617

1718
### Changed

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

+10-7
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@
4949
public final class PrettyFormatter implements ConcurrentEventListener, ColorAware {
5050

5151
private static final String SCENARIO_INDENT = "";
52-
private static final String STEP_INDENT = " ";
53-
private static final String STEP_SCENARIO_INDENT = " ";
52+
private static final String STEP_INDENT = SCENARIO_INDENT + " ";
53+
private static final String STEP_SCENARIO_INDENT = STEP_INDENT + " ";
54+
private static final String STACK_TRACE_INDENT = STEP_SCENARIO_INDENT + " ";
5455

5556
private final Map<UUID, Integer> commentStartIndex = new HashMap<>();
5657

@@ -120,7 +121,7 @@ private void preCalculateLocationIndent(TestCaseStarted event) {
120121
private void printTags(TestCaseStarted event) {
121122
List<String> tags = event.getTestCase().getTags();
122123
if (!tags.isEmpty()) {
123-
out.println(PrettyFormatter.SCENARIO_INDENT + String.join(" ", tags));
124+
out.println(SCENARIO_INDENT + String.join(" ", tags));
124125
}
125126
}
126127

@@ -187,21 +188,23 @@ private String formatLocationComment(
187188

188189
private void printError(TestStepFinished event) {
189190
Result result = event.getResult();
190-
printError(result);
191+
printError(STACK_TRACE_INDENT, result);
191192
}
192193

193194
private void printError(TestRunFinished event) {
194195
Result result = event.getResult();
195-
printError(result);
196+
printError(SCENARIO_INDENT, result);
196197
}
197198

198-
private void printError(Result result) {
199+
private void printError(String prefix, Result result) {
199200
Throwable error = result.getError();
200201
if (error != null) {
201202
String name = result.getStatus().name().toLowerCase(ROOT);
202203
Format format = formats.get(name);
203204
String text = printStackTrace(error);
204-
out.println(" " + format.text(text));
205+
// TODO: Java 12+ use String.indent
206+
String indented = text.replaceAll("(\r\n|\r|\n)", "$1" + prefix).trim();
207+
out.println(prefix + format.text(indented));
205208
}
206209
}
207210

cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.regex.Pattern;
4040

4141
import static io.cucumber.core.options.Constants.FILTER_TAGS_PROPERTY_NAME;
42+
import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators;
4243
import static io.cucumber.core.resource.ClasspathSupport.rootPackageUri;
4344
import static java.util.Arrays.asList;
4445
import static java.util.Collections.emptyMap;
@@ -50,7 +51,6 @@
5051
import static org.hamcrest.CoreMatchers.is;
5152
import static org.hamcrest.CoreMatchers.not;
5253
import static org.hamcrest.MatcherAssert.assertThat;
53-
import static org.hamcrest.Matchers.equalToCompressingWhiteSpace;
5454
import static org.hamcrest.collection.IsEmptyCollection.empty;
5555
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
5656
import static org.hamcrest.collection.IsMapContaining.hasEntry;
@@ -411,7 +411,7 @@ void ensure_less_than_1_thread_is_not_allowed() {
411411
parser
412412
.parse("--threads", "0")
413413
.build();
414-
assertThat(output(), equalToCompressingWhiteSpace("--threads must be > 0"));
414+
assertThat(output(), equalCompressingLineSeparators("--threads must be > 0"));
415415
assertThat(parser.exitStatus(), is(Optional.of((byte) 0x1)));
416416
}
417417

@@ -518,7 +518,7 @@ void ensure_less_than_1_count_is_not_allowed() {
518518
parser
519519
.parse("--count", "0")
520520
.build();
521-
assertThat(output(), equalToCompressingWhiteSpace("--count must be > 0"));
521+
assertThat(output(), equalCompressingLineSeparators("--count must be > 0"));
522522
assertThat(parser.exitStatus(), is(Optional.of((byte) 0x1)));
523523
}
524524

cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
import java.util.Locale;
2020
import java.util.UUID;
2121

22+
import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators;
2223
import static java.nio.charset.StandardCharsets.UTF_8;
2324
import static java.time.Instant.ofEpochSecond;
2425
import static java.util.Collections.singletonList;
2526
import static org.hamcrest.MatcherAssert.assertThat;
26-
import static org.hamcrest.text.IsEqualCompressingWhiteSpace.equalToCompressingWhiteSpace;
2727

2828
class DefaultSummaryPrinterTest {
2929

@@ -56,7 +56,7 @@ void does_not_print_duplicate_snippets() {
5656

5757
bus.send(new TestRunFinished(bus.getInstant(), new Result(Status.PASSED, Duration.ZERO, null)));
5858

59-
assertThat(new String(out.toByteArray(), UTF_8), equalToCompressingWhiteSpace("" +
59+
assertThat(new String(out.toByteArray(), UTF_8), equalCompressingLineSeparators("" +
6060
"\n" +
6161
"0 Scenarios\n" +
6262
"0 Steps\n" +
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.cucumber.core.plugin;
2+
3+
import org.hamcrest.Description;
4+
import org.hamcrest.Matcher;
5+
import org.hamcrest.TypeSafeMatcher;
6+
7+
import java.util.Objects;
8+
9+
public class IsEqualCompressingLineSeparators extends TypeSafeMatcher<String> {
10+
11+
private final String expected;
12+
13+
public IsEqualCompressingLineSeparators(String expected) {
14+
Objects.requireNonNull(expected);
15+
this.expected = expected;
16+
}
17+
18+
public String getExpected() {
19+
return expected;
20+
}
21+
22+
@Override
23+
public boolean matchesSafely(String actual) {
24+
return compressNewLines(expected).equals(compressNewLines(actual));
25+
}
26+
27+
@Override
28+
public void describeMismatchSafely(String item, Description mismatchDescription) {
29+
mismatchDescription.appendText("was ").appendValue(item);
30+
}
31+
32+
@Override
33+
public void describeTo(Description description) {
34+
description.appendText("a string equal to ")
35+
.appendValue(expected)
36+
.appendText(" compressing newlines");
37+
}
38+
39+
public String compressNewLines(String actual) {
40+
return actual.replaceAll("[\r\n]+", "\n").trim();
41+
}
42+
43+
public static Matcher<String> equalCompressingLineSeparators(String expectedString) {
44+
return new IsEqualCompressingLineSeparators(expectedString);
45+
}
46+
47+
}

cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java

+27
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.io.ByteArrayOutputStream;
2020
import java.io.InputStream;
21+
import java.io.PrintStream;
22+
import java.io.PrintWriter;
2123
import java.util.Scanner;
2224
import java.util.UUID;
2325

@@ -1486,4 +1488,29 @@ void should_handle_several_features() throws JSONException {
14861488
assertJsonEquals(expected, out);
14871489
}
14881490

1491+
private static class StubException extends RuntimeException {
1492+
1493+
private final String stacktrace;
1494+
1495+
StubException(String message, String stacktrace) {
1496+
super(message);
1497+
this.stacktrace = stacktrace;
1498+
}
1499+
1500+
public StubException() {
1501+
this("message", "the stack trace");
1502+
}
1503+
1504+
@Override
1505+
public void printStackTrace(PrintWriter printWriter) {
1506+
printWriter.print(stacktrace);
1507+
}
1508+
1509+
@Override
1510+
public void printStackTrace(PrintStream printStream) {
1511+
printStream.print(stacktrace);
1512+
}
1513+
1514+
}
1515+
14891516
}

0 commit comments

Comments
 (0)