Skip to content
This repository has been archived by the owner on Nov 15, 2024. It is now read-only.

Commit

Permalink
Merge pull request #50 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth authored Nov 15, 2024
2 parents 5e57a12 + a8d1929 commit bd75f55
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
java-version: '8'

- name: Setup git credentials
uses: oleksiyrudenko/gha-git-credentials@v2.1.1
uses: oleksiyrudenko/gha-git-credentials@v2-latest
with:
name: 'reportportal.io'
email: '[email protected]'
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
# Changelog

## [Unreleased]
### Added
- Common Stack Trace frames skip in description and logs, by @HardNorth
- Reporting of Last Error Log in Item description, by @HardNorth and @ArtemOAS
### Changed
- Client version updated on [5.2.21](https://github.com/reportportal/client-java/releases/tag/5.2.21), by @HardNorth

## [5.2.2]
### Changed
- Client version updated on [5.2.13](https://github.com/reportportal/client-java/releases/tag/5.2.13), by @HardNorth
### Removed
- `OkHttp` dependency, by @HardNorth
- JSR-305 dependency, by @HardNorth

## [5.2.1]
### Changed
Expand Down
1 change: 1 addition & 0 deletions README_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Cucumber2 Agent for ReportPortal
> **End of support**: The agent lifecycle already ended. You are free to use it as long as you prefer, but no new updates will be published.
> **DISCLAIMER**: We use Google Analytics for sending anonymous usage information such as agent's and client's names,
> and their versions after a successful launch start. This information might help us to improve both ReportPortal
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ repositories {
}

dependencies {
api 'com.epam.reportportal:client-java:5.2.13'
api 'com.epam.reportportal:client-java:5.2.21'
api "io.cucumber:cucumber-java:${project.cucumber_version}"

implementation 'org.slf4j:slf4j-api:2.0.7'
Expand All @@ -58,7 +58,7 @@ dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:${project.junit_version}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${project.junit_version}"
testImplementation "org.junit.jupiter:junit-jupiter-engine:${project.junit_version}"
testImplementation 'org.apache.commons:commons-io:1.3.2'
testImplementation 'commons-io:commons-io:2.16.1'
}

test {
Expand Down
122 changes: 91 additions & 31 deletions src/main/java/com/epam/reportportal/cucumber/AbstractReporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
import com.epam.reportportal.service.tree.TestItemTree;
import com.epam.reportportal.utils.*;
import com.epam.reportportal.utils.files.ByteSource;
import com.epam.reportportal.utils.formatting.MarkdownUtils;
import com.epam.reportportal.utils.http.ContentType;
import com.epam.reportportal.utils.markdown.MarkdownUtils;
import com.epam.reportportal.utils.properties.SystemAttributesExtractor;
import com.epam.ta.reportportal.ws.model.FinishExecutionRQ;
import com.epam.ta.reportportal.ws.model.FinishTestItemRQ;
Expand All @@ -47,6 +47,7 @@
import gherkin.ast.Tag;
import gherkin.pickles.*;
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,10 +67,11 @@
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 com.epam.reportportal.utils.formatting.ExceptionUtils.getStackTrace;
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;
import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace;

/**
* Abstract Cucumber 2.x formatter for Report Portal
Expand All @@ -87,6 +89,7 @@ public abstract class AbstractReporter implements Formatter {
private static final String GET_LOCATION_METHOD_NAME = "getLocation";
private static final String METHOD_OPENING_BRACKET = "(";
private static final String DOCSTRING_DECORATOR = "\n\"\"\"\n";
private static final String ERROR_FORMAT = "Error:\n%s";

public static final TestItemTree ITEM_TREE = new TestItemTree();
private static volatile ReportPortal REPORT_PORTAL = ReportPortal.builder().build();
Expand All @@ -106,6 +109,15 @@ public abstract class AbstractReporter implements Formatter {
// 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<Maybe<String>, String> descriptionsMap = new ConcurrentHashMap<>();
/**
* This map uses to record errors to append to the description.
*/
private final Map<Maybe<String>, Throwable> errorMap = new ConcurrentHashMap<>();

public static ReportPortal getReportPortal() {
return REPORT_PORTAL;
}
Expand Down Expand Up @@ -312,13 +324,19 @@ protected RunningContext.ScenarioContext getCurrentScenarioContext() {
* @param scenarioContext current scenario context
*/
protected void beforeScenario(RunningContext.FeatureContext featureContext, RunningContext.ScenarioContext scenarioContext) {
String scenarioName = Utils.buildName(scenarioContext.getKeyword(),
String scenarioName = Utils.buildName(
scenarioContext.getKeyword(),
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);
descriptionsMap.put(id, ofNullable(startTestItemRQ.getDescription()).orElse(StringUtils.EMPTY));
scenarioContext.setId(id);
if (launch.get().getParameters().isCallbackReportingEnabled()) {
addToTree(featureContext, scenarioContext);
Expand All @@ -338,6 +356,9 @@ private void removeFromTree(RunningContext.FeatureContext featureContext, Runnin
*/
protected void afterScenario(TestCaseFinished event) {
RunningContext.ScenarioContext context = getCurrentScenarioContext();
if (mapItemStatus(event.result.getStatus()) == ItemStatus.FAILED) {
Optional.ofNullable(event.result.getError()).ifPresent(error -> errorMap.put(context.getId(), error));
}
String featureUri = context.getFeatureUri();
currentScenarioContextMap.remove(Pair.of(context.getLine(), featureUri));
Date endTime = finishTestItem(context.getId(), event.result.getStatus());
Expand Down Expand Up @@ -381,7 +402,8 @@ protected Maybe<String> startStep(@Nonnull Maybe<String> scenarioId, @Nonnull St
}

private void addToTree(RunningContext.ScenarioContext scenarioContext, String text, Maybe<String> stepId) {
retrieveLeaf(scenarioContext.getFeatureUri(),
retrieveLeaf(
scenarioContext.getFeatureUri(),
scenarioContext.getLine(),
ITEM_TREE
).ifPresent(scenarioLeaf -> scenarioLeaf.getChildItems().put(createKey(text), TestItemTree.createTestItemLeaf(stepId)));
Expand All @@ -395,11 +417,14 @@ private void addToTree(RunningContext.ScenarioContext scenarioContext, String te
protected void beforeStep(TestStep testStep) {
RunningContext.ScenarioContext context = getCurrentScenarioContext();
Step step = context.getStep(testStep);
Maybe<String> stepId = startStep(context.getId(), buildStartStepRequest(testStep, context.getStepPrefix(), step.getKeyword()));
StartTestItemRQ startTestItemRQ = buildStartStepRequest(testStep, context.getStepPrefix(), step.getKeyword());
Maybe<String> stepId = startStep(context.getId(), startTestItemRQ);
context.setCurrentStepId(stepId);
String stepText = step.getText();
context.setCurrentText(stepText);

if (startTestItemRQ.isHasStats()) {
descriptionsMap.put(stepId, ofNullable(startTestItemRQ.getDescription()).orElse(StringUtils.EMPTY));
}
if (launch.get().getParameters().isCallbackReportingEnabled()) {
addToTree(context, stepText, stepId);
}
Expand All @@ -413,6 +438,9 @@ 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 -> errorMap.put(context.getCurrentStepId(), error));
}
finishTestItem(context.getCurrentStepId(), result.getStatus());
context.setCurrentStepId(null);
context.setCurrentText(null);
Expand All @@ -436,8 +464,8 @@ protected StartTestItemRQ buildStartHookRequest(HookType hookType) {
/**
* Start before/after-hook item on Report Portal
*
* @param parentId parent item id
* @param rq hook start request
* @param parentId parent item id
* @param rq hook start request
* @return hook item id
*/
@Nonnull
Expand Down Expand Up @@ -514,7 +542,7 @@ protected void reportResult(Result result, String message) {
if (errorMessage != null) {
sendLog(errorMessage, level);
} else if (result.getError() != null) {
sendLog(getStackTrace(result.getError()), level);
sendLog(getStackTrace(result.getError(), new Throwable()), level);
}
}

Expand All @@ -537,7 +565,8 @@ private static String getDataType(@Nonnull byte[] data) {
protected void embedding(String mimeType, byte[] data) {
String type = ofNullable(mimeType).filter(ContentType::isValidType).orElseGet(() -> getDataType(data));
String attachmentName = ofNullable(type).map(t -> t.substring(0, t.indexOf("/"))).orElse("");
ReportPortal.emitLog(new ReportPortalMessage(ByteSource.wrap(data), type, attachmentName),
ReportPortal.emitLog(
new ReportPortalMessage(ByteSource.wrap(data), type, attachmentName),
"UNKNOWN",
Calendar.getInstance().getTime()
);
Expand Down Expand Up @@ -625,25 +654,29 @@ protected void handleStartOfTestCase(TestCaseStarted event) {
TestCase testCase = event.testCase;
RunningContext.FeatureContext newFeatureContext = new RunningContext.FeatureContext(testCase);
String featureUri = newFeatureContext.getUri();
RunningContext.FeatureContext featureContext = currentFeatureContextMap.computeIfAbsent(featureUri, u -> {
getRootItemId(); // trigger root item creation
newFeatureContext.setFeatureId(startFeature(buildStartFeatureRequest(newFeatureContext.getFeature(), featureUri)));
if (launch.get().getParameters().isCallbackReportingEnabled()) {
addToTree(newFeatureContext);
}
return newFeatureContext;
});
RunningContext.FeatureContext featureContext = currentFeatureContextMap.computeIfAbsent(
featureUri, u -> {
getRootItemId(); // trigger root item creation
newFeatureContext.setFeatureId(startFeature(buildStartFeatureRequest(newFeatureContext.getFeature(), featureUri)));
if (launch.get().getParameters().isCallbackReportingEnabled()) {
addToTree(newFeatureContext);
}
return newFeatureContext;
}
);

if (!featureContext.getUri().equals(testCase.getUri())) {
throw new IllegalStateException("Scenario URI does not match Feature URI.");
}

RunningContext.ScenarioContext newScenarioContext = featureContext.getScenarioContext(testCase);
Pair<Integer, String> scenarioLineFeatureURI = Pair.of(newScenarioContext.getLine(), featureContext.getUri());
RunningContext.ScenarioContext scenarioContext = currentScenarioContextMap.computeIfAbsent(scenarioLineFeatureURI, k -> {
currentScenarioContext.set(newScenarioContext);
return newScenarioContext;
});
RunningContext.ScenarioContext scenarioContext = currentScenarioContextMap.computeIfAbsent(
scenarioLineFeatureURI, k -> {
currentScenarioContext.set(newScenarioContext);
return newScenarioContext;
}
);

beforeScenario(featureContext, scenarioContext);
}
Expand Down Expand Up @@ -678,15 +711,37 @@ protected void handleTestStepFinished(TestStepFinished event) {
* @return finish request
*/
@Nonnull
@SuppressWarnings("unused")
protected FinishTestItemRQ buildFinishTestItemRequest(@Nonnull Maybe<String> itemId, @Nullable Date finishTime,
@Nullable ItemStatus status) {
FinishTestItemRQ rq = new FinishTestItemRQ();
if (status == ItemStatus.FAILED) {
Optional<String> currentDescription = Optional.ofNullable(descriptionsMap.remove(itemId));
Optional<Throwable> errorDescription = Optional.ofNullable(errorMap.remove(itemId));
currentDescription.flatMap(description -> errorDescription.map(errorMessage -> resolveDescriptionErrorMessage(
description,
errorMessage
))).ifPresent(rq::setDescription);
}
ofNullable(status).ifPresent(s -> rq.setStatus(s.name()));
rq.setEndTime(ofNullable(finishTime).orElse(Calendar.getInstance().getTime()));
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) {
String errorStr = format(ERROR_FORMAT, getStackTrace(error, new Throwable()));
return Optional.ofNullable(currentDescription)
.filter(StringUtils::isNotBlank)
.map(description -> MarkdownUtils.asTwoParts(currentDescription, errorStr))
.orElse(errorStr);
}

/**
* Finish a feature with specific date and time
*
Expand All @@ -698,8 +753,10 @@ protected void finishFeature(Maybe<String> itemId, Date dateTime) {
LOGGER.error("BUG: Trying to finish unspecified test item.");
return;
}
Date endTime = ofNullable(dateTime).orElse(Calendar.getInstance().getTime());
FinishTestItemRQ finishTestItemRQ = buildFinishTestItemRequest(itemId, endTime, null);
//noinspection ReactiveStreamsUnusedPublisher
launch.get().finishTestItem(itemId, buildFinishTestItemRequest(itemId, dateTime, null));
launch.get().finishTestItem(itemId, finishTestItemRQ);
}

/**
Expand All @@ -714,7 +771,8 @@ protected ItemStatus mapItemStatus(@Nullable Result.Type status) {
return null;
} else {
if (STATUS_MAPPING.get(status) == null) {
LOGGER.error(String.format("Unable to find direct mapping between Cucumber and ReportPortal for TestItem with status: '%s'.",
LOGGER.error(String.format(
"Unable to find direct mapping between Cucumber and ReportPortal for TestItem with status: '%s'.",
status
));
return ItemStatus.SKIPPED;
Expand All @@ -737,10 +795,11 @@ protected Date finishTestItem(@Nullable Maybe<String> itemId, @Nullable Result.T
return null;
}

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

/**
Expand Down Expand Up @@ -906,7 +965,8 @@ protected TestCaseIdEntry getTestCaseId(@Nonnull TestStep testStep, @Nullable St
if (definitionMatch != null) {
try {
Method method = retrieveMethod(definitionMatch);
return TestCaseIdUtils.getTestCaseId(method.getAnnotation(TestCaseId.class),
return TestCaseIdUtils.getTestCaseId(
method.getAnnotation(TestCaseId.class),
method,
codeRef,
(List<Object>) ARGUMENTS_TRANSFORM.apply(testStep.getDefinitionArgument())
Expand Down
Loading

0 comments on commit bd75f55

Please sign in to comment.