fix(测试跟踪): 修复引用场景失败重试未执行问题

--bug=1024776 --user=王孝刚 【测试跟踪】测试计划-特定场景在执行测试计划时选择失败重试未执行重试执行 https://www.tapd.cn/55049933/s/1356728

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2023-03-28 17:44:50 +08:00 committed by fit2-zhao
parent 7a217adf0d
commit 176eea9102
12 changed files with 75 additions and 135 deletions

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.definition.request.assertions.MsAssertions;
import io.metersphere.api.dto.definition.request.auth.MsAuthManager;
import io.metersphere.api.dto.definition.request.controller.MsIfController;
import io.metersphere.api.dto.definition.request.controller.MsLoopController;
import io.metersphere.api.dto.definition.request.controller.MsRetryLoopController;
import io.metersphere.api.dto.definition.request.controller.MsTransactionController;
import io.metersphere.api.dto.definition.request.dns.MsDNSCacheManager;
import io.metersphere.api.dto.definition.request.extract.MsExtract;
@ -1013,4 +1014,17 @@ public class ElementUtil {
tree.add(headerManager);
}
}
public static HashTree retryHashTree(String name, long retryNum, HashTree tree) {
if (StringUtils.isNotBlank(name) &&
(name.startsWith(ResultParseUtil.POST_PROCESS_SCRIPT) ||
name.startsWith(ResultParseUtil.PRE_PROCESS_SCRIPT))) {
return tree;
}
MsRetryLoopController loopController = new MsRetryLoopController();
loopController.setClazzName(MsRetryLoopController.class.getCanonicalName());
loopController.setRetryNum(retryNum);
loopController.setEnable(true);
return loopController.controller(tree, name);
}
}

View File

