Skip to content

Commit

Permalink
Merge pull request #26 from ArtemOAS/RPP_Agents_Java_Cucumber_5_LastE…
Browse files Browse the repository at this point in the history
…rrorLog

Last Error Log
  • Loading branch information
HardNorth authored Nov 21, 2024
2 parents 667e43a + 2171320 commit d0827f8
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 14 deletions.
76 changes: 62 additions & 14 deletions src/main/java/com/epam/reportportal/cucumber/AbstractReporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.event.*;
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 @@ -62,6 +63,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.ofNullable;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace;
Expand All @@ -84,6 +86,8 @@ public abstract class AbstractReporter implements ConcurrentEventListener {
private static final String METHOD_OPENING_BRACKET = "(";
private static final String STEP_DEFINITION_FIELD_NAME = "stepDefinition";
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 @@ -100,6 +104,15 @@ public abstract class AbstractReporter implements ConcurrentEventListener {
// End of feature occurs once launch is finished.
private final Map<URI, 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<>();

private final ThreadLocal<RunningContext.ScenarioContext> currentScenarioContext = new ThreadLocal<>();

public static ReportPortal getReportPortal() {
Expand Down Expand Up @@ -223,10 +236,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 All @@ -247,6 +260,10 @@ protected void afterScenario(TestCaseFinished event) {
RunningContext.ScenarioContext context = getCurrentScenarioContext();
URI featureUri = context.getFeatureUri();
currentScenarioContextMap.remove(Pair.of(context.getLine(), featureUri));
if (mapItemStatus(event.getResult().getStatus()) == ItemStatus.FAILED){
Optional.ofNullable(event.getResult().getError())
.ifPresent(error -> context.getId().subscribe(id -> errorMap.put(id, error)));
}
Date endTime = finishTestItem(context.getId(), event.getResult().getStatus());
featureEndTime.put(featureUri, endTime);
currentScenarioContext.remove();
Expand Down Expand Up @@ -353,7 +370,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 @@ -367,6 +386,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 @@ -687,12 +710,34 @@ protected void removeFromTree(@Nonnull RunningContext.ScenarioContext scenarioCo
*/
@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(id));
Optional<Throwable> currentError = Optional.ofNullable(errorMap.get(id));
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 @@ -706,8 +751,10 @@ protected void finishFeature(@Nullable Maybe<String> itemId, @Nullable Date date
LOGGER.error("BUG: Trying to finish unspecified test item.");
return;
}
Date endTime = ofNullable(dateTime).orElse(Calendar.getInstance().getTime());
Maybe<FinishTestItemRQ> rqMaybe = buildFinishTestItemRequest(itemId, endTime, null);
//noinspection ReactiveStreamsUnusedPublisher
launch.get().finishTestItem(itemId, buildFinishTestItemRequest(itemId, dateTime, null));
rqMaybe.subscribe(rq -> launch.get().finishTestItem(itemId, rq));
}

/**
Expand All @@ -723,11 +770,12 @@ protected Date finishTestItem(@Nullable Maybe<String> itemId, @Nullable Status s
LOGGER.error("BUG: Trying to finish unspecified test item.");
return null;
}
FinishTestItemRQ rq = buildFinishTestItemRequest(itemId, null, mapItemStatus(status));
Date endTime = Calendar.getInstance().getTime();
Maybe<FinishTestItemRQ> rqMaybe = buildFinishTestItemRequest(itemId, endTime, mapItemStatus(status));
new FinishTestItemRQ();
//noinspection ReactiveStreamsUnusedPublisher
launch.get().finishTestItem(itemId, rq);
return rq.getEndTime();
rqMaybe.subscribe(rq -> launch.get().finishTestItem(itemId, rq));
return endTime;
}

/**
Expand Down
54 changes: 54 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,11 @@
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 Pair<String, String> SCENARIO_CODE_REFERENCES_WITH_ERROR = Pair.of("file:///",
"/agent-java-cucumber5/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 +146,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()))
);
}
}

0 comments on commit d0827f8

Please sign in to comment.