Skip to content

Commit c0c3e35

Browse files
committed
Support diff toolwindow in IntelliJ IDEA
For the toolwindow are required the "expected" and "actual" attributes in the test-failed-output message. This fixes issue: cucumber#2607
1 parent 2fa32d2 commit c0c3e35

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

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

+85-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ public class TeamCityPlugin implements EventListener {
7777
+ "[testFinished timestamp = '%s' duration = '%s' name = '%s']";
7878
private static final String TEMPLATE_TEST_FAILED = TEAMCITY_PREFIX
7979
+ "[testFailed timestamp = '%s' duration = '%s' message = '%s' details = '%s' name = '%s']";
80+
81+
private static final String TEMPLATE_TEST_COMPARISON_FAILED = TEAMCITY_PREFIX
82+
+ "[testFailed timestamp = '%s' duration = '%s' message = '%s' details = '%s' expected='%s' actual='%s' name = '%s']";
8083
private static final String TEMPLATE_TEST_IGNORED = TEAMCITY_PREFIX
8184
+ "[testIgnored timestamp = '%s' duration = '%s' message = '%s' name = '%s']";
8285

@@ -101,6 +104,22 @@ public class TeamCityPlugin implements EventListener {
101104
private static final Pattern ANNOTATION_GLUE_CODE_LOCATION_PATTERN = Pattern.compile("^(.*)\\.(.*)\\([^:]*\\)");
102105
private static final Pattern LAMBDA_GLUE_CODE_LOCATION_PATTERN = Pattern.compile("^(.*)\\.(.*)\\(.*:.*\\)");
103106

107+
private static final Pattern[] COMPARE_PATTERNS = new Pattern[] {
108+
Pattern.compile("expected: is \"(.*)\"\n\\s*got: \"(.*)\"",
109+
Pattern.DOTALL | Pattern.CASE_INSENSITIVE),
110+
Pattern.compile("expected: is \"(.*)\"\n\\s*but:\\s+was \"(.*)\"",
111+
Pattern.DOTALL | Pattern.CASE_INSENSITIVE),
112+
Pattern.compile("expected:\\s*(.*)\n\\s*got:\\s*(.*)", Pattern.DOTALL | Pattern.CASE_INSENSITIVE),
113+
Pattern.compile("expected same:<(.*)> was not:<(.*)>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE),
114+
Pattern.compile("expected:\\s*\"(.*)\"\n\\s*but: was\\s*\"(.*)\"",
115+
Pattern.DOTALL | Pattern.CASE_INSENSITIVE),
116+
Pattern.compile("expected:\\s*(.*)\n\\s*but: was\\s*(.*)", Pattern.DOTALL | Pattern.CASE_INSENSITIVE),
117+
Pattern.compile("expected:\\s*(.*)\\s+but was:\\s*(.*)",
118+
Pattern.DOTALL | Pattern.CASE_INSENSITIVE),
119+
Pattern.compile("expecting:\\s+<(.*)> to be equal to:\\s+<(.*)>\\s+but was not",
120+
Pattern.DOTALL | Pattern.CASE_INSENSITIVE)
121+
};
122+
104123
private final PrintStream out;
105124
private final List<SnippetsSuggestedEvent> suggestions = new ArrayList<>();
106125
private final Map<URI, Collection<Node>> parsedTestSources = new HashMap<>();
@@ -281,7 +300,16 @@ private void printTestStepFinished(TestStepFinished event) {
281300
case AMBIGUOUS:
282301
case FAILED: {
283302
String details = printStackTrace(error);
284-
print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step failed", details, name);
303+
String message = error.getMessage();
304+
ComparisonFailureData comparisonFailureData = message != null
305+
? createExceptionNotification(message.trim())
306+
: null;
307+
if (comparisonFailureData != null) {
308+
print(TEMPLATE_TEST_COMPARISON_FAILED, timeStamp, duration, "Step failed", details,
309+
comparisonFailureData.getExpected(), comparisonFailureData.getActual(), name);
310+
} else {
311+
print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step failed", details, name);
312+
}
285313
break;
286314
}
287315
default:
@@ -290,6 +318,27 @@ private void printTestStepFinished(TestStepFinished event) {
290318
print(TEMPLATE_TEST_FINISHED, timeStamp, duration, name);
291319
}
292320

321+
private static ComparisonFailureData createExceptionNotification(String message, Pattern pattern) {
322+
final Matcher matcher = pattern.matcher(message);
323+
if (matcher.find() && matcher.end() == message.length()) {
324+
String expected = matcher.group(1);
325+
String actual = matcher.group(2);
326+
return new ComparisonFailureData(expected.replaceAll("\\\\n", "\n"),
327+
actual.replaceAll("\\\\n", "\n"));
328+
}
329+
return null;
330+
}
331+
332+
private static ComparisonFailureData createExceptionNotification(String message) {
333+
for (Pattern pattern : COMPARE_PATTERNS) {
334+
ComparisonFailureData result = createExceptionNotification(message, pattern);
335+
if (result != null) {
336+
return result;
337+
}
338+
}
339+
return null;
340+
}
341+
293342
private String extractName(TestStep step) {
294343
if (step instanceof PickleStepTestStep) {
295344
PickleStepTestStep pickleStepTestStep = (PickleStepTestStep) step;
@@ -420,4 +469,39 @@ private String escape(String source) {
420469
.replace("]", "|]");
421470
}
422471

472+
private static class ComparisonFailureData {
473+
private final String expected;
474+
475+
private final String actual;
476+
477+
ComparisonFailureData(String expected, String actual) {
478+
if (isWrappedWith(expected, '<', '>') && isWrappedWith(actual, '<', '>')) {
479+
expected = unwrap(expected);
480+
actual = unwrap(actual);
481+
}
482+
if (isWrappedWith(expected, '[', ']') && isWrappedWith(actual, '[', ']')) {
483+
expected = unwrap(expected);
484+
actual = unwrap(actual);
485+
}
486+
this.expected = expected;
487+
this.actual = actual;
488+
}
489+
490+
private static boolean isWrappedWith(String text, char startChar, char endChar) {
491+
return text != null && text.length() > 0 &&
492+
text.charAt(0) == startChar && text.charAt(text.length() - 1) == endChar;
493+
}
494+
495+
private static String unwrap(String text) {
496+
return text.substring(1, text.length() - 1);
497+
}
498+
499+
public String getExpected() {
500+
return expected;
501+
}
502+
503+
public String getActual() {
504+
return actual;
505+
}
506+
}
423507
}

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

+43
Original file line numberDiff line numberDiff line change
@@ -342,4 +342,47 @@ void should_print_system_failure_for_failed_hooks() {
342342
"##teamcity[testFinished timestamp = '1970-01-01T12:00:00.000+0000' name = 'Before All/After All']"));
343343
}
344344

345+
@Test
346+
void should_print_comparison_failure_for_failed_assert_equal() {
347+
Feature feature = TestFeatureParser.parse("path/test.feature", "" +
348+
"Feature: feature name\n" +
349+
" Scenario: scenario name\n" +
350+
" Given first step\n");
351+
352+
ByteArrayOutputStream out = new ByteArrayOutputStream();
353+
Runtime.builder()
354+
.withFeatureSupplier(new StubFeatureSupplier(feature))
355+
.withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out)))
356+
.withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID))
357+
.withBackendSupplier(new StubBackendSupplier(
358+
emptyList(),
359+
singletonList(new StubStepDefinition("first step", new RuntimeException("expected: <1> but was: <2>"))),
360+
emptyList()))
361+
.build()
362+
.run();
363+
364+
assertThat(out, bytesContainsString("expected='one value' actual='1' name = '2']"));
365+
}
366+
367+
@Test
368+
void should_print_comparison_failure_for_failed_assert_equal_for_strings() {
369+
Feature feature = TestFeatureParser.parse("path/test.feature", "" +
370+
"Feature: feature name\n" +
371+
" Scenario: scenario name\n" +
372+
" Given first step\n");
373+
374+
ByteArrayOutputStream out = new ByteArrayOutputStream();
375+
Runtime.builder()
376+
.withFeatureSupplier(new StubFeatureSupplier(feature))
377+
.withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out)))
378+
.withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID))
379+
.withBackendSupplier(new StubBackendSupplier(
380+
emptyList(),
381+
singletonList(new StubStepDefinition("first step", new RuntimeException("expected: <[one value]> but was: <[another value]>"))),
382+
emptyList()))
383+
.build()
384+
.run();
385+
386+
assertThat(out, bytesContainsString("expected='one value' actual='another value' name = 'first step']"));
387+
}
345388
}

0 commit comments

Comments
 (0)