feat(接口测试): 场景增加逻辑控制器的解析
This commit is contained in:
parent
ee591fc936
commit
51c7327727
|
@ -2,6 +2,7 @@ package io.metersphere.api.constants;
|
|||
|
||||
/**
|
||||
* 步骤的类型
|
||||
*
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2024-01-10 11:24
|
||||
*/
|
||||
|
@ -21,6 +22,18 @@ public enum ApiScenarioStepType {
|
|||
/**
|
||||
* 场景
|
||||
*/
|
||||
API_SCENARIO
|
||||
// todo 逻辑控制器等
|
||||
API_SCENARIO,
|
||||
/**
|
||||
* 循环控制器
|
||||
*/
|
||||
LOOP_CONTROLLER,
|
||||
/**
|
||||
* 条件控制器
|
||||
*/
|
||||
IF_CONTROLLER,
|
||||
/**
|
||||
* 一次控制器
|
||||
*/
|
||||
ONCE_ONLY_CONTROLLER,
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.api.dto.request.controller;
|
||||
|
||||
public enum LoopType {
|
||||
LOOP_COUNT, WHILE, FOREACH
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package io.metersphere.api.dto.request.controller;
|
||||
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MsIfController extends AbstractMsTestElement {
|
||||
/**
|
||||
* 变量名称 ${variable} 长度255
|
||||
*/
|
||||
private String variable;
|
||||
/**
|
||||
* 操作符 == ,!=, < ,<=, >, >=, contains (=~),not contains (!~), is empty, is not empty
|
||||
*/
|
||||
private String condition;
|
||||
/**
|
||||
* 值 ${value} 长度255
|
||||
*/
|
||||
private String value;
|
||||
|
||||
public boolean isValid() {
|
||||
if (StringUtils.contains(condition, "is empty")) {
|
||||
return StringUtils.isNotBlank(variable);
|
||||
}
|
||||
return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(condition) && StringUtils.isNotBlank(value);
|
||||
}
|
||||
|
||||
public String getContentValue() {
|
||||
try {
|
||||
String content = this.variable;
|
||||
String pattern = "\\$\\{([^}]*)\\}";
|
||||
Pattern regex = Pattern.compile(pattern);
|
||||
Matcher matcher = regex.matcher(content);
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
while (matcher.find()) {
|
||||
stringBuilder.append(matcher.group(1)).append(",");
|
||||
}
|
||||
if (!stringBuilder.isEmpty()) {
|
||||
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
|
||||
}
|
||||
if (StringUtils.isEmpty(stringBuilder.toString())) {
|
||||
return this.variable;
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getConditionValue() {
|
||||
String key = getContentValue();
|
||||
|
||||
String variable = (StringUtils.isEmpty(key) || key.equals(this.variable)) || key.startsWith("__")
|
||||
? StringUtils.join("\"", this.variable, "\"")
|
||||
: StringUtils.join("vars.get('", key, "')");
|
||||
|
||||
String operator = this.condition;
|
||||
String value;
|
||||
|
||||
switch (operator) {
|
||||
case "is empty":
|
||||
variable = variable + "==" + "\"\\" + this.variable + "\"" + "|| empty(" + variable + ")";
|
||||
operator = "";
|
||||
value = "";
|
||||
break;
|
||||
case "is not empty":
|
||||
variable = variable + "!=" + "\"\\" + this.variable + "\"" + "&& !empty(" + variable + ")";
|
||||
operator = "";
|
||||
value = "";
|
||||
break;
|
||||
case "<":
|
||||
case ">":
|
||||
case "<=":
|
||||
case ">=":
|
||||
value = this.value;
|
||||
break;
|
||||
case "=~":
|
||||
case "!~":
|
||||
value = "\"(\\n|.)*" + this.value + "(\\n|.)*\"";
|
||||
break;
|
||||
default:
|
||||
value = "\"" + this.value + "\"";
|
||||
break;
|
||||
}
|
||||
|
||||
return "${__jexl3(" + variable + operator + value + ")}";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,40 @@
|
|||
package io.metersphere.api.dto.request.controller;
|
||||
|
||||
import io.metersphere.plugin.api.annotation.PluginSubType;
|
||||
import io.metersphere.plugin.api.dto.TestElementDTO;
|
||||
import io.metersphere.api.dto.request.controller.loop.MsCountController;
|
||||
import io.metersphere.api.dto.request.controller.loop.MsForEachController;
|
||||
import io.metersphere.api.dto.request.controller.loop.MsWhileController;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.system.valid.EnumValue;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@PluginSubType("MsLoopController")
|
||||
public class MsLoopController extends TestElementDTO {
|
||||
private String loopType;
|
||||
public class MsLoopController extends AbstractMsTestElement {
|
||||
|
||||
/**
|
||||
* 循环类型
|
||||
*
|
||||
* @see LoopType
|
||||
*/
|
||||
@EnumValue(enumClass = LoopType.class)
|
||||
private String loopType = LoopType.LOOP_COUNT.name();
|
||||
/**
|
||||
* 次数控制器
|
||||
*/
|
||||
private MsCountController msCountController;
|
||||
/**
|
||||
* ForEach控制器
|
||||
*/
|
||||
private MsForEachController forEachController;
|
||||
/**
|
||||
* While控制器
|
||||
*/
|
||||
private MsWhileController whileController;
|
||||
private String currentTime = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.api.dto.request.controller;
|
||||
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MsOnceOnlyController extends AbstractMsTestElement {
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.api.dto.request.controller;
|
||||
|
||||
public enum WhileConditionType {
|
||||
CONDITION, SCRIPT
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.api.dto.request.controller.loop;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsCountController {
|
||||
/**
|
||||
* 循环次数
|
||||
*/
|
||||
private String loops;
|
||||
/**
|
||||
* 循环间隔
|
||||
*/
|
||||
private long loopTime = 0;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.api.dto.request.controller.loop;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsForEachController {
|
||||
/**
|
||||
* 变量值 255
|
||||
*/
|
||||
private String variable;
|
||||
/**
|
||||
* 变量前缀 255
|
||||
*/
|
||||
private String value;
|
||||
/**
|
||||
* 循环间隔
|
||||
*/
|
||||
private long loopTime = 0;
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package io.metersphere.api.dto.request.controller.loop;
|
||||
|
||||
import io.metersphere.api.dto.request.controller.WhileConditionType;
|
||||
import io.metersphere.system.valid.EnumValue;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsWhileController {
|
||||
/**
|
||||
* 变量对象
|
||||
*/
|
||||
private MsWhileVariable msWhileVariable;
|
||||
/**
|
||||
* 脚本对象
|
||||
*/
|
||||
private MsWhileScript msWhileScript;
|
||||
/**
|
||||
* 超时时间
|
||||
*/
|
||||
private long timeout = 3000;
|
||||
/**
|
||||
* 条件类型
|
||||
*
|
||||
* @see io.metersphere.api.dto.request.controller.WhileConditionType
|
||||
*/
|
||||
@EnumValue(enumClass = WhileConditionType.class)
|
||||
private String conditionType = WhileConditionType.CONDITION.name();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.api.dto.request.controller.loop;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsWhileScript {
|
||||
/**
|
||||
* 脚本内容
|
||||
*/
|
||||
private String scriptValue;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package io.metersphere.api.dto.request.controller.loop;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Data
|
||||
public class MsWhileVariable {
|
||||
/**
|
||||
* 变量 255
|
||||
*/
|
||||
private String variable;
|
||||
/**
|
||||
* 操作符 == ,!=, < ,<=, >, >=, contains (=~),not contains (!~), is empty, is not empty
|
||||
*/
|
||||
private String condition;
|
||||
/**
|
||||
* 值 255
|
||||
*/
|
||||
private String value;
|
||||
|
||||
public String getConditionValue() {
|
||||
String variable = "\"" + this.getVariable() + "\"";
|
||||
String operator = this.getCondition();
|
||||
String value = null;
|
||||
switch (operator) {
|
||||
case "is empty":
|
||||
variable = "(" + variable + "==" + "\"\\" + this.getVariable() + "\"" + "|| empty(" + variable + "))";
|
||||
operator = "";
|
||||
break;
|
||||
case "is not empty":
|
||||
variable = "(" + variable + "!=" + "\"\\" + this.getVariable() + "\"" + "&& !empty(" + variable + "))";
|
||||
operator = "";
|
||||
break;
|
||||
case "<":
|
||||
case ">":
|
||||
case "<=":
|
||||
case ">=":
|
||||
if (StringUtils.isNumeric(this.getValue())) {
|
||||
value = this.getValue();
|
||||
} else {
|
||||
value = "\"" + this.getValue() + "\"";
|
||||
}
|
||||
break;
|
||||
case "=~":
|
||||
case "!~":
|
||||
value = "\"(\\n|.)*" + this.getVariable() + "(\\n|.)*\"";
|
||||
break;
|
||||
default:
|
||||
value = "\"" + this.getValue() + "\"";
|
||||
break;
|
||||
}
|
||||
|
||||
return variable + operator + value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package io.metersphere.api.parser.jmeter;
|
||||
|
||||
import io.metersphere.api.dto.request.controller.MsIfController;
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.control.IfController;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
public class MsIfControllerConverter extends AbstractJmeterElementConverter<MsIfController> {
|
||||
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, MsIfController element, ParameterConfig config) {
|
||||
|
||||
if (BooleanUtils.isFalse(element.getEnable())) {
|
||||
LogUtils.info("MsIfController is disabled");
|
||||
return;
|
||||
}
|
||||
//TODO 这里需要处理csv文件
|
||||
|
||||
|
||||
HashTree groupTree = tree.add(ifController(element));
|
||||
parseChild(groupTree, element, config);
|
||||
|
||||
}
|
||||
|
||||
private IfController ifController(MsIfController element) {
|
||||
IfController ifController = new IfController();
|
||||
ifController.setEnabled(element.getEnable());
|
||||
ifController.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "If Controller");
|
||||
ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName());
|
||||
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
|
||||
ifController.setCondition(element.getConditionValue());
|
||||
ifController.setEvaluateAll(false);
|
||||
ifController.setUseExpression(true);
|
||||
return ifController;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
package io.metersphere.api.parser.jmeter;
|
||||
|
||||
import io.metersphere.api.dto.request.controller.LoopType;
|
||||
import io.metersphere.api.dto.request.controller.MsLoopController;
|
||||
import io.metersphere.api.dto.request.controller.WhileConditionType;
|
||||
import io.metersphere.api.parser.jmeter.processor.ScriptFilter;
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
|
||||
import io.metersphere.project.constants.ScriptLanguageType;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.control.*;
|
||||
import org.apache.jmeter.modifiers.JSR223PreProcessor;
|
||||
import org.apache.jmeter.protocol.java.sampler.JSR223Sampler;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jmeter.timers.ConstantTimer;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
public class MsLoopControllerConverter extends AbstractJmeterElementConverter<MsLoopController> {
|
||||
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, MsLoopController element, ParameterConfig config) {
|
||||
|
||||
if (BooleanUtils.isFalse(element.getEnable())) {
|
||||
LogUtils.info("MsLoopController is disabled");
|
||||
return;
|
||||
}
|
||||
final HashTree groupTree = controller(tree, element);
|
||||
//TODO 这里需要处理csv文件 场景变量
|
||||
|
||||
if (groupTree == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 循环间隔时长设置
|
||||
ConstantTimer constantTimer = getConstantTimer(element);
|
||||
if (constantTimer != null) {
|
||||
groupTree.add(constantTimer);
|
||||
}
|
||||
|
||||
parseChild(groupTree, element, config);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private HashTree controller(HashTree tree, MsLoopController element) {
|
||||
if (StringUtils.equals(element.getLoopType(), LoopType.WHILE.name()) && element.getWhileController() != null) {
|
||||
RunTime runTime = new RunTime();
|
||||
runTime.setEnabled(true);
|
||||
runTime.setProperty(TestElement.TEST_CLASS, RunTime.class.getName());
|
||||
runTime.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("RunTimeGui"));
|
||||
long timeout = element.getWhileController().getTimeout() / 1000;
|
||||
if (timeout < 1) {
|
||||
timeout = 1;
|
||||
}
|
||||
runTime.setRuntime(timeout);
|
||||
String condition;
|
||||
if (StringUtils.equals(WhileConditionType.CONDITION.name(), element.getWhileController().getConditionType())) {
|
||||
condition = element.getWhileController().getMsWhileVariable().getConditionValue();
|
||||
} else {
|
||||
condition = element.getWhileController().getMsWhileScript().getScriptValue();
|
||||
}
|
||||
String ifCondition = "${__jexl3(" + condition + ")}";
|
||||
String whileCondition = "${__jexl3(" + condition + " && \"${" + element.getCurrentTime() + "}\" !=\"stop\")}";
|
||||
HashTree ifHashTree = tree.add(ifController(ifCondition, element.getEnable()));
|
||||
addPreProc(ifHashTree, element);
|
||||
|
||||
HashTree hashTree = ifHashTree.add(initWhileController(element, whileCondition));
|
||||
// 添加超时处理,防止死循环
|
||||
JSR223PreProcessor jsr223PreProcessor = new JSR223PreProcessor();
|
||||
jsr223PreProcessor.setName("While 循环控制器超时处理脚本");
|
||||
jsr223PreProcessor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
|
||||
jsr223PreProcessor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
jsr223PreProcessor.setProperty("scriptLanguage", ScriptLanguageType.BEANSHELL.name().toLowerCase());
|
||||
|
||||
ScriptFilter.verify(ScriptLanguageType.BEANSHELL.name().toLowerCase(), element.getName(), script(element));
|
||||
|
||||
jsr223PreProcessor.setProperty("script", script(element));
|
||||
hashTree.add(jsr223PreProcessor);
|
||||
return hashTree;
|
||||
}
|
||||
if (StringUtils.equals(element.getLoopType(), LoopType.FOREACH.name()) && element.getForEachController() != null) {
|
||||
return tree.add(initForeachController(element));
|
||||
}
|
||||
if (StringUtils.equals(element.getLoopType(), LoopType.LOOP_COUNT.name()) && element.getMsCountController() != null) {
|
||||
String ifCondition = StringUtils.join("${__jexl3(", element.getMsCountController().getLoops(), " > 0 ", ")}");
|
||||
HashTree ifHashTree = tree.add(ifController(ifCondition, element.getEnable()));
|
||||
return ifHashTree.add(initLoopController(element));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String script(MsLoopController element) {
|
||||
String script = """
|
||||
|
||||
import java.util.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import org.apache.jmeter.threads.JMeterContextService;
|
||||
|
||||
// 循环控制器超时后结束循环
|
||||
try{
|
||||
String ms_current_timer = vars.get("%s");
|
||||
long _nowTime = System.currentTimeMillis();
|
||||
if(ms_current_timer == null ){
|
||||
vars.put("%s",_nowTime.toString());
|
||||
}
|
||||
long time = Long.parseLong(vars.get("%s"));
|
||||
if((_nowTime - time) > %s ){
|
||||
vars.put("%s", "stop");
|
||||
log.info( "结束循环");
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.info( e.getMessage());
|
||||
vars.put("%s", "stop");
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
return String.format(script, element.getCurrentTime(), element.getCurrentTime(), element.getCurrentTime(), element.getWhileController().getTimeout(), element.getCurrentTime(), element.getCurrentTime());
|
||||
}
|
||||
|
||||
private void addPreProc(HashTree hashTree, MsLoopController element) {
|
||||
JSR223Sampler sampler = new JSR223Sampler();
|
||||
sampler.setName("MS_CLEAR_LOOPS_VAR_" + element.getCurrentTime());
|
||||
sampler.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
sampler.setProperty("scriptLanguage", ScriptLanguageType.BEANSHELL.name().toLowerCase());
|
||||
ScriptFilter.verify(ScriptLanguageType.BEANSHELL.name().toLowerCase(), element.getName(), script(element));
|
||||
sampler.setProperty("script", "vars.put(\"" + element.getCurrentTime() + "\", null);");
|
||||
hashTree.add(sampler);
|
||||
}
|
||||
|
||||
private LoopController initLoopController(MsLoopController element) {
|
||||
LoopController loopController = new LoopController();
|
||||
loopController.setEnabled(element.getEnable());
|
||||
loopController.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "Loop Controller");
|
||||
loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());
|
||||
loopController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("LoopControlPanel"));
|
||||
loopController.setLoops(element.getMsCountController().getLoops());
|
||||
if (StringUtils.isNotEmpty(element.getMsCountController().getLoops())) {
|
||||
loopController.setContinueForever(true);
|
||||
}
|
||||
return loopController;
|
||||
}
|
||||
|
||||
private IfController ifController(String condition, boolean enable) {
|
||||
IfController ifController = new IfController();
|
||||
ifController.setEnabled(enable);
|
||||
ifController.setName("while ifController");
|
||||
ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName());
|
||||
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
|
||||
ifController.setCondition(condition);
|
||||
ifController.setEvaluateAll(false);
|
||||
ifController.setUseExpression(true);
|
||||
return ifController;
|
||||
}
|
||||
|
||||
private WhileController initWhileController(MsLoopController element, String condition) {
|
||||
WhileController controller = new WhileController();
|
||||
controller.setEnabled(element.getEnable());
|
||||
controller.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "While Controller");
|
||||
controller.setProperty(TestElement.TEST_CLASS, WhileController.class.getName());
|
||||
controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("WhileControllerGui"));
|
||||
controller.setCondition(condition);
|
||||
return controller;
|
||||
}
|
||||
|
||||
private ForeachController initForeachController(MsLoopController element) {
|
||||
ForeachController controller = new ForeachController();
|
||||
controller.setEnabled(element.getEnable());
|
||||
controller.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "Foreach Controller");
|
||||
controller.setProperty(TestElement.TEST_CLASS, ForeachController.class.getName());
|
||||
controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ForeachControlPanel"));
|
||||
controller.setInputVal(element.getForEachController().getVariable());
|
||||
controller.setReturnVal(element.getForEachController().getValue());
|
||||
controller.setUseSeparator(true);
|
||||
return controller;
|
||||
}
|
||||
|
||||
private ConstantTimer getConstantTimer(MsLoopController element) {
|
||||
ConstantTimer constantTimer = new ConstantTimer();
|
||||
constantTimer.setEnabled(element.getEnable());
|
||||
constantTimer.setProperty(TestElement.TEST_CLASS, ConstantTimer.class.getName());
|
||||
constantTimer.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ConstantTimerGui"));
|
||||
if (StringUtils.equals(element.getLoopType(), LoopType.WHILE.name()) && element.getWhileController() != null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.equals(element.getLoopType(), LoopType.FOREACH.name()) && element.getForEachController() != null &&
|
||||
element.getForEachController().getLoopTime() > 0) {
|
||||
constantTimer.setProperty("ConstantTimer.delay", element.getForEachController().getLoopTime());
|
||||
constantTimer.setDelay(element.getForEachController().getLoopTime() + StringUtils.EMPTY);
|
||||
constantTimer.setName(element.getForEachController().getLoopTime() + " ms");
|
||||
return constantTimer;
|
||||
}
|
||||
if (StringUtils.equals(element.getLoopType(), LoopType.LOOP_COUNT.name()) && element.getMsCountController() != null &&
|
||||
element.getMsCountController().getLoopTime() > 0) {
|
||||
constantTimer.setProperty("ConstantTimer.delay", element.getMsCountController().getLoopTime() + "");
|
||||
constantTimer.setDelay(element.getMsCountController().getLoopTime() + StringUtils.EMPTY);
|
||||
constantTimer.setName(element.getMsCountController().getLoopTime() + " ms");
|
||||
return constantTimer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package io.metersphere.api.parser.jmeter;
|
||||
|
||||
import io.metersphere.api.dto.request.controller.MsOnceOnlyController;
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.control.OnceOnlyController;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
public class MsOnceOnlyControllerConverter extends AbstractJmeterElementConverter<MsOnceOnlyController> {
|
||||
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, MsOnceOnlyController element, ParameterConfig config) {
|
||||
if (BooleanUtils.isFalse(element.getEnable())) {
|
||||
LogUtils.info("MsOnceOnlyController is disabled");
|
||||
return;
|
||||
}
|
||||
//TODO 这里需要处理csv文件
|
||||
|
||||
HashTree groupTree = tree.add(onceOnlyController(element));
|
||||
parseChild(groupTree, element, config);
|
||||
}
|
||||
|
||||
private OnceOnlyController onceOnlyController(MsOnceOnlyController element) {
|
||||
OnceOnlyController onceOnlyController = new OnceOnlyController();
|
||||
onceOnlyController.setEnabled(element.getEnable());
|
||||
onceOnlyController.setName(StringUtils.isNotBlank(element.getName()) ? element.getName() : "Once Only Controller");
|
||||
onceOnlyController.setProperty(TestElement.TEST_CLASS, OnceOnlyController.class.getName());
|
||||
onceOnlyController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("OnceOnlyControllerGui"));
|
||||
return onceOnlyController;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,9 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
|
|||
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import io.metersphere.api.dto.request.MsCommonElement;
|
||||
import io.metersphere.api.dto.request.controller.MsIfController;
|
||||
import io.metersphere.api.dto.request.controller.MsLoopController;
|
||||
import io.metersphere.api.dto.request.controller.MsOnceOnlyController;
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
|
||||
|
@ -34,6 +37,9 @@ public class ApiDataUtils {
|
|||
// 默认内置的子组件
|
||||
namedTypes.add(new NamedType(MsHTTPElement.class, MsHTTPElement.class.getSimpleName()));
|
||||
namedTypes.add(new NamedType(MsCommonElement.class, MsCommonElement.class.getSimpleName()));
|
||||
namedTypes.add(new NamedType(MsIfController.class, MsIfController.class.getSimpleName()));
|
||||
namedTypes.add(new NamedType(MsLoopController.class, MsLoopController.class.getSimpleName()));
|
||||
namedTypes.add(new NamedType(MsOnceOnlyController.class, MsOnceOnlyController.class.getSimpleName()));
|
||||
|
||||
setObjectMapper(objectMapper);
|
||||
namedTypes.forEach(objectMapper::registerSubtypes);
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package io.metersphere.api.utils;
|
||||
|
||||
import io.metersphere.api.parser.jmeter.MsCommentScriptElementConverter;
|
||||
import io.metersphere.api.parser.jmeter.MsCommonElementConverter;
|
||||
import io.metersphere.api.parser.jmeter.MsHTTPElementConverter;
|
||||
import io.metersphere.api.parser.jmeter.MsScenarioConverter;
|
||||
import io.metersphere.api.parser.jmeter.*;
|
||||
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
|
||||
import io.metersphere.plugin.api.spi.MsTestElement;
|
||||
import io.metersphere.plugin.sdk.util.PluginLogUtils;
|
||||
|
@ -32,6 +29,9 @@ public class JmeterElementConverterRegister {
|
|||
register(MsCommonElementConverter.class);
|
||||
register(MsCommentScriptElementConverter.class);
|
||||
register(MsScenarioConverter.class);
|
||||
register(MsIfControllerConverter.class);
|
||||
register(MsLoopControllerConverter.class);
|
||||
register(MsOnceOnlyControllerConverter.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,8 @@ import io.metersphere.api.dto.ApiFile;
|
|||
import io.metersphere.api.dto.assertion.MsAssertionConfig;
|
||||
import io.metersphere.api.dto.debug.ModuleCreateRequest;
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.dto.request.controller.*;
|
||||
import io.metersphere.api.dto.request.controller.loop.*;
|
||||
import io.metersphere.api.dto.request.http.Header;
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.api.dto.request.http.QueryParam;
|
||||
|
@ -642,6 +644,7 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
|
||||
/**
|
||||
* 测试关联的文件更新
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testHandleFileAssociationUpgrade() throws Exception {
|
||||
|
@ -665,7 +668,8 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
try {
|
||||
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiScenarioStepBlob.getContent()), AbstractMsTestElement.class);
|
||||
apiFiles.addAll(apiCommonService.getApiFilesByFileId(fileId, msTestElement));
|
||||
} catch (Exception e) {}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
return apiFiles;
|
||||
}
|
||||
|
@ -724,6 +728,7 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
partialScenarioStep.setRefType(ApiScenarioStepRefType.PARTIAL_REF.name());
|
||||
partialScenarioStep.setChildren(getApiScenarioStepRequests());
|
||||
steps.add(partialScenarioStep);
|
||||
|
||||
request.setId(addApiScenario.getId());
|
||||
request.setSteps(steps);
|
||||
request.setStepDetails(new HashMap<>());
|
||||
|
@ -762,6 +767,207 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, DEBUG, request);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
public void testLoopController() throws Exception {
|
||||
// @@请求成功
|
||||
mockPost("/api/debug", "");
|
||||
baseResourcePoolTestService.initProjectResourcePool();
|
||||
|
||||
// @@请求成功
|
||||
ApiScenarioDebugRequest request = new ApiScenarioDebugRequest();
|
||||
request.setProjectId(DEFAULT_PROJECT_ID);
|
||||
request.setScenarioConfig(new ScenarioConfig());
|
||||
request.setEnvironmentId(envId);
|
||||
request.setGrouped(false);
|
||||
ScenarioStepConfig scenarioStepConfig = new ScenarioStepConfig();
|
||||
scenarioStepConfig.setEnableScenarioEnv(true);
|
||||
|
||||
List<ApiScenarioStepRequest> steps = new ArrayList<>();
|
||||
Map<String, Object> stepDetails = new HashMap<>();
|
||||
|
||||
MsIfController msIfController = ifController("test-if", true);
|
||||
ApiScenarioStepRequest msIf = BeanUtils.copyBean(new ApiScenarioStepRequest(), msIfController);
|
||||
msIf.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
msIf.setProjectId(DEFAULT_PROJECT_ID);
|
||||
msIf.setStepType(ApiScenarioStepType.IF_CONTROLLER.name());
|
||||
msIf.setId(IDGenerator.nextStr());
|
||||
steps.add(msIf);
|
||||
stepDetails.put(msIf.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msIfController)));
|
||||
msIfController = ifController(null, true);
|
||||
msIf = BeanUtils.copyBean(new ApiScenarioStepRequest(), msIfController);
|
||||
msIf.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
msIf.setProjectId(DEFAULT_PROJECT_ID);
|
||||
msIf.setStepType(ApiScenarioStepType.IF_CONTROLLER.name());
|
||||
msIf.setId(IDGenerator.nextStr());
|
||||
steps.add(msIf);
|
||||
stepDetails.put(msIf.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msIfController)));
|
||||
msIfController = ifController(null, false);
|
||||
msIf = BeanUtils.copyBean(new ApiScenarioStepRequest(), msIfController);
|
||||
msIf.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
msIf.setProjectId(DEFAULT_PROJECT_ID);
|
||||
msIf.setStepType(ApiScenarioStepType.IF_CONTROLLER.name());
|
||||
msIf.setId(IDGenerator.nextStr());
|
||||
steps.add(msIf);
|
||||
stepDetails.put(msIf.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msIfController)));
|
||||
MsOnceOnlyController msOnceOnlyController = onceController("test-once-only", true);
|
||||
ApiScenarioStepRequest onceOnlyController = BeanUtils.copyBean(new ApiScenarioStepRequest(), msOnceOnlyController);
|
||||
onceOnlyController.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
onceOnlyController.setProjectId(DEFAULT_PROJECT_ID);
|
||||
onceOnlyController.setStepType(ApiScenarioStepType.ONCE_ONLY_CONTROLLER.name());
|
||||
onceOnlyController.setId(IDGenerator.nextStr());
|
||||
steps.add(onceOnlyController);
|
||||
stepDetails.put(onceOnlyController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msOnceOnlyController)));
|
||||
msOnceOnlyController = onceController(null, true);
|
||||
onceOnlyController = BeanUtils.copyBean(new ApiScenarioStepRequest(), msOnceOnlyController);
|
||||
onceOnlyController.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
onceOnlyController.setProjectId(DEFAULT_PROJECT_ID);
|
||||
onceOnlyController.setStepType(ApiScenarioStepType.ONCE_ONLY_CONTROLLER.name());
|
||||
onceOnlyController.setId(IDGenerator.nextStr());
|
||||
steps.add(onceOnlyController);
|
||||
stepDetails.put(onceOnlyController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msOnceOnlyController)));
|
||||
msOnceOnlyController = onceController(null, false);
|
||||
onceOnlyController = BeanUtils.copyBean(new ApiScenarioStepRequest(), msOnceOnlyController);
|
||||
onceOnlyController.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
onceOnlyController.setProjectId(DEFAULT_PROJECT_ID);
|
||||
onceOnlyController.setStepType(ApiScenarioStepType.ONCE_ONLY_CONTROLLER.name());
|
||||
onceOnlyController.setId(IDGenerator.nextStr());
|
||||
steps.add(onceOnlyController);
|
||||
stepDetails.put(onceOnlyController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msOnceOnlyController)));
|
||||
MsLoopController loopCount = loopController("LOOP_COUNT", null, true);
|
||||
ApiScenarioStepRequest loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), loopCount);
|
||||
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
loopController.setProjectId(DEFAULT_PROJECT_ID);
|
||||
loopController.setId(IDGenerator.nextStr());
|
||||
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
|
||||
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(loopCount)));
|
||||
steps.add(loopController);
|
||||
loopCount = loopController("LOOP_COUNT", null, false);
|
||||
loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), loopCount);
|
||||
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
loopController.setProjectId(DEFAULT_PROJECT_ID);
|
||||
loopController.setId(IDGenerator.nextStr());
|
||||
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
|
||||
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(loopCount)));
|
||||
steps.add(loopController);
|
||||
MsLoopController aWhile = loopController("WHILE", "CONDITION", true);
|
||||
loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), aWhile);
|
||||
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
loopController.setProjectId(DEFAULT_PROJECT_ID);
|
||||
loopController.setId(IDGenerator.nextStr());
|
||||
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
|
||||
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(aWhile)));
|
||||
steps.add(loopController);
|
||||
aWhile = loopController("WHILE", "SCRIPT", true);
|
||||
loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), aWhile);
|
||||
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
loopController.setProjectId(DEFAULT_PROJECT_ID);
|
||||
loopController.setId(IDGenerator.nextStr());
|
||||
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
|
||||
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(aWhile)));
|
||||
steps.add(loopController);
|
||||
MsLoopController foreach = loopController("FOREACH", null, true);
|
||||
loopController = BeanUtils.copyBean(new ApiScenarioStepRequest(), foreach);
|
||||
loopController.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||
loopController.setProjectId(DEFAULT_PROJECT_ID);
|
||||
loopController.setId(IDGenerator.nextStr());
|
||||
loopController.setStepType(ApiScenarioStepType.LOOP_CONTROLLER.name());
|
||||
stepDetails.put(loopController.getId(), JSON.parseObject(ApiDataUtils.toJSONString(foreach)));
|
||||
steps.add(loopController);
|
||||
|
||||
request.setId(addApiScenario.getId());
|
||||
request.setSteps(steps);
|
||||
request.setStepDetails(new HashMap<>());
|
||||
request.setReportId(IDGenerator.nextStr());
|
||||
request.setStepDetails(stepDetails);
|
||||
this.requestPostWithOk(DEBUG, request);
|
||||
}
|
||||
|
||||
//条件控制器
|
||||
public MsIfController ifController(String name, boolean enable) {
|
||||
//条件控制器
|
||||
MsIfController msIfController = new MsIfController();
|
||||
msIfController.setCondition("==");
|
||||
msIfController.setName(StringUtils.isNotBlank(name) ? name : "条件控制器");
|
||||
msIfController.setEnable(enable);
|
||||
msIfController.setVariable("1");
|
||||
msIfController.setValue("1");
|
||||
msIfController.setStepId(IDGenerator.nextStr());
|
||||
LinkedList<AbstractMsTestElement> msTestElements = new LinkedList<>();
|
||||
//条件控制器下的步骤
|
||||
MsHTTPElement msHTTPElement = MsHTTPElementTest.getMsHttpElement();
|
||||
msTestElements.add(msHTTPElement);
|
||||
msIfController.setChildren(msTestElements);
|
||||
return msIfController;
|
||||
}
|
||||
|
||||
//一次控制器
|
||||
public MsOnceOnlyController onceController(String name, boolean enable) {
|
||||
//一次控制器
|
||||
MsOnceOnlyController onceOnlyController = new MsOnceOnlyController();
|
||||
onceOnlyController.setName(StringUtils.isNotBlank(name) ? name : "一次控制器");
|
||||
onceOnlyController.setStepId(IDGenerator.nextStr());
|
||||
onceOnlyController.setEnable(enable);
|
||||
LinkedList<AbstractMsTestElement> msTestElements = new LinkedList<>();
|
||||
//一次控制器下的步骤
|
||||
MsHTTPElement msHTTPElement = MsHTTPElementTest.getMsHttpElement();
|
||||
msTestElements.add(msHTTPElement);
|
||||
onceOnlyController.setChildren(msTestElements);
|
||||
return onceOnlyController;
|
||||
}
|
||||
|
||||
//循环控制器 loopController
|
||||
public MsLoopController loopController(String loopType, String condition, boolean enable) {
|
||||
//循环控制器
|
||||
MsLoopController msLoopController = new MsLoopController();
|
||||
msLoopController.setStepId(IDGenerator.nextStr());
|
||||
switch (loopType) {
|
||||
case "LOOP_COUNT" -> {
|
||||
MsCountController msCountController = new MsCountController();
|
||||
msLoopController.setName("次数循环控制器");
|
||||
msCountController.setLoops("1");
|
||||
msCountController.setLoopTime(300L);
|
||||
msLoopController.setLoopType(LoopType.LOOP_COUNT.name());
|
||||
msLoopController.setMsCountController(msCountController);
|
||||
}
|
||||
case "WHILE" -> {
|
||||
MsWhileController whileController = new MsWhileController();
|
||||
msLoopController.setLoopType(LoopType.WHILE.name());
|
||||
msLoopController.setName("while 循环控制器");
|
||||
if (StringUtils.isNotBlank(condition) && StringUtils.equals(condition, "CONDITION")) {
|
||||
MsWhileVariable msWhileVariable = new MsWhileVariable();
|
||||
msWhileVariable.setVariable("1");
|
||||
msWhileVariable.setCondition("==");
|
||||
msWhileVariable.setValue("1");
|
||||
whileController.setMsWhileVariable(msWhileVariable);
|
||||
} else {
|
||||
whileController.setConditionType(WhileConditionType.SCRIPT.name());
|
||||
MsWhileScript msWhileScript = new MsWhileScript();
|
||||
msWhileScript.setScriptValue("test");
|
||||
whileController.setMsWhileScript(msWhileScript);
|
||||
}
|
||||
whileController.setTimeout(300L);
|
||||
msLoopController.setWhileController(whileController);
|
||||
}
|
||||
default -> {
|
||||
MsForEachController forEachController = new MsForEachController();
|
||||
msLoopController.setLoopType(LoopType.FOREACH.name());
|
||||
forEachController.setVariable("1");
|
||||
msLoopController.setName("foreach 循环控制器");
|
||||
forEachController.setValue("1");
|
||||
forEachController.setLoopTime(300L);
|
||||
msLoopController.setForEachController(forEachController);
|
||||
}
|
||||
}
|
||||
msLoopController.setEnable(enable);
|
||||
LinkedList<AbstractMsTestElement> msTestElements = new LinkedList<>();
|
||||
//循环控制器下的步骤
|
||||
MsHTTPElement msHTTPElement = MsHTTPElementTest.getMsHttpElement();
|
||||
msTestElements.add(msHTTPElement);
|
||||
msLoopController.setChildren(msTestElements);
|
||||
return msLoopController;
|
||||
}
|
||||
|
||||
public Plugin addEnvTestPlugin() throws Exception {
|
||||
PluginUpdateRequest request = new PluginUpdateRequest();
|
||||
File jarFile = new File(
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.metersphere.api.controller;
|
|||
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||
import io.metersphere.api.dto.request.controller.MsLoopController;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.plugin.api.dto.TestElementDTO;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.plugin.api.spi.MsTestElement;
|
||||
import io.metersphere.system.base.BaseApiPluginTestService;
|
||||
|
@ -38,18 +37,6 @@ public class PluginSubTypeTests {
|
|||
ApiDataUtils.setResolver(namedTypes);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void newPluginSubTypeTest() throws Exception {
|
||||
MsLoopController loopController = new MsLoopController();
|
||||
loopController.setName("测试loopController");
|
||||
String json = ApiDataUtils.toJSONString(loopController);
|
||||
|
||||
TestElementDTO testElementDTO = ApiDataUtils.parseObject(json, TestElementDTO.class);
|
||||
Assertions.assertNotNull(testElementDTO);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void jdbcPluginSubTypeTest() throws Exception {
|
||||
|
|
Loading…
Reference in New Issue