diff --git a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java index fe1f20966f..3a07da3ca2 100644 --- a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java @@ -96,15 +96,27 @@ public class JmeterDocumentParser implements DocumentParser { processCheckoutArguments(ele); processCheckoutResponseAssertion(ele); } else if (nodeNameEquals(ele, CONCURRENCY_THREAD_GROUP)) { - processConcurrencyThreadGroup(ele); + processThreadGroupName(ele); processCheckoutTimer(ele); processCheckoutBackendListener(ele); } else if (nodeNameEquals(ele, VARIABLE_THROUGHPUT_TIMER)) { processVariableThroughputTimer(ele); } else if (nodeNameEquals(ele, THREAD_GROUP)) { - processThreadGroup(ele); - // - processConcurrencyThreadGroup(ele); + Object threadType = context.getProperty("threadType"); + if (threadType instanceof List) { + Object o = ((List) threadType).get(0); + ((List) threadType).remove(0); + if ("DURATION".equals(o)) { + processThreadGroup(ele); + } + if ("ITERATION".equals(o)) { + processIterationThreadGroup(ele); + } + } else { + processThreadGroup(ele); + } + + processThreadGroupName(ele); processCheckoutTimer(ele); processCheckoutBackendListener(ele); } else if (nodeNameEquals(ele, BACKEND_LISTENER)) { @@ -772,22 +784,120 @@ public class JmeterDocumentParser implements DocumentParser { */ removeChildren(threadGroup); // elementProp + Object targetLevels = context.getProperty("TargetLevel"); + String threads = "10"; + if (targetLevels instanceof List) { + Object o = ((List) targetLevels).get(0); + ((List) targetLevels).remove(0); + threads = o.toString(); + } + Object rampUps = context.getProperty("RampUp"); + String rampUp = "1"; + if (rampUps instanceof List) { + Object o = ((List) rampUps).get(0); + ((List) rampUps).remove(0); + rampUp = o.toString(); + } + Object steps = context.getProperty("Steps"); + String step = "2"; + if (steps instanceof List) { + Object o = ((List) steps).get(0); + ((List) steps).remove(0); + step = o.toString(); + } + Object holds = context.getProperty("Hold"); + String hold = "2"; + if (holds instanceof List) { + Object o = ((List) holds).get(0); + ((List) holds).remove(0); + hold = o.toString(); + } Element elementProp = document.createElement("elementProp"); elementProp.setAttribute("name", "ThreadGroup.main_controller"); elementProp.setAttribute("elementType", "com.blazemeter.jmeter.control.VirtualUserController"); threadGroup.appendChild(elementProp); - threadGroup.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "continue")); - threadGroup.appendChild(createStringProp(document, "TargetLevel", "2")); - threadGroup.appendChild(createStringProp(document, "RampUp", "12")); - threadGroup.appendChild(createStringProp(document, "Steps", "2")); - threadGroup.appendChild(createStringProp(document, "Hold", "1")); + threadGroup.appendChild(createStringProp(document, "TargetLevel", threads)); + threadGroup.appendChild(createStringProp(document, "RampUp", rampUp)); + threadGroup.appendChild(createStringProp(document, "Steps", step)); + threadGroup.appendChild(createStringProp(document, "Hold", hold)); threadGroup.appendChild(createStringProp(document, "LogFilename", "")); // bzm - Concurrency Thread Group "Thread Iterations Limit:" 设置为空 // threadGroup.appendChild(createStringProp(document, "Iterations", "1")); threadGroup.appendChild(createStringProp(document, "Unit", "S")); } + private void processIterationThreadGroup(Element threadGroup) { + // 检查 threadgroup 后面的hashtree是否为空 + Node hashTree = threadGroup.getNextSibling(); + while (!(hashTree instanceof Element)) { + hashTree = hashTree.getNextSibling(); + } + if (!hashTree.hasChildNodes()) { + MSException.throwException(Translator.get("jmx_content_valid")); + } + // 重命名 tagName + Document document = threadGroup.getOwnerDocument(); + removeChildren(threadGroup); + + // 选择按照迭代次数处理线程组 + /* + continue + + false + 1 + + 100 + 5 + true + 10 + + true + */ + // elementProp + Object targetLevels = context.getProperty("TargetLevel"); + String threads = "10"; + if (targetLevels instanceof List) { + Object o = ((List) targetLevels).get(0); + ((List) targetLevels).remove(0); + threads = o.toString(); + } + Object iterateNum = context.getProperty("iterateNum"); + String loops = "1"; + if (iterateNum instanceof List) { + Object o = ((List) iterateNum).get(0); + ((List) iterateNum).remove(0); + loops = o.toString(); + } + Object rampUps = context.getProperty("iterateRampUpTime"); + String rampUp = "10"; + if (rampUps instanceof List) { + Object o = ((List) rampUps).get(0); + ((List) rampUps).remove(0); + rampUp = o.toString(); + } + Element elementProp = document.createElement("elementProp"); + elementProp.setAttribute("name", "ThreadGroup.main_controller"); + elementProp.setAttribute("elementType", "LoopController"); + elementProp.setAttribute("guiclass", "LoopControlPanel"); + elementProp.setAttribute("testclass", "LoopController"); + elementProp.setAttribute("testname", "Loop Controller"); + elementProp.setAttribute("enabled", "true"); + elementProp.appendChild(createBoolProp(document, "LoopController.continue_forever", false)); + elementProp.appendChild(createStringProp(document, "LoopController.loops", loops)); + threadGroup.appendChild(elementProp); + + threadGroup.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "continue")); + threadGroup.appendChild(createStringProp(document, "ThreadGroup.num_threads", threads)); + threadGroup.appendChild(createStringProp(document, "ThreadGroup.ramp_time", rampUp)); + threadGroup.appendChild(createBoolProp(document, "ThreadGroup.scheduler", false)); // 不指定执行时间 + threadGroup.appendChild(createStringProp(document, "Hold", "1")); + threadGroup.appendChild(createStringProp(document, "ThreadGroup.duration", "10")); + threadGroup.appendChild(createStringProp(document, "ThreadGroup.delay", "")); + threadGroup.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true)); + } + + private void processCheckoutTimer(Element element) { /* @@ -856,25 +966,9 @@ public class JmeterDocumentParser implements DocumentParser { return unit; } - private void processConcurrencyThreadGroup(Element concurrencyThreadGroup) { - String testname = concurrencyThreadGroup.getAttribute("testname"); - concurrencyThreadGroup.setAttribute("testname", testname + "-" + context.getResourceIndex()); - if (concurrencyThreadGroup.getChildNodes().getLength() > 0) { - final NodeList childNodes = concurrencyThreadGroup.getChildNodes(); - for (int i = 0; i < childNodes.getLength(); i++) { - Node node = childNodes.item(i); - if (node instanceof Element) { - Element ele = (Element) node; - if (invalid(ele)) { - continue; - } - - if (nodeNameEquals(ele, STRING_PROP)) { - parseStringProp(ele); - } - } - } - } + private void processThreadGroupName(Element threadGroup) { + String testname = threadGroup.getAttribute("testname"); + threadGroup.setAttribute("testname", testname + "-" + context.getResourceIndex()); } private void processVariableThroughputTimer(Element variableThroughputTimer) { diff --git a/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue b/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue index 456f61e8d3..ee34766b45 100644 --- a/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue +++ b/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue @@ -21,45 +21,85 @@ size="mini"/>
- - + + + {{ $t('load_test.by_duration') }} + {{ $t('load_test.by_iteration') }} +
- - -   - - -
- - - - - - - +
+ + + +
+ + +   + + +
+ + + + + + + +
+
+ + + +
+ + +   + + +
+ + + + +
@@ -83,6 +123,10 @@ const STEPS = "Steps"; const DURATION = "duration"; const RPS_LIMIT = "rpsLimit"; const RPS_LIMIT_ENABLE = "rpsLimitEnable"; +const THREAD_TYPE = "threadType"; +const ITERATE_NUM = "iterateNum"; +const ITERATE_RAMP_UP = "iterateRampUpTime"; + const hexToRgba = function (hex, opacity) { return 'rgba(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5)) + ',' + parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')'; @@ -128,6 +172,9 @@ export default { case RAMP_UP: this.threadGroups[i].rampUpTime = item.value; break; + case ITERATE_RAMP_UP: + this.threadGroups[i].iterateRampUp = item.value; + break; case DURATION: if (item.unit) { this.threadGroups[i].duration = item.value; @@ -144,6 +191,12 @@ export default { case RPS_LIMIT_ENABLE: this.threadGroups[i].rpsLimitEnable = item.value; break; + case THREAD_TYPE: + this.threadGroups[i].threadType = item.value; + break; + case ITERATE_NUM: + this.threadGroups[i].iterateNum = item.value; + break; default: break; } @@ -157,6 +210,9 @@ export default { case RAMP_UP: this.threadGroups[0].rampUpTime = d.value; break; + case ITERATE_RAMP_UP: + this.threadGroups[0].iterateRampUp = d.value; + break; case DURATION: if (d.unit) { this.threadGroups[0].duration = d.value; @@ -173,6 +229,12 @@ export default { case RPS_LIMIT_ENABLE: this.threadGroups[0].rpsLimitEnable = d.value; break; + case THREAD_TYPE: + this.threadGroups[0].threadType = d.value; + break; + case ITERATE_NUM: + this.threadGroups[0].iterateNum = d.value; + break; default: break; } diff --git a/frontend/src/business/components/performance/test/EditPerformanceTest.vue b/frontend/src/business/components/performance/test/EditPerformanceTest.vue index 2e2769f1a1..8af0e56832 100644 --- a/frontend/src/business/components/performance/test/EditPerformanceTest.vue +++ b/frontend/src/business/components/performance/test/EditPerformanceTest.vue @@ -298,6 +298,9 @@ export default { tg.rampUpTime = tg.rampUpTime || 5; tg.step = tg.step || 5; tg.rpsLimit = tg.rpsLimit || 10; + tg.threadType = tg.threadType || 'DURATION'; + tg.iterateNum = tg.iterateNum || 1; + tg.iterateRampUp = tg.iterateRampUp || 10; handler.calculateChart(tg); }); } diff --git a/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue b/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue index c531212baa..88098c33af 100644 --- a/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue +++ b/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue @@ -33,45 +33,83 @@ size="mini"/>

- - + + + {{ $t('load_test.by_duration') }} + {{ $t('load_test.by_iteration') }} +
- - -   - - -
- - - - - - - +
+ + + +
+ + +   + + +
+ + + + + + + +
+
+ + + +
+ + +   + + +
+ + + + +
@@ -91,11 +129,14 @@ import {findTestPlan, findThreadGroup} from "@/business/components/performance/t const TARGET_LEVEL = "TargetLevel"; const RAMP_UP = "RampUp"; +const ITERATE_RAMP_UP = "iterateRampUpTime"; const STEPS = "Steps"; const DURATION = "duration"; const RPS_LIMIT = "rpsLimit"; const RPS_LIMIT_ENABLE = "rpsLimitEnable"; const HOLD = "Hold"; +const THREAD_TYPE = "threadType"; +const ITERATE_NUM = "iterateNum"; const hexToRgba = function (hex, opacity) { return 'rgba(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5)) + ',' @@ -186,6 +227,9 @@ export default { case RAMP_UP: this.threadGroups[i].rampUpTime = item.value; break; + case ITERATE_RAMP_UP: + this.threadGroups[i].iterateRampUp = item.value; + break; case DURATION: if (item.unit) { this.threadGroups[i].duration = item.value; @@ -202,9 +246,19 @@ export default { case RPS_LIMIT_ENABLE: this.threadGroups[i].rpsLimitEnable = item.value; break; + case THREAD_TYPE: + this.threadGroups[i].threadType = item.value; + break; + case ITERATE_NUM: + this.threadGroups[i].iterateNum = item.value; + break; default: break; } + // + this.$set(this.threadGroups[i], "threadType", this.threadGroups[i].threadType || 'DURATION'); + this.$set(this.threadGroups[i], "iterateNum", this.threadGroups[i].iterateNum || 1); + this.$set(this.threadGroups[i], "iterateRampUp", this.threadGroups[i].iterateRampUp || 10); }) this.calculateChart(this.threadGroups[i]); } else { @@ -215,6 +269,9 @@ export default { case RAMP_UP: this.threadGroups[0].rampUpTime = d.value; break; + case ITERATE_RAMP_UP: + this.threadGroups[0].iterateRampUp = d.value; + break; case DURATION: if (d.unit) { this.threadGroups[0].duration = d.value; @@ -231,9 +288,18 @@ export default { case RPS_LIMIT_ENABLE: this.threadGroups[0].rpsLimitEnable = d.value; break; + case THREAD_TYPE: + this.threadGroups[0].threadType = d.value; + break; + case ITERATE_NUM: + this.threadGroups[0].iterateNum = d.value; + break; default: break; } + this.$set(this.threadGroups[0], "threadType", this.threadGroups[0].threadType || 'DURATION'); + this.$set(this.threadGroups[0], "iterateNum", this.threadGroups[0].iterateNum || 1); + this.$set(this.threadGroups[0], "iterateRampUp", this.threadGroups[0].iterateRampUp || 10); this.calculateChart(this.threadGroups[0]); } } @@ -461,7 +527,7 @@ export default { for (let i = 0; i < this.threadGroups.length; i++) { if (!this.threadGroups[i].threadNumber || !this.threadGroups[i].duration - || !this.threadGroups[i].rampUpTime || !this.threadGroups[i].step) { + || !this.threadGroups[i].rampUpTime || !this.threadGroups[i].step || !this.threadGroups[i].iterateNum) { this.$warning(this.$t('load_test.pressure_config_params_is_empty')); this.$emit('changeActive', '1'); return false; @@ -488,6 +554,9 @@ export default { {key: RPS_LIMIT, value: this.threadGroups[i].rpsLimit}, {key: RPS_LIMIT_ENABLE, value: this.threadGroups[i].rpsLimitEnable}, {key: HOLD, value: this.threadGroups[i].duration - this.threadGroups[i].rampUpTime}, + {key: THREAD_TYPE, value: this.threadGroups[i].threadType}, + {key: ITERATE_NUM, value: this.threadGroups[i].iterateNum}, + {key: ITERATE_RAMP_UP, value: this.threadGroups[i].iterateRampUp}, ]); } return result; diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 64b464a867..892aec3823 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -437,6 +437,10 @@ export default { input_rps_limit: 'Please enter a limit', ramp_up_time_within: 'In', ramp_up_time_minutes: 'seconds, separate', + ramp_up_time_seconds: 'seconds add concurrent users', + iterate_num: 'Iterations: ', + by_iteration: 'By iterations', + by_duration: 'By duration', ramp_up_time_times: 'add concurrent users', advanced_config_error: 'Advanced configuration verification failed', domain_bind: 'Domain bind', diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 7ad85052b2..ecd5a5c8dc 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -433,6 +433,10 @@ export default { input_rps_limit: '请输入限制', ramp_up_time_within: '在', ramp_up_time_minutes: '秒内,分', + ramp_up_time_seconds: '秒内增加并发用户', + iterate_num: '迭代次数 (次): ', + by_iteration: '按迭代次数', + by_duration: '按持续时间', ramp_up_time_times: '次增加并发用户', advanced_config_error: '高级配置校验失败', domain_bind: '域名绑定', diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index ef8bc6ae5d..2cc4e2a0b3 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -433,6 +433,10 @@ export default { input_rps_limit: '請輸入限制', ramp_up_time_within: '在', ramp_up_time_minutes: '秒內,分', + ramp_up_time_seconds: '秒內增加並發用戶', + iterate_num: '迭代次數 (次): ', + by_iteration: '按迭代次數', + by_duration: '按壓測時長', ramp_up_time_times: '次增加並發用戶', advanced_config_error: '高級配置校驗失敗', domain_bind: '域名綁定',