refactor(测试计划): 优化测试计划执行方法

This commit is contained in:
Jianguo-Genius 2024-06-18 16:14:20 +08:00 committed by Craftsman
parent 59bd127bb8
commit a020a8e595
3 changed files with 292 additions and 200 deletions

View File

@ -2,12 +2,14 @@ package io.metersphere.plan.service;
import com.esotericsoftware.minlog.Log;
import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.TestPlanReportPostParam;
import io.metersphere.plan.dto.request.TestPlanBatchExecuteRequest;
import io.metersphere.plan.dto.request.TestPlanExecuteRequest;
import io.metersphere.plan.dto.request.TestPlanReportGenRequest;
import io.metersphere.plan.mapper.*;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.constants.ApiBatchRunMode;
import io.metersphere.sdk.constants.CaseType;
import io.metersphere.sdk.constants.TaskTriggerMode;
import io.metersphere.sdk.constants.TestPlanConstants;
import io.metersphere.sdk.dto.queue.TestPlanExecutionQueue;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.JSON;
@ -17,7 +19,6 @@ import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -27,6 +28,8 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static io.metersphere.plan.service.TestPlanExecuteSupportService.*;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanExecuteService {
@ -36,8 +39,6 @@ public class TestPlanExecuteService {
@Resource
private ExtTestPlanReportMapper extTestPlanReportMapper;
@Resource
private ExtTestPlanMapper extTestPlanMapper;
@Resource
private TestPlanConfigMapper testPlanConfigMapper;
@Resource
private TestPlanService testPlanService;
@ -55,14 +56,10 @@ public class TestPlanExecuteService {
@Resource
private RedisTemplate<String, String> redisTemplate;
public static final String QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE = "test-plan-batch-execute:";
public static final String QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE = "test-plan-group-execute:";
public static final String QUEUE_PREFIX_TEST_PLAN_CASE_TYPE = "test-plan-case-type-execute:";
public static final String QUEUE_PREFIX_TEST_PLAN_COLLECTION = "test-plan-collection-execute:";
public static final String LAST_QUEUE_PREFIX = "last-queue:";
@Resource
private TestPlanReportMapper testPlanReportMapper;
@Resource
private TestPlanExecuteSupportService testPlanExecuteSupportService;
// 停止测试计划的执行
public void stopTestPlanRunning(String testPlanReportId) {
@ -85,19 +82,19 @@ public class TestPlanExecuteService {
继续执行 test-plan-batch-execute:randomId 队列的下一条
*/
// 获取下一个要执行的测试计划节点目的是得到最后一条的queueId
TestPlanExecutionQueue nextTestPlanQueue = this.getNextQueue(testPlanReportId, QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE);
TestPlanExecutionQueue nextTestPlanQueue = testPlanExecuteSupportService.getNextQueue(testPlanReportId, QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE);
if (nextTestPlanQueue == null || !StringUtils.equalsIgnoreCase(nextTestPlanQueue.getParentQueueType(), QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE)) {
return;
}
String groupExecuteQueueId = genQueueKey(testPlanReportId, QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE);
this.deleteRedisKey(groupExecuteQueueId);
String groupExecuteQueueId = testPlanExecuteSupportService.genQueueKey(testPlanReportId, QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE);
testPlanExecuteSupportService.deleteRedisKey(groupExecuteQueueId);
testPlanItemReport.forEach(item -> {
this.deepDeleteTestPlanCaseType(item);
//统计子测试计划报告
summaryTestPlanReport(item.getId(), false, true);
testPlanExecuteSupportService.summaryTestPlanReport(item.getId(), false, true);
});
summaryTestPlanReport(testPlanReportId, true, true);
testPlanExecuteSupportService.summaryTestPlanReport(testPlanReportId, true, true);
this.testPlanExecuteQueueFinish(nextTestPlanQueue.getParentQueueId(), nextTestPlanQueue.getParentQueueType());
} else {
/*
@ -110,27 +107,26 @@ public class TestPlanExecuteService {
进行当前报告结算
继续执行 队列的下一条
*/
TestPlanExecutionQueue nextTestPlanQueue = this.getNextQueue(testPlanReportId, QUEUE_PREFIX_TEST_PLAN_CASE_TYPE);
TestPlanExecutionQueue nextTestPlanQueue = testPlanExecuteSupportService.getNextQueue(testPlanReportId, QUEUE_PREFIX_TEST_PLAN_CASE_TYPE);
testPlanExecuteSupportService.summaryTestPlanReport(testPlanReportId, false, true);
if (nextTestPlanQueue == null || !StringUtils.equalsAnyIgnoreCase(nextTestPlanQueue.getParentQueueType(), QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE, QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE)) {
return;
testPlanExecuteSupportService.updateReportStopped(testPlanReportId);
} else {
this.deepDeleteTestPlanCaseType(testPlanReport);
this.testPlanExecuteQueueFinish(nextTestPlanQueue.getParentQueueId(), nextTestPlanQueue.getParentQueueType());
}
this.deepDeleteTestPlanCaseType(testPlanReport);
summaryTestPlanReport(testPlanReportId, false, true);
this.testPlanExecuteQueueFinish(nextTestPlanQueue.getParentQueueId(), nextTestPlanQueue.getParentQueueType());
}
// todo @wxg 是在 deepDeleteTestPlanCaseType()方法中删除用例执行队列时同步到执行机停止执行任务还是其它操作 由你来决定了
}
private void deepDeleteTestPlanCaseType(TestPlanReport report) {
this.deleteRedisKey(genQueueKey(report.getId(), QUEUE_PREFIX_TEST_PLAN_CASE_TYPE));
testPlanExecuteSupportService.deleteRedisKey(
testPlanExecuteSupportService.genQueueKey(report.getId(), QUEUE_PREFIX_TEST_PLAN_CASE_TYPE));
TestPlanCollectionExample collectionExample = new TestPlanCollectionExample();
collectionExample.createCriteria().andTestPlanIdEqualTo(report.getTestPlanId()).andParentIdEqualTo(TestPlanConstants.DEFAULT_PARENT_ID);
List<TestPlanCollection> parentTestPlanCollectionList = testPlanCollectionMapper.selectByExample(collectionExample);
parentTestPlanCollectionList.forEach(parentCollection -> {
this.deleteRedisKey(genQueueKey(report.getId() + "_" + parentCollection.getId(), QUEUE_PREFIX_TEST_PLAN_COLLECTION));
testPlanExecuteSupportService.deleteRedisKey(
testPlanExecuteSupportService.genQueueKey(report.getId() + "_" + parentCollection.getId(), QUEUE_PREFIX_TEST_PLAN_COLLECTION));
//todo @Chen-Jianxing 这里要同步清理用例/场景的执行队列
});
}
@ -154,34 +150,23 @@ public class TestPlanExecuteService {
System.currentTimeMillis(),
executionQueue.getQueueId(),
QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE,
null,
null,
executionQueue.getQueueId(),
executionQueue.getQueueType(),
request.getExecuteId(),
executionQueue.getRunMode(),
executionQueue.getExecutionSource(),
IDGenerator.nextStr()
);
String redisKey = genQueueKey(executionQueue.getQueueId(), QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE);
redisTemplate.opsForList().rightPush(genQueueKey(executionQueue.getQueueId(), QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE), JSON.toJSONString(singleExecuteRootQueue));
redisTemplate.expire(genQueueKey(executionQueue.getQueueId(), QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE), 1, TimeUnit.DAYS);
String redisKey = testPlanExecuteSupportService.genQueueKey(executionQueue.getQueueId(), QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE);
redisTemplate.opsForList().rightPush(redisKey, JSON.toJSONString(singleExecuteRootQueue));
redisTemplate.expire(redisKey, 1, TimeUnit.DAYS);
LogUtils.info("测试计划的单独执行start计划报告[{}] , 资源ID[{}]", singleExecuteRootQueue.getPrepareReportId(), singleExecuteRootQueue.getSourceID());
return executeTestPlanOrGroup(executionQueue);
}
private void setRedisForList(String key, List<String> list) {
redisTemplate.opsForList().rightPushAll(key, list);
redisTemplate.expire(key, 1, TimeUnit.DAYS);
}
private void deleteRedisKey(String redisKey) {
//清除list的key last key节点
redisTemplate.delete(redisKey);
redisTemplate.delete(genQueueKey(redisKey, LAST_QUEUE_PREFIX));
}
//批量执行测试计划组
public void batchExecuteTestPlan(TestPlanBatchExecuteRequest request, String userId) {
List<String> rightfulIds = testPlanService.selectRightfulIds(request.getExecuteIds());
@ -213,11 +198,12 @@ public class TestPlanExecuteService {
);
}
}
this.setRedisForList(genQueueKey(queueId, queueType), testPlanExecutionQueues.stream().map(JSON::toJSONString).toList());
testPlanExecuteSupportService.setRedisForList(
testPlanExecuteSupportService.genQueueKey(queueId, queueType), testPlanExecutionQueues.stream().map(JSON::toJSONString).toList());
LogUtils.info("测试计划的批量执行start队列ID[{}] ,队列类型[{}] , 资源ID[{}]", queueId, queueType, JSON.toJSONString(rightfulIds));
if (StringUtils.equalsIgnoreCase(request.getRunMode(), ApiBatchRunMode.SERIAL.name())) {
//串行
TestPlanExecutionQueue nextQueue = this.getNextQueue(queueId, queueType);
TestPlanExecutionQueue nextQueue = testPlanExecuteSupportService.getNextQueue(queueId, queueType);
executeTestPlanOrGroup(nextQueue);
} else {
//并行
@ -243,7 +229,6 @@ public class TestPlanExecuteService {
testPlanService.setActualStartTime(executionQueue.getSourceID());
testPlanService.setTestPlanUnderway(executionQueue.getSourceID());
List<TestPlan> children = testPlanService.selectNotArchivedChildren(testPlan.getId());
// 预生成计划组报告
Map<String, String> reportMap = testPlanReportService.genReportByExecution(executionQueue.getPrepareReportId(), genReportRequest, executionQueue.getCreateUser());
@ -276,7 +261,7 @@ public class TestPlanExecuteService {
//本次的测试计划组执行完成
this.testPlanGroupQueueFinish(executionQueue.getQueueId(), executionQueue.getQueueType());
} else {
this.setRedisForList(genQueueKey(queueId, queueType), childrenQueue.stream().map(JSON::toJSONString).toList());
testPlanExecuteSupportService.setRedisForList(testPlanExecuteSupportService.genQueueKey(queueId, queueType), childrenQueue.stream().map(JSON::toJSONString).toList());
// 更新报告的执行时间
if (MapUtils.isNotEmpty(reportMap)) {
@ -285,7 +270,7 @@ public class TestPlanExecuteService {
if (StringUtils.equalsIgnoreCase(executionQueue.getRunMode(), ApiBatchRunMode.SERIAL.name())) {
//串行
TestPlanExecutionQueue nextQueue = this.getNextQueue(queueId, queueType);
TestPlanExecutionQueue nextQueue = testPlanExecuteSupportService.getNextQueue(queueId, queueType);
executeTestPlan(nextQueue);
} else {
//并行
@ -309,57 +294,63 @@ public class TestPlanExecuteService {
//执行测试计划里不同类型的用例 回调caseTypeExecuteQueueFinish
public void executeTestPlan(TestPlanExecutionQueue executionQueue) {
testPlanService.setActualStartTime(executionQueue.getSourceID());
testPlanService.setTestPlanUnderway(executionQueue.getSourceID());
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(executionQueue.getSourceID());
TestPlanCollectionExample testPlanCollectionExample = new TestPlanCollectionExample();
testPlanCollectionExample.createCriteria().andTestPlanIdEqualTo(testPlan.getId()).andParentIdEqualTo("NONE");
testPlanCollectionExample.setOrderByClause("pos asc");
//过滤掉功能用例的测试集
List<TestPlanCollection> testPlanCollectionList = testPlanCollectionMapper.selectByExample(testPlanCollectionExample).stream().filter(
testPlanCollection -> !StringUtils.equalsIgnoreCase(testPlanCollection.getType(), CaseType.FUNCTIONAL_CASE.getKey())
).toList();
int pos = 0;
TestPlanConfig testPlanConfig = testPlanConfigMapper.selectByPrimaryKey(testPlan.getId());
String runMode = StringUtils.isBlank(testPlanConfig.getCaseRunMode()) ? ApiBatchRunMode.SERIAL.name() : testPlanConfig.getCaseRunMode();
String queueId = executionQueue.getPrepareReportId();
String queueType = QUEUE_PREFIX_TEST_PLAN_CASE_TYPE;
List<TestPlanExecutionQueue> childrenQueue = new ArrayList<>();
for (TestPlanCollection collection : testPlanCollectionList) {
childrenQueue.add(
new TestPlanExecutionQueue(
pos++,
executionQueue.getCreateUser(),
System.currentTimeMillis(),
queueId,
queueType,
executionQueue.getQueueId(),
executionQueue.getQueueType(),
collection.getId(),
runMode,
executionQueue.getExecutionSource(),
executionQueue.getPrepareReportId())
);
}
LogUtils.info("测试计划执行节点 --- 队列ID[{}],队列类型[{}],父队列ID[{}],父队列类型[{}],执行模式[{}]", queueId, queueType, executionQueue.getParentQueueId(), executionQueue.getParentQueueType(), runMode);
if (CollectionUtils.isEmpty(childrenQueue)) {
//本次的测试计划组执行完成
boolean testPlanStopped = testPlanExecuteSupportService.checkTestPlanStopped(executionQueue.getPrepareReportId());
if (testPlanStopped) {
//测试计划报告状态已停止的话便不再执行执行下一个队列
this.testPlanExecuteQueueFinish(executionQueue.getQueueId(), executionQueue.getQueueType());
} else {
this.setRedisForList(genQueueKey(queueId, queueType), childrenQueue.stream().map(JSON::toJSONString).toList());
testPlanService.setActualStartTime(executionQueue.getSourceID());
testPlanService.setTestPlanUnderway(executionQueue.getSourceID());
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(executionQueue.getSourceID());
TestPlanCollectionExample testPlanCollectionExample = new TestPlanCollectionExample();
testPlanCollectionExample.createCriteria().andTestPlanIdEqualTo(testPlan.getId()).andParentIdEqualTo("NONE");
testPlanCollectionExample.setOrderByClause("pos asc");
//过滤掉功能用例的测试集
List<TestPlanCollection> testPlanCollectionList = testPlanCollectionMapper.selectByExample(testPlanCollectionExample).stream().filter(
testPlanCollection -> !StringUtils.equalsIgnoreCase(testPlanCollection.getType(), CaseType.FUNCTIONAL_CASE.getKey())
).toList();
//开始根据测试计划集合执行测试用例
if (StringUtils.equalsIgnoreCase(runMode, ApiBatchRunMode.SERIAL.name())) {
//串行
TestPlanExecutionQueue nextQueue = this.getNextQueue(queueId, queueType);
this.executeByTestPlanCollection(nextQueue);
int pos = 0;
TestPlanConfig testPlanConfig = testPlanConfigMapper.selectByPrimaryKey(testPlan.getId());
String runMode = StringUtils.isBlank(testPlanConfig.getCaseRunMode()) ? ApiBatchRunMode.SERIAL.name() : testPlanConfig.getCaseRunMode();
String queueId = executionQueue.getPrepareReportId();
String queueType = QUEUE_PREFIX_TEST_PLAN_CASE_TYPE;
List<TestPlanExecutionQueue> childrenQueue = new ArrayList<>();
for (TestPlanCollection collection : testPlanCollectionList) {
childrenQueue.add(
new TestPlanExecutionQueue(
pos++,
executionQueue.getCreateUser(),
System.currentTimeMillis(),
queueId,
queueType,
executionQueue.getQueueId(),
executionQueue.getQueueType(),
collection.getId(),
runMode,
executionQueue.getExecutionSource(),
executionQueue.getPrepareReportId())
);
}
LogUtils.info("测试计划执行节点 --- 队列ID[{}],队列类型[{}],父队列ID[{}],父队列类型[{}],执行模式[{}]", queueId, queueType, executionQueue.getParentQueueId(), executionQueue.getParentQueueType(), runMode);
if (CollectionUtils.isEmpty(childrenQueue)) {
//本次的测试计划组执行完成
this.testPlanExecuteQueueFinish(executionQueue.getQueueId(), executionQueue.getQueueType());
} else {
//并行
childrenQueue.forEach(childQueue -> {
this.executeByTestPlanCollection(childQueue);
});
testPlanExecuteSupportService.setRedisForList(testPlanExecuteSupportService.genQueueKey(queueId, queueType), childrenQueue.stream().map(JSON::toJSONString).toList());
//开始根据测试计划集合执行测试用例
if (StringUtils.equalsIgnoreCase(runMode, ApiBatchRunMode.SERIAL.name())) {
//串行
TestPlanExecutionQueue nextQueue = testPlanExecuteSupportService.getNextQueue(queueId, queueType);
this.executeByTestPlanCollection(nextQueue);
} else {
//并行
childrenQueue.forEach(childQueue -> {
this.executeByTestPlanCollection(childQueue);
});
}
}
}
}
@ -400,10 +391,11 @@ public class TestPlanExecuteService {
//本次的测试集执行完成
this.caseTypeExecuteQueueFinish(executionQueue.getQueueId(), executionQueue.getQueueType());
} else {
this.setRedisForList(genQueueKey(queueId, queueType), childrenQueue.stream().map(JSON::toJSONString).toList());
testPlanExecuteSupportService.setRedisForList(
testPlanExecuteSupportService.genQueueKey(queueId, queueType), childrenQueue.stream().map(JSON::toJSONString).toList());
if (StringUtils.equalsIgnoreCase(runMode, ApiBatchRunMode.SERIAL.name())) {
//串行
TestPlanExecutionQueue nextQueue = this.getNextQueue(queueId, queueType);
TestPlanExecutionQueue nextQueue = testPlanExecuteSupportService.getNextQueue(queueId, queueType);
this.executeCase(nextQueue);
} else {
//并行
@ -423,30 +415,31 @@ public class TestPlanExecuteService {
private void executeCase(TestPlanExecutionQueue testPlanExecutionQueue) {
String queueId = testPlanExecutionQueue.getQueueId();
LogUtils.info("测试集执行节点 --- 队列ID[{}],队列类型[{}],父队列ID[{}],父队列类型[{}],执行模式[{}]", queueId, testPlanExecutionQueue.getQueueType(), testPlanExecutionQueue.getParentQueueId(), testPlanExecutionQueue.getParentQueueType(), testPlanExecutionQueue.getRunMode());
boolean execOver = false;
try {
boolean isFinish = false;
TestPlanCollection collection = JSON.parseObject(testPlanExecutionQueue.getTestPlanCollectionJson(), TestPlanCollection.class);
TestPlanCollection extendedRootCollection = testPlanApiBatchRunBaseService.getExtendedRootCollection(collection);
String executeMethod = extendedRootCollection == null ? collection.getExecuteMethod() : extendedRootCollection.getExecuteMethod();
if (StringUtils.equalsIgnoreCase(collection.getType(), CaseType.API_CASE.getKey())) {
if (StringUtils.equals(executeMethod, ApiBatchRunMode.PARALLEL.name())) {
isFinish = planRunTestPlanApiCaseService.parallelExecute(testPlanExecutionQueue);
execOver = planRunTestPlanApiCaseService.parallelExecute(testPlanExecutionQueue);
} else {
isFinish = planRunTestPlanApiCaseService.serialExecute(testPlanExecutionQueue);
execOver = planRunTestPlanApiCaseService.serialExecute(testPlanExecutionQueue);
}
} else if (StringUtils.equalsIgnoreCase(collection.getType(), CaseType.SCENARIO_CASE.getKey())) {
if (StringUtils.equals(executeMethod, ApiBatchRunMode.PARALLEL.name())) {
isFinish = planRunTestPlanApiScenarioService.parallelExecute(testPlanExecutionQueue);
execOver = planRunTestPlanApiScenarioService.parallelExecute(testPlanExecutionQueue);
} else {
isFinish = planRunTestPlanApiScenarioService.serialExecute(testPlanExecutionQueue);
execOver = planRunTestPlanApiScenarioService.serialExecute(testPlanExecutionQueue);
}
}
if (isFinish) {
// 如果没有要执行的用例可能会出现空测试集的情况直接调用回调
collectionExecuteQueueFinish(queueId);
}
} catch (Exception e) {
Log.error("按测试集执行失败!", e);
execOver = true;
}
if (execOver) {
// 如果没有要执行的用例可能会出现空测试集的情况直接调用回调
collectionExecuteQueueFinish(queueId);
}
}
@ -455,14 +448,19 @@ public class TestPlanExecuteService {
public void collectionExecuteQueueFinish(String queueID) {
String queueType = QUEUE_PREFIX_TEST_PLAN_COLLECTION;
LogUtils.info("收到测试集执行完成的信息: 队列ID[{}],队列类型[{}],下一个节点的执行工作准备中...", queueID, queueType);
TestPlanExecutionQueue nextQueue = getNextQueue(queueID, queueType);
TestPlanExecutionQueue nextQueue = testPlanExecuteSupportService.getNextQueue(queueID, queueType);
if (StringUtils.equalsIgnoreCase(nextQueue.getRunMode(), ApiBatchRunMode.SERIAL.name())) {
//串行时由于是先拿出节点再判断执行所以要判断节点的isExecuteFinish
if (!nextQueue.isExecuteFinish()) {
boolean execError = false;
try {
LogUtils.info("测试集该节点的串行执行完成! --- 队列ID[{}],队列类型[{}]开始执行下一个队列ID[{}],类型[{}]", queueID, queueType, nextQueue.getQueueId(), nextQueue.getQueueType());
this.executeNextNode(nextQueue);
} catch (Exception e) {
Log.error("测试集下一个节点执行失败!", e);
execError = true;
}
if (execError) {
this.collectionExecuteQueueFinish(nextQueue.getQueueId());
}
} else {
@ -480,14 +478,18 @@ public class TestPlanExecuteService {
//测试计划中当前用例类型的全部执行完成
private void caseTypeExecuteQueueFinish(String queueID, String queueType) {
LogUtils.info("收到用例类型执行队列的执行完成的信息: 队列ID[{}],队列类型[{}],下一个节点的执行工作准备中...", queueID, queueType);
TestPlanExecutionQueue nextQueue = getNextQueue(queueID, queueType);
TestPlanExecutionQueue nextQueue = testPlanExecuteSupportService.getNextQueue(queueID, queueType);
if (StringUtils.equalsIgnoreCase(nextQueue.getRunMode(), ApiBatchRunMode.SERIAL.name())) {
//串行时由于是先拿出节点再判断执行所以要判断节点的isExecuteFinish
if (!nextQueue.isExecuteFinish()) {
boolean execError = false;
try {
LogUtils.info("用例类型该节点的串行执行完成! --- 队列ID[{}],队列类型[{}]开始执行下一个队列ID[{}],类型[{}]", queueID, queueType, nextQueue.getQueueId(), nextQueue.getQueueType());
this.executeNextNode(nextQueue);
} catch (Exception e) {
execError = true;
}
if (execError) {
this.caseTypeExecuteQueueFinish(nextQueue.getQueueId(), nextQueue.getQueueType());
}
} else {
@ -505,13 +507,17 @@ public class TestPlanExecuteService {
//测试计划执行完成
private void testPlanExecuteQueueFinish(String queueID, String queueType) {
LogUtils.info("收到测试计划执行完成的信息: 队列ID[{}],队列类型[{}],下一个节点的执行工作准备中...", queueID, queueType);
TestPlanExecutionQueue nextQueue = getNextQueue(queueID, queueType);
TestPlanExecutionQueue nextQueue = testPlanExecuteSupportService.getNextQueue(queueID, queueType);
if (StringUtils.equalsIgnoreCase(nextQueue.getRunMode(), ApiBatchRunMode.SERIAL.name())) {
if (!nextQueue.isExecuteFinish()) {
boolean execError = false;
try {
LogUtils.info("测试计划该节点的串行执行完成! --- 队列ID[{}],队列类型[{}]开始执行下一个队列ID[{}],类型[{}]", queueID, queueType, nextQueue.getQueueId(), nextQueue.getQueueType());
this.executeNextNode(nextQueue);
} catch (Exception e) {
execError = true;
}
if (execError) {
this.testPlanExecuteQueueFinish(nextQueue.getQueueId(), nextQueue.getQueueType());
}
} else {
@ -528,16 +534,20 @@ public class TestPlanExecuteService {
//测试计划批量执行队列节点执行完成
private void testPlanGroupQueueFinish(String queueID, String queueType) {
LogUtils.info("收到计划组执行完成的信息: 队列ID[{}],队列类型[{}],下一个节点的执行工作准备中...", queueID, queueType);
TestPlanExecutionQueue nextQueue = getNextQueue(queueID, queueType);
TestPlanExecutionQueue nextQueue = testPlanExecuteSupportService.getNextQueue(queueID, queueType);
if (nextQueue == null) {
return;
}
if (StringUtils.equalsIgnoreCase(nextQueue.getRunMode(), ApiBatchRunMode.SERIAL.name())) {
if (!nextQueue.isExecuteFinish()) {
boolean execError = false;
try {
LogUtils.info("计划组该节点的串行执行完成! --- 队列ID[{}],队列类型[{}]开始执行下一个队列ID[{}],类型[{}]", queueID, queueType, nextQueue.getQueueId(), nextQueue.getQueueType());
this.executeNextNode(nextQueue);
} catch (Exception e) {
execError = true;
}
if (execError) {
this.testPlanGroupQueueFinish(queueID, queueType);
}
} else {
@ -564,111 +574,27 @@ public class TestPlanExecuteService {
}
}
private void summaryTestPlanReport(String reportId, boolean isGroupReport, boolean isStop) {
LogUtils.info("开始合并报告: --- 报告ID[{}],是否是报告组[{}]", reportId, isGroupReport);
try {
if (isGroupReport) {
testPlanReportService.summaryGroupReport(reportId);
} else {
testPlanReportService.summaryPlanReport(reportId);
}
TestPlanReportPostParam postParam = new TestPlanReportPostParam();
postParam.setReportId(reportId);
// 执行生成报告, 执行状态为已完成, 执行及结束时间为当前时间
postParam.setEndTime(System.currentTimeMillis());
postParam.setExecStatus(isStop ? ExecStatus.STOPPED.name() : ExecStatus.COMPLETED.name());
testPlanReportService.postHandleReport(postParam, false);
if (!isGroupReport) {
TestPlanReport testPlanReport = testPlanReportService.selectById(reportId);
if (testPlanReport != null) {
testPlanService.refreshTestPlanStatus(testPlanReport.getTestPlanId());
}
}
} catch (Exception e) {
LogUtils.error("Cannot find test plan report for " + reportId, e);
}
}
private void queueExecuteFinish(TestPlanExecutionQueue queue) {
LogUtils.info("当前节点执行完成: --- 队列ID[{}],队列类型[{}]父队列ID[{}],父队列类型[{}]", queue.getQueueId(), queue.getQueueType(), queue.getParentQueueId(), queue.getParentQueueType());
if (StringUtils.equalsIgnoreCase(queue.getParentQueueType(), QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE)) {
if (StringUtils.equalsIgnoreCase(queue.getQueueType(), QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE)) {
// 计划组报告汇总并统计
this.summaryTestPlanReport(queue.getQueueId(), true, false);
testPlanExecuteSupportService.summaryTestPlanReport(queue.getQueueId(), true, false);
} else if (StringUtils.equalsIgnoreCase(queue.getQueueType(), QUEUE_PREFIX_TEST_PLAN_CASE_TYPE)) {
/*
此时处于批量勾选执行中的游离态测试计划执行所以队列顺序为QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE -> QUEUE_PREFIX_TEST_PLAN_CASE_TYPE
此时queue节点为testPlanCollection的节点 而测试计划节点串行状态下在执行之前就被弹出了
所以获取报告ID的方式为读取queueId caseType队列和collection队列的queueId都是报告ID
*/
this.summaryTestPlanReport(queue.getQueueId(), false, false);
testPlanExecuteSupportService.summaryTestPlanReport(queue.getQueueId(), false, false);
}
this.testPlanGroupQueueFinish(queue.getParentQueueId(), queue.getParentQueueType());
} else if (StringUtils.equalsIgnoreCase(queue.getParentQueueType(), QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE)) {
// 计划报告汇总并统计
this.summaryTestPlanReport(queue.getQueueId(), false, false);
testPlanExecuteSupportService.summaryTestPlanReport(queue.getQueueId(), false, false);
this.testPlanExecuteQueueFinish(queue.getParentQueueId(), queue.getParentQueueType());
} else if (StringUtils.equalsIgnoreCase(queue.getParentQueueType(), QUEUE_PREFIX_TEST_PLAN_CASE_TYPE)) {
this.caseTypeExecuteQueueFinish(queue.getParentQueueId(), queue.getParentQueueType());
}
}
/**
* 获取下一个队列节点
*/
private TestPlanExecutionQueue getNextQueue(String queueId, String queueType) {
if (StringUtils.isAnyBlank(queueId, queueType)) {
return null;
}
String queueKey = this.genQueueKey(queueId, queueType);
ListOperations<String, String> listOps = redisTemplate.opsForList();
String queueDetail = listOps.leftPop(queueKey);
if (StringUtils.isBlank(queueDetail)) {
// 重试1次获取
try {
Thread.sleep(1000);
} catch (Exception ignore) {
}
queueDetail = redisTemplate.opsForList().leftPop(queueKey);
}
if (StringUtils.isNotBlank(queueDetail)) {
TestPlanExecutionQueue returnQueue = JSON.parseObject(queueDetail, TestPlanExecutionQueue.class);
Long size = listOps.size(queueKey);
if (size == null || size == 0) {
returnQueue.setLastOne(true);
if (StringUtils.equalsIgnoreCase(returnQueue.getRunMode(), ApiBatchRunMode.SERIAL.name())) {
//串行的执行方式意味着最后一个节点要单独存储
redisTemplate.opsForValue().setIfAbsent(genQueueKey(queueKey, LAST_QUEUE_PREFIX), JSON.toJSONString(returnQueue), 1, TimeUnit.DAYS);
}
// 最后一个节点清理队列
deleteQueue(queueKey);
}
return returnQueue;
} else {
String lastQueueJson = redisTemplate.opsForValue().getAndDelete(genQueueKey(queueKey, LAST_QUEUE_PREFIX));
if (StringUtils.isNotBlank(lastQueueJson)) {
TestPlanExecutionQueue nextQueue = JSON.parseObject(lastQueueJson, TestPlanExecutionQueue.class);
nextQueue.setExecuteFinish(true);
return nextQueue;
}
}
// 整体获取完清理队列
deleteQueue(queueKey);
return null;
}
private void deleteQueue(String queueKey) {
redisTemplate.delete(queueKey);
}
//生成队列key
private String genQueueKey(String queueId, String queueType) {
return queueType + queueId;
}
}

View File

@ -0,0 +1,169 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.TestPlanReportPostParam;
import io.metersphere.plan.mapper.TestPlanReportApiCaseMapper;
import io.metersphere.plan.mapper.TestPlanReportApiScenarioMapper;
import io.metersphere.plan.mapper.TestPlanReportMapper;
import io.metersphere.sdk.constants.ApiBatchRunMode;
import io.metersphere.sdk.constants.ExecStatus;
import io.metersphere.sdk.dto.queue.TestPlanExecutionQueue;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanExecuteSupportService {
@Resource
private TestPlanService testPlanService;
@Resource
private TestPlanReportService testPlanReportService;
@Resource
private RedisTemplate<String, String> redisTemplate;
@Resource
private TestPlanReportApiCaseMapper testPlanReportApiCaseMapper;
@Resource
private TestPlanReportApiScenarioMapper testPlanReportApiScenarioMapper;
public static final String QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE = "test-plan-batch-execute:";
public static final String QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE = "test-plan-group-execute:";
public static final String QUEUE_PREFIX_TEST_PLAN_CASE_TYPE = "test-plan-case-type-execute:";
public static final String QUEUE_PREFIX_TEST_PLAN_COLLECTION = "test-plan-collection-execute:";
public static final String LAST_QUEUE_PREFIX = "last-queue:";
@Resource
private TestPlanReportMapper testPlanReportMapper;
public void setRedisForList(String key, List<String> list) {
redisTemplate.opsForList().rightPushAll(key, list);
redisTemplate.expire(key, 1, TimeUnit.DAYS);
}
public void deleteRedisKey(String redisKey) {
//清除list的key last key节点
redisTemplate.delete(redisKey);
redisTemplate.delete(genQueueKey(redisKey, LAST_QUEUE_PREFIX));
}
public void summaryTestPlanReport(String reportId, boolean isGroupReport, boolean isStop) {
LogUtils.info("开始合并报告: --- 报告ID[{}],是否是报告组[{}]", reportId, isGroupReport);
try {
if (isGroupReport) {
testPlanReportService.summaryGroupReport(reportId);
} else {
testPlanReportService.summaryPlanReport(reportId);
}
TestPlanReportPostParam postParam = new TestPlanReportPostParam();
postParam.setReportId(reportId);
// 执行生成报告, 执行状态为已完成, 执行及结束时间为当前时间
postParam.setEndTime(System.currentTimeMillis());
postParam.setExecStatus(isStop ? ExecStatus.STOPPED.name() : ExecStatus.COMPLETED.name());
testPlanReportService.postHandleReport(postParam, false);
if (!isGroupReport) {
TestPlanReport testPlanReport = testPlanReportService.selectById(reportId);
if (testPlanReport != null) {
testPlanService.refreshTestPlanStatus(testPlanReport.getTestPlanId());
}
}
} catch (Exception e) {
LogUtils.error("Cannot find test plan report for " + reportId, e);
}
}
/**
* 获取下一个队列节点
*/
public TestPlanExecutionQueue getNextQueue(String queueId, String queueType) {
if (StringUtils.isAnyBlank(queueId, queueType)) {
return null;
}
String queueKey = this.genQueueKey(queueId, queueType);
ListOperations<String, String> listOps = redisTemplate.opsForList();
String queueDetail = listOps.leftPop(queueKey);
if (StringUtils.isBlank(queueDetail)) {
// 重试1次获取
try {
Thread.sleep(1000);
} catch (Exception ignore) {
}
queueDetail = redisTemplate.opsForList().leftPop(queueKey);
}
if (StringUtils.isNotBlank(queueDetail)) {
TestPlanExecutionQueue returnQueue = JSON.parseObject(queueDetail, TestPlanExecutionQueue.class);
Long size = listOps.size(queueKey);
if (size == null || size == 0) {
returnQueue.setLastOne(true);
if (StringUtils.equalsIgnoreCase(returnQueue.getRunMode(), ApiBatchRunMode.SERIAL.name())) {
//串行的执行方式意味着最后一个节点要单独存储
redisTemplate.opsForValue().setIfAbsent(genQueueKey(queueKey, LAST_QUEUE_PREFIX), JSON.toJSONString(returnQueue), 1, TimeUnit.DAYS);
}
// 最后一个节点清理队列
deleteQueue(queueKey);
}
return returnQueue;
} else {
String lastQueueJson = redisTemplate.opsForValue().getAndDelete(genQueueKey(queueKey, LAST_QUEUE_PREFIX));
if (StringUtils.isNotBlank(lastQueueJson)) {
TestPlanExecutionQueue nextQueue = JSON.parseObject(lastQueueJson, TestPlanExecutionQueue.class);
nextQueue.setExecuteFinish(true);
return nextQueue;
}
}
// 整体获取完清理队列
deleteQueue(queueKey);
return null;
}
public void deleteQueue(String queueKey) {
redisTemplate.delete(queueKey);
}
//生成队列key
public String genQueueKey(String queueId, String queueType) {
return queueType + queueId;
}
public boolean checkTestPlanStopped(String prepareReportId) {
TestPlanReportExample reportExample = new TestPlanReportExample();
reportExample.createCriteria().andIdEqualTo(prepareReportId).andExecStatusEqualTo(ExecStatus.STOPPED.name());
return testPlanReportMapper.countByExample(reportExample) > 0;
}
public void updateReportStopped(String testPlanReportId) {
TestPlanReport testPlanReport = new TestPlanReport();
testPlanReport.setId(testPlanReportId);
testPlanReport.setExecStatus(ExecStatus.STOPPED.name());
testPlanReportMapper.updateByPrimaryKeySelective(testPlanReport);
TestPlanReportApiCaseExample apiCaseExample = new TestPlanReportApiCaseExample();
apiCaseExample.createCriteria().andTestPlanReportIdEqualTo(testPlanReportId).andApiCaseExecuteResultIsNull();
TestPlanReportApiCase testPlanReportApiCase = new TestPlanReportApiCase();
testPlanReportApiCase.setApiCaseExecuteResult(ExecStatus.STOPPED.name());
testPlanReportApiCaseMapper.updateByExampleSelective(testPlanReportApiCase, apiCaseExample);
TestPlanReportApiScenarioExample scenarioExample = new TestPlanReportApiScenarioExample();
scenarioExample.createCriteria().andTestPlanReportIdEqualTo(testPlanReportId).andApiScenarioExecuteResultIsNull();
TestPlanReportApiScenario testPlanReportApiScenario = new TestPlanReportApiScenario();
testPlanReportApiScenario.setApiScenarioExecuteResult(ExecStatus.STOPPED.name());
testPlanReportApiScenarioMapper.updateByExampleSelective(testPlanReportApiScenario, scenarioExample);
}
}

View File

@ -256,9 +256,6 @@ public class TestPlanExecuteTests extends BaseTest {
}
}
}
// Assertions.assertTrue(!collectionQueueIdList.isEmpty());
// Assertions.assertTrue(!allQueueIds.isEmpty());
this.checkRedisKeyEmpty(allQueueIds, collectionQueueIdList);
}