Skip to content

Commit

Permalink
[Core] Mark pending steps as failed in teamcity plugin (#2264)
Browse files Browse the repository at this point in the history
* [Core] Mark pending steps as failed in teamcity plugin

The teamcity plugin communicates test results to IntelliJ IDEA using teamcity
service messages[1]. Currently the teamcity plugin marks pending steps as
skipped rather then failed. This is inconsistent with the changes from #1960
in which pending steps are considered a test failure.

 1: https://www.jetbrains.com/help/teamcity/service-messages.html

* [Core] Fix teamcity link for cucumber-java8 hooks

The teamcity plugin communicates test results to IntelliJ IDEA using teamcity
service messages[1]. These messages can link to files using a url-like protocol.
The code use to convert code locations from cucumber-java8 step definitions
did not correctly extract the method from this string representation.
  • Loading branch information
mpkorstanje authored Mar 15, 2021
1 parent 19efceb commit 1b42c4c
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Removed

### Fixed
* [Core] Mark pending steps as failed in teamcity plugin ([#2264](https://github.com/cucumber/cucumber-jvm/pull/2264)) M.P. Korstanje)

## [6.10.1] (2021-03-08)

Expand Down
40 changes: 26 additions & 14 deletions core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.joining;

/**
* Outputs Teamcity services messages to std out.
*
* @see <a
* href=https://www.jetbrains.com/help/teamcity/service-messages.html>TeamCity
* - Service Messages</a>
*/
public class TeamCityPlugin implements EventListener {

private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ss.SSSZ");
Expand Down Expand Up @@ -230,7 +237,7 @@ private String extractSourceLocation(TestStep testStep) {
if (java8Matcher.matches()) {
String fqDeclaringClassName = java8Matcher.group(1);
String declaringClassName;
int indexOfPackageSeparator = fqDeclaringClassName.indexOf(".");
int indexOfPackageSeparator = fqDeclaringClassName.lastIndexOf(".");
if (indexOfPackageSeparator != -1) {
declaringClassName = fqDeclaringClassName.substring(indexOfPackageSeparator + 1);
} else {
Expand All @@ -250,22 +257,27 @@ private void printTestStepFinished(TestStepFinished event) {
Throwable error = event.getResult().getError();
Status status = event.getResult().getStatus();
switch (status) {
case SKIPPED:
print(TEMPLATE_TEST_IGNORED, timeStamp, duration, error == null ? "Step skipped" : error.getMessage(),
name);
case SKIPPED: {
String message = error == null ? "Step skipped" : error.getMessage();
print(TEMPLATE_TEST_IGNORED, timeStamp, duration, message, name);
break;
case PENDING:
print(TEMPLATE_TEST_IGNORED, timeStamp, duration, error == null ? "Step pending" : error.getMessage(),
name);
}
case PENDING: {
String details = error == null ? "" : error.getMessage();
print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step pending", details, name);
break;
case UNDEFINED:
print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step undefined", getSnippets(currentTestCase), name);
}
case UNDEFINED: {
String snippets = getSnippets(currentTestCase);
print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step undefined", snippets, name);
break;
}
case AMBIGUOUS:
case FAILED:
case FAILED: {
String details = extractStackTrace(error);
print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step failed", details, name);
break;
}
default:
break;
}
Expand Down Expand Up @@ -307,8 +319,8 @@ private String getSnippets(TestCase testCase) {
URI uri = testCase.getUri();
Location location = testCase.getLocation();
List<Suggestion> suggestionForTestCase = suggestions.stream()
.filter(suggestions -> suggestions.getUri().equals(uri) &&
suggestions.getTestCaseLocation().equals(location))
.filter(suggestion -> suggestion.getUri().equals(uri) &&
suggestion.getTestCaseLocation().equals(location))
.map(SnippetsSuggestedEvent::getSuggestion)
.collect(Collectors.toList());
return createMessage(suggestionForTestCase);
Expand All @@ -322,7 +334,7 @@ private static String createMessage(Collection<Suggestion> suggestions) {
if (suggestions.size() > 1) {
sb.append(" and ").append(suggestions.size() - 1).append(" other step(s)");
}
sb.append("using the snippet(s) below:\n\n");
sb.append(" using the snippet(s) below:\n\n");
String snippets = suggestions
.stream()
.map(Suggestion::getSnippets)
Expand Down Expand Up @@ -379,7 +391,7 @@ private String formatCommand(String command, Object... parameters) {
escapedParameters[i] = escape(parameters[i].toString());
}

return String.format(command, escapedParameters);
return String.format(command, (Object[]) escapedParameters);
}

private String escape(String source) {
Expand Down
50 changes: 48 additions & 2 deletions core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.cucumber.core.plugin;

import io.cucumber.core.backend.StubHookDefinition;
import io.cucumber.core.backend.StubPendingException;
import io.cucumber.core.backend.StubStepDefinition;
import io.cucumber.core.backend.TestCaseState;
import io.cucumber.core.feature.TestFeatureParser;
Expand Down Expand Up @@ -212,7 +213,29 @@ void should_print_error_message_for_undefined_steps() {
.run();

assertThat(out, bytesContainsString("" +
"##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' duration = '0' message = 'Step undefined' details = 'You can implement this step and 1 other step(s)using the snippet(s) below:|n|ntest snippet 0|ntest snippet 1|n' name = 'first step']"));
"##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' duration = '0' message = 'Step undefined' details = 'You can implement this step and 1 other step(s) using the snippet(s) below:|n|ntest snippet 0|ntest snippet 1|n' name = 'first step']"));
}

@Test
void should_print_error_message_for_pending_steps() {
Feature feature = TestFeatureParser.parse("path/test.feature", "" +
"Feature: feature name\n" +
" Scenario: scenario name\n" +
" Given first step\n" +
" Given second step\n");

ByteArrayOutputStream out = new ByteArrayOutputStream();
Runtime.builder()
.withFeatureSupplier(new StubFeatureSupplier(feature))
.withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out)))
.withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID))
.withBackendSupplier(
new StubBackendSupplier(new StubStepDefinition("first step", new StubPendingException())))
.build()
.run();

assertThat(out, bytesContainsString("" +
"##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' duration = '0' message = 'Step pending' details = 'TODO: implement me' name = 'first step']"));
}

@Test
Expand Down Expand Up @@ -241,7 +264,7 @@ void should_print_error_message_for_before_hooks() {
}

@Test
void should_print_location_hint_for_hooks() {
void should_print_location_hint_for_java_hooks() {
Feature feature = TestFeatureParser.parse("path/test.feature", "" +
"Feature: feature name\n" +
" Scenario: scenario name\n" +
Expand All @@ -263,4 +286,27 @@ void should_print_location_hint_for_hooks() {
"##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' locationHint = 'java:test://com.example.HookDefinition/beforeHook' captureStandardOutput = 'true' name = 'Before']\n"));
}

@Test
void should_print_location_hint_for_lambda_hooks() {
Feature feature = TestFeatureParser.parse("path/test.feature", "" +
"Feature: feature name\n" +
" Scenario: scenario name\n" +
" Given first step\n");

ByteArrayOutputStream out = new ByteArrayOutputStream();
Runtime.builder()
.withFeatureSupplier(new StubFeatureSupplier(feature))
.withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out)))
.withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID))
.withBackendSupplier(new StubBackendSupplier(
singletonList(new StubHookDefinition("com.example.HookDefinition.<init>(HookDefinition.java:12)")),
singletonList(new StubStepDefinition("first step")),
emptyList()))
.build()
.run();

assertThat(out, bytesContainsString("" +
"##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' locationHint = 'java:test://com.example.HookDefinition/HookDefinition' captureStandardOutput = 'true' name = 'Before']\n"));
}

}

0 comments on commit 1b42c4c

Please sign in to comment.