com.chutneytesting.execution.domain.campaign.CampaignExecutionEngine Maven / Gradle / Ivy
package com.chutneytesting.execution.domain.campaign;
import static java.util.Collections.singleton;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import com.chutneytesting.design.domain.campaign.Campaign;
import com.chutneytesting.design.domain.campaign.CampaignExecutionReport;
import com.chutneytesting.design.domain.campaign.CampaignNotFoundException;
import com.chutneytesting.design.domain.campaign.CampaignRepository;
import com.chutneytesting.design.domain.campaign.ScenarioExecutionReportCampaign;
import com.chutneytesting.design.domain.dataset.DataSetHistoryRepository;
import com.chutneytesting.design.domain.scenario.ScenarioNotFoundException;
import com.chutneytesting.design.domain.scenario.ScenarioNotParsableException;
import com.chutneytesting.design.domain.scenario.TestCase;
import com.chutneytesting.design.domain.scenario.TestCaseRepository;
import com.chutneytesting.execution.domain.ExecutionRequest;
import com.chutneytesting.execution.domain.history.ExecutionHistory;
import com.chutneytesting.execution.domain.history.ExecutionHistoryRepository;
import com.chutneytesting.execution.domain.jira.JiraXrayPlugin;
import com.chutneytesting.execution.domain.report.ScenarioExecutionReport;
import com.chutneytesting.execution.domain.report.ServerReportStatus;
import com.chutneytesting.execution.domain.scenario.FailedExecutionAttempt;
import com.chutneytesting.execution.domain.scenario.ScenarioExecutionEngine;
import com.chutneytesting.execution.domain.scenario.composed.ExecutableComposedTestCase;
import com.chutneytesting.instrument.domain.ChutneyMetrics;
import com.chutneytesting.tools.Try;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Load campaigns with {@link CampaignRepository}
* Run each scenario with @{@link ScenarioExecutionEngine}
*/
public class CampaignExecutionEngine {
private static final Logger LOGGER = LoggerFactory.getLogger(Campaign.class);
private final ExecutorService executor;
private final CampaignRepository campaignRepository;
private final ScenarioExecutionEngine scenarioExecutionEngine;
private final ExecutionHistoryRepository executionHistoryRepository;
private final TestCaseRepository testCaseRepository;
private final DataSetHistoryRepository dataSetHistoryRepository;
private final JiraXrayPlugin jiraXrayPlugin;
private final ChutneyMetrics metrics;
private final Map currentCampaignExecutions = new ConcurrentHashMap<>();
private final Map currentCampaignExecutionsStopRequests = new ConcurrentHashMap<>();
public CampaignExecutionEngine(CampaignRepository campaignRepository,
ScenarioExecutionEngine scenarioExecutionEngine,
ExecutionHistoryRepository executionHistoryRepository,
TestCaseRepository testCaseRepository,
DataSetHistoryRepository dataSetHistoryRepository,
JiraXrayPlugin jiraXrayPlugin,
ChutneyMetrics metrics,
ExecutorService executorService) {
this.campaignRepository = campaignRepository;
this.scenarioExecutionEngine = scenarioExecutionEngine;
this.executionHistoryRepository = executionHistoryRepository;
this.testCaseRepository = testCaseRepository;
this.dataSetHistoryRepository = dataSetHistoryRepository;
this.jiraXrayPlugin = jiraXrayPlugin;
this.metrics = metrics;
this.executor = executorService;
}
public List executeByName(String campaignName, String userId) {
return executeByName(campaignName, null, userId);
}
public List executeByName(String campaignName, String environment, String userId) {
List campaigns = campaignRepository.findByName(campaignName);
return campaigns.stream()
.map(campaign -> selectExecutionEnvironment(campaign, environment))
.map(campaign -> executeCampaign(campaign, userId))
.collect(Collectors.toList());
}
public CampaignExecutionReport executeById(Long campaignId, String userId) {
return executeById(campaignId, null, userId);
}
public CampaignExecutionReport executeById(Long campaignId, String environment, String userId) {
return Optional.ofNullable(campaignRepository.findById(campaignId))
.map(campaign -> selectExecutionEnvironment(campaign, environment))
.map(campaign -> executeCampaign(campaign, userId))
.orElseThrow(() -> new CampaignNotFoundException(campaignId));
}
public Optional currentExecution(Long campaignId) {
return Optional.ofNullable(campaignId)
.map(id -> currentCampaignExecutions.get(campaignId));
}
public List currentExecutions() {
return new ArrayList<>(currentCampaignExecutions.values());
}
public void stopExecution(Long executionId) {
LOGGER.trace("Stop requested for " + executionId);
Optional.ofNullable(currentCampaignExecutionsStopRequests.computeIfPresent(executionId, (aLong, aBoolean) -> Boolean.TRUE))
.orElseThrow(() -> new CampaignExecutionNotFoundException(executionId));
}
public CampaignExecutionReport executeScenarioInCampaign(List failedIds, Campaign campaign, String userId) {
verifyNotAlreadyRunning(campaign);
Long executionId = campaignRepository.newCampaignExecution();
CampaignExecutionReport campaignExecutionReport = new CampaignExecutionReport(
executionId,
campaign.title,
!failedIds.isEmpty(),
campaign.executionEnvironment(),
isNotBlank(campaign.externalDatasetId) ? campaign.externalDatasetId : null,
isNotBlank(campaign.externalDatasetId) ? dataSetHistoryRepository.lastVersion(campaign.externalDatasetId) : null,
userId
);
currentCampaignExecutions.put(campaign.id, campaignExecutionReport);
currentCampaignExecutionsStopRequests.put(executionId, Boolean.FALSE);
try {
if (failedIds.isEmpty()) {
return execute(campaign, campaignExecutionReport, campaign.scenarioIds);
} else {
return execute(campaign, campaignExecutionReport, failedIds);
}
} catch (Exception e) {
LOGGER.error("Not managed exception occurred", e);
throw new RuntimeException(e);
} finally {
campaignExecutionReport.endCampaignExecution();
LOGGER.info("Save campaign {} execution {} with status {}", campaign.id, campaignExecutionReport.executionId, campaignExecutionReport.status());
currentCampaignExecutionsStopRequests.remove(executionId);
currentCampaignExecutions.remove(campaign.id);
Try.exec(() -> {
campaignRepository.saveReport(campaign.id, campaignExecutionReport);
return null;
}).ifFailed(e -> LOGGER.error("Error saving report of campaign {} execution {}", campaign.id, campaignExecutionReport.executionId));
Try.exec(() -> {
metrics.onCampaignExecutionEnded(campaign, campaignExecutionReport);
return null;
}).ifFailed(e -> LOGGER.error("Error saving metrics for campaign {} execution {}", campaign.id, campaignExecutionReport.executionId));
}
}
private CampaignExecutionReport execute(Campaign campaign, CampaignExecutionReport campaignExecutionReport, List scenariosToExecute) {
LOGGER.trace("Execute campaign {} : {}", campaign.id, campaign.title);
List testCases = scenariosToExecute.stream()
.map(testCaseRepository::findById)
.filter(Objects::nonNull)
.collect(Collectors.toList());
campaignExecutionReport.initExecution(testCases, campaign.executionEnvironment(), campaignExecutionReport.userId);
try {
if (campaign.parallelRun) {
Collection> toExecute = Lists.newArrayList();
for (TestCase t : testCases) {
toExecute.add(Executors.callable(() -> executeScenarioInCampaign(campaign, campaignExecutionReport).accept(t)));
}
executor.invokeAll(toExecute);
} else {
for (TestCase t : testCases) {
executor.invokeAll(singleton(Executors.callable(() -> executeScenarioInCampaign(campaign, campaignExecutionReport).accept(t))));
}
}
} catch (InterruptedException e) {
LOGGER.error("Error ", e);
} catch (Exception e) {
LOGGER.error("Unexpected error ", e);
}
return campaignExecutionReport;
}
private Consumer executeScenarioInCampaign(Campaign campaign, CampaignExecutionReport campaignExecutionReport) {
return testCase -> {
// Is stop requested ?
if (!currentCampaignExecutionsStopRequests.get(campaignExecutionReport.executionId)) {
// Init scenario execution in campaign report
campaignExecutionReport.startScenarioExecution(testCase, campaign.executionEnvironment(), campaignExecutionReport.userId);
// Execute scenario
ScenarioExecutionReportCampaign scenarioExecutionReport = executeScenario(campaign, testCase, campaignExecutionReport.userId);
// Retry one time if failed
if (campaign.retryAuto && ServerReportStatus.FAILURE.equals(scenarioExecutionReport.status())) {
scenarioExecutionReport = executeScenario(campaign, testCase, campaignExecutionReport.userId);
}
// Add scenario report to campaign's one
Optional.ofNullable(scenarioExecutionReport)
.ifPresent(serc -> {
campaignExecutionReport.endScenarioExecution(serc);
// update xray test
ExecutionHistory.Execution execution = executionHistoryRepository.getExecution(serc.scenarioId, serc.execution.executionId());
jiraXrayPlugin.updateTestExecution(campaign.id, serc.scenarioId, execution.report());
});
}
};
}
private ScenarioExecutionReportCampaign executeScenario(Campaign campaign, TestCase testCase, String userId) {
Long executionId;
String scenarioName;
try {
LOGGER.trace("Execute scenario {} for campaign {}", testCase.id(), campaign.id);
ExecutionRequest executionRequest = buildExecutionRequest(campaign, testCase, userId);
ScenarioExecutionReport scenarioExecutionReport = scenarioExecutionEngine.execute(executionRequest);
executionId = scenarioExecutionReport.executionId;
scenarioName = scenarioExecutionReport.scenarioName;
} catch (FailedExecutionAttempt e) {
LOGGER.warn("Failed execution attempt for scenario {} for campaign {}", testCase.id(), campaign.id);
executionId = e.executionId;
scenarioName = e.title;
} catch (ScenarioNotFoundException | ScenarioNotParsableException se) {
LOGGER.error("Scenario error for scenario {} for campaign {}", testCase.id(), campaign.id, se);
// TODO - Do not hide scenario problem
return null;
}
// TODO - why an extra DB request when we already have the report above ?
ExecutionHistory.Execution execution = executionHistoryRepository.getExecution(testCase.id(), executionId);
return new ScenarioExecutionReportCampaign(testCase.id(), scenarioName, execution.summary());
}
private ExecutionRequest buildExecutionRequest(Campaign campaign, TestCase testCase, String userId) {
// Only composable test cases can use an external dataset, which can be override if set on the campaign
if (isNotBlank(campaign.externalDatasetId) && testCase instanceof ExecutableComposedTestCase) {
return executionWithDatasetIdOverrideByCampaign(campaign, testCase, userId);
} else {
return executionWithCombinedParametersFromCampaignAndTestCase(campaign, testCase, userId);
}
}
private ExecutionRequest executionWithDatasetIdOverrideByCampaign(Campaign campaign, TestCase testCase, String userId) {
return new ExecutionRequest(
((ExecutableComposedTestCase) testCase).withDataSetId(campaign.externalDatasetId),
campaign.executionEnvironment(),
true,
userId
);
}
private ExecutionRequest executionWithCombinedParametersFromCampaignAndTestCase(Campaign campaign, TestCase testCase, String userId) {
Map executionParameters = new HashMap<>(testCase.executionParameters());
executionParameters.putAll(campaign.executionParameters);
return new ExecutionRequest(testCase.usingExecutionParameters(executionParameters), campaign.executionEnvironment(), userId);
}
private CampaignExecutionReport executeCampaign(Campaign campaign, String userId) {
return executeScenarioInCampaign(Collections.emptyList(), campaign, userId);
}
private void verifyNotAlreadyRunning(Campaign campaign) {
Optional currentReport = currentExecution(campaign.id);
if (currentReport.isPresent() && !currentReport.get().status().isFinal()) {
throw new CampaignAlreadyRunningException(currentReport.get());
}
}
private Campaign selectExecutionEnvironment(Campaign campaign, String environment) {
Optional.ofNullable(environment).ifPresent(campaign::executionEnvironment);
return campaign;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy