From 387c6854558cc7b6bfc1a60244bae76ccf866002 Mon Sep 17 00:00:00 2001 From: fit2-zhao Date: Tue, 19 Jan 2021 15:54:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=8E=A5=E5=8F=A3=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96):=20=E5=BE=AA=E7=8E=AF=E6=8E=A7=E5=88=B6=E5=99=A8?= =?UTF-8?q?=E6=AD=BB=E5=BE=AA=E7=8E=AF=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/controller/MsLoopController.java | 84 ++++++++++++---- .../request/processors/MsJSR223Processor.java | 3 + .../api/service/ApiScenarioReportService.java | 9 +- .../apache/jmeter/reporters/ResultAction.java | 98 +++++++++++++++++++ .../api/automation/report/ApiReportDetail.vue | 34 ++++--- .../scenario/component/ApiComponent.vue | 4 + .../component/ApiScenarioComponent.vue | 4 + .../scenario/component/ConstantTimer.vue | 6 +- .../scenario/component/IfController.vue | 4 + .../scenario/component/LoopController.vue | 16 +-- 10 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 backend/src/main/java/org/apache/jmeter/reporters/ResultAction.java diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsLoopController.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsLoopController.java index b6b33c8049..1fa59c1c7e 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsLoopController.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsLoopController.java @@ -11,12 +11,11 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.jmeter.control.ForeachController; -import org.apache.jmeter.control.GenericController; -import org.apache.jmeter.control.LoopController; -import org.apache.jmeter.control.WhileController; +import org.apache.jmeter.control.*; +import org.apache.jmeter.reporters.ResultAction; import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.timers.ConstantTimer; import org.apache.jorphan.collections.HashTree; import java.util.List; @@ -43,11 +42,21 @@ public class MsLoopController extends MsTestElement { if (!this.isEnable()) { return; } - GenericController controller = controller(); - if (controller == null) { - return; + final HashTree groupTree = controller(tree); + + // 不打开执行成功后轮询功能,则成功后就停止循环 + if (StringUtils.equals(this.loopType, "LOOP_COUNT") && this.countController != null && !countController.isProceed()) { + ResultAction resultAction = new ResultAction(); + resultAction.setName("ResultAction"); + resultAction.setProperty("OnError.action", "1000"); + groupTree.add(resultAction); } - final HashTree groupTree = tree.add(controller); + // 循环间隔时长设置 + ConstantTimer cnstantTimer = getCnstantTimer(); + if (cnstantTimer != null) { + groupTree.add(cnstantTimer); + } + if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { el.toHashTree(groupTree, el.getHashTree(), config); @@ -58,14 +67,11 @@ public class MsLoopController extends MsTestElement { private LoopController loopController() { LoopController loopController = new LoopController(); loopController.setEnabled(true); - loopController.setName(this.getLabel()); + loopController.setName("LoopController"); loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName()); loopController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("LoopControlPanel")); loopController.setLoops(countController.getLoops()); - // 不打开执行成功后轮询功能,则成功后就停止循环 - if (!countController.isProceed()) { - - } + loopController.setContinueForever(false); return loopController; } @@ -93,19 +99,23 @@ public class MsLoopController extends MsTestElement { } private WhileController whileController() { + String condition = getCondition(); + if (StringUtils.isEmpty(condition)) { + return null; + } WhileController controller = new WhileController(); controller.setEnabled(true); - controller.setName(this.getLabel()); + controller.setName("WhileController"); controller.setProperty(TestElement.TEST_CLASS, WhileController.class.getName()); controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("WhileControllerGui")); - controller.setCondition(getCondition()); + controller.setCondition(condition); return controller; } private ForeachController foreachController() { ForeachController controller = new ForeachController(); controller.setEnabled(true); - controller.setName(this.getLabel()); + controller.setName("ForeachController"); controller.setProperty(TestElement.TEST_CLASS, ForeachController.class.getName()); controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ForeachControlPanel")); controller.setInputVal(this.forEachController.getInputVal()); @@ -114,15 +124,49 @@ public class MsLoopController extends MsTestElement { return controller; } - private GenericController controller() { + private HashTree controller(HashTree tree) { if (StringUtils.equals(this.loopType, "WHILE") && this.whileController != null) { - return whileController(); + 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 = this.whileController.getTimeout() / 1000; + if (timeout < 1) { + timeout = 1; + } + runTime.setRuntime(timeout); + // 添加超时处理,防止死循环 + HashTree hashTree = tree.add(runTime); + return hashTree.add(whileController()); } if (StringUtils.equals(this.loopType, "FOREACH") && this.forEachController != null) { - return foreachController(); + return tree.add(foreachController()); } if (StringUtils.equals(this.loopType, "LOOP_COUNT") && this.countController != null) { - return loopController(); + return tree.add(loopController()); + } + return null; + } + + private ConstantTimer getCnstantTimer() { + ConstantTimer constantTimer = new ConstantTimer(); + constantTimer.setEnabled(true); + constantTimer.setProperty(TestElement.TEST_CLASS, ConstantTimer.class.getName()); + constantTimer.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ConstantTimerGui")); + if (StringUtils.equals(this.loopType, "WHILE") && this.whileController != null) { + return null; + } + if (StringUtils.equals(this.loopType, "FOREACH") && this.forEachController != null) { + constantTimer.setProperty("ConstantTimer.delay", this.forEachController.getInterval()); + constantTimer.setDelay(this.forEachController.getInterval()); + constantTimer.setName(this.forEachController.getInterval() + " ms"); + return constantTimer; + } + if (StringUtils.equals(this.loopType, "LOOP_COUNT") && this.countController != null) { + constantTimer.setProperty("ConstantTimer.delay", this.countController.getInterval() + ""); + constantTimer.setDelay(this.countController.getInterval() + ""); + constantTimer.setName(this.countController.getInterval() + " ms"); + return constantTimer; } return null; } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java index f9cb893ac6..d83998137f 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java @@ -39,6 +39,9 @@ public class MsJSR223Processor extends MsTestElement { } else { processor.setName("JSR223Processor"); } + if (config != null && StringUtils.isNotEmpty(config.getStep())) { + processor.setName(this.getName() + "<->" + config.getStep()); + } processor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName()); processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); processor.setProperty("cacheKey", "true"); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java index b196e89ddc..f4a04659b5 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -24,6 +24,7 @@ import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.i18n.Translator; import io.metersphere.track.service.TestPlanReportService; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -211,13 +212,19 @@ public class ApiScenarioReportService { } TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class); - testPlanReportService.updateReport(testPlanReportIdList,ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ReportTriggerMode.SCHEDULE.name()); + testPlanReportService.updateReport(testPlanReportIdList, ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ReportTriggerMode.SCHEDULE.name()); return lastReport; } public ApiScenarioReport updateScenario(TestResult result) { ApiScenarioReport lastReport = null; + if (CollectionUtils.isEmpty(result.getScenarios())) { + ScenarioResult test = new ScenarioResult(); + test.setName(result.getTestId()); + ApiScenarioReport report = editReport(test); + return report; + } for (ScenarioResult item : result.getScenarios()) { // 更新报告状态 ApiScenarioReport report = editReport(item); diff --git a/backend/src/main/java/org/apache/jmeter/reporters/ResultAction.java b/backend/src/main/java/org/apache/jmeter/reporters/ResultAction.java new file mode 100644 index 0000000000..10e611ead9 --- /dev/null +++ b/backend/src/main/java/org/apache/jmeter/reporters/ResultAction.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.reporters; + +import org.apache.jmeter.samplers.SampleEvent; +import org.apache.jmeter.samplers.SampleListener; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.testelement.OnErrorTestElement; +import org.apache.jmeter.threads.JMeterContext.TestLogicalAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Serializable; + +/** + * ResultAction - take action based on the status of the last Result + */ +public class ResultAction extends OnErrorTestElement implements Serializable, SampleListener { + + private static final long serialVersionUID = 242L; + + private static final Logger log = LoggerFactory.getLogger(ResultAction.class); + + /** + * Constructor is initially called once for each occurrence in the test plan + * For GUI, several more instances are created Then clear is called at start + * of test Called several times during test startup The name will not + * necessarily have been set at this point. + */ + public ResultAction() { + super(); + } + + /** + * Examine the sample(s) and take appropriate action + * + * @see SampleListener#sampleOccurred(SampleEvent) + */ + @Override + public void sampleOccurred(SampleEvent e) { + SampleResult s = e.getResult(); + if (log.isDebugEnabled()) { + log.debug("ResultStatusHandler {} for {} OK? {}", getName(), s.getSampleLabel(), s.isSuccessful()); + } + if (!s.isSuccessful()) { + if (isStopTestNow()) { + s.setStopTestNow(true); + } else if (isStopTest()) { + s.setStopTest(true); + } else if (isStopThread()) { + s.setStopThread(true); + } else if (isStartNextThreadLoop()) { + s.setTestLogicalAction(TestLogicalAction.START_NEXT_ITERATION_OF_THREAD); + } else if (isStartNextIterationOfCurrentLoop()) { + s.setTestLogicalAction(TestLogicalAction.START_NEXT_ITERATION_OF_CURRENT_LOOP); + } else if (isBreakCurrentLoop()) { + s.setTestLogicalAction(TestLogicalAction.BREAK_CURRENT_LOOP); + } + } else { + if (getErrorAction() == 1000) { + s.setTestLogicalAction(TestLogicalAction.BREAK_CURRENT_LOOP); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void sampleStarted(SampleEvent e) { + // not used + } + + /** + * {@inheritDoc} + */ + @Override + public void sampleStopped(SampleEvent e) { + // not used + } + +} diff --git a/frontend/src/business/components/api/automation/report/ApiReportDetail.vue b/frontend/src/business/components/api/automation/report/ApiReportDetail.vue index ed6453e2a0..c6cd8c15d7 100644 --- a/frontend/src/business/components/api/automation/report/ApiReportDetail.vue +++ b/frontend/src/business/components/api/automation/report/ApiReportDetail.vue @@ -110,6 +110,10 @@ if (this.isNotRunning) { try { this.content = JSON.parse(this.report.content); + console.log(this.content) + if (!this.content) { + this.content = {scenarios: []}; + } this.$emit('refresh'); } catch (e) { throw e; @@ -130,21 +134,23 @@ if (this.isNotRunning) { this.fails = []; this.totalTime = 0 - this.content.scenarios.forEach((scenario) => { - this.totalTime = this.totalTime + Number(scenario.responseTime) - let failScenario = Object.assign({}, scenario); - if (scenario.error > 0) { - this.fails.push(failScenario); - failScenario.requestResults = []; - scenario.requestResults.forEach((request) => { - if (!request.success) { - let failRequest = Object.assign({}, request); - failScenario.requestResults.push(failRequest); - } - }) + if (this.content.scenarios) { + this.content.scenarios.forEach((scenario) => { + this.totalTime = this.totalTime + Number(scenario.responseTime) + let failScenario = Object.assign({}, scenario); + if (scenario.error > 0) { + this.fails.push(failScenario); + failScenario.requestResults = []; + scenario.requestResults.forEach((request) => { + if (!request.success) { + let failRequest = Object.assign({}, request); + failScenario.requestResults.push(failRequest); + } + }) - } - }) + } + }) + } } }, requestResult(requestResult) { diff --git a/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue b/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue index 36bb764ccb..9db463f989 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue @@ -68,6 +68,10 @@ request: {}, currentScenario: {}, node: {}, + draggable: { + type: Boolean, + default: false, + }, currentEnvironmentId: String, }, components: { diff --git a/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue b/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue index 5202cb6461..e706dc9740 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue @@ -31,6 +31,10 @@ props: { scenario: {}, node: {}, + draggable: { + type: Boolean, + default: false, + }, }, watch: {}, created() { diff --git a/frontend/src/business/components/api/automation/scenario/component/ConstantTimer.vue b/frontend/src/business/components/api/automation/scenario/component/ConstantTimer.vue index 39a5d5fb73..2df66bf88c 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ConstantTimer.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ConstantTimer.vue @@ -10,7 +10,7 @@ :title="$t('api_test.automation.wait_controller')"> @@ -24,6 +24,10 @@ props: { timer: {}, node: {}, + draggable: { + type: Boolean, + default: false, + }, }, data() { return {} diff --git a/frontend/src/business/components/api/automation/scenario/component/IfController.vue b/frontend/src/business/components/api/automation/scenario/component/IfController.vue index e93839dec9..3bf1b390a1 100644 --- a/frontend/src/business/components/api/automation/scenario/component/IfController.vue +++ b/frontend/src/business/components/api/automation/scenario/component/IfController.vue @@ -33,6 +33,10 @@ controller: {}, node: {}, index: Object, + draggable: { + type: Boolean, + default: false, + }, }, data() { return { diff --git a/frontend/src/business/components/api/automation/scenario/component/LoopController.vue b/frontend/src/business/components/api/automation/scenario/component/LoopController.vue index ef259521e9..a8d5fb7e07 100644 --- a/frontend/src/business/components/api/automation/scenario/component/LoopController.vue +++ b/frontend/src/business/components/api/automation/scenario/component/LoopController.vue @@ -25,8 +25,8 @@ {{$t('loop.interval')}} - - + + ms {{$t('loop.proceed')}} @@ -51,8 +51,8 @@ {{$t('loop.interval')}} - - + + ms @@ -65,8 +65,8 @@ {{$t('loop.timeout')}} - - + + ms @@ -82,6 +82,10 @@ controller: {}, node: {}, index: Object, + draggable: { + type: Boolean, + default: false, + }, }, data() { return {