Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Changelog

## [Unreleased]
### Added
- Item name formatting according to passed parameters, by @HardNorth

## [5.4.8]
### Added
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/epam/reportportal/service/Launch.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.epam.reportportal.service.step.DefaultStepReporter;
import com.epam.reportportal.service.step.StepReporter;
import com.epam.reportportal.utils.StaticStructuresUtils;
import com.epam.reportportal.utils.formatting.templating.TemplateConfiguration;
import com.epam.ta.reportportal.ws.model.FinishExecutionRQ;
import com.epam.ta.reportportal.ws.model.FinishTestItemRQ;
import com.epam.ta.reportportal.ws.model.OperationCompletionRS;
Expand Down Expand Up @@ -237,6 +238,14 @@ public ReportPortalClient getClient() {
@Nonnull
public abstract Maybe<String> getLaunch();

/**
* Returns current configuration of Template engine to adjust it.
*
* @return Template engine configuration.
*/
@Nonnull
public abstract TemplateConfiguration getTemplateConfiguration();

/**
* Launch implementation for disabled Reporting, every method does nothing.
*/
Expand All @@ -249,6 +258,8 @@ public ReportPortalClient getClient() {
new ListenerParameters(),
StepReporter.NOOP_STEP_REPORTER
) {
private final TemplateConfiguration templateConfiguration = new TemplateConfiguration();

@Override
public boolean useMicroseconds() {
return false;
Expand Down Expand Up @@ -323,6 +334,12 @@ public Maybe<OperationCompletionRS> finishTestItem(Maybe<String> itemId, FinishT
public Maybe<String> getLaunch() {
return Maybe.empty();
}

@Nonnull
@Override
public TemplateConfiguration getTemplateConfiguration() {
return templateConfiguration;
}
};

/**
Expand Down
69 changes: 61 additions & 8 deletions src/main/java/com/epam/reportportal/service/LaunchImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import com.epam.reportportal.service.statistics.StatisticsService;
import com.epam.reportportal.utils.*;
import com.epam.reportportal.utils.files.ByteSource;
import com.epam.reportportal.utils.formatting.templating.TemplateConfiguration;
import com.epam.reportportal.utils.formatting.templating.TemplateProcessing;
import com.epam.reportportal.utils.http.HttpRequestUtils;
import com.epam.reportportal.utils.properties.DefaultProperties;
import com.epam.ta.reportportal.ws.model.*;
Expand All @@ -47,18 +49,22 @@
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static com.epam.reportportal.service.logs.LaunchLoggingCallback.LOG_ERROR;
import static com.epam.reportportal.service.logs.LaunchLoggingCallback.LOG_SUCCESS;
import static com.epam.reportportal.utils.BasicUtils.compareSemanticVersions;
import static com.epam.reportportal.utils.ObjectUtils.clonePojo;
import static com.epam.reportportal.utils.ParameterUtils.NULL_VALUE;
import static com.epam.reportportal.utils.SubscriptionUtils.*;
import static com.epam.reportportal.utils.files.ImageConverter.convert;
import static com.epam.reportportal.utils.files.ImageConverter.isImage;
Expand Down Expand Up @@ -135,6 +141,7 @@ public class LaunchImpl extends Launch {
private final ExecutorService executor;
private final Scheduler scheduler;
private final LoggingSubscriber loggingSubscriber;
private final TemplateConfiguration templateConfiguration;
private StatisticsService statisticsService;
private volatile Boolean useMicroseconds;

Expand Down Expand Up @@ -199,6 +206,7 @@ protected LaunchImpl(@Nonnull final ReportPortalClient reportPortalClient, @Nonn
logEmitter = createLogEmitter(getClient(), getParameters(), getScheduler(), loggingSubscriber);
projectSettings = getProjectSettings(getClient(), getScheduler());
apiInfo = getApiInfo(getClient(), getScheduler());
templateConfiguration = new TemplateConfiguration();
}

/**
Expand Down Expand Up @@ -242,6 +250,7 @@ protected LaunchImpl(@Nonnull final ReportPortalClient reportPortalClient, @Nonn
logEmitter = createLogEmitter(getClient(), getParameters(), getScheduler(), loggingSubscriber);
projectSettings = getProjectSettings(getClient(), getScheduler());
apiInfo = getApiInfo(getClient(), getScheduler());
templateConfiguration = new TemplateConfiguration();
}

private Supplier<Maybe<String>> getLaunchSupplier(@Nonnull final ReportPortalClient client, @Nonnull final Scheduler scheduler,
Expand Down Expand Up @@ -330,6 +339,17 @@ StatisticsService getStatisticsService() {
return statisticsService;
}

/**
* Returns current configuration of Template engine to adjust it.
*
* @return Template engine configuration.
*/
@Nonnull
@Override
public TemplateConfiguration getTemplateConfiguration() {
return templateConfiguration;
}

private void truncateName(@Nonnull final StartTestItemRQ rq) {
if (!getParameters().isTruncateFields() || rq.getName() == null || rq.getName().isEmpty()) {
return;
Expand All @@ -339,6 +359,34 @@ private void truncateName(@Nonnull final StartTestItemRQ rq) {
rq.setName(BasicUtils.truncateString(name, params.getTruncateItemNamesLimit(), params.getTruncateReplacement()));
}

private void applyNameFormat(@Nonnull StartTestItemRQ rq) {
TemplateConfiguration config = getTemplateConfiguration();
List<ParameterResource> params = rq.getParameters();
if (params == null || params.isEmpty() || rq.getName() == null || rq.getName().isEmpty()) {
return;
}
Map<String, Object> formatParameters = IntStream.range(0, params.size()).boxed().flatMap(i -> {
var param = params.get(i);
var newParam = new ParameterResource();
newParam.setKey(param.getKey());
newParam.setValue(param.getValue());
var idxParam = new ParameterResource();
idxParam.setKey(String.valueOf(i));
idxParam.setValue(param.getValue());
return Stream.of(newParam, idxParam);
}).peek(param -> {
if (param.getKey() == null) {
param.setKey(NULL_VALUE);
}
if (param.getValue() == null) {
param.setValue(NULL_VALUE);
}
}).collect(Collectors.toMap(
ParameterResource::getKey, ParameterResource::getValue, (existing, replacement) -> replacement
));
rq.setName(TemplateProcessing.processTemplate(rq.getName(), null, null, formatParameters, config));
}

@Nullable
private Set<ItemAttributesRQ> truncateAttributes(@Nullable final Set<ItemAttributesRQ> attributes) {
if (!getParameters().isTruncateFields() || attributes == null || attributes.isEmpty()) {
Expand Down Expand Up @@ -574,6 +622,17 @@ private static <T> Maybe<T> createErrorResponse(Throwable cause) {
return Maybe.error(cause);
}

@NotNull
private StartTestItemRQ applyRequestModifications(StartTestItemRQ request) {
StartTestItemRQ rq = clonePojo(request, StartTestItemRQ.class);
rq.setStartTime(convertIfNecessary(rq.getStartTime()));
truncateName(rq); // Truncate before templating to not allow too long names to be passed to Template engine
applyNameFormat(rq);
truncateName(rq); // Truncate after templating to not allow too long names to be posted
truncateAttributes(rq);
return rq;
}

/**
* Starts new root test item in ReportPortal asynchronously (non-blocking).
*
Expand All @@ -589,10 +648,7 @@ public Maybe<String> startTestItem(final StartTestItemRQ request) {
*/
return createErrorResponse(new NullPointerException("StartTestItemRQ should not be null"));
}
StartTestItemRQ rq = clonePojo(request, StartTestItemRQ.class);
rq.setStartTime(convertIfNecessary(rq.getStartTime()));
truncateName(rq);
truncateAttributes(rq);
StartTestItemRQ rq = applyRequestModifications(request);

String itemDescription = String.format("root test item [%s] '%s'", rq.getType(), rq.getName());
final Maybe<String> item = getLaunch().flatMap((Function<String, Maybe<String>>) launchId -> {
Expand Down Expand Up @@ -627,10 +683,7 @@ public Maybe<String> startTestItem(final Maybe<String> parentId, final StartTest
*/
return createErrorResponse(new NullPointerException("StartTestItemRQ should not be null"));
}
StartTestItemRQ rq = clonePojo(request, StartTestItemRQ.class);
rq.setStartTime(convertIfNecessary(rq.getStartTime()));
truncateName(rq);
truncateAttributes(rq);
StartTestItemRQ rq = applyRequestModifications(request);

String itemDescription = String.format("child test item [%s] '%s'", rq.getType(), rq.getName());
final Maybe<String> item = RxJavaPlugins.onAssembly(Maybe.zip(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ public OAuth2PasswordGrantAuthInterceptor(@Nonnull ListenerParameters parameters
clientBuilder.proxy(Proxy.NO_PROXY);
}

ofNullable(parameters.getHttpConnectTimeout()).ifPresent(d -> clientBuilder.connectTimeout(d.toMillis(), TimeUnit.MILLISECONDS));
ofNullable(parameters.getHttpReadTimeout()).ifPresent(d -> clientBuilder.readTimeout(d.toMillis(), TimeUnit.MILLISECONDS));
ofNullable(parameters.getHttpWriteTimeout()).ifPresent(d -> clientBuilder.writeTimeout(d.toMillis(), TimeUnit.MILLISECONDS));
ofNullable(parameters.getHttpCallTimeout()).ifPresent(d -> clientBuilder.callTimeout(d.toMillis(), TimeUnit.MILLISECONDS));
ofNullable(parameters.getHttpConnectTimeout()).ifPresent(clientBuilder::connectTimeout);
ofNullable(parameters.getHttpReadTimeout()).ifPresent(clientBuilder::readTimeout);
ofNullable(parameters.getHttpWriteTimeout()).ifPresent(clientBuilder::writeTimeout);
ofNullable(parameters.getHttpCallTimeout()).ifPresent(clientBuilder::callTimeout);

client = clientBuilder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static Set<ItemAttributesRQ> parseAsSet(@Nullable String rawAttributes) {
if (null == rawAttributes) {
return Collections.emptySet();
}
Set<ItemAttributesRQ> attributes = new HashSet<>();
Set<ItemAttributesRQ> attributes = new LinkedHashSet<>();

String[] attributesSplit = rawAttributes.trim().split(ATTRIBUTES_SPLITTER);
for (String s : attributesSplit) {
Expand Down
53 changes: 53 additions & 0 deletions src/test/java/com/epam/reportportal/service/LaunchTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@
import com.epam.ta.reportportal.ws.model.launch.StartLaunchRQ;
import io.reactivex.Maybe;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.RandomStringUtils;
import org.aspectj.lang.reflect.MethodSignature;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
Expand All @@ -52,10 +55,12 @@
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Stream;

import static com.epam.reportportal.test.TestUtils.*;
import static com.epam.reportportal.util.test.CommonUtils.shutdownExecutorService;
Expand Down Expand Up @@ -350,6 +355,54 @@ public void launch_should_truncate_long_item_names() {
assertThat(testName, allOf(hasLength(1024), endsWith("...")));
}

@Nonnull
private static ParameterResource parameter(@Nullable String key, @Nullable String value) {
ParameterResource resource = new ParameterResource();
resource.setKey(key);
resource.setValue(value);
return resource;
}

private static Stream<Arguments> item_name_templates() {
return Stream.of(
Arguments.of("Step {param}", List.of(parameter("param", "value")), "Step value"),
Arguments.of("Step {0}", List.of(parameter("paramByIdx", "valueByIdx")), "Step valueByIdx"),
Arguments.of("Step {param}", null, "Step {param}"),
Arguments.of("Step {0}", List.of(parameter(null, "value")), "Step value"),
Arguments.of("Step {param}", List.of(parameter("param", "value"), parameter("param", "value2")), "Step value2"),
Arguments.of("Step {0} {1}", List.of(parameter("param", "value"), parameter("param", "value2")), "Step value value2"),
Arguments.of("Step {param}", List.of(parameter("param", null)), "Step NULL")
);
}

@ParameterizedTest
@MethodSource("item_name_templates")
public void launch_should_format_item_name_template(String templateName, List<ParameterResource> parameters, String expectedName) {
simulateStartLaunchResponse(rpClient);
simulateStartTestItemResponse(rpClient);
simulateStartChildTestItemResponse(rpClient);
Launch launch = createLaunch();

StartTestItemRQ rootRq = standardStartSuiteRequest();
rootRq.setName(templateName);
rootRq.setParameters(parameters);
StartTestItemRQ childRq = standardStartTestRequest();
childRq.setName(templateName);
childRq.setParameters(parameters);

launch.start();
Maybe<String> rootId = launch.startTestItem(rootRq);
launch.startTestItem(rootId, childRq);

ArgumentCaptor<StartTestItemRQ> rootCaptor = ArgumentCaptor.forClass(StartTestItemRQ.class);
verify(rpClient, timeout(1000)).startTestItem(rootCaptor.capture());
assertThat(rootCaptor.getValue().getName(), equalTo(expectedName));

ArgumentCaptor<StartTestItemRQ> childCaptor = ArgumentCaptor.forClass(StartTestItemRQ.class);
verify(rpClient, timeout(1000)).startTestItem(eq(rootId.blockingGet()), childCaptor.capture());
assertThat(childCaptor.getValue().getName(), equalTo(expectedName));
}

private static void verify_attribute_truncation(Set<ItemAttributesRQ> attributes) {
assertThat(attributes, hasSize(1));
ItemAttributesRQ suiteAttribute = attributes.iterator().next();
Expand Down
Loading