@ -107,6 +107,7 @@ public class MsScenario extends MsTestElement {
ParameterConfig newConfig = new ParameterConfig();
if (this.isEnvironmentEnable()) {
this.setNewConfig(envConfig, newConfig);
newConfig.setRetryNum(config.getRetryNum());
}
if (config != null && StringUtils.equals(this.getId(), config.getScenarioId())) {
config.setTransferVariables(this.variables);

View File

@ -71,7 +71,7 @@ public class ParameterConfig extends MsParameter {
*/
private boolean isOperating;
/**
* 导入/导出操作时取样器的testname值
* 导入/导出操作时取样器的testName值
*/
private String operatingSampleTestName;
/**
@ -92,6 +92,10 @@ public class ParameterConfig extends MsParameter {
private String browserLanguage;
private boolean isApi;
/**
* 失败重试次数
*/
private long retryNum;
/**
* 排除生成临界控制器的场景
*/

View File

@ -27,7 +27,7 @@ public class MsRetryLoopController extends MsTestElement {
@Override
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, MsParameter msParameter) {
final HashTree groupTree = controller(tree);
final HashTree groupTree = controller(tree, this.getName());
if (CollectionUtils.isNotEmpty(hashTree)) {
hashTree.forEach(el -> {
@ -38,13 +38,13 @@ public class MsRetryLoopController extends MsTestElement {
}
}
private WhileController initWhileController(String condition) {
private WhileController initWhileController(String condition, String name) {
if (StringUtils.isEmpty(condition)) {
return null;
}
WhileController controller = new WhileController();
controller.setEnabled(this.isEnable());
controller.setName("WhileController");
controller.setName(StringUtils.join("RetryWhile_", name));
controller.setProperty(TestElement.TEST_CLASS, WhileController.class.getName());
controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("WhileControllerGui"));
controller.setCondition(condition);
@ -73,9 +73,9 @@ public class MsRetryLoopController extends MsTestElement {
return script;
}
private HashTree controller(HashTree tree) {
public HashTree controller(HashTree tree, String name) {
String whileCondition = "${__jexl3(" + "\"${" + ms_current_timer + "}\" !=\"stop\")}";
HashTree hashTree = tree.add(initWhileController(whileCondition));
HashTree hashTree = tree.add(initWhileController(whileCondition, name));
// 添加超时处理防止死循环
JSR223Listener postProcessor = new JSR223Listener();
postProcessor.setName("Retry-controller");

View File

@ -58,7 +58,14 @@ public class MsJSR223Processor extends MsTestElement {
String resourceId = StringUtils.isNotEmpty(this.getId()) ? this.getId() : this.getResourceId();
ElementUtil.setBaseParams(processor, this.getParent(), config, resourceId, this.getIndex());
final HashTree jsr223PreTree = tree.add(processor);
// 失败重试
HashTree jsr223PreTree;
if (config.getRetryNum() > 0) {
final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree);
jsr223PreTree = loopTree.add(processor);
} else {
jsr223PreTree = tree.add(processor);
}
if (CollectionUtils.isNotEmpty(hashTree)) {
hashTree.forEach(el -> {
el.toHashTree(jsr223PreTree, el.getHashTree(), config);

View File

@ -78,9 +78,14 @@ public class MsDubboSampler extends MsTestElement {
}
hashTree = this.getHashTree();
}
final HashTree testPlanTree = tree.add(dubboSample(config));
// 失败重试
HashTree testPlanTree;
if (config.getRetryNum() > 0) {
final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree);
testPlanTree = loopTree.add(dubboSample(config));
} else {
testPlanTree = tree.add(dubboSample(config));
}
//添加全局前后置脚本
EnvironmentConfig envConfig = null;
if (config.getConfig() != null) {

View File

@ -167,7 +167,14 @@ public class MsHTTPSamplerProxy extends MsTestElement {
}
}
}
final HashTree httpSamplerTree = tree.add(sampler);
// 失败重试
HashTree httpSamplerTree;
if (config.getRetryNum() > 0) {
final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree);
httpSamplerTree = loopTree.add(sampler);
} else {
httpSamplerTree = tree.add(sampler);
}
// 注意顺序放在config前面会优先于环境的请求头生效
if (httpConfig != null && httpConfig.isMock() && StringUtils.isNotEmpty(this.getId())) {
//如果选择的是mock环境则自动添加一个apiHeader

View File

@ -114,7 +114,14 @@ public class MsJDBCSampler extends MsTestElement {
MSException.throwException(StringUtils.isNotEmpty(this.getName()) ? this.getName() + "" + message : message);
}
JDBCSampler jdbcSampler = jdbcSampler(config);
final HashTree samplerHashTree = tree.add(jdbcSampler);
// 失败重试
HashTree samplerHashTree;
if (config.getRetryNum() > 0) {
final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree);
samplerHashTree = loopTree.add(jdbcSampler);
} else {
samplerHashTree = tree.add(jdbcSampler);
}
tree.add(ElementUtil.jdbcDataSource(jdbcSampler.getDataSource(), this.dataSource));
ElementUtil.jdbcArguments(this.getName(), this.getVariables(), tree);

View File

@ -140,7 +140,14 @@ public class MsTCPSampler extends MsTestElement {
final HashTree samplerHashTree = new ListedHashTree();
samplerHashTree.add(tcpConfig());
TCPSampler tcpSampler = tcpSampler(config);
tree.set(tcpSampler, samplerHashTree);
// 失败重试
if (config.getRetryNum() > 0) {
final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree);
loopTree.set(tcpSampler, samplerHashTree);
} else {
tree.set(tcpSampler, samplerHashTree);
}
setUserParameters(samplerHashTree);
if (tcpPreProcessor != null && StringUtils.isNotBlank(tcpPreProcessor.getScript())) {
samplerHashTree.add(tcpPreProcessor.getShellProcessor());

View File

@ -26,7 +26,6 @@ import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.dto.ProjectJarConfig;
import io.metersphere.environment.service.BaseEnvironmentService;
import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.ApiRetryOnFailureService;
import io.metersphere.service.RemakeReportService;
import io.metersphere.utils.LoggerUtil;
import jakarta.annotation.Resource;
@ -54,8 +53,6 @@ public class ApiCaseSerialService {
private RedisTemplate<String, Object> redisTemplate;
@Resource
private TestPlanApiCaseMapper testPlanApiCaseMapper;
@Resource
private ApiRetryOnFailureService apiRetryOnFailureService;
public void serial(DBTestQueue executionQueue) {
ApiExecutionQueueDetail queue = executionQueue.getDetail();
@ -148,21 +145,13 @@ public class ApiCaseSerialService {
JSONObject element = JSONUtil.parseObject(caseWithBLOBs.getRequest());
ElementUtil.dataFormatting(element);
parse(element, testId, envId, caseWithBLOBs.getProjectId());
String runData = element.toString();
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
try {
// 失败重试
String retryData = apiRetryOnFailureService.retry(runData, runRequest.getRetryNum(), true);
if (StringUtils.isNotBlank(retryData)) {
runData = retryData;
}
} catch (Exception e) {
LoggerUtil.error("失败重试脚本生成失败 ", runRequest.getReportId(), e);
}
}
group.getHashTree().add(JSONUtil.parseObject(runData, MsTestElement.class));
group.getHashTree().add(JSONUtil.parseObject(element.toString(), MsTestElement.class));
testPlan.getHashTree().add(group);
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
ParameterConfig config = new ParameterConfig();
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
config.setRetryNum(runRequest.getRetryNum());
}
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), config);
LoggerUtil.info("用例资源:" + caseWithBLOBs.getName() + ", 生成执行脚本JMX成功", runRequest.getReportId());
return jmeterHashTree;
}

View File

@ -17,7 +17,6 @@ import io.metersphere.dto.*;
import io.metersphere.environment.service.BaseEnvGroupProjectService;
import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.ApiExecutionQueueService;
import io.metersphere.service.ApiRetryOnFailureService;
import io.metersphere.service.RemakeReportService;
import io.metersphere.utils.LoggerUtil;
import io.metersphere.vo.BooleanPool;
@ -144,22 +143,7 @@ public class GenerateHashTreeUtil {
Map<String, List<ProjectJarConfig>> jarsMap = NewDriverManager.getJars(projectIds, runRequest.getPool());
testPlan.setProjectJarIds(jarsMap.keySet().stream().toList());
testPlan.setPoolJarsMap(jarsMap);
String data = definition;
// 失败重试
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
try {
ApiRetryOnFailureService apiRetryOnFailureService = CommonBeanFactory.getBean(ApiRetryOnFailureService.class);
String retryData = apiRetryOnFailureService.retry(data, runRequest.getRetryNum(), false);
if (StringUtils.isNotBlank(retryData)) {
data = retryData;
}
} catch (Exception e) {
LoggerUtil.error("失败重试脚本生成失败 ", runRequest.getReportId(), e);
}
}
GenerateHashTreeUtil.parse(data, scenario);
GenerateHashTreeUtil.parse(definition, scenario);
group.setEnableCookieShare(scenario.isEnableCookieShare());
LinkedList<MsTestElement> scenarios = new LinkedList<>();
scenarios.add(scenario);
@ -170,6 +154,9 @@ public class GenerateHashTreeUtil {
ParameterConfig config = new ParameterConfig();
config.setScenarioId(item.getId());
config.setReportType(runRequest.getReportType());
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
config.setRetryNum(runRequest.getRetryNum());
}
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), config);
LoggerUtil.info("场景资源:" + item.getName() + ", 生成执行脚本JMX成功", runRequest.getReportId());

View File

@ -1,88 +0,0 @@
package io.metersphere.service;
import io.metersphere.api.dto.definition.request.controller.MsRetryLoopController;
import io.metersphere.commons.utils.JSONUtil;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.plugin.core.MsTestElement;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
public class ApiRetryOnFailureService {
public final static List<String> requests = List.of(
"HTTPSamplerProxy",
"DubboSampler",
"JDBCSampler",
"TCPSampler",
"JSR223Processor");
private final static String HASH_TREE_ELEMENT = "hashTree";
private final static String TYPE = "type";
private final static String RESOURCE_ID = "resourceId";
private final static String RETRY = "MsRetry_";
private final static String LOOP = "LoopController";
public String retry(String data, long retryNum, boolean isCase) {
if (StringUtils.isNotEmpty(data)) {
JSONObject element = JSONUtil.parseObject(data);
if (element != null && isCase) {
return formatSampler(element, retryNum).toString();
}
if (element != null && element.has(HASH_TREE_ELEMENT) && !StringUtils.equalsIgnoreCase(element.optString(TYPE), LOOP)) {
JSONArray hashTree = element.getJSONArray(HASH_TREE_ELEMENT);
setRetry(hashTree, retryNum);
}
return element.toString();
}
return null;
}
public MsTestElement retryParse(String data) {
try {
return JSONUtil.parseObject(data, MsRetryLoopController.class);
} catch (Exception e) {
LogUtil.error(e);
}
return null;
}
public void setRetry(JSONArray hashTree, long retryNum) {
for (int i = 0; i < hashTree.length(); i++) {
JSONObject element = hashTree.getJSONObject(i);
if (StringUtils.equalsIgnoreCase(element.optString(TYPE), LOOP)) {
continue;
}
JSONObject whileObj = formatSampler(element, retryNum);
if (whileObj != null) {
hashTree.put(i, whileObj);
} else if (element.has(HASH_TREE_ELEMENT)) {
JSONArray elementJSONArray = element.getJSONArray(HASH_TREE_ELEMENT);
setRetry(elementJSONArray, retryNum);
}
}
}
private JSONObject formatSampler(JSONObject element, long retryNum) {
if (element.has(TYPE) && requests.contains(element.optString(TYPE))) {
MsRetryLoopController loopController = new MsRetryLoopController();
loopController.setClazzName(MsRetryLoopController.class.getCanonicalName());
loopController.setName(RETRY + element.optString(RESOURCE_ID));
loopController.setRetryNum(retryNum);
loopController.setEnable(true);
loopController.setResourceId(UUID.randomUUID().toString());
JSONObject whileObj = JSONUtil.parseObject(JSONUtil.toJSONString(loopController));
JSONArray hashTree = new JSONArray();
hashTree.put(element);
whileObj.put(HASH_TREE_ELEMENT, hashTree);
return whileObj;
}
return null;
}
}