diff --git a/CHANGELOG.md b/CHANGELOG.md index a16f3a6..578db59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog ## [Unreleased] +### Changed +- Client version updated on [5.4.4](https://github.com/reportportal/client-java/releases/tag/5.4.4), by @HardNorth +### Fixed +- Some data cleanup in `ReportPortalHook` class, by @HardNorth +- Some step order issues in rare cases, by @HardNorth ## [5.3.1] ### Added diff --git a/gradle.properties b/gradle.properties index 8bf0c88..525cbfd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ karate_version=1.4.1 junit_version=5.10.1 mockito_version=5.4.0 test_utils_version=0.1.0 -client_version=5.4.3 +client_version=5.4.4 slf4j_api_version=2.0.7 logger_version=5.4.0 hamcrest_version=2.2 diff --git a/src/main/java/com/epam/reportportal/karate/ReportPortalHook.java b/src/main/java/com/epam/reportportal/karate/ReportPortalHook.java index 850e7e9..a269a45 100644 --- a/src/main/java/com/epam/reportportal/karate/ReportPortalHook.java +++ b/src/main/java/com/epam/reportportal/karate/ReportPortalHook.java @@ -44,7 +44,6 @@ import org.slf4j.LoggerFactory; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -66,7 +65,7 @@ public class ReportPortalHook implements RuntimeHook { private final Map> backgroundIdMap = new ConcurrentHashMap<>(); private final Map backgroundStatusMap = new ConcurrentHashMap<>(); private final Map> stepIdMap = new ConcurrentHashMap<>(); - private final Map, Instant> stepStartTimeMap = new ConcurrentHashMap<>(); + private final Map stepStartTimeMap = new ConcurrentHashMap<>(); private final Set> innerFeatures = Collections.newSetFromMap(new ConcurrentHashMap<>()); private volatile Thread shutDownHook; @@ -343,6 +342,7 @@ protected void embedAttachments(@Nonnull Maybe itemId, @Nullable List scenarioId = scenarioIdMap.get(sr.scenario.getUniqueId()); + stepStartTimeMap.remove(sr.scenario.getUniqueId()); finishBackground(null, sr); if (scenarioId == null) { @@ -370,20 +370,12 @@ public void afterScenario(ScenarioRuntime sr) { * Get step start time. To keep the steps order in case previous step startTime == current step startTime or * previous step startTime > current step startTime. * - * @param stepId step ID. + * @param scenarioUniqueId Karate's Scenario Unique ID * @return step new startTime in Instant format. */ @Nonnull - private Instant getStepStartTime(@Nullable Maybe stepId) { - Instant currentStepStartTime = Instant.now(); - if (stepId == null || stepStartTimeMap.isEmpty()) { - return currentStepStartTime; - } - Instant lastStepStartTime = stepStartTimeMap.get(stepId); - if (lastStepStartTime.compareTo(currentStepStartTime) >= 0) { - currentStepStartTime = lastStepStartTime.plus(1, ChronoUnit.MICROS); - } - return currentStepStartTime; + private Instant getStepStartTime(@Nullable String scenarioUniqueId) { + return ReportPortalUtils.getStepStartTime(scenarioUniqueId, stepStartTimeMap, launch.get().useMicroseconds()); } /** @@ -396,8 +388,7 @@ private Instant getStepStartTime(@Nullable Maybe stepId) { @Nonnull protected StartTestItemRQ buildStartStepRq(@Nonnull Step step, @Nonnull ScenarioRuntime sr) { StartTestItemRQ rq = ReportPortalUtils.buildStartStepRq(step, sr.scenario); - Maybe stepId = stepIdMap.get(sr.scenario.getUniqueId()); - Instant startTime = getStepStartTime(stepId); + Instant startTime = getStepStartTime(sr.scenario.getUniqueId()); rq.setStartTime(startTime); return rq; } @@ -424,7 +415,6 @@ public boolean beforeStep(Step step, ScenarioRuntime sr) { String scenarioId = sr.scenario.getUniqueId(); Maybe stepId = launch.get().startTestItem(background ? backgroundId : scenarioIdMap.get(scenarioId), stepRq); - stepStartTimeMap.put(stepId, (Instant) stepRq.getStartTime()); stepIdMap.put(scenarioId, stepId); ofNullable(stepRq.getParameters()).filter(params -> !params.isEmpty()) .ifPresent(params -> sendLog(stepId, String.format(PARAMETERS_PATTERN, formatParametersAsTable(params)), LogLevel.INFO)); @@ -490,7 +480,7 @@ public void afterStep(StepResult stepResult, ScenarioRuntime sr) { } sendStepResults(stepResult, sr); - Maybe stepId = stepIdMap.get(sr.scenario.getUniqueId()); + Maybe stepId = stepIdMap.remove(sr.scenario.getUniqueId()); if (stepId == null) { LOGGER.error("ERROR: Trying to finish unspecified step."); return; diff --git a/src/main/java/com/epam/reportportal/karate/ReportPortalPublisher.java b/src/main/java/com/epam/reportportal/karate/ReportPortalPublisher.java index c12bd3a..a623e43 100644 --- a/src/main/java/com/epam/reportportal/karate/ReportPortalPublisher.java +++ b/src/main/java/com/epam/reportportal/karate/ReportPortalPublisher.java @@ -35,7 +35,6 @@ import org.slf4j.LoggerFactory; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -55,7 +54,7 @@ public class ReportPortalPublisher { protected final MemoizingSupplier launch; private final Map> featureIdMap = new HashMap<>(); private final Map> scenarioIdMap = new HashMap<>(); - private final Map, Instant> stepStartTimeMap = new HashMap<>(); + private final Map stepStartTimeMap = new HashMap<>(); private Maybe backgroundId; private ItemStatus backgroundStatus; private Maybe stepId; @@ -164,7 +163,6 @@ public void finishFeature(FeatureResult featureResult) { finishStep(stepResult, scenarioResult); } - stepStartTimeMap.clear(); finishScenario(scenarioResult); } @@ -220,6 +218,7 @@ public void finishScenario(ScenarioResult scenarioResult) { FinishTestItemRQ rq = buildFinishScenarioRq(scenarioResult); Maybe removedScenarioId = scenarioIdMap.remove(scenarioResult.getScenario().getName()); + stepStartTimeMap.remove(scenarioResult.getScenario().getUniqueId()); //noinspection ReactiveStreamsUnusedPublisher launch.get().finishTestItem(removedScenarioId, rq); finishBackground(null, scenarioResult); @@ -285,20 +284,12 @@ public void finishBackground(@Nullable StepResult stepResult, @Nonnull ScenarioR * Get step start time. To keep the steps order in case previous step startTime == current step startTime or * previous step startTime > current step startTime. * - * @param stepId step ID. + * @param scenarioUniqueId Karate's Scenario Unique ID * @return step new startTime in Instant format. */ - private Instant getStepStartTime(@Nonnull Maybe stepId) { - Instant currentStepStartTime = Instant.now(); - - if (!stepStartTimeMap.isEmpty()) { - Instant lastStepStartTime = stepStartTimeMap.get(stepId); - - if (lastStepStartTime.compareTo(currentStepStartTime) >= 0) { - currentStepStartTime = lastStepStartTime.plus(1, ChronoUnit.MICROS); - } - } - return currentStepStartTime; + @Nonnull + private Instant getStepStartTime(@Nullable String scenarioUniqueId) { + return ReportPortalUtils.getStepStartTime(scenarioUniqueId, stepStartTimeMap, launch.get().useMicroseconds()); } /** @@ -311,7 +302,7 @@ private Instant getStepStartTime(@Nonnull Maybe stepId) { @Nonnull protected StartTestItemRQ buildStartStepRq(@Nonnull StepResult stepResult, @Nonnull ScenarioResult scenarioResult) { StartTestItemRQ rq = ReportPortalUtils.buildStartStepRq(stepResult.getStep(), scenarioResult.getScenario()); - Instant startTime = getStepStartTime(stepId); + Instant startTime = getStepStartTime(scenarioResult.getScenario().getUniqueId()); rq.setStartTime(startTime); return rq; } @@ -335,7 +326,6 @@ public void startStep(StepResult stepResult, ScenarioResult scenarioResult) { background && backgroundId != null ? backgroundId : scenarioIdMap.get(scenarioResult.getScenario().getName()), stepRq ); - stepStartTimeMap.put(stepId, (Instant) stepRq.getStartTime()); ofNullable(stepRq.getParameters()).filter(params -> !params.isEmpty()) .ifPresent(params -> sendLog(stepId, String.format(PARAMETERS_PATTERN, formatParametersAsTable(params)), LogLevel.INFO)); ofNullable(step.getTable()).ifPresent(table -> sendLog(stepId, "Table:\n\n" + formatDataTable(table.getRows()), LogLevel.INFO)); diff --git a/src/main/java/com/epam/reportportal/karate/ReportPortalUtils.java b/src/main/java/com/epam/reportportal/karate/ReportPortalUtils.java index 29d989f..25e6177 100644 --- a/src/main/java/com/epam/reportportal/karate/ReportPortalUtils.java +++ b/src/main/java/com/epam/reportportal/karate/ReportPortalUtils.java @@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -472,21 +473,23 @@ public static void sendLog(Maybe itemId, String message, LogLevel level) * @param embed Karate's Embed object */ public static void embedAttachment(@Nonnull Maybe itemId, @Nonnull Embed embed) { - ReportPortal.emitLog(itemId, id -> { - SaveLogRQ rq = new SaveLogRQ(); - rq.setItemUuid(id); - rq.setLevel(LogLevel.INFO.name()); - rq.setLogTime(Instant.now()); - rq.setMessage("Attachment: " + embed.getResourceType().contentType); - - SaveLogRQ.File file = new SaveLogRQ.File(); - file.setName(embed.getFile().getName()); - file.setContent(embed.getBytes()); - file.setContentType(embed.getResourceType().contentType); - rq.setFile(file); - - return rq; - }); + ReportPortal.emitLog( + itemId, id -> { + SaveLogRQ rq = new SaveLogRQ(); + rq.setItemUuid(id); + rq.setLevel(LogLevel.INFO.name()); + rq.setLogTime(Instant.now()); + rq.setMessage("Attachment: " + embed.getResourceType().contentType); + + SaveLogRQ.File file = new SaveLogRQ.File(); + file.setName(embed.getFile().getName()); + file.setContent(embed.getBytes()); + file.setContentType(embed.getResourceType().contentType); + rq.setFile(file); + + return rq; + } + ); } /** @@ -539,4 +542,38 @@ public static String getInnerScenarioName(String name) { public static String getInnerFeatureName(String name) { return FEATURE_TAG + name; } + + /** + * Get step start time. To keep the steps order in case previous step startTime == current step startTime or + * previous step startTime > current step startTime. + * + * @param scenarioUniqueId Karate's Scenario Unique ID, a key for stepStartTimeMap + * @param stepStartTimeMap a holder for start times for every particular scenario + * @param useMicroseconds if server supports microseconds + * @return step new startTime in Instant format. + */ + public static Instant getStepStartTime(@Nullable String scenarioUniqueId, Map stepStartTimeMap, + boolean useMicroseconds) { + Instant currentStepStartTime = Instant.now().truncatedTo(ChronoUnit.MICROS); + if (scenarioUniqueId == null || stepStartTimeMap.isEmpty()) { + stepStartTimeMap.put(scenarioUniqueId, currentStepStartTime); + return currentStepStartTime; + } + Instant lastStepStartTime = stepStartTimeMap.get(scenarioUniqueId); + if (lastStepStartTime == null) { + stepStartTimeMap.put(scenarioUniqueId, currentStepStartTime); + return currentStepStartTime; + } + if (useMicroseconds) { + if (lastStepStartTime.compareTo(currentStepStartTime) >= 0) { + currentStepStartTime = lastStepStartTime.plus(1, ChronoUnit.MICROS); + } + } else { + if (lastStepStartTime.truncatedTo(ChronoUnit.MILLIS).compareTo(currentStepStartTime.truncatedTo(ChronoUnit.MILLIS)) >= 0) { + currentStepStartTime = lastStepStartTime.plus(1, ChronoUnit.MILLIS); + } + } + stepStartTimeMap.put(scenarioUniqueId, currentStepStartTime); + return currentStepStartTime; + } } diff --git a/src/test/java/com/epam/reportportal/karate/timing/SimpleTimingTest.java b/src/test/java/com/epam/reportportal/karate/timing/SimpleTimingTest.java index 9ed2d16..c87e6d7 100644 --- a/src/test/java/com/epam/reportportal/karate/timing/SimpleTimingTest.java +++ b/src/test/java/com/epam/reportportal/karate/timing/SimpleTimingTest.java @@ -23,12 +23,13 @@ import com.epam.ta.reportportal.ws.model.StartTestItemRQ; import com.epam.ta.reportportal.ws.model.launch.StartLaunchRQ; import com.intuit.karate.Results; +import io.reactivex.Maybe; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; -import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -54,13 +55,29 @@ public void setupMock() { mockBatchLogging(client); } + public static Stream testCases() { + return Stream.of( // + Arguments.of(false, false), // + Arguments.of(false, true), // + Arguments.of(true, false), // + Arguments.of(true, true) // + ); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) @ParameterizedTest - @ValueSource(booleans = { true, false }) - public void test_each_item_has_correct_start_date(boolean report) { + @MethodSource("testCases") + public void test_each_item_has_correct_start_date(boolean report, boolean useMicroseconds) { Results results; if (report) { + if (useMicroseconds) { + when(client.getApiInfo()).thenReturn(Maybe.just(TestUtils.testApiInfo())); + } results = TestUtils.runAsReport(rp, TEST_FEATURE); } else { + if (useMicroseconds) { + when(client.getApiInfo()).thenReturn(Maybe.just(TestUtils.testApiInfo())); + } results = TestUtils.runAsHook(rp, TEST_FEATURE); } assertThat(results.getFailCount(), equalTo(0)); @@ -80,13 +97,13 @@ public void test_each_item_has_correct_start_date(boolean report) { assertThat( "Launch start time is greater than Feature start time.", - (Date) featureRq.getStartTime(), - greaterThanOrEqualTo((Date) launchRq.getStartTime()) + (Comparable) featureRq.getStartTime(), + greaterThanOrEqualTo((Comparable) launchRq.getStartTime()) ); assertThat( "Feature start time is greater than Scenario start time.", - (Date) scenarioRq.getStartTime(), - greaterThanOrEqualTo((Date) featureRq.getStartTime()) + (Comparable) scenarioRq.getStartTime(), + greaterThanOrEqualTo((Comparable) featureRq.getStartTime()) ); List steps = stepCaptor.getAllValues(); @@ -98,19 +115,19 @@ public void test_each_item_has_correct_start_date(boolean report) { .orElseThrow(); assertThat( - "Scenario start time is greater than Step start time.", - (Date) firstStep.getStartTime(), - greaterThanOrEqualTo((Date) scenarioRq.getStartTime()) + "First Step start time is greater or equal than Scenario start time.", + (Comparable) firstStep.getStartTime(), + greaterThanOrEqualTo((Comparable) scenarioRq.getStartTime()) ); assertThat( - "First Step start time is greater or equal than Second Step start time.", - (Date) secondStep.getStartTime(), - greaterThan((Date) firstStep.getStartTime()) + "Second Step start time is greater than First Step start time.", + (Comparable) secondStep.getStartTime(), + greaterThan((Comparable) firstStep.getStartTime()) ); assertThat( - "Second Step start time is greater or equal than Third Step start time.", - (Date) thirdStep.getStartTime(), - greaterThan((Date) secondStep.getStartTime()) + "Third Step start time is greater than Second Step start time.", + (Comparable) thirdStep.getStartTime(), + greaterThan((Comparable) secondStep.getStartTime()) ); } } diff --git a/src/test/java/com/epam/reportportal/karate/utils/TestUtils.java b/src/test/java/com/epam/reportportal/karate/utils/TestUtils.java index 1249c4d..311d46c 100644 --- a/src/test/java/com/epam/reportportal/karate/utils/TestUtils.java +++ b/src/test/java/com/epam/reportportal/karate/utils/TestUtils.java @@ -19,10 +19,12 @@ import com.epam.reportportal.karate.KarateReportPortalRunner; import com.epam.reportportal.karate.ReportPortalHook; import com.epam.reportportal.listeners.ListenerParameters; +import com.epam.reportportal.service.LaunchImpl; import com.epam.reportportal.service.ReportPortal; import com.epam.reportportal.service.ReportPortalClient; import com.epam.reportportal.util.test.CommonUtils; import com.epam.reportportal.utils.http.HttpRequestUtils; +import com.epam.ta.reportportal.ws.model.ApiInfo; import com.epam.ta.reportportal.ws.model.BatchSaveOperatingRS; import com.epam.ta.reportportal.ws.model.Constants; import com.epam.ta.reportportal.ws.model.OperationCompletionRS; @@ -250,4 +252,12 @@ public static List> extractBinaryParts(List