Skip to content

Commit

Permalink
RPP_Agents_Java_Spock_LastErrorLog - Task is to add a new feature to …
Browse files Browse the repository at this point in the history
…RP agent for Spock. Feature - add last error log to step description.
  • Loading branch information
Yevhen Piskun committed Apr 18, 2024
1 parent 88552d2 commit 23bae32
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.epam.ta.reportportal.ws.model.attribute.ItemAttributesRQ;
import com.epam.ta.reportportal.ws.model.launch.StartLaunchRQ;
import io.reactivex.Maybe;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.codehaus.groovy.runtime.StackTraceUtils;
import org.slf4j.Logger;
Expand All @@ -44,6 +45,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -74,6 +76,7 @@ public class ReportPortalSpockListener extends AbstractRunListener {
.orElseThrow(() -> new IllegalStateException("Unknown Spock version.")));

private final MemoizingSupplier<Launch> launch;
private final Map<Maybe<String>, Pair<String, String>> errorDescriptionMap = new ConcurrentHashMap<>();

// stores the bindings of Spock method kinds to the RP-specific notation
private static final Map<MethodKind, String> ITEM_TYPES_REGISTRY = Collections.unmodifiableMap(new HashMap<MethodKind, String>() {{
Expand Down Expand Up @@ -274,6 +277,7 @@ protected Maybe<String> startIteration(@Nonnull Maybe<String> parentId, @Nonnull
protected void reportIterationStart(@Nonnull Maybe<String> parentId, @Nonnull StartTestItemRQ rq, @Nonnull IterationInfo iteration) {
Maybe<String> testItemId = startIteration(parentId, rq);
launchContext.addRunningIteration(testItemId, iteration);
errorDescriptionMap.put(launchContext.findIterationFootprint(iteration).getId(), Pair.of(rq.getDescription(), StringUtils.EMPTY));
}

public void registerIteration(@Nonnull IterationInfo iteration) {
Expand Down Expand Up @@ -303,6 +307,15 @@ protected FinishTestItemRQ buildFinishTestItemRq(@Nonnull Maybe<String> itemId,
FinishTestItemRQ rq = new FinishTestItemRQ();
ofNullable(status).ifPresent(s -> rq.setStatus(s.name()));
rq.setEndTime(Calendar.getInstance().getTime());
if (Objects.equals(status, ItemStatus.FAILED) && errorDescriptionMap.containsKey(itemId)) {
if (Objects.nonNull(errorDescriptionMap.get(itemId).getLeft())) {
rq.setDescription(
String.format("%s\nError:\n%s", errorDescriptionMap.get(itemId).getLeft(), errorDescriptionMap.get(itemId).getRight()));
} else {
rq.setDescription(String.format("Error:\n%s", errorDescriptionMap.get(itemId).getRight()));
}
}
errorDescriptionMap.remove(itemId);
return rq;
}

Expand Down Expand Up @@ -439,6 +452,10 @@ public void reportError(@Nonnull ErrorInfo error) {
ofNullable(launchContext.findFeatureFootprint(method.getFeature())).ifPresent(f -> f.setStatus(FAILED));
ofNullable(launchContext.getRuntimePointerForSpec(method.getParent())
.getCurrentIteration()).map(launchContext::findIterationFootprint).ifPresent(i -> i.setStatus(FAILED));
Maybe<String> itemId = launchContext.findIterationFootprint(error.getMethod().getIteration()).getId();
String startDescriptions = errorDescriptionMap.get(itemId).getLeft();
Pair<String, String> startFinishDescriptions = Pair.of(startDescriptions, error.getException().toString());
errorDescriptionMap.put(itemId, startFinishDescriptions);
logError(error);
} else if (ITERATION_EXECUTION == kind) {
ofNullable(launchContext.findIterationFootprint(method.getIteration())).ifPresent(i -> i.setStatus(FAILED));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.epam.reportportal.spock.description;

import com.epam.reportportal.listeners.ItemStatus;
import com.epam.reportportal.service.ReportPortal;
import com.epam.reportportal.service.ReportPortalClient;
import com.epam.reportportal.spock.ReportPortalSpockListener;
import com.epam.reportportal.spock.features.fail.HelloSpockSpecFailed;
import com.epam.reportportal.spock.utils.TestExtension;
import com.epam.reportportal.spock.utils.TestUtils;
import com.epam.reportportal.util.test.CommonUtils;
import com.epam.ta.reportportal.ws.model.FinishTestItemRQ;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.platform.launcher.listeners.TestExecutionSummary;
import org.mockito.ArgumentCaptor;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.epam.reportportal.spock.utils.TestUtils.runClasses;
import static com.epam.reportportal.spock.utils.TestUtils.standardParameters;
import static com.epam.reportportal.spock.utils.TestUtils.testExecutor;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class ConditionNotSatisfiedErrorMessageWithoutDescriptionTest {

private final String classId = CommonUtils.namedId("class_");
private final String methodId = CommonUtils.namedId("method_");
private final List<String> nestedSteps = Stream.generate(() -> CommonUtils.namedId("method_")).limit(3).collect(Collectors.toList());
private final List<Pair<String, String>> nestedStepsLink = nestedSteps.stream().map(s -> Pair.of(methodId, s)).collect(Collectors.toList());

private final ReportPortalClient client = mock(ReportPortalClient.class);

private static final String ERROR_DESCRIPTION = "Error:\nCondition not satisfied:\n\nname.size() == length\n| | | |\n| 6 | 7\nScotty false\n";


@BeforeEach
public void setupMock() {
TestUtils.mockLaunch(client, null, classId, methodId);
TestUtils.mockNestedSteps(client, nestedStepsLink);
TestUtils.mockBatchLogging(client);
TestExtension.listener = new ReportPortalSpockListener(ReportPortal.create(client, standardParameters(), testExecutor()));
}

@Test
public void verify_error_condition_not_satisfied_message_without_description() {

TestExecutionSummary result = runClasses(HelloSpockSpecFailed.class);

assertThat(result.getTotalFailureCount(), equalTo(1L));

ArgumentCaptor<FinishTestItemRQ> finishNestedStepCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);
nestedSteps.forEach(s -> verify(client).finishTestItem(eq(s), finishNestedStepCaptor.capture()));

List<FinishTestItemRQ> passedFinishItemStatuses = finishNestedStepCaptor.getAllValues().stream()
.filter(finishTestItemRQ -> finishTestItemRQ.getStatus().equals(ItemStatus.PASSED.name()))
.collect(Collectors.toList());

assertThat(passedFinishItemStatuses, hasSize(2));

passedFinishItemStatuses.forEach(finishTestItemRQ -> assertThat(finishTestItemRQ.getDescription(), is(nullValue())));

List<FinishTestItemRQ> failedFinishItemStatuses = finishNestedStepCaptor.getAllValues().stream()
.filter(finishTestItemRQ -> finishTestItemRQ.getStatus().equals(ItemStatus.FAILED.name()))
.collect(Collectors.toList());

assertThat(failedFinishItemStatuses, hasSize(1));

assertThat(failedFinishItemStatuses.iterator().next().getDescription(), equalTo(ERROR_DESCRIPTION));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.epam.reportportal.spock.description;

import com.epam.reportportal.listeners.ItemStatus;
import com.epam.reportportal.service.ReportPortal;
import com.epam.reportportal.service.ReportPortalClient;
import com.epam.reportportal.spock.ReportPortalSpockListener;
import com.epam.reportportal.spock.features.fail.FailsInDifferentMethod;
import com.epam.reportportal.spock.utils.TestExtension;
import com.epam.reportportal.spock.utils.TestUtils;
import com.epam.reportportal.util.test.CommonUtils;
import com.epam.ta.reportportal.ws.model.FinishTestItemRQ;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.platform.launcher.listeners.TestExecutionSummary;
import org.mockito.ArgumentCaptor;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.epam.reportportal.spock.utils.TestUtils.runClasses;
import static com.epam.reportportal.spock.utils.TestUtils.standardParameters;
import static com.epam.reportportal.spock.utils.TestUtils.testExecutor;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class ExceptionErrorMessageWithDescriptionTest {

private final String launchId = CommonUtils.namedId("launch_");
private final String classId = CommonUtils.namedId("class_");
private final List<String> methodIds = Stream.generate(() -> CommonUtils.namedId("method_")).limit(1).collect(Collectors.toList());

private final ReportPortalClient client = mock(ReportPortalClient.class);

private static final String ERROR_DESCRIPTION_EXCEPTION = "Setup: \nError:\njava.lang.IllegalStateException: Some test flow failure";

@BeforeEach
public void setupMock() {
TestUtils.mockLaunch(client, launchId, classId, methodIds);
TestUtils.mockBatchLogging(client);
TestExtension.listener = new ReportPortalSpockListener(ReportPortal.create(client, standardParameters(), testExecutor()));
}

@Test
public void verify_error_exception_message_with_description() {

TestUtils.mockLaunch(client, launchId, classId, methodIds);
TestExtension.listener = new ReportPortalSpockListener(ReportPortal.create(client, standardParameters(), testExecutor()));

TestExecutionSummary result = runClasses(FailsInDifferentMethod.class);

assertThat(result.getTotalFailureCount(), equalTo(1L));

ArgumentCaptor<FinishTestItemRQ> finishStepCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);

methodIds.forEach(s -> verify(client).finishTestItem(eq(s), finishStepCaptor.capture()));

List<FinishTestItemRQ> failedFinishItemStatuses = finishStepCaptor.getAllValues().stream()
.filter(fis -> fis.getStatus().equals(ItemStatus.FAILED.name()))
.collect(Collectors.toList());

assertThat(failedFinishItemStatuses, hasSize(1));
assertThat(failedFinishItemStatuses.iterator().next().getDescription(), equalTo(ERROR_DESCRIPTION_EXCEPTION));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.epam.reportportal.spock.description;

import com.epam.reportportal.listeners.ItemStatus;
import com.epam.reportportal.service.ReportPortal;
import com.epam.reportportal.service.ReportPortalClient;
import com.epam.reportportal.spock.ReportPortalSpockListener;
import com.epam.reportportal.spock.features.fail.FailWithExceptionWithoutMessage;
import com.epam.reportportal.spock.utils.TestExtension;
import com.epam.reportportal.spock.utils.TestUtils;
import com.epam.reportportal.util.test.CommonUtils;
import com.epam.ta.reportportal.ws.model.FinishTestItemRQ;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.platform.launcher.listeners.TestExecutionSummary;
import org.mockito.ArgumentCaptor;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.epam.reportportal.spock.utils.TestUtils.runClasses;
import static com.epam.reportportal.spock.utils.TestUtils.standardParameters;
import static com.epam.reportportal.spock.utils.TestUtils.testExecutor;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class ExceptionWithoutErrorMessageWithDescriptionTest {

private final String launchId = CommonUtils.namedId("launch_");
private final String classId = CommonUtils.namedId("class_");
private final List<String> methodIds = Stream.generate(() -> CommonUtils.namedId("method_")).limit(1).collect(Collectors.toList());

private final ReportPortalClient client = mock(ReportPortalClient.class);

private static final String ERROR_DESCRIPTION_EXCEPTION = "Setup: \r\nWhen: \r\nThen: \nError:\njava.util.NoSuchElementException";

@BeforeEach
public void setupMock() {
TestUtils.mockLaunch(client, launchId, classId, methodIds);
TestUtils.mockBatchLogging(client);
TestExtension.listener = new ReportPortalSpockListener(ReportPortal.create(client, standardParameters(), testExecutor()));
}

@Test
public void verify_exception_without_error_message_with_description() {

TestUtils.mockLaunch(client, launchId, classId, methodIds);
TestExtension.listener = new ReportPortalSpockListener(ReportPortal.create(client, standardParameters(), testExecutor()));

TestExecutionSummary result = runClasses(FailWithExceptionWithoutMessage.class);

assertThat(result.getTotalFailureCount(), equalTo(1L));

ArgumentCaptor<FinishTestItemRQ> finishStepCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);

methodIds.forEach(s -> verify(client).finishTestItem(eq(s), finishStepCaptor.capture()));

List<FinishTestItemRQ> failedFinishItemStatuses = finishStepCaptor.getAllValues()
.stream()
.filter(fis -> fis.getStatus().equals(ItemStatus.FAILED.name()))
.collect(Collectors.toList());

assertThat(failedFinishItemStatuses, hasSize(1));
assertThat(failedFinishItemStatuses.iterator().next().getDescription(), equalTo(ERROR_DESCRIPTION_EXCEPTION));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.epam.reportportal.spock.features.fail

import spock.lang.Specification

class FailWithExceptionWithoutMessage extends Specification {

def "should demonstrate NoSuchElementException given-when-then fail"() {
given:
def polygon = new ArrayList()

when:
polygon.size()

then:
throw new NoSuchElementException()
}
}

0 comments on commit 23bae32

Please sign in to comment.