fix(测试计划): 修复串行某个执行点超时后导致整个后续执行链都超时问题

This commit is contained in:
fit2-zhao 2022-04-28 18:15:13 +08:00 committed by 刘瑞斌
parent ba99cde0ee
commit 817828da87
3 changed files with 93 additions and 26 deletions

View File

@ -9,7 +9,7 @@ import org.springframework.stereotype.Component;
public class FixedTask {
private ApiExecutionQueueService queueService;
@Scheduled(cron = "0 0/2 * * * ?")
@Scheduled(cron = "0 0/5 * * * ?")
public void execute() {
if (queueService == null) {
queueService = CommonBeanFactory.getBean(ApiExecutionQueueService.class);

View File

@ -12,7 +12,10 @@ import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ext.ExtApiExecutionQueueMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ExecuteResult;
import io.metersphere.commons.constants.TestPlanReportStatus;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.constants.RunModeConstants;
@ -24,12 +27,14 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
@ -39,6 +44,8 @@ public class ApiExecutionQueueService {
@Resource
private ApiExecutionQueueDetailMapper executionQueueDetailMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private ApiScenarioSerialService apiScenarioSerialService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@ -280,6 +287,7 @@ public class ApiExecutionQueueService {
}
return;
}
// 获取串行下一个执行节点
DBTestQueue executionQueue = this.handleQueue(dto.getQueueId(), dto.getTestId());
if (executionQueue != null) {
// 串行失败停止
@ -289,12 +297,17 @@ public class ApiExecutionQueueService {
return;
}
}
LoggerUtil.info("开始处理执行队列:" + executionQueue.getId() + " 当前资源是:" + dto.getTestId());
LoggerUtil.info("开始处理执行队列:" + executionQueue.getId() + " 当前资源是:" + dto.getTestId() + "报告ID" + dto.getReportId());
if (executionQueue.getQueue() != null && StringUtils.isNotEmpty(executionQueue.getQueue().getTestId())) {
if (StringUtils.equals(dto.getRunType(), RunModeConstants.SERIAL.toString())) {
LoggerUtil.info("当前执行队列是:" + JSON.toJSONString(executionQueue.getQueue()));
// 防止重复执行
boolean isNext = redisTemplate.opsForValue().setIfAbsent(RunModeConstants.SERIAL.name() + "_" + executionQueue.getQueue().getReportId(), executionQueue.getQueue().getQueueId());
if (isNext) {
redisTemplate.expire(RunModeConstants.SERIAL.name() + "_" + executionQueue.getQueue().getReportId(), 60, TimeUnit.MINUTES);
apiScenarioSerialService.serial(executionQueue, executionQueue.getQueue());
}
}
} else {
if (StringUtils.equals(dto.getReportType(), RunModeConstants.SET_REPORT.toString())) {
String reportId = dto.getReportId();
@ -360,29 +373,32 @@ public class ApiExecutionQueueService {
ResultDTO dto = new ResultDTO();
dto.setQueueId(item.getQueueId());
dto.setTestId(item.getTestId());
ApiExecutionQueue executionQueue = queueMapper.selectByPrimaryKey(item.getQueueId());
if (executionQueue != null && StringUtils.equalsIgnoreCase(item.getType(), RunModeConstants.SERIAL.toString())) {
if (queue != null && StringUtils.equalsIgnoreCase(item.getType(), RunModeConstants.SERIAL.toString())) {
// 删除串行资源锁
redisTemplate.delete(RunModeConstants.SERIAL.name() + "_" + dto.getReportId());
LoggerUtil.info("超时处理报告:【" + report.getId() + "】进入下一个执行");
dto.setTestPlanReportId(executionQueue.getReportId());
dto.setReportId(executionQueue.getReportId());
dto.setRunMode(executionQueue.getRunMode());
dto.setTestPlanReportId(queue.getReportId());
dto.setReportId(queue.getReportId());
dto.setRunMode(queue.getRunMode());
dto.setRunType(item.getType());
dto.setReportType(executionQueue.getReportType());
dto.setReportType(queue.getReportType());
queueNext(dto);
} else {
executionQueueDetailMapper.deleteByPrimaryKey(item.getId());
}
}
} else {
// 用例/接口超时结果处理
ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByPrimaryKey(item.getReportId());
if (result != null && StringUtils.equalsAnyIgnoreCase(result.getStatus(), TestPlanReportStatus.RUNNING.name(), APITestStatus.Waiting.name())) {
if (result != null && StringUtils.equalsAnyIgnoreCase(result.getStatus(), TestPlanReportStatus.RUNNING.name())) {
result.setStatus(ScenarioStatus.Timeout.name());
apiDefinitionExecResultMapper.updateByPrimaryKeySelective(result);
executionQueueDetailMapper.deleteByPrimaryKey(item.getId());
}
}
}
// 集成报告超时处理
ApiExecutionQueueExample queueDetailExample = new ApiExecutionQueueExample();
queueDetailExample.createCriteria().andReportTypeEqualTo(RunModeConstants.SET_REPORT.toString()).andCreateTimeLessThan(timeout);
List<ApiExecutionQueue> executionQueues = queueMapper.selectByExample(queueDetailExample);
@ -441,6 +457,7 @@ public class ApiExecutionQueueService {
/**
* 性能测试监听检查
*
* @param loadTestReport
*/
public void checkExecutionQueueByLoadTest(LoadTestReport loadTestReport) {

View File

@ -4,14 +4,15 @@ import io.metersphere.api.dto.automation.ApiTestReportVariable;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.base.mapper.UiScenarioMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.RequestResult;
import io.metersphere.dto.ResultDTO;
@ -26,14 +27,12 @@ import io.metersphere.track.service.TestPlanTestCaseService;
import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
@Service
@Transactional(rollbackFor = Exception.class)
@ -58,7 +57,47 @@ public class TestResultService {
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
@Resource
private ApiEnvironmentRunningParamService apiEnvironmentRunningParamService;
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 场景
private static final List<String> scenarioRunModes = new ArrayList<>() {{
this.add(ApiRunMode.SCENARIO.name());
this.add(ApiRunMode.SCENARIO_PLAN.name());
this.add(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
this.add(ApiRunMode.SCHEDULE_SCENARIO.name());
this.add(ApiRunMode.JENKINS_SCENARIO_PLAN.name());
}};
// 接口测试 用例/接口
private static final List<String> caseRunModes = new ArrayList<>() {{
this.add(ApiRunMode.DEFINITION.name());
this.add(ApiRunMode.JENKINS.name());
this.add(ApiRunMode.API_PLAN.name());
}};
// 测试计划 用例/接口
private static final List<String> planCaseRunModes = new ArrayList<>() {{
this.add(ApiRunMode.SCHEDULE_API_PLAN.name());
this.add(ApiRunMode.JENKINS_API_PLAN.name());
this.add(ApiRunMode.MANUAL_PLAN.name());
}};
// ui 执行触发类型
private static final List<String> uiRunModes = new ArrayList<>() {{
this.add(ApiRunMode.UI_SCENARIO.name());
this.add(ApiRunMode.UI_SCENARIO_PLAN.name());
this.add(ApiRunMode.UI_JENKINS_SCENARIO_PLAN.name());
this.add(ApiRunMode.UI_SCHEDULE_SCENARIO.name());
this.add(ApiRunMode.UI_SCHEDULE_SCENARIO_PLAN.name());
}};
/**
* 执行结果存储
*
* @param dto 执行结果
*/
public void saveResults(ResultDTO dto) {
// 处理环境
List<String> environmentList = new LinkedList<>();
@ -68,21 +107,29 @@ public class TestResultService {
//处理环境参数
if (CollectionUtils.isNotEmpty(environmentList)) {
apiEnvironmentRunningParamService.parseEnvironment(environmentList);
}
//测试计划定时任务-接口执行逻辑的话需要同步测试计划的报告数据
if (StringUtils.equalsAny(dto.getRunMode(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name(), ApiRunMode.MANUAL_PLAN.name())) {
// 测试计划用例触发结果处理
if (planCaseRunModes.contains(dto.getRunMode())) {
apiDefinitionExecResultService.saveApiResultByScheduleTask(dto);
} else if (StringUtils.equalsAny(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.JENKINS.name(), ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name(), ApiRunMode.MANUAL_PLAN.name())) {
} else if (caseRunModes.contains(dto.getRunMode())) {
// 手动触发/批量触发 用例结果处理
apiDefinitionExecResultService.saveApiResult(dto);
} else if (StringUtils.equalsAny(dto.getRunMode(), ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
} else if (scenarioRunModes.contains(dto.getRunMode())) {
// 场景报告结果处理
apiScenarioReportService.saveResult(dto);
} else if (StringUtils.equalsAny(dto.getRunMode(), ApiRunMode.UI_SCENARIO.name(), ApiRunMode.UI_SCENARIO_PLAN.name(), ApiRunMode.UI_JENKINS_SCENARIO_PLAN.name(), ApiRunMode.UI_SCHEDULE_SCENARIO.name(), ApiRunMode.UI_SCHEDULE_SCENARIO_PLAN.name())) {
} else if (uiRunModes.contains(dto.getRunMode())) {
// ui 结果处理
apiScenarioReportService.saveUiResult(dto.getRequestResults(), dto);
}
updateTestCaseStates(dto.getRequestResults(), dto.getRunMode());
}
/**
* 批量存储执行结果
*
* @param resultDtoMap
*/
public void batchSaveResults(Map<String, List<ResultDTO>> resultDtoMap) {
// 处理环境
List<String> environmentList = new LinkedList<>();
@ -98,7 +145,6 @@ public class TestResultService {
}
// 处理用例/场景和计划关系
updateTestCaseStates(dto.getRequestResults(), dto.getRunMode());
}
//测试计划定时任务-接口执行逻辑的话需要同步测试计划的报告数据
if (StringUtils.equals(key, "schedule-task")) {
@ -113,8 +159,11 @@ public class TestResultService {
}
public void testEnded(ResultDTO dto) {
if (StringUtils.equalsAny(dto.getRunMode(), ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())
|| dto.getRunMode().startsWith("UI")) {
// 删除串行资源锁
if (StringUtils.equals(dto.getRunType(), RunModeConstants.SERIAL.toString())) {
redisTemplate.delete(RunModeConstants.SERIAL.name() + "_" + dto.getReportId());
}
if (scenarioRunModes.contains(dto.getRunMode()) || dto.getRunMode().startsWith("UI")) {
ApiScenarioReport scenarioReport = apiScenarioReportService.testEnded(dto);
if (scenarioReport != null) {
String environment = "";
@ -122,6 +171,7 @@ public class TestResultService {
String userName = "";
//负责人
String principal = "";
if (dto.getRunMode().startsWith("UI")) {
UiScenarioWithBLOBs uiScenario = uiScenarioMapper.selectByPrimaryKey(scenarioReport.getScenarioId());
if (uiScenario != null) {