Skip to content

Commit

Permalink
Merge pull request #104 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth authored Nov 19, 2024
2 parents 0cd7ec9 + a9c73a4 commit 3c71380
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 85 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# 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.3]
### Changed
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 'info.cukes:gherkin:2.12.2'

implementation 'org.slf4j:slf4j-api:2.0.7'
Expand All @@ -61,7 +61,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
74 changes: 63 additions & 11 deletions src/main/java/com/epam/reportportal/cucumber/AbstractReporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
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.properties.SystemAttributesExtractor;
import com.epam.ta.reportportal.ws.model.FinishExecutionRQ;
Expand All @@ -39,6 +40,7 @@
import gherkin.formatter.Reporter;
import gherkin.formatter.model.*;
import io.reactivex.Maybe;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.text.StringEscapeUtils;
import org.slf4j.Logger;
Expand All @@ -51,6 +53,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand All @@ -59,10 +62,12 @@
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 com.epam.reportportal.utils.formatting.MarkdownUtils.formatDataTable;
import static java.lang.String.format;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace;

/**
* Abstract Cucumber formatter/reporter for Report Portal
Expand All @@ -79,6 +84,7 @@ public abstract class AbstractReporter implements Formatter, Reporter {
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 @@ -89,6 +95,15 @@ public abstract class AbstractReporter implements Formatter, Reporter {
protected final ThreadLocal<RunningContext.FeatureContext> currentFeatureContext = new ThreadLocal<>();
protected final ThreadLocal<RunningContext.ScenarioContext> currentScenarioContext = new ThreadLocal<>();

/**
* 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<>();

private AtomicBoolean finished = new AtomicBoolean(false);

protected final Supplier<Launch> launch = new MemoizingSupplier<>(new Supplier<Launch>() {
Expand Down Expand Up @@ -259,6 +274,7 @@ protected void beforeScenario(Scenario scenario, String outlineIteration) {
scenarioContext.setId(startScenario(featureContext.getId(), rq));
scenarioContext.setLine(scenario.getLine());
scenarioContext.setFeatureUri(uri);
descriptionsMap.put(scenarioContext.getId(), ofNullable(rq.getDescription()).orElse(StringUtils.EMPTY));
if (myLaunch.getParameters().isCallbackReportingEnabled()) {
addToTree(featureContext, scenarioContext);
}
Expand All @@ -280,11 +296,34 @@ private void removeFromTree(RunningContext.FeatureContext featureContext, Runnin
@SuppressWarnings("unused")
protected FinishTestItemRQ buildFinishTestItemRequest(@Nonnull Maybe<String> itemId, @Nullable ItemStatus status) {
FinishTestItemRQ rq = new FinishTestItemRQ();
if (status == ItemStatus.FAILED) {
Optional<String> currentDescription = Optional.ofNullable(descriptionsMap.get(itemId));
Optional<Throwable> currentError = Optional.ofNullable(errorMap.get(itemId));
currentDescription.flatMap(description -> currentError.map(errorMessage -> resolveDescriptionErrorMessage(
description,
errorMessage
))).ifPresent(rq::setDescription);
}
ofNullable(status).ifPresent(s -> rq.setStatus(s.name()));
rq.setEndTime(Calendar.getInstance().getTime());
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 test item with specified status
*
Expand All @@ -296,8 +335,9 @@ protected void finishTestItem(@Nullable Maybe<String> itemId, @Nullable ItemStat
LOGGER.error("BUG: Trying to finish unspecified test item.");
return;
}
FinishTestItemRQ finishTestItemRQ = buildFinishTestItemRequest(itemId, status);
//noinspection ReactiveStreamsUnusedPublisher
launch.get().finishTestItem(itemId, buildFinishTestItemRequest(itemId, status));
launch.get().finishTestItem(itemId, finishTestItemRQ);
}

/**
Expand Down Expand Up @@ -388,7 +428,8 @@ protected Maybe<String> startStep(@Nonnull Maybe<String> scenarioId, @Nonnull St
}

private void addToTree(@Nonnull RunningContext.ScenarioContext scenarioContext, @Nullable String text, @Nullable 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 @@ -406,6 +447,9 @@ protected void beforeStep(Step step, Match match) {
Maybe<String> stepId = startStep(context.getId(), rq);
context.setCurrentStepId(stepId);
String stepText = step.getName();
if (rq.isHasStats()) {
descriptionsMap.put(stepId, ofNullable(rq.getDescription()).orElse(StringUtils.EMPTY));
}

if (launch.get().getParameters().isCallbackReportingEnabled()) {
addToTree(context, stepText, stepId);
Expand Down Expand Up @@ -441,8 +485,8 @@ protected StartTestItemRQ buildStartHookRequest(boolean isBefore) {
/**
* 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 @@ -503,10 +547,15 @@ protected void reportResult(@Nonnull Result result, @Nullable 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);
}
RunningContext.ScenarioContext currentScenario = getCurrentScenarioContext();
currentScenario.updateStatus(mapStatus(result.getStatus()));
ItemStatus itemStatus = mapStatus(result.getStatus());
currentScenario.updateStatus(itemStatus);
if (itemStatus == ItemStatus.FAILED) {
errorMap.put(currentScenario.getId(), result.getError());
errorMap.put(currentScenario.getCurrentStepId(), result.getError());
}
}

/**
Expand Down Expand Up @@ -572,7 +621,8 @@ private static String getDataType(@Nonnull byte[] data) {
public 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 @@ -702,7 +752,8 @@ protected TestCaseIdEntry getTestCaseId(@Nonnull Match match, @Nullable String c
if (method == null) {
return getTestCaseId(codeRef, match.getArguments());
}
return TestCaseIdUtils.getTestCaseId(method.getAnnotation(TestCaseId.class),
return TestCaseIdUtils.getTestCaseId(
method.getAnnotation(TestCaseId.class),
method,
codeRef,
(List<Object>) ARGUMENTS_TRANSFORM.apply(match.getArguments())
Expand Down Expand Up @@ -866,8 +917,9 @@ protected List<ParameterResource> getParameters(@Nonnull Step step, @Nullable St
.filter(ds -> !ds.isEmpty())
.ifPresent(ds -> params.add(Pair.of("docstring", StringEscapeUtils.escapeHtml4(ds))));
ofNullable(step.getRows()).filter(rows -> !rows.isEmpty())
.ifPresent(rows -> params.add(Pair.of("datatable",
Utils.formatDataTable(rows.stream().map(Row::getCells).collect(Collectors.toList()))
.ifPresent(rows -> params.add(Pair.of(
"datatable",
formatDataTable(rows.stream().map(Row::getCells).collect(Collectors.toList()))
)));
return params.isEmpty() ? Collections.emptyList() : ParameterUtils.getParameters(codeRef, params);
}
Expand Down
55 changes: 4 additions & 51 deletions src/main/java/com/epam/reportportal/cucumber/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,18 @@
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static java.util.Optional.ofNullable;

public class Utils {
private static final String STEP_DEFINITION_FIELD_NAME = "stepDefinition";
private static final String METHOD_FIELD_NAME = "method";
public static final String ONE_SPACE = "\u00A0";
private static final String NEW_LINE = "\r\n";
public static final String TABLE_INDENT = "\u00A0\u00A0\u00A0\u00A0";
public static final String TABLE_COLUMN_SEPARATOR = "|";
public static final String TABLE_ROW_SEPARATOR = "-";

public static final Map<String, ItemStatus> STATUS_MAPPING = Collections.unmodifiableMap(new HashMap<String, ItemStatus>() {{
put("passed", ItemStatus.PASSED);
Expand Down Expand Up @@ -84,48 +81,4 @@ public static Method retrieveMethod(@Nonnull Match match) throws NoSuchFieldExce
public static final Function<List<Argument>, List<?>> ARGUMENTS_TRANSFORM = arguments -> ofNullable(arguments).map(args -> args.stream()
.map(Argument::getVal)
.collect(Collectors.toList())).orElse(null);

/**
* Converts a table represented as List of Lists to a formatted table string
*
* @param table a table object
* @return string representation of the table
*/
@Nonnull
public static String formatDataTable(@Nonnull final List<List<String>> table) {
StringBuilder result = new StringBuilder();
int tableLength = table.stream().mapToInt(List::size).max().orElse(-1);
List<Iterator<String>> iterList = table.stream().map(List::iterator).collect(Collectors.toList());
List<Integer> colSizes = IntStream.range(0, tableLength)
.mapToObj(n -> iterList.stream().filter(Iterator::hasNext).map(Iterator::next).collect(Collectors.toList()))
.map(col -> col.stream().mapToInt(String::length).max().orElse(0))
.collect(Collectors.toList());

boolean header = true;
for (List<String> row : table) {
result.append(TABLE_INDENT).append(TABLE_COLUMN_SEPARATOR);
for (int i = 0; i < row.size(); i++) {
String cell = row.get(i);
int maxSize = colSizes.get(i) - cell.length() + 2;
int lSpace = maxSize / 2;
int rSpace = maxSize - lSpace;
IntStream.range(0, lSpace).forEach(j -> result.append(ONE_SPACE));
result.append(cell);
IntStream.range(0, rSpace).forEach(j -> result.append(ONE_SPACE));
result.append(TABLE_COLUMN_SEPARATOR);
}
if (header) {
header = false;
result.append(NEW_LINE);
result.append(TABLE_INDENT).append(TABLE_COLUMN_SEPARATOR);
for (int i = 0; i < row.size(); i++) {
int maxSize = colSizes.get(i) + 2;
IntStream.range(0, maxSize).forEach(j -> result.append(TABLE_ROW_SEPARATOR));
result.append(TABLE_COLUMN_SEPARATOR);
}
}
result.append(NEW_LINE);
}
return result.toString().trim();
}
}
Loading

0 comments on commit 3c71380

Please sign in to comment.