fix(接口测试): 修复场景停止执行无法查看报告的缺陷

--bug=1025888 --user=王孝刚 【接口测试】github#23649,接口自动化调试点击停止,希望保留已执行步骤的执行结果
https://www.tapd.cn/55049933/s/1367785
This commit is contained in:
wxg0103 2023-04-27 19:15:11 +08:00 committed by fit2-zhao
parent 1091268602
commit 70e6ccc1e9
21 changed files with 139 additions and 30 deletions

View File

@ -60,4 +60,5 @@ public class RunDefinitionRequest {
private boolean syncResult;
private boolean editCaseRequest;
private String triggerMode;
}

View File

@ -140,7 +140,7 @@ public class ApiCaseExecuteService {
String reportType = request.getConfig().getReportType();
String poolId = request.getConfig().getResourcePoolId();
String runMode = StringUtils.equals(request.getTriggerMode(), TriggerMode.MANUAL.name()) ? ApiRunMode.API_PLAN.name() : ApiRunMode.SCHEDULE_API_PLAN.name();
DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.API_PLAN.name(), request.getPlanReportId(), reportType, runMode, request.getConfig());
DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.API_PLAN.name(), request.getPlanReportId(), reportType, runMode, request.getTriggerMode(), request.getConfig());
// 开始选择执行模式
if (deQueue != null && deQueue.getDetail() != null) {
@ -344,7 +344,7 @@ public class ApiCaseExecuteService {
String reportType = request.getConfig().getReportType();
String poolId = request.getConfig().getResourcePoolId();
DBTestQueue queue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.DEFINITION.name(), serialReportId, reportType, ApiRunMode.DEFINITION.name(), request.getConfig());
DBTestQueue queue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.DEFINITION.name(), serialReportId, reportType, ApiRunMode.DEFINITION.name(), request.getTriggerMode(), request.getConfig());
// 开始选择执行模式
if (queue != null && queue.getDetail() != null) {
Thread thread = new Thread(() -> {

View File

@ -49,7 +49,7 @@ public class ApiCaseParallelExecuteService {
}
ApiDefinitionExecResultWithBLOBs result = executeQueue.get(testId);
String reportId = result.getId();
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testId, reportId, runMode);
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testId, reportId, runMode, executionQueue.getTriggerMode());
try {
runRequest.setPool(pool);
runRequest.setTestPlanReportId(executionQueue.getReportId());

View File

@ -128,7 +128,7 @@ public class ApiExecuteService {
jMeterService.verifyPool(testCaseWithBLOBs.getProjectId(), runModeConfigDTO);
// 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testCaseWithBLOBs.getId(), StringUtils.isEmpty(request.getReportId()) ? request.getId() : request.getReportId(), request.getRunMode(), null);
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testCaseWithBLOBs.getId(), StringUtils.isEmpty(request.getReportId()) ? request.getId() : request.getReportId(), request.getRunMode(), null, null);
if (testCaseWithBLOBs != null && StringUtils.isNotEmpty(testCaseWithBLOBs.getRequest())) {
try {
HashTree jmeterHashTree = this.generateHashTree(request, testCaseWithBLOBs, runModeConfigDTO);
@ -236,7 +236,7 @@ public class ApiExecuteService {
// 检查执行内容合规性
PerformInspectionUtil.inspection(jmx, testId, 4);
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testId, request.getId(), runMode, hashTree);
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testId, request.getId(), runMode,request.getTriggerMode(), hashTree);
runRequest.setDebug(request.isDebug());
runRequest.setRunMode(runMode);
runRequest.setExtendedParameters(new HashMap<String, Object>() {{

View File

@ -1,5 +1,6 @@
package io.metersphere.api.exec.queue;
import io.metersphere.api.jmeter.ApiLocalRunner;
import io.metersphere.commons.utils.NamedThreadFactory;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.utils.LoggerUtil;
@ -118,6 +119,7 @@ public class ExecThreadPoolExecutor {
}
public void removeQueue(String reportId) {
ApiLocalRunner.stop(reportId);
// 检查缓冲区
Queue<JmeterRunRequestDTO> bufferQueue = msRejectedExecutionHandler.getBufferQueue();
if (CollectionUtils.isNotEmpty(bufferQueue)) {

View File

@ -155,7 +155,9 @@ public class ApiScenarioExecuteService {
String planReportId = StringUtils.isNotEmpty(request.getTestPlanReportId()) ? request.getTestPlanReportId() : serialReportId;
// 生成执行队列
DBTestQueue executionQueue = apiExecutionQueueService.add(
executeQueue, request.getConfig().getResourcePoolId(), ApiRunMode.SCENARIO.name(), planReportId, reportType, request.getRunMode(), request.getConfig());
executeQueue, request.getConfig().getResourcePoolId(),
ApiRunMode.SCENARIO.name(), planReportId, reportType,
request.getRunMode(), request.getTriggerMode(), request.getConfig());
// 预生成报告
if (!request.isRerun() && !GenerateHashTreeUtil.isSetReport(request.getConfig())) {
@ -445,7 +447,7 @@ public class ApiScenarioExecuteService {
this.testElement(request);
HashTree hashTree = request.getTestElement().generateHashTree(config);
String runMode = StringUtils.isEmpty(request.getRunMode()) ? ApiRunMode.SCENARIO.name() : request.getRunMode();
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(request.getId(), request.getId(), runMode, hashTree);
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(request.getId(), request.getId(), runMode, request.getTriggerMode(), hashTree);
runRequest.setDebug(true);
String jmx = request.getTestElement().getJmx(hashTree);

View File

@ -70,7 +70,7 @@ public class ApiScenarioParallelService {
protected JmeterRunRequestDTO getJmeterRunRequestDTO(RunScenarioRequest request, String serialReportId, DBTestQueue executionQueue,
BaseSystemConfigDTO baseInfo, String reportId, RunModeDataDTO dataDTO) {
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(dataDTO.getTestId(), StringUtils.isNotEmpty(serialReportId) ? serialReportId : reportId, request.getRunMode());
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(dataDTO.getTestId(), StringUtils.isNotEmpty(serialReportId) ? serialReportId : reportId, request.getRunMode(), request.getTriggerMode());
runRequest.setReportType(StringUtils.isNotEmpty(serialReportId) ? RunModeConstants.SET_REPORT.toString() : RunModeConstants.INDEPENDENCE.toString());
runRequest.setQueueId(executionQueue.getId());
runRequest.setTestPlanReportId(request.getTestPlanReportId());

View File

@ -0,0 +1,92 @@
package io.metersphere.api.jmeter;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.jmeter.LocalRunner;
import io.metersphere.utils.LoggerUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.engine.StandardJMeterEngine;
import org.apache.jorphan.collections.HashTree;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@NoArgsConstructor
public class ApiLocalRunner extends LocalRunner {
private final static Map<String, StandardJMeterEngine> runningTasks = new ConcurrentHashMap<>();
private boolean isRunning = true;
private HashTree jmxTree;
public ApiLocalRunner(HashTree jmxTree) {
this.jmxTree = jmxTree;
}
public void run(String report, String runMode, String trigger) {
StandardJMeterEngine engine = new StandardJMeterEngine();
engine.configure(this.jmxTree);
List<String> triggerList = List.of(TriggerMode.BATCH.name(),TriggerMode.MANUAL.name());
List<String> runModeList = List.of(ApiRunMode.SCENARIO_PLAN.name(),
ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(),
ApiRunMode.SCHEDULE_SCENARIO.name(),
ApiRunMode.JENKINS_SCENARIO_PLAN.name(),
ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name(),
ApiRunMode.JENKINS_API_PLAN.name());
if ( triggerList.contains(trigger) &&
!runModeList.contains(runMode)) {
runningTasks.put(report, engine);
}
try {
LoggerUtil.info("LocalRunner 开始执行报告", report);
engine.runTest();
} catch (Exception e) {
engine.stopTest(true);
}
}
public static void stop(String report) {
if (MapUtils.isNotEmpty(runningTasks)) {
StandardJMeterEngine engine = runningTasks.get(report);
if (engine != null) {
engine.stopTest(true);
runningTasks.remove(report);
}
}
}
public static void clearCache(String report) {
if (StringUtils.isNotEmpty(report)) {
runningTasks.remove(report);
}
}
@PostConstruct
public void checkRunningTasks() {
new Thread(() -> {
while (isRunning) {
try {
Thread.sleep(1000 * 60);
if (MapUtils.isNotEmpty(runningTasks)) {
runningTasks.keySet().stream()
.filter(reportId -> !runningTasks.get(reportId).isActive())
.forEach(runningTasks::remove);
}
} catch (Exception e) {
LoggerUtil.error("检查运行中的任务异常:", e);
}
}
}).start();
}
@PreDestroy
public void destroy() {
isRunning = false;
}
}

View File

@ -20,8 +20,10 @@ import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.*;
import io.metersphere.engine.Engine;
import io.metersphere.jmeter.JMeterBase;
import io.metersphere.jmeter.LocalRunner;
import io.metersphere.service.*;
import io.metersphere.service.ApiPoolDebugService;
import io.metersphere.service.PluginService;
import io.metersphere.service.RedisTemplateService;
import io.metersphere.service.RemakeReportService;
import io.metersphere.utils.LoggerUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
@ -141,8 +143,8 @@ public class JMeterService {
}
LoggerUtil.info("资源:[" + request.getTestId() + "] 加入JMETER中开始执行", request.getReportId());
LocalRunner runner = new LocalRunner(request.getHashTree());
runner.run(request.getReportId());
ApiLocalRunner runner = new ApiLocalRunner(request.getHashTree());
runner.run(request.getReportId(), request.getRunMode() , request.getTriggerMode());
}
private void fileProcessing(JmeterRunRequestDTO request) {

View File

@ -76,6 +76,7 @@ public class MsApiBackendListener extends AbstractBackendListenerClient implemen
resultVO = ReportStatusUtil.getStatus(dto, resultVO);
dto.getArbitraryData().put(CommonConstants.LOCAL_STATUS_KEY, resultVO);
sampleResults.clear();
ApiLocalRunner.clearCache(dto.getReportId());
}
}
@ -129,6 +130,7 @@ public class MsApiBackendListener extends AbstractBackendListenerClient implemen
LoggerUtil.info("进入监听开始关闭CSV", dto.getReportId());
FileServer.getFileServer().closeCsv(dto.getReportId());
}
ApiLocalRunner.clearCache(dto.getReportId());
}
}

View File

@ -116,6 +116,7 @@ public class MsDebugListener extends AbstractListenerElement implements SampleLi
//删除可能出现的临时文件
FileUtils.deleteBodyTmpFiles(this.getName());
JvmUtil.memoryInfo();
ApiLocalRunner.clearCache(this.getName());
}
@Override

View File

@ -10,7 +10,7 @@ import io.metersphere.service.SystemParameterService;
public class RequestParamsUtil {
public static JmeterRunRequestDTO init(ApiExecutionQueue executionQueue, ApiExecutionQueueDetail queue, String reportId) {
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(queue.getTestId(), reportId, executionQueue.getRunMode());
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(queue.getTestId(), reportId, executionQueue.getRunMode(), executionQueue.getTriggerMode());
runRequest.setRetryEnable(queue.getRetryEnable() == null ? false : queue.getRetryEnable());
runRequest.setRetryNum(queue.getRetryNumber());
runRequest.setReportType(executionQueue.getReportType());

View File

@ -216,6 +216,7 @@ public class ApiScenarioController {
if (StringUtils.isEmpty(request.getExecuteType())) {
request.setExecuteType(ExecuteType.Debug.name());
}
request.setTriggerMode(TriggerMode.MANUAL.name());
apiAutomationService.debugRun(request, bodyFiles, scenarioFiles);
} catch (Exception e) {
return e.getMessage();

View File

@ -77,12 +77,12 @@ public class ApiExecutionQueueService {
private ApiExecutionQueueDetailMapper apiExecutionQueueDetailMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public DBTestQueue add(Object runObj, String poolId, String type, String reportId, String reportType, String runMode, RunModeConfigDTO config) {
public DBTestQueue add(Object runObj, String poolId, String type, String reportId, String reportType, String runMode, String triggerMode, RunModeConfigDTO config) {
LoggerUtil.info("报告【" + type + "】开始生成执行链", reportId);
if (config.getEnvMap() == null) {
config.setEnvMap(new LinkedHashMap<>());
}
ApiExecutionQueue executionQueue = getApiExecutionQueue(poolId, reportId, reportType, runMode, config);
ApiExecutionQueue executionQueue = getApiExecutionQueue(poolId, reportId, reportType, runMode,triggerMode, config);
queueMapper.insert(executionQueue);
DBTestQueue resQueue = new DBTestQueue();
BeanUtils.copyBean(resQueue, executionQueue);
@ -152,7 +152,7 @@ public class ApiExecutionQueueService {
resQueue.setDetailMap(detailMap);
}
protected ApiExecutionQueue getApiExecutionQueue(String poolId, String reportId, String reportType, String runMode, RunModeConfigDTO config) {
protected ApiExecutionQueue getApiExecutionQueue(String poolId, String reportId, String reportType, String runMode, String triggerMode, RunModeConfigDTO config) {
ApiExecutionQueue executionQueue = new ApiExecutionQueue();
executionQueue.setId(UUID.randomUUID().toString());
executionQueue.setCreateTime(System.currentTimeMillis());
@ -161,6 +161,7 @@ public class ApiExecutionQueueService {
executionQueue.setReportId(reportId);
executionQueue.setReportType(StringUtils.isNotEmpty(reportType) ? reportType : RunModeConstants.INDEPENDENCE.toString());
executionQueue.setRunMode(runMode);
executionQueue.setTriggerMode(triggerMode);
return executionQueue;
}

View File

@ -117,9 +117,8 @@ public class ApiJMeterFileService {
}
return scenario;
}
public byte[] downloadJmeterFiles(String runMode, String remoteTestId, String reportId, String reportType, String queueId) {
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(remoteTestId, reportId, runMode);
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(remoteTestId, reportId, runMode, null);
runRequest.setReportType(reportType);
runRequest.setQueueId(queueId);
BooleanPool booleanPool = new BooleanPool();

View File

@ -1,6 +1,7 @@
package io.metersphere.service;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.api.jmeter.ApiLocalRunner;
import io.metersphere.api.jmeter.utils.JmxFileUtil;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.CommonBeanFactory;
@ -36,6 +37,7 @@ public class RemakeReportService {
} catch (Exception e) {
LoggerUtil.error("回退报告异常", request.getReportId(), e);
} finally {
ApiLocalRunner.clearCache(request.getReportId());
redisTemplateService.delete(JmxFileUtil.getExecuteScriptKey(request.getReportId(), request.getTestId()));
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(request.getReportId()));
}

View File

@ -2025,6 +2025,7 @@ public class ApiDefinitionService {
record.setId(request.getTestElement().getHashTree().get(0).getHashTree().get(0).getName());
apiTestCaseMapper.updateByPrimaryKeySelective(record);
}
request.setTriggerMode(TriggerMode.MANUAL.name());
return apiExecuteService.debug(request, bodyFiles);
}

View File

@ -25,6 +25,7 @@ import io.metersphere.task.dto.TaskCenterRequest;
import io.metersphere.task.dto.TaskRequestDTO;
import io.metersphere.task.service.TaskService;
import io.metersphere.utils.LoggerUtil;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
@ -32,7 +33,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -231,5 +231,4 @@ public class ExtApiTaskService extends TaskService {
}
return new ArrayList<>();
}
}

View File

@ -14,6 +14,8 @@ public class ApiExecutionQueue implements Serializable {
private String runMode;
private String triggerMode;
private String poolId;
private Long createTime;

View File

@ -311,8 +311,8 @@ export default {
if (status === "pending" || status === 'stopped') {
return 0;
}
if (status === 'saved' || status === 'completed' || status === 'success' || status === 'error' || status ===
'pending' || status === 'fake_error') {
let statusArray = ['saved', 'completed', 'success','error','pending','fake_error'];
if (statusArray.includes(status)) {
return 100;
}
}
@ -321,8 +321,8 @@ export default {
showStop(status) {
if (status) {
status = status.toLowerCase();
if (status === "stopped" || status === 'saved' || status === 'completed' || status === 'success' || status ===
'error' || status === 'pending' || status === 'fake_error') {
let statusArray = ['saved', 'completed', 'success','error','pending','fake_error', 'stopped'];
if (statusArray.includes(status)) {
return false;
}
}
@ -348,8 +348,8 @@ export default {
let status = row.executionStatus;
if (status) {
status = row.executionStatus.toLowerCase();
if (status === 'saved' || status === 'completed' || status === 'success' || status === 'error' || status ===
'pending' || status === 'fake_error') {
let statusArray = ['saved', 'completed', 'success','error','pending','fake_error', 'stopped'];
if (statusArray.includes(status)) {
this.executionModule = null;
this.$nextTick(() => {
this.size = window.innerWidth;
@ -357,8 +357,6 @@ export default {
this.executionModule = row.executionModule;
this.reportType = row.reportType;
})
} else if (status === 'stopped') {
this.$warning(this.$t('commons.run_stop'));
} else {
this.$warning(this.$t('commons.run_warning'))
}

View File

@ -97,22 +97,26 @@ public class JmeterRunRequestDTO {
//自定义jar信息
private Map<String, List<ProjectJarConfig>> customJarInfo;
private String triggerMode;
public JmeterRunRequestDTO() {
}
public JmeterRunRequestDTO(String testId, String reportId, String runMode) {
public JmeterRunRequestDTO(String testId, String reportId, String runMode, String triggerMode) {
this.testId = testId;
this.reportId = reportId;
this.runMode = runMode;
this.triggerMode = triggerMode;
this.reportType = RunModeConstants.INDEPENDENCE.name();
this.pool = new BooleanPool();
this.extendedParameters = new LinkedHashMap<>();
}
public JmeterRunRequestDTO(String testId, String reportId, String runMode, HashTree hashTree) {
public JmeterRunRequestDTO(String testId, String reportId, String runMode, String triggerMode , HashTree hashTree) {
this.testId = testId;
this.reportId = reportId;
this.runMode = runMode;
this.triggerMode = triggerMode;
this.reportType = RunModeConstants.INDEPENDENCE.name();
this.hashTree = hashTree;
this.pool = new BooleanPool();