Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Last Error Log #90

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 62 additions & 15 deletions src/main/java/com/epam/reportportal/cucumber/AbstractReporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import gherkin.pickles.PickleTable;
import gherkin.pickles.PickleTag;
import io.reactivex.Maybe;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -66,6 +67,7 @@
import static com.epam.reportportal.cucumber.Utils.*;
import static com.epam.reportportal.cucumber.util.ItemTreeUtils.createKey;
import static com.epam.reportportal.cucumber.util.ItemTreeUtils.retrieveLeaf;
import static java.lang.String.format;
import static java.util.Optional.of;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
Expand All @@ -89,6 +91,8 @@ public abstract class AbstractReporter implements ConcurrentEventListener {
private static final String FILE_PREFIX = "file:";
private static final String HOOK_ = "Hook: ";
private static final String DOCSTRING_DECORATOR = "\n\"\"\"\n";
private static final String ERROR_FORMAT = "Error:\n%s";
private static final String DESCRIPTION_ERROR_FORMAT = "%s\n" + ERROR_FORMAT;

public static final TestItemTree ITEM_TREE = new TestItemTree();
private static volatile ReportPortal REPORT_PORTAL = ReportPortal.builder().build();
Expand All @@ -108,6 +112,15 @@ public abstract class AbstractReporter implements ConcurrentEventListener {
// End of feature occurs once launch is finished.
private final Map<String, Date> featureEndTime = new ConcurrentHashMap<>();

/**
* This map uses to record the description of the scenario and the step to append the error to the description.
*/
private final Map<String, String> descriptionsMap = new ConcurrentHashMap<>();
/**
* This map uses to record errors to append to the description.
*/
private final Map<String, Throwable> errorMap = new ConcurrentHashMap<>();

public static ReportPortal getReportPortal() {
return REPORT_PORTAL;
}
Expand Down Expand Up @@ -239,10 +252,10 @@ protected void beforeScenario(RunningContext.FeatureContext featureContext, Runn
AbstractReporter.COLON_INFIX,
scenarioContext.getTestCase().getName()
);
Maybe<String> id = startScenario(featureContext.getFeatureId(),
buildStartScenarioRequest(scenarioContext.getTestCase(), scenarioName, featureContext.getUri(), scenarioContext.getLine())
);
StartTestItemRQ startTestItemRQ = buildStartScenarioRequest(scenarioContext.getTestCase(), scenarioName, featureContext.getUri(), scenarioContext.getLine());
Maybe<String> id = startScenario(featureContext.getFeatureId(), startTestItemRQ);
scenarioContext.setId(id);
id.subscribe(scenarioId -> descriptionsMap.put(scenarioId, ofNullable(startTestItemRQ.getDescription()).orElse(StringUtils.EMPTY)));
if (launch.get().getParameters().isCallbackReportingEnabled()) {
addToTree(featureContext, scenarioContext);
}
Expand Down Expand Up @@ -279,12 +292,34 @@ protected ItemStatus mapItemStatus(@Nullable Result.Type status) {
*/
@Nonnull
@SuppressWarnings("unused")
protected FinishTestItemRQ buildFinishTestItemRequest(@Nonnull Maybe<String> itemId, @Nullable Date finishTime,
@Nullable ItemStatus status) {
FinishTestItemRQ rq = new FinishTestItemRQ();
ofNullable(status).ifPresent(s -> rq.setStatus(s.name()));
rq.setEndTime(ofNullable(finishTime).orElse(Calendar.getInstance().getTime()));
return rq;
protected Maybe<FinishTestItemRQ> buildFinishTestItemRequest(@Nonnull Maybe<String> itemId, @Nullable Date finishTime,
@Nullable ItemStatus status) {
return itemId.map(id -> {
FinishTestItemRQ rq = new FinishTestItemRQ();
if (status == ItemStatus.FAILED) {
Optional<String> currentDescription = Optional.ofNullable(descriptionsMap.get(itemId.blockingGet()));
Optional<Throwable> currentError = Optional.ofNullable(errorMap.get(itemId.blockingGet()));
currentDescription.flatMap(description -> currentError
.map(errorMessage -> resolveDescriptionErrorMessage(description, errorMessage)))
.ifPresent(rq::setDescription);
}
ofNullable(status).ifPresent(s -> rq.setStatus(s.name()));
rq.setEndTime(finishTime);
return rq;
});
}

/**
* Resolve description
* @param currentDescription Current description
* @param error Error message
* @return Description with error
*/
private String resolveDescriptionErrorMessage(String currentDescription, Throwable error) {
return Optional.ofNullable(currentDescription)
.filter(StringUtils::isNotBlank)
.map(description -> format(DESCRIPTION_ERROR_FORMAT, currentDescription, error))
.orElse(format(ERROR_FORMAT, error));
}

/**
Expand All @@ -300,10 +335,11 @@ protected Date finishTestItem(Maybe<String> itemId, Result.Type status) {
return null;
}

FinishTestItemRQ rq = buildFinishTestItemRequest(itemId, null, mapItemStatus(status));
Date endTime = Calendar.getInstance().getTime();
Maybe<FinishTestItemRQ> rqMaybe = buildFinishTestItemRequest(itemId, endTime, mapItemStatus(status));
//noinspection ReactiveStreamsUnusedPublisher
launch.get().finishTestItem(itemId, rq);
return rq.getEndTime();
rqMaybe.subscribe(rq -> launch.get().finishTestItem(itemId, rq));
return endTime;
}

/**
Expand All @@ -330,6 +366,10 @@ protected void afterScenario(TestCaseFinished event) {
RunningContext.ScenarioContext context = getCurrentScenarioContext();
String featureUri = context.getFeatureUri();
currentScenarioContextMap.remove(Pair.of(context.getLine(), featureUri));
if (mapItemStatus(event.result.getStatus()) == ItemStatus.FAILED) {
Optional.ofNullable(event.result.getError())
.ifPresent(error -> context.getId().subscribe(id -> errorMap.put(id, error)));
}
Date endTime = finishTestItem(context.getId(), event.result.getStatus());
featureEndTime.put(featureUri, endTime);
currentScenarioContext.remove();
Expand Down Expand Up @@ -436,7 +476,9 @@ protected void beforeStep(TestStep testStep) {
context.setCurrentStepId(stepId);
String stepText = step.getText();
context.setCurrentText(stepText);

if (rq.isHasStats()) {
stepId.subscribe(id -> descriptionsMap.put(id, ofNullable(rq.getDescription()).orElse(StringUtils.EMPTY)));
}
if (launch.get().getParameters().isCallbackReportingEnabled()) {
addToTree(context, stepText, stepId);
}
Expand All @@ -450,6 +492,10 @@ protected void beforeStep(TestStep testStep) {
protected void afterStep(Result result) {
reportResult(result, null);
RunningContext.ScenarioContext context = getCurrentScenarioContext();
if (mapItemStatus(result.getStatus()) == ItemStatus.FAILED){
Optional.ofNullable(result.getError())
.ifPresent(error -> context.getCurrentStepId().subscribe(id -> errorMap.put(id, error)));
}
finishTestItem(context.getCurrentStepId(), result.getStatus());
context.setCurrentStepId(null);
}
Expand Down Expand Up @@ -620,9 +666,10 @@ protected void finishFeature(Maybe<String> itemId, Date dateTime) {
LOGGER.error("BUG: Trying to finish unspecified test item.");
return;
}
FinishTestItemRQ rq = buildFinishTestItemRequest(itemId, dateTime, null);
Date endTime = ofNullable(dateTime).orElse(Calendar.getInstance().getTime());
Maybe<FinishTestItemRQ> rqMaybe = buildFinishTestItemRequest(itemId, endTime, null);
//noinspection ReactiveStreamsUnusedPublisher
launch.get().finishTestItem(itemId, rq);
rqMaybe.subscribe(rq -> launch.get().finishTestItem(itemId, rq));
}

private void removeFromTree(RunningContext.FeatureContext featureContext) {
Expand Down
57 changes: 57 additions & 0 deletions src/test/java/com/epam/reportportal/cucumber/FailedTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,13 @@

import static com.epam.reportportal.cucumber.integration.util.TestUtils.filterLogs;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.mockito.Mockito.*;

/**
Expand All @@ -52,6 +57,14 @@
public class FailedTest {

private static final String EXPECTED_ERROR = "java.lang.IllegalStateException: " + FailedSteps.ERROR_MESSAGE;
private static final String ERROR_LOG_TEXT = "Error:\n" + EXPECTED_ERROR;
private static final String DESCRIPTION_ERROR_LOG_TEXT =
"file:src/test/resources/features/FailedScenario.feature\n"
+ ERROR_LOG_TEXT;

private static final Pair<String, String> SCENARIO_CODE_REFERENCES_WITH_ERROR = Pair.of("file:",
"src/test/resources/features/FailedScenario.feature\n" + ERROR_LOG_TEXT
);

@CucumberOptions(features = "src/test/resources/features/FailedScenario.feature", glue = {
"com.epam.reportportal.cucumber.integration.feature" }, plugin = { "pretty",
Expand Down Expand Up @@ -136,4 +149,48 @@ public void verify_failed_step_reporting_step_reporter() {
SaveLogRQ expectedError = expectedErrorList.get(0);
assertThat(expectedError.getItemUuid(), equalTo(stepId));
}

@Test
@SuppressWarnings("unchecked")
public void verify_failed_nested_step_description_scenario_reporter() {
TestUtils.runTests(FailedScenarioReporter.class);

verify(client).startTestItem(any());
verify(client).startTestItem(same(suiteId), any());
verify(client).startTestItem(same(testId), any());
verify(client).startTestItem(same(stepId), any());
ArgumentCaptor<FinishTestItemRQ> finishCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);
verify(client).finishTestItem(same(nestedStepId), finishCaptor.capture());
verify(client).finishTestItem(same(stepId), any());
verify(client).finishTestItem(same(testId), finishCaptor.capture());

List<FinishTestItemRQ> finishRqs = finishCaptor.getAllValues();
finishRqs.subList(0, finishRqs.size() - 1).forEach(e -> assertThat(e.getStatus(), equalTo(ItemStatus.FAILED.name())));

FinishTestItemRQ step = finishRqs.get(0);
assertThat(step.getDescription(), not(equalTo(ERROR_LOG_TEXT)));
}

@Test
@SuppressWarnings("unchecked")
public void verify_failed_step_description_step_reporter() {
TestUtils.runTests(FailedStepReporter.class);

verify(client).startTestItem(any());
verify(client).startTestItem(same(suiteId), any());
verify(client).startTestItem(same(testId), any());
ArgumentCaptor<FinishTestItemRQ> finishCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);
verify(client).finishTestItem(same(stepId), finishCaptor.capture());
verify(client).finishTestItem(same(testId), finishCaptor.capture());

List<FinishTestItemRQ> finishRqs = finishCaptor.getAllValues();
finishRqs.forEach(e -> assertThat(e.getStatus(), equalTo(ItemStatus.FAILED.name())));

FinishTestItemRQ step = finishRqs.get(0);
assertThat(step.getDescription(), equalTo(ERROR_LOG_TEXT));
FinishTestItemRQ test = finishRqs.get(1);
assertThat(test.getDescription(),
allOf(notNullValue(), startsWith(SCENARIO_CODE_REFERENCES_WITH_ERROR.getKey()), endsWith(SCENARIO_CODE_REFERENCES_WITH_ERROR.getValue()))
);
}
}