Skip to content

Commit

Permalink
Merge pull request #36 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth authored Sep 13, 2024
2 parents 5eb223c + e2fdfa6 commit 8ebda74
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 52 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ on:
pull_request:
branches:
- main
- develop

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
java-version: '11'

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

## [Unreleased]
### Changed
- Client version updated on [5.2.14](https://github.com/reportportal/client-java/releases/tag/5.2.14), by @HardNorth
- Called inner Features are now Nested Steps inside base Feature, by @HardNorth
- Unify Markdown description generation with other agents, by @HardNorth

## [5.0.5]
### Changed
Expand Down
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'

repositories {
mavenLocal()
mavenCentral()
}

Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
name=agent-java-karate
version=5.0.6-SNAPSHOT
version=5.1.0-SNAPSHOT
description=EPAM ReportPortal. Karate test framework [1.3.1, ) adapter
gradle_version=8.2
karate_version=1.4.1
junit_version=5.10.1
mockito_version=5.4.0
test_utils_version=0.0.3
client_version=5.2.13
client_version=5.2.14
slf4j_api_version=2.0.7
logger_version=5.2.2
hamcrest_version=2.2
Expand Down
58 changes: 46 additions & 12 deletions src/main/java/com/epam/reportportal/karate/ReportPortalHook.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

import com.epam.reportportal.karate.utils.BlockingConcurrentHashMap;
import com.epam.reportportal.listeners.ItemStatus;
import com.epam.reportportal.listeners.ItemType;
import com.epam.reportportal.listeners.ListenerParameters;
import com.epam.reportportal.listeners.LogLevel;
import com.epam.reportportal.service.Launch;
import com.epam.reportportal.service.ReportPortal;
import com.epam.reportportal.utils.MemoizingSupplier;
import com.epam.reportportal.utils.StatusEvaluation;
import com.epam.reportportal.utils.markdown.MarkdownUtils;
import com.epam.ta.reportportal.ws.model.FinishExecutionRQ;
import com.epam.ta.reportportal.ws.model.FinishTestItemRQ;
import com.epam.ta.reportportal.ws.model.StartTestItemRQ;
Expand All @@ -34,15 +36,13 @@
import com.intuit.karate.http.HttpRequest;
import com.intuit.karate.http.Response;
import io.reactivex.Maybe;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

Expand All @@ -64,6 +64,7 @@ public class ReportPortalHook implements RuntimeHook {
private final Map<String, ItemStatus> backgroundStatusMap = new ConcurrentHashMap<>();
private final Map<String, Maybe<String>> stepIdMap = new ConcurrentHashMap<>();
private final Map<Maybe<String>, Date> stepStartTimeMap = new ConcurrentHashMap<>();
private final Set<Maybe<String>> innerFeatures = Collections.newSetFromMap(new ConcurrentHashMap<>());
private volatile Thread shutDownHook;

/**
Expand All @@ -72,9 +73,9 @@ public class ReportPortalHook implements RuntimeHook {
* @param reportPortal the ReportPortal instance
*/
public ReportPortalHook(ReportPortal reportPortal) {
ListenerParameters params = reportPortal.getParameters();
StartLaunchRQ rq = buildStartLaunchRq(params);
launch = new MemoizingSupplier<>(() -> {
ListenerParameters params = reportPortal.getParameters();
StartLaunchRQ rq = buildStartLaunchRq(params);
Launch newLaunch = reportPortal.newLaunch(rq);
//noinspection ReactiveStreamsUnusedPublisher
newLaunch.start();
Expand Down Expand Up @@ -153,7 +154,7 @@ protected StartTestItemRQ buildStartFeatureRq(@Nonnull FeatureRuntime fr) {
String parameters = String.format(PARAMETERS_PATTERN, formatParametersAsTable(getParameters(args)));
String description = rq.getDescription();
if (isNotBlank(description)) {
rq.setDescription(String.format(MARKDOWN_DELIMITER_PATTERN, parameters, description));
rq.setDescription(MarkdownUtils.asTwoParts(parameters, description));
} else {
rq.setDescription(parameters);
}
Expand All @@ -163,9 +164,28 @@ protected StartTestItemRQ buildStartFeatureRq(@Nonnull FeatureRuntime fr) {

@Override
public boolean beforeFeature(FeatureRuntime fr) {
StartTestItemRQ rq = buildStartFeatureRq(fr);
featureIdMap.computeIfAbsent(fr.featureCall.feature.getNameForReport(),
f -> new MemoizingSupplier<>(() -> launch.get().startTestItem(buildStartFeatureRq(fr)))
);
f -> new MemoizingSupplier<>(() -> {
if(fr.caller == null || fr.caller.depth == 0) {
return launch.get().startTestItem(rq);
} else {
Maybe<String> scenarioId = scenarioIdMap.get(fr.caller.parentRuntime.scenario.getUniqueId());
if (scenarioId == null) {
LOGGER.error("ERROR: Trying to post unspecified scenario.");
return launch.get().startTestItem(rq);
}
rq.setType(ItemType.STEP.name());
rq.setHasStats(false);
rq.setName(getInnerFeatureName(rq.getName()));
Maybe<String> itemId = launch.get().startTestItem(scenarioId, rq);
innerFeatures.add(itemId);
if (StringUtils.isNotBlank(rq.getDescription())) {
ReportPortalUtils.sendLog(itemId, rq.getDescription(), LogLevel.INFO, rq.getStartTime());
}
return itemId;
}
}));
return true;
}

Expand All @@ -189,6 +209,7 @@ public void afterFeature(FeatureRuntime fr) {
optionalId.ifPresent(featureId -> {
//noinspection ReactiveStreamsUnusedPublisher
launch.get().finishTestItem(featureId, buildFinishFeatureRq(fr));
innerFeatures.remove(featureId);
});
}

Expand All @@ -200,18 +221,31 @@ public void afterFeature(FeatureRuntime fr) {
*/
@Nonnull
protected StartTestItemRQ buildStartScenarioRq(@Nonnull ScenarioRuntime sr) {
return ReportPortalUtils.buildStartScenarioRq(sr.result);
StartTestItemRQ rq = ReportPortalUtils.buildStartScenarioRq(sr.result);
ofNullable(featureIdMap.get(sr.featureRuntime.featureCall.feature.getNameForReport()))
.map(Supplier::get)
.map(featureId -> innerFeatures.contains(featureId) ? featureId : null)
.ifPresent(featureId -> {
rq.setType(ItemType.STEP.name());
rq.setHasStats(false);
rq.setName(getInnerScenarioName(rq.getName()));
});
return rq;
}

@Override
public boolean beforeScenario(ScenarioRuntime sr) {
Optional<Maybe<String>> optionalId = ofNullable(featureIdMap.get(sr.featureRuntime.featureCall.feature.getNameForReport())).map(Supplier::get);
StartTestItemRQ rq = buildStartScenarioRq(sr);
Optional<Maybe<String>> optionalId = ofNullable(featureIdMap.get(sr.featureRuntime.featureCall.feature.getNameForReport()))
.map(Supplier::get);
if (optionalId.isEmpty()) {
LOGGER.error("ERROR: Trying to post unspecified feature.");
}
optionalId.ifPresent(featureId -> {
StartTestItemRQ rq = buildStartScenarioRq(sr);
Maybe<String> scenarioId = launch.get().startTestItem(featureId, rq);
if (innerFeatures.contains(featureId) && StringUtils.isNotBlank(rq.getDescription())) {
ReportPortalUtils.sendLog(scenarioId, rq.getDescription(), LogLevel.INFO);
}
scenarioIdMap.put(sr.scenario.getUniqueId(), scenarioId);
});
return true;
Expand Down
51 changes: 45 additions & 6 deletions src/main/java/com/epam/reportportal/karate/ReportPortalUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.epam.reportportal.utils.AttributeParser;
import com.epam.reportportal.utils.ParameterUtils;
import com.epam.reportportal.utils.TestCaseIdUtils;
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 Down Expand Up @@ -60,8 +61,10 @@ public class ReportPortalUtils {
public static final String SKIPPED_ISSUE_KEY = "skippedIssue";
public static final String SCENARIO_CODE_REFERENCE_PATTERN = "%s/[SCENARIO:%s]";
public static final String EXAMPLE_CODE_REFERENCE_PATTERN = "%s/[EXAMPLE:%s%s]";
public static final String MARKDOWN_DELIMITER = "\n\n---\n\n";
public static final String MARKDOWN_DELIMITER = "\n" + MarkdownUtils.LOGICAL_SEPARATOR + "\n";
public static final String MARKDOWN_DELIMITER_PATTERN = "%s" + MARKDOWN_DELIMITER + "%s";
public static final String FEATURE_TAG = "Feature: ";
public static final String SCENARIO_TAG = "Scenario: ";
private static final Logger LOGGER = LoggerFactory.getLogger(ReportPortalUtils.class);
private static final String PARAMETER_ITEMS_START = "[";
private static final String PARAMETER_ITEMS_END = "]";
Expand Down Expand Up @@ -244,7 +247,7 @@ public static StartTestItemRQ buildStartFeatureRq(@Nonnull Feature feature) {
String featurePath = feature.getResource().getUri().toString();
String description = feature.getDescription();
if (isNotBlank(description)) {
rq.setDescription(String.format(MARKDOWN_DELIMITER_PATTERN, featurePath, description));
rq.setDescription(MarkdownUtils.asTwoParts(featurePath, description));
} else {
rq.setDescription(featurePath);
}
Expand Down Expand Up @@ -319,13 +322,17 @@ public static StartTestItemRQ buildStartScenarioRq(@Nonnull ScenarioResult resul
@Nonnull
public static FinishTestItemRQ buildFinishScenarioRq(@Nonnull ScenarioResult result) {
Scenario scenario = result.getScenario();
FinishTestItemRQ rq = buildFinishTestItemRq(Calendar.getInstance().getTime(), result.getFailureMessageForDisplay() == null ? ItemStatus.PASSED : ItemStatus.FAILED);
FinishTestItemRQ rq = buildFinishTestItemRq(
Calendar.getInstance().getTime(),
result.getFailureMessageForDisplay() == null ? ItemStatus.PASSED : ItemStatus.FAILED
);
rq.setDescription(buildDescription(scenario, result.getErrorMessage(), getParameters(scenario)));
return rq;
}

@Nonnull
private static String buildDescription(@Nonnull Scenario scenario, @Nullable String errorMessage, @Nullable List<ParameterResource> parameters) {
private static String buildDescription(@Nonnull Scenario scenario, @Nullable String errorMessage,
@Nullable List<ParameterResource> parameters) {
StringBuilder descriptionBuilder = new StringBuilder();

if (parameters != null && !parameters.isEmpty()) {
Expand Down Expand Up @@ -425,18 +432,30 @@ public static ItemStatus getStepStatus(String status) {
* @param itemId item ID future
* @param message log message to send
* @param level log level
* @param logTime log time
*/
public static void sendLog(Maybe<String> itemId, String message, LogLevel level) {
public static void sendLog(Maybe<String> itemId, String message, LogLevel level, Date logTime) {
ReportPortal.emitLog(itemId, id -> {
SaveLogRQ rq = new SaveLogRQ();
rq.setMessage(message);
rq.setItemUuid(id);
rq.setLevel(level.name());
rq.setLogTime(Calendar.getInstance().getTime());
rq.setLogTime(logTime);
return rq;
});
}

/**
* Send Step logs to ReportPortal.
*
* @param itemId item ID future
* @param message log message to send
* @param level log level
*/
public static void sendLog(Maybe<String> itemId, String message, LogLevel level) {
sendLog(itemId, message, level, Calendar.getInstance().getTime());
}

/**
* Builds markdown representation of some code or script to be logged to ReportPortal
*
Expand All @@ -446,4 +465,24 @@ public static void sendLog(Maybe<String> itemId, String message, LogLevel level)
public static String asMarkdownCode(String code) {
return String.format(MARKDOWN_CODE_PATTERN, code);
}

/**
* Build name of inner scenario (called by another scenario).
*
* @param name Scenario name
* @return Inner scenario name
*/
public static String getInnerScenarioName(String name) {
return SCENARIO_TAG + name;
}

/**
* Build name of inner feature (called by another scenario).
*
* @param name Feature name
* @return Inner feature name
*/
public static String getInnerFeatureName(String name) {
return FEATURE_TAG + name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
* This class ensures that {@link Map#computeIfAbsent(Object, Function)} is called only once for the same key. It has inner blocking timeout
* of 1 minute to wait for the value to be computed.
*
* @param <K> a key type for the map
* @param <V> a value type to store
*/
public class BlockingConcurrentHashMap<K, V> {
private static final Logger LOGGER = LoggerFactory.getLogger(BlockingConcurrentHashMap.class);

Expand Down Expand Up @@ -69,7 +76,7 @@ public T get(long timeout, TimeUnit unit) throws InterruptedException {

private final Map<K, BlockingReference<V>> map = new ConcurrentHashMap<>();

public void computeIfAbsent(@Nonnull K key, Function<?, V> mappingFunction) {
public void computeIfAbsent(@Nonnull K key, Function<K, V> mappingFunction) {
map.computeIfAbsent(key, k -> new BlockingReference<>()).set(mappingFunction);
}

Expand Down
Loading

0 comments on commit 8ebda74

Please sign in to comment.