From 5561193ae9e2c00ae2c176d131ae5593ebf08fe1 Mon Sep 17 00:00:00 2001 From: wxg0103 <727495428@qq.com> Date: Thu, 2 Mar 2023 13:45:06 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):?= =?UTF-8?q?=20=E4=BC=98=E5=8C=96=E8=AF=AF=E6=8A=A5=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --story=1011259 --user=王孝刚 【Bug转需求】误报机制和失败重试机制冲突导致问题“【测试跟踪】测试计划失败重试,所有的接口 / 场景步骤 都重试了“ https://www.tapd.cn/55049933/s/1344889 --- ...ParseDTO.java => FakeErrorLibraryDTO.java} | 4 +- .../api/dto/definition/FakeError.java | 11 + .../api/dto/definition/MsRegexDTO.java | 12 + .../request/assertions/MsAssertions.java | 20 +- .../request/sampler/MsHTTPSamplerProxy.java | 15 +- .../request/sampler/MsJDBCSampler.java | 2 +- .../request/sampler/MsTCPSampler.java | 12 +- .../api/dto/scenario/HttpConfig.java | 2 + .../api/jmeter/MsKafkaListener.java | 21 +- .../commons/utils/ErrorReportLibraryUtil.java | 256 ------------------ .../commons/utils/FakeErrorParse.java | 106 ++++++++ .../commons/utils/HashTreeUtil.java | 59 +--- .../commons/utils/ResponseUtil.java | 4 +- .../commons/utils/ResultConversionUtil.java | 19 +- .../service/ExtErrorReportLibraryService.java | 67 ----- .../dto/ErrorReportAssertionResult.java | 13 - .../io/metersphere/dto/RequestResult.java | 2 + .../io/metersphere/jmeter/JMeterBase.java | 9 +- .../apache/jmeter/samplers/SampleResult.java | 192 ++++++------- 19 files changed, 290 insertions(+), 536 deletions(-) rename api-test/backend/src/main/java/io/metersphere/api/dto/{ErrorReportLibraryParseDTO.java => FakeErrorLibraryDTO.java} (89%) create mode 100644 api-test/backend/src/main/java/io/metersphere/api/dto/definition/FakeError.java create mode 100644 api-test/backend/src/main/java/io/metersphere/api/dto/definition/MsRegexDTO.java delete mode 100644 api-test/backend/src/main/java/io/metersphere/commons/utils/ErrorReportLibraryUtil.java create mode 100644 api-test/backend/src/main/java/io/metersphere/commons/utils/FakeErrorParse.java delete mode 100644 api-test/backend/src/main/java/io/metersphere/service/ExtErrorReportLibraryService.java delete mode 100644 framework/sdk-parent/jmeter/src/main/java/io/metersphere/dto/ErrorReportAssertionResult.java diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/ErrorReportLibraryParseDTO.java b/api-test/backend/src/main/java/io/metersphere/api/dto/FakeErrorLibraryDTO.java similarity index 89% rename from api-test/backend/src/main/java/io/metersphere/api/dto/ErrorReportLibraryParseDTO.java rename to api-test/backend/src/main/java/io/metersphere/api/dto/FakeErrorLibraryDTO.java index ababe7a031..c83cceebd9 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/ErrorReportLibraryParseDTO.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/FakeErrorLibraryDTO.java @@ -11,12 +11,12 @@ import java.util.List; @Getter @Setter -public class ErrorReportLibraryParseDTO { +public class FakeErrorLibraryDTO { private List errorCodeList; private RequestResult result; private String requestStatus; - public ErrorReportLibraryParseDTO() { + public FakeErrorLibraryDTO() { this.errorCodeList = new ArrayList<>(); } diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/FakeError.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/FakeError.java new file mode 100644 index 0000000000..d1844683c5 --- /dev/null +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/FakeError.java @@ -0,0 +1,11 @@ +package io.metersphere.api.dto.definition; + +import lombok.Data; + +@Data +public class FakeError { + private String projectId; + private boolean higherThanSuccess; + private boolean higherThanError; +} + diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/MsRegexDTO.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/MsRegexDTO.java new file mode 100644 index 0000000000..b33fbab29e --- /dev/null +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/MsRegexDTO.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.definition; + +import lombok.Data; + +@Data +public class MsRegexDTO { + private String subject; + private String condition; + private String value; + private String errorCode; + private boolean pass; +} diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java index 3d9da90745..e1d4471578 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java @@ -5,7 +5,6 @@ import io.metersphere.api.dto.definition.request.assertions.document.MsAssertion import io.metersphere.commons.constants.ElementConstants; import io.metersphere.commons.constants.PropertyConstant; import io.metersphere.commons.utils.CommonBeanFactory; -import io.metersphere.commons.utils.ErrorReportLibraryUtil; import io.metersphere.plugin.core.MsParameter; import io.metersphere.plugin.core.MsTestElement; import io.metersphere.service.definition.ApiDefinitionService; @@ -105,24 +104,19 @@ public class MsAssertions extends MsTestElement { } private ResponseAssertion responseAssertion(MsAssertionRegex assertionRegex) { - ResponseAssertion assertion = null; - boolean isErrorReportAssertion = false; - if (StringUtils.startsWith(this.getName(), "ErrorReportAssertion:")) { - assertion = new ErrorReportAssertion(); - isErrorReportAssertion = true; + ResponseAssertion assertion = new ResponseAssertion(); + assertion.setEnabled(this.isEnable()); + if (StringUtils.isNotEmpty(assertionRegex.getDescription())) { + assertion.setName(StringUtils.join(this.getName(), delimiter, assertionRegex.getDescription())); } else { - assertion = new ResponseAssertion(); + assertion.setName(StringUtils.join(this.getName(), delimiter, "AssertionRegex")); } assertion.setEnabled(this.isEnable()); if (StringUtils.isNotEmpty(assertionRegex.getDescription())) { - if (!isErrorReportAssertion) { - //正常断言要在desc增加匹配信息,用于接受结果后和误报断言进行匹配 - assertionRegex.setDescription(assertionRegex.getDescription() + ErrorReportLibraryUtil.ASSERTION_CONTENT_REGEX_DELIMITER + assertionRegex.getSubject() + ":" + assertionRegex.getExpression()); - } - assertion.setName(this.getName() + delimiter + assertionRegex.getDescription()); + assertion.setName(StringUtils.join(this.getName(), delimiter, assertionRegex.getDescription())); } else { - assertion.setName(this.getName() + delimiter + "AssertionRegex" + ErrorReportLibraryUtil.ASSERTION_CONTENT_REGEX_DELIMITER + assertionRegex.getSubject() + ":" + assertionRegex.getExpression()); + assertion.setName(StringUtils.join(this.getName(), delimiter, "AssertionRegex")); } assertion.setProperty(TestElement.TEST_CLASS, ResponseAssertion.class.getName()); assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("AssertionGui")); diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java index 0912a9c91e..47ab2e59fd 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -1,5 +1,6 @@ package io.metersphere.api.dto.definition.request.sampler; +import io.metersphere.api.dto.definition.FakeError; import io.metersphere.api.dto.definition.request.ElementUtil; import io.metersphere.api.dto.definition.request.ParameterConfig; import io.metersphere.api.dto.definition.request.assertions.MsAssertions; @@ -31,12 +32,14 @@ import io.metersphere.service.definition.ApiTestCaseService; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.KeystoreConfig; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.protocol.http.util.HTTPArgument; import org.apache.jmeter.protocol.http.util.HTTPConstants; +import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; @@ -213,10 +216,8 @@ public class MsHTTPSamplerProxy extends MsTestElement { //根据配置增加全局前后至脚本 JMeterScriptUtil.setScriptByHttpConfig(httpConfig, httpSamplerTree, config, useEnvironment, this.getEnvironmentId(), false); //增加误报、断言 - if (!config.isOperating() && CollectionUtils.isNotEmpty(httpConfig.getErrorReportAssertions())) { - for (MsAssertions assertion : httpConfig.getErrorReportAssertions()) { - assertion.toHashTree(httpSamplerTree, assertion.getHashTree(), config); - } + if (ObjectUtils.isNotEmpty(httpConfig.getFakeError())) { + sampler.setProperty(SampleResult.MS_FAKE_ERROR, JSONUtil.toJSONString(httpConfig.getFakeError())); } if (CollectionUtils.isNotEmpty(httpConfig.getAssertions())) { for (MsAssertions assertion : httpConfig.getAssertions()) { @@ -335,7 +336,11 @@ public class MsHTTPSamplerProxy extends MsTestElement { httpConfig.setAssertions(ElementUtil.copyAssertion(environmentConfig.getAssertions())); } if (environmentConfig.isUseErrorCode()) { - httpConfig.setErrorReportAssertions(HashTreeUtil.getErrorReportByProjectId(this.getProjectId(), environmentConfig.isHigherThanSuccess(), environmentConfig.isHigherThanError())); + FakeError fakeError = new FakeError(); + fakeError.setHigherThanError(environmentConfig.isHigherThanError()); + fakeError.setProjectId(this.getProjectId()); + fakeError.setHigherThanSuccess(environmentConfig.isHigherThanSuccess()); + httpConfig.setFakeError(fakeError); } return httpConfig; } diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java index 2e92a4314d..ca0249ce3a 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java @@ -126,7 +126,7 @@ public class MsJDBCSampler extends MsTestElement { //添加csv ElementUtil.addApiVariables(config, tree, this.getProjectId()); //增加误报、全局断言 - HashTreeUtil.addPositive(envConfig, samplerHashTree, config, this.getProjectId()); + HashTreeUtil.addPositive(envConfig, samplerHashTree, config, this.getProjectId(), jdbcSampler); //处理全局前后置脚本(步骤内) String environmentId = this.getEnvironmentId(); if (environmentId == null) { diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java index 47ecd17556..2bf5686044 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java @@ -3,7 +3,6 @@ package io.metersphere.api.dto.definition.request.sampler; import io.metersphere.api.dto.automation.TcpTreeTableDataStruct; import io.metersphere.api.dto.definition.request.ElementUtil; import io.metersphere.api.dto.definition.request.ParameterConfig; -import io.metersphere.api.dto.definition.request.assertions.MsAssertions; import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor; import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; @@ -140,13 +139,14 @@ public class MsTCPSampler extends MsTestElement { ElementUtil.addApiVariables(config, tree, this.getProjectId()); final HashTree samplerHashTree = new ListedHashTree(); samplerHashTree.add(tcpConfig()); - tree.set(tcpSampler(config), samplerHashTree); + TCPSampler tcpSampler = tcpSampler(config); + tree.set(tcpSampler, samplerHashTree); setUserParameters(samplerHashTree); if (tcpPreProcessor != null && StringUtils.isNotBlank(tcpPreProcessor.getScript())) { samplerHashTree.add(tcpPreProcessor.getShellProcessor()); } //增加误报、全局断言 - HashTreeUtil.addPositive(envConfig, samplerHashTree, config, this.getProjectId()); + HashTreeUtil.addPositive(envConfig, samplerHashTree, config, this.getProjectId(), tcpSampler); //处理全局前后置脚本(步骤内) String environmentId = this.getEnvironmentId(); if (environmentId == null) { @@ -154,15 +154,9 @@ public class MsTCPSampler extends MsTestElement { } //根据配置将脚本放置在私有脚本之前 JMeterScriptUtil.setScriptByEnvironmentConfig(envConfig, samplerHashTree, GlobalScriptFilterRequest.TCP.name(), environmentId, config, false); - HashTreeUtil hashTreeUtil = new HashTreeUtil(); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree = ElementUtil.order(hashTree); - EnvironmentConfig finalEnvConfig = envConfig; hashTree.forEach(el -> { - if (el instanceof MsAssertions && finalEnvConfig != null) { - //断言设置需要和全局断言、误报进行去重 - el = hashTreeUtil.duplicateRegexInAssertions(ElementUtil.copyAssertion(finalEnvConfig.getAssertions()), (MsAssertions) el); - } el.toHashTree(samplerHashTree, el.getHashTree(), config); }); } diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java b/api-test/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java index 4437ec0d09..34b5cbe4c9 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java @@ -1,5 +1,6 @@ package io.metersphere.api.dto.scenario; +import io.metersphere.api.dto.definition.FakeError; import io.metersphere.api.dto.definition.request.assertions.MsAssertions; import io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor; import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor; @@ -30,6 +31,7 @@ public class HttpConfig { private List assertions; private List errorReportAssertions; private String description; + private FakeError fakeError; public HttpConfig initHttpConfig(HttpConfigCondition configCondition) { diff --git a/api-test/backend/src/main/java/io/metersphere/api/jmeter/MsKafkaListener.java b/api-test/backend/src/main/java/io/metersphere/api/jmeter/MsKafkaListener.java index a9810c5a61..592ad9c97b 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/jmeter/MsKafkaListener.java +++ b/api-test/backend/src/main/java/io/metersphere/api/jmeter/MsKafkaListener.java @@ -1,9 +1,11 @@ package io.metersphere.api.jmeter; import io.metersphere.api.dto.MsgDTO; +import io.metersphere.api.dto.RequestResultExpandDTO; import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.KafkaTopicConstants; import io.metersphere.commons.utils.*; +import io.metersphere.dto.RequestResult; import io.metersphere.service.ApiExecutionQueueService; import io.metersphere.service.RedisTemplateService; import io.metersphere.service.TestResultService; @@ -42,6 +44,9 @@ public class MsKafkaListener { @Resource private RedisTemplateService redisTemplateService; + @Resource + private ApiDefinitionEnvService apiDefinitionEnvService; + private final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, @@ -76,9 +81,19 @@ public class MsKafkaListener { LoggerUtil.info("接收到执行结果:", record.key()); if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) { MsgDTO dto = JSONUtil.parseObject(record.value(), MsgDTO.class); - if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name()) && dto.getContent().startsWith("result_")) { - ApiDefinitionEnvService apiDefinitionEnvService = CommonBeanFactory.getBean(ApiDefinitionEnvService.class); - apiDefinitionEnvService.setEnvAndPoolName(dto); + if (StringUtils.isNotBlank(dto.getContent()) && dto.getContent().startsWith("result_")) { + String content = dto.getContent().substring(7); + if (StringUtils.isNotBlank(content)) { + RequestResult baseResult = JSONUtil.parseObject(content, RequestResult.class); + if (ObjectUtils.isNotEmpty(baseResult)) { + //解析是否含有误报库信息 + RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(baseResult); + dto.setContent(StringUtils.join("result_", JSON.toJSONString(expandDTO))); + if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name()) && dto.getContent().startsWith("result_")) { + apiDefinitionEnvService.setEnvAndPoolName(dto); + } + } + } } WebSocketUtil.sendMessageSingle(dto); } diff --git a/api-test/backend/src/main/java/io/metersphere/commons/utils/ErrorReportLibraryUtil.java b/api-test/backend/src/main/java/io/metersphere/commons/utils/ErrorReportLibraryUtil.java deleted file mode 100644 index 416cfb6a46..0000000000 --- a/api-test/backend/src/main/java/io/metersphere/commons/utils/ErrorReportLibraryUtil.java +++ /dev/null @@ -1,256 +0,0 @@ -package io.metersphere.commons.utils; - -import io.metersphere.api.dto.ErrorReportLibraryParseDTO; -import io.metersphere.commons.enums.ApiReportStatus; -import io.metersphere.dto.ErrorReportAssertionResult; -import io.metersphere.dto.RequestResult; -import io.metersphere.dto.ResponseAssertionResult; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 误报解析类 - */ -public class ErrorReportLibraryUtil { - - private static final String ERROR_REPORT_NAME_START = "ErrorReportAssertion:"; - private static final String ERROR_REPORT_SUCCESS_MESSAGE_FLAG = " Final result is success"; - public static final String ASSERTION_CONTENT_REGEX_DELIMITER = "--REGEX-->"; - - public static ErrorReportLibraryParseDTO parseAssertions(RequestResult result) { - ErrorReportLibraryParseDTO returnDTO = new ErrorReportLibraryParseDTO(); - if (result != null && result.getResponseResult() != null && CollectionUtils.isNotEmpty(result.getResponseResult().getAssertions())) { - AssertionParserResult assertionParserResult = parserAndFormatAssertion(result.getResponseResult().getAssertions()); - List errorReportAssertionList = assertionParserResult.errorReportAssertionList; - Map> successAssertionMessageMap = assertionParserResult.successAssertionMessageMap; - Map> errorAssertionMessageMap = assertionParserResult.errorAssertionMessageMap; - boolean higherThanSuccess = assertionParserResult.higherThanSuccess; - boolean higherThanError = assertionParserResult.higherThanError; - /* - 默认优先级(higherThanSuccess=false,higherThanError=false): - 1.如果一个请求同时有 成功、误报、失败 三个断言(断言规则不一样)时,那么请求最终结果处理成失败,展示匹配到的误报断言; - 2.如果一个请求同时有 失败、误报(断言规则不一样)那么处理成失败,展示匹配到的误报断言; - 3.如果一个请求同时有 成功、误报(断言规则不一样)那么处理成误报,展示匹配到的误报断言; - 4.如果一个请求同时有 成功、误报(断言规则一样)那么处理成成功,不展示匹配到的误报断言。 - 能和误报断言匹配上的成功断言,都转为成功断言; - 如果含有失败断言,则结果处理成失败,展示剩下的误报断言 - 如果还有误报断言,那么结果处理成误报,展示剩下的误报断言 - 最后就是成功 - - 如果配置的误报与失败的优先级 / 误报与成功的优先级 与上述出现偏差,则对应的结果也会改变 - */ - if (CollectionUtils.isNotEmpty(errorReportAssertionList)) { - List unMatchErrorReportAssertions = new ArrayList<>(); - Map> passedErrorReportAssertionMap = new HashMap<>(); - - //过滤出没有命中的误报断言,并整理出命中误报断言的数据 - for (ResponseAssertionResult assertion : errorReportAssertionList) { - if (StringUtils.endsWith(assertion.getMessage(), ERROR_REPORT_SUCCESS_MESSAGE_FLAG)) { - String regexString = assertion.getContent(); - if (passedErrorReportAssertionMap.containsKey(regexString)) - passedErrorReportAssertionMap.get(regexString).add(assertion); - else { - List list = new ArrayList<>(); - list.add(assertion); - passedErrorReportAssertionMap.put(regexString, list); - } - } else { - unMatchErrorReportAssertions.add(assertion); - } - } - - //根据配置来筛选断言、获取误报编码、获取接口状态是否是误报 - AssertionFilterResult filterResult = filterAssertions(passedErrorReportAssertionMap, successAssertionMessageMap, errorAssertionMessageMap, result.isSuccess(), higherThanSuccess, higherThanError); - int filteredSuccessAssertionCount = unMatchErrorReportAssertions.size() + filterResult.filteredSuccessAssertionList.size(); - unMatchErrorReportAssertions.addAll(filterResult.filteredSuccessAssertionList); - unMatchErrorReportAssertions.addAll(filterResult.filteredErrorAssertionList); - returnDTO.setRequestStatus(filterResult.requestStatus); - returnDTO.setErrorCodeList(filterResult.errorCodeList); - - if (CollectionUtils.isNotEmpty(unMatchErrorReportAssertions)) { - // 未被误报断言匹配到的结果,清除该请求的误报断言记录,并将断言涉及到的统计结果恢复正常 - result.setTotalAssertions(result.getTotalAssertions() - unMatchErrorReportAssertions.size()); - int passAssertionCount = (result.getPassAssertions() - filteredSuccessAssertionCount); - if (passAssertionCount < 0) { - result.setPassAssertions(0); - } else { - result.setPassAssertions(passAssertionCount); - } - List allAssertions = result.getResponseResult().getAssertions(); - List newAssertions = new ArrayList<>(); - - allAssertionForeach: - for (ResponseAssertionResult assertion : allAssertions) { - for (ResponseAssertionResult unMatchAssertion : unMatchErrorReportAssertions) { - if (StringUtils.equals(unMatchAssertion.getContent(), assertion.getContent())) { - continue allAssertionForeach; - } - } - newAssertions.add(assertion); - } - result.getResponseResult().getAssertions().clear(); - result.getResponseResult().getAssertions().addAll(newAssertions); - } - } - } - returnDTO.setResult(result); - return returnDTO; - } - - /** - * 过滤断言并统计最后结果 - * 1.higherThanError=true:含有误报断言就为误报,否则就为失败 - * 2.higherThanSuccess=true:含有误报断言就为误报,否则就为成功 - * - * @param errorReportAssertionMap 匹配到的误报断言 - * @param successAssertionMap 匹配到的成功断言 - * @param errorAssertionMap 匹配到的失败断言 - * @param higherThanSuccess 误报断言优先级小于成功断言优先级 - * @param higherThanError 误报断言优先级大于成功断言优先级 - */ - private static AssertionFilterResult filterAssertions(Map> errorReportAssertionMap, - Map> successAssertionMap, - Map> errorAssertionMap, - boolean resultIsSuccess, boolean higherThanSuccess, boolean higherThanError) { - AssertionFilterResult result = new AssertionFilterResult(); - if (MapUtils.isNotEmpty(errorReportAssertionMap)) { - List removedSuccessList = removeAssertions(errorReportAssertionMap, successAssertionMap, higherThanSuccess); - if (CollectionUtils.isNotEmpty(removedSuccessList)) { - result.filteredSuccessAssertionList.addAll(removedSuccessList); - } - List removedErrorList = removeAssertions(errorReportAssertionMap, errorAssertionMap, higherThanError); - if (CollectionUtils.isNotEmpty(removedErrorList)) { - if (higherThanError) { - result.filteredErrorAssertionList.addAll(removedErrorList); - } else { - result.filteredSuccessAssertionList.addAll(removedErrorList); - } - - } - for (List list : errorReportAssertionMap.values()) { - list.forEach(item -> { - String assertionName = item.getName(); - if (StringUtils.startsWith(assertionName, ERROR_REPORT_NAME_START)) { - String errorCode = StringUtils.substring(assertionName, ERROR_REPORT_NAME_START.length()); - result.errorCodeList.add(errorCode); - } - }); - } - - if (CollectionUtils.isNotEmpty(result.errorCodeList)) { - if ((higherThanError && !resultIsSuccess) || (higherThanSuccess && resultIsSuccess)) { - result.requestStatus = ApiReportStatus.FAKE_ERROR.name(); - } - } - } - LogUtil.info(" FAKE_ERROR result: config-higherThanError:" + higherThanError - + ", config-higherThanSuccess:" + higherThanSuccess - + ", resultIsSuccess: " + resultIsSuccess - + ", isFakeError: " + ((higherThanError && !resultIsSuccess) || (higherThanSuccess && resultIsSuccess)) - + "; status:" + result.requestStatus); - return result; - } - - /** - * 将匹配的断言移除 - * - * @param firstMap - * @param secondMap - * @param removeDataInSecondMap 是否移除map2中的数据?true:移除map2中的数据 false:移除map1中的数据 - * @return - */ - private static List removeAssertions(Map> firstMap, Map> secondMap, boolean removeDataInSecondMap) { - List returnList = new ArrayList<>(); - if (MapUtils.isNotEmpty(firstMap) && MapUtils.isNotEmpty(secondMap)) { - if (removeDataInSecondMap) { - for (String regex : firstMap.keySet()) { - if (secondMap.containsKey(regex)) { - returnList.addAll(secondMap.get(regex)); - secondMap.remove(regex); - } - } - } else { - for (String regex : secondMap.keySet()) { - if (firstMap.containsKey(regex)) { - returnList.addAll(firstMap.get(regex)); - firstMap.remove(regex); - } - } - } - } - return returnList; - } - - /** - * 解析并重新格式化请求中的所有断言 - * - * @param assertions - * @return - */ - private static AssertionParserResult parserAndFormatAssertion(List assertions) { - AssertionParserResult result = new AssertionParserResult(); - for (ResponseAssertionResult assertion : assertions) { - if (assertion instanceof ErrorReportAssertionResult || StringUtils.startsWith(assertion.getName(), ERROR_REPORT_NAME_START)) { - String expression = assertion.getContent().trim(); - if (StringUtils.contains(expression, ASSERTION_CONTENT_REGEX_DELIMITER)) { - String[] contentArr = expression.split(ASSERTION_CONTENT_REGEX_DELIMITER); - assertion.setContent(contentArr[0]); - if (contentArr.length == 3) { - result.higherThanSuccess = BooleanUtils.toBoolean(contentArr[1]); - result.higherThanError = BooleanUtils.toBoolean(contentArr[2]); - } - } - result.errorReportAssertionList.add(assertion); - } else { - if (StringUtils.isNotEmpty(assertion.getContent())) { - String expression = assertion.getContent().trim(); - String regexString = expression; - if (regexString.contains(ASSERTION_CONTENT_REGEX_DELIMITER)) { - regexString = expression.split(ASSERTION_CONTENT_REGEX_DELIMITER)[1]; - assertion.setContent(expression.split(ASSERTION_CONTENT_REGEX_DELIMITER)[0]); - } - if (assertion.isPass()) { - if (result.successAssertionMessageMap.containsKey(regexString)) - result.successAssertionMessageMap.get(regexString).add(assertion); - else { - List list = new ArrayList<>(); - list.add(assertion); - result.successAssertionMessageMap.put(regexString, list); - } - } else { - if (result.errorAssertionMessageMap.containsKey(regexString)) - result.errorAssertionMessageMap.get(regexString).add(assertion); - else { - List list = new ArrayList<>(); - list.add(assertion); - result.errorAssertionMessageMap.put(regexString, list); - } - } - } - } - } - return result; - } -} - -class AssertionParserResult { - boolean higherThanSuccess = false; - boolean higherThanError = false; - List errorReportAssertionList = new ArrayList<>(); - Map> successAssertionMessageMap = new HashMap<>(); - Map> errorAssertionMessageMap = new HashMap<>(); -} - -class AssertionFilterResult { - String requestStatus; - List errorCodeList = new ArrayList<>(); - List filteredSuccessAssertionList = new ArrayList<>(); - List filteredErrorAssertionList = new ArrayList<>(); -} diff --git a/api-test/backend/src/main/java/io/metersphere/commons/utils/FakeErrorParse.java b/api-test/backend/src/main/java/io/metersphere/commons/utils/FakeErrorParse.java new file mode 100644 index 0000000000..0e5dba1abb --- /dev/null +++ b/api-test/backend/src/main/java/io/metersphere/commons/utils/FakeErrorParse.java @@ -0,0 +1,106 @@ +package io.metersphere.commons.utils; + +import io.metersphere.api.dto.FakeErrorLibraryDTO; +import io.metersphere.api.dto.definition.FakeError; +import io.metersphere.api.dto.definition.MsRegexDTO; +import io.metersphere.base.domain.ErrorReportLibraryExample; +import io.metersphere.base.domain.ErrorReportLibraryWithBLOBs; +import io.metersphere.commons.enums.ApiReportStatus; +import io.metersphere.dto.RequestResult; +import io.metersphere.utils.JsonUtils; +import io.metersphere.xpack.fake.error.ErrorReportLibraryService; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 误报解析类 + * + * @author xiaogang + */ +public class FakeErrorParse { + + public static FakeErrorLibraryDTO parseAssertions(RequestResult result) { + FakeErrorLibraryDTO fakeError = new FakeErrorLibraryDTO(); + if (StringUtils.isNotBlank(result.getFakeErrorMessage())) { + FakeError errorReportDTO = JsonUtils.parseObject(result.getFakeErrorMessage(), FakeError.class); + ErrorReportLibraryService service = CommonBeanFactory.getBean(ErrorReportLibraryService.class); + ErrorReportLibraryExample example = new ErrorReportLibraryExample(); + example.createCriteria().andProjectIdEqualTo(errorReportDTO.getProjectId()).andStatusEqualTo(true); + List bloBs = service.selectByExampleWithBLOBs(example); + List regexList = new ArrayList<>(); + bloBs.forEach(item -> { + if (StringUtils.isNotEmpty(item.getContent())) { + try { + Map assertionMap = JSON.parseObject(item.getContent(), Map.class); + if (assertionMap != null) { + MsRegexDTO regexConfig = JSON.parseObject(JSONUtil.toJSONString(assertionMap.get("regexConfig")), MsRegexDTO.class); + regexConfig.setErrorCode(item.getErrorCode()); + regexList.add(regexConfig); + } + } catch (Exception e) { + LogUtil.error(e); + } + } + }); + //根据配置来筛选断言、获取误报编码、获取接口状态是否是误报 + List errorCodeList = new ArrayList<>(); + regexList.forEach(item -> { + if (StringUtils.isNotEmpty(item.getSubject())) { + switch (item.getSubject()) { + case "Response Code" -> + item.setPass(parseResponseCode(result.getResponseResult().getResponseCode(), item.getValue(), item.getCondition())); + + case "Response Headers" -> + item.setPass(parseResponseCode(result.getResponseResult().getHeaders(), item.getValue(), item.getCondition())); + + case "Response Data" -> + item.setPass(parseResponseCode(result.getResponseResult().getBody(), item.getValue(), item.getCondition())); + default -> item.setPass(false); + } + } + if (item.isPass()) { + errorCodeList.add(item.getErrorCode()); + } + }); + boolean higherThanError = errorReportDTO.isHigherThanError(); + boolean higherThanSuccess = errorReportDTO.isHigherThanSuccess(); + if (CollectionUtils.isNotEmpty(errorCodeList)) { + if ((higherThanError && !result.isSuccess()) || (higherThanSuccess && result.isSuccess())) { + fakeError.setRequestStatus(ApiReportStatus.FAKE_ERROR.name()); + } + fakeError.setErrorCodeList(errorCodeList); + } + LogUtil.info(" FAKE_ERROR result: config-higherThanError:" + higherThanError + + ", config-higherThanSuccess:" + higherThanSuccess + + ", resultIsSuccess: " + result.isSuccess() + + ", isFakeError: " + ((higherThanError && !result.isSuccess()) || (higherThanSuccess && result.isSuccess())) + + "; status:" + fakeError.getRequestStatus()); + } + fakeError.setResult(result); + return fakeError; + } + + private static boolean parseResponseCode(String result, String regexDTO, String condition) { + return switch (condition.toUpperCase()) { + case "CONTAINS" -> result.contains(regexDTO); + + case "NOT_CONTAINS" -> notContains(result, regexDTO); + + case "EQUALS" -> StringUtils.equals(result, regexDTO); + + case "START_WITH" -> result.startsWith(regexDTO); + + case "END_WITH" -> result.endsWith(regexDTO); + + default -> false; + }; + } + + private static boolean notContains(String result, String regexDTO) { + return !result.contains(regexDTO); + } +} \ No newline at end of file diff --git a/api-test/backend/src/main/java/io/metersphere/commons/utils/HashTreeUtil.java b/api-test/backend/src/main/java/io/metersphere/commons/utils/HashTreeUtil.java index 1fdb28a3ad..a622912c1d 100644 --- a/api-test/backend/src/main/java/io/metersphere/commons/utils/HashTreeUtil.java +++ b/api-test/backend/src/main/java/io/metersphere/commons/utils/HashTreeUtil.java @@ -1,9 +1,9 @@ package io.metersphere.commons.utils; import io.metersphere.api.dto.RunningParamKeys; +import io.metersphere.api.dto.definition.FakeError; import io.metersphere.api.dto.definition.request.ElementUtil; import io.metersphere.api.dto.definition.request.ParameterConfig; -import io.metersphere.api.dto.definition.request.assertions.MsAssertionRegex; import io.metersphere.api.dto.definition.request.assertions.MsAssertions; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; @@ -15,7 +15,6 @@ import io.metersphere.dto.JmeterRunRequestDTO; import io.metersphere.environment.service.BaseEnvironmentService; import io.metersphere.metadata.service.FileMetadataService; import io.metersphere.request.BodyFile; -import io.metersphere.service.ExtErrorReportLibraryService; import io.metersphere.utils.LoggerUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; @@ -23,6 +22,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.extractor.JSR223PostProcessor; import org.apache.jmeter.modifiers.JSR223PreProcessor; import org.apache.jmeter.protocol.java.sampler.JSR223Sampler; +import org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler; +import org.apache.jmeter.protocol.tcp.sampler.TCPSampler; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.testelement.AbstractTestElement; import org.apache.jorphan.collections.HashTree; import org.json.JSONArray; import org.json.JSONObject; @@ -182,53 +185,19 @@ public class HashTreeUtil { return execute_env_param_dataMap; } - public MsAssertions duplicateRegexInAssertions(List compareList, MsAssertions target) { - if (target != null && CollectionUtils.isNotEmpty(target.getRegex())) { - List compareRegexList = new ArrayList<>(); - if (CollectionUtils.isNotEmpty(compareList)) { - for (MsAssertions assertions : compareList) { - if (assertions != null && CollectionUtils.isNotEmpty(assertions.getRegex())) { - compareRegexList.addAll(assertions.getRegex()); - } - } - } - - List duplicatedList = new ArrayList<>(); - for (MsAssertionRegex regex : target.getRegex()) { - boolean isExit = false; - for (MsAssertionRegex compareRegex : compareRegexList) { - if (StringUtils.equals(regex.getType(), compareRegex.getType()) - && StringUtils.equals(regex.getSubject(), compareRegex.getSubject()) - && StringUtils.equals(regex.getExpression(), compareRegex.getExpression())) { - isExit = true; - break; - } - } - if (!isExit) { - duplicatedList.add(regex); - } - } - target.setRegex(duplicatedList); - } - return target; - } - - public static List getErrorReportByProjectId(String projectId, boolean higherThanSuccess, boolean higherThanError) { - ExtErrorReportLibraryService service = CommonBeanFactory.getBean(ExtErrorReportLibraryService.class); - if (service == null) { - return new ArrayList<>(); - } - return service.getAssertionByProjectIdAndStatusIsOpen(projectId, higherThanSuccess, higherThanError); - } - - public static void addPositive(EnvironmentConfig envConfig, HashTree samplerHashTree, ParameterConfig config, String projectId) { + public static void addPositive(EnvironmentConfig envConfig, HashTree samplerHashTree, ParameterConfig config, String projectId, AbstractTestElement sample) { if (envConfig == null) { return; } if (!config.isOperating() && envConfig.isUseErrorCode()) { - List errorReportAssertion = HashTreeUtil.getErrorReportByProjectId(projectId, envConfig.isHigherThanSuccess(), envConfig.isHigherThanError()); - for (MsAssertions assertion : errorReportAssertion) { - assertion.toHashTree(samplerHashTree, assertion.getHashTree(), config); + FakeError fakeError = new FakeError(); + fakeError.setHigherThanError(envConfig.isHigherThanError()); + fakeError.setProjectId(projectId); + fakeError.setHigherThanSuccess(envConfig.isHigherThanSuccess()); + if (sample instanceof JDBCSampler) { + sample.setProperty(SampleResult.MS_FAKE_ERROR, JSONUtil.toJSONString(fakeError)); + } else if (sample instanceof TCPSampler) { + sample.setProperty(SampleResult.MS_FAKE_ERROR, JSONUtil.toJSONString(fakeError)); } } if (CollectionUtils.isNotEmpty(envConfig.getAssertions())) { diff --git a/api-test/backend/src/main/java/io/metersphere/commons/utils/ResponseUtil.java b/api-test/backend/src/main/java/io/metersphere/commons/utils/ResponseUtil.java index c25f29fd9b..a62d9f3087 100644 --- a/api-test/backend/src/main/java/io/metersphere/commons/utils/ResponseUtil.java +++ b/api-test/backend/src/main/java/io/metersphere/commons/utils/ResponseUtil.java @@ -3,7 +3,7 @@ package io.metersphere.commons.utils; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.ObjectMapper; -import io.metersphere.api.dto.ErrorReportLibraryParseDTO; +import io.metersphere.api.dto.FakeErrorLibraryDTO; import io.metersphere.api.dto.RequestResultExpandDTO; import io.metersphere.commons.enums.ApiReportStatus; import io.metersphere.commons.enums.ResponseFormatType; @@ -24,7 +24,7 @@ public class ResponseUtil { //根据responseheader的信息来处理返回数据 baseResult = ResponseUtil.parseResponseBodyByHeader(baseResult); //解析是否含有误报库信息 - ErrorReportLibraryParseDTO errorCodeDTO = ErrorReportLibraryUtil.parseAssertions(baseResult); + FakeErrorLibraryDTO errorCodeDTO = FakeErrorParse.parseAssertions(baseResult); RequestResult requestResult = errorCodeDTO.getResult(); RequestResultExpandDTO expandDTO = new RequestResultExpandDTO(); BeanUtils.copyBean(expandDTO, requestResult); diff --git a/api-test/backend/src/main/java/io/metersphere/commons/utils/ResultConversionUtil.java b/api-test/backend/src/main/java/io/metersphere/commons/utils/ResultConversionUtil.java index e7c033f7ea..fbf98b144c 100644 --- a/api-test/backend/src/main/java/io/metersphere/commons/utils/ResultConversionUtil.java +++ b/api-test/backend/src/main/java/io/metersphere/commons/utils/ResultConversionUtil.java @@ -2,8 +2,7 @@ package io.metersphere.commons.utils; import io.metersphere.api.dto.ApiScenarioReportBaseInfoDTO; -import io.metersphere.api.dto.ErrorReportLibraryParseDTO; -import io.metersphere.base.domain.ApiScenarioReportResult; +import io.metersphere.api.dto.FakeErrorLibraryDTO; import io.metersphere.base.domain.ApiScenarioReportResultWithBLOBs; import io.metersphere.commons.enums.ApiReportStatus; import io.metersphere.dto.RequestResult; @@ -12,27 +11,13 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import java.nio.charset.StandardCharsets; -import java.util.LinkedList; -import java.util.List; import java.util.UUID; public class ResultConversionUtil { - public static List getApiScenarioReportResults(String reportId, List requestResults) { - //解析误报内容 - List list = new LinkedList<>(); - if (CollectionUtils.isEmpty(requestResults)) { - return list; - } - requestResults.forEach(item -> { - list.add(getApiScenarioReportResult(reportId, item)); - }); - return list; - } - public static ApiScenarioReportResultWithBLOBs getApiScenarioReportResult(String reportId, RequestResult requestResult) { //解析误报内容 - ErrorReportLibraryParseDTO errorCodeDTO = ErrorReportLibraryUtil.parseAssertions(requestResult); + FakeErrorLibraryDTO errorCodeDTO = FakeErrorParse.parseAssertions(requestResult); RequestResult result = errorCodeDTO.getResult(); String resourceId = result.getResourceId(); diff --git a/api-test/backend/src/main/java/io/metersphere/service/ExtErrorReportLibraryService.java b/api-test/backend/src/main/java/io/metersphere/service/ExtErrorReportLibraryService.java deleted file mode 100644 index b8125243ea..0000000000 --- a/api-test/backend/src/main/java/io/metersphere/service/ExtErrorReportLibraryService.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.metersphere.service; - - -import io.metersphere.api.dto.definition.request.assertions.MsAssertionDuration; -import io.metersphere.api.dto.definition.request.assertions.MsAssertionRegex; -import io.metersphere.api.dto.definition.request.assertions.MsAssertions; -import io.metersphere.api.dto.definition.request.assertions.document.MsAssertionDocument; -import io.metersphere.base.domain.ErrorReportLibraryExample; -import io.metersphere.base.domain.ErrorReportLibraryWithBLOBs; -import io.metersphere.commons.utils.*; -import io.metersphere.xpack.fake.error.ErrorReportLibraryService; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * @author song.tianyang - * @Date 2022/1/3 3:51 下午 - */ -@Service -@Transactional(rollbackFor = Exception.class) -public class ExtErrorReportLibraryService { - public List getAssertionByProjectIdAndStatusIsOpen(String projectId, boolean higherThanSuccess, boolean higherThanError) { - ErrorReportLibraryService service = CommonBeanFactory.getBean(ErrorReportLibraryService.class); - if (service == null) { - return new ArrayList<>(); - } - List returnList = new ArrayList<>(); - ErrorReportLibraryExample example = new ErrorReportLibraryExample(); - example.createCriteria().andProjectIdEqualTo(projectId).andStatusEqualTo(true); - List bloBs = service.selectByExampleWithBLOBs(example); - bloBs.forEach(item -> { - if (StringUtils.isNotEmpty(item.getContent())) { - try { - MsAssertions assertions = new MsAssertions(); - Map assertionMap = JSON.parseObject(item.getContent(), Map.class); - if (assertionMap != null) { - MsAssertionDuration duration = JSONUtil.parseObject(JSONUtil.toJSONString(assertionMap.get("duration")), MsAssertionDuration.class); - List regexList = JSON.parseArray(JSONUtil.toJSONString(assertionMap.get("regex")), MsAssertionRegex.class); - MsAssertionDocument document = JSONUtil.parseObject(JSONUtil.toJSONString(assertionMap.get("document")), MsAssertionDocument.class); - assertions.setDuration(duration); - assertions.setRegex(regexList); - assertions.setDocument(document); - } - if (assertions != null && CollectionUtils.isNotEmpty(assertions.getRegex())) { - if (StringUtils.isEmpty(assertions.getRegex().get(0).getDescription())) { - String desc = assertions.getRegex().get(0).getSubject() + ":" + assertions.getRegex().get(0).getExpression() - + ErrorReportLibraryUtil.ASSERTION_CONTENT_REGEX_DELIMITER + higherThanSuccess - + ErrorReportLibraryUtil.ASSERTION_CONTENT_REGEX_DELIMITER + higherThanError; - assertions.getRegex().get(0).setDescription(desc); - } - assertions.setName("ErrorReportAssertion:" + item.getErrorCode()); - returnList.add(assertions); - } - } catch (Exception e) { - LogUtil.error(e); - } - } - }); - return returnList; - } -} diff --git a/framework/sdk-parent/jmeter/src/main/java/io/metersphere/dto/ErrorReportAssertionResult.java b/framework/sdk-parent/jmeter/src/main/java/io/metersphere/dto/ErrorReportAssertionResult.java deleted file mode 100644 index 1f40de37f9..0000000000 --- a/framework/sdk-parent/jmeter/src/main/java/io/metersphere/dto/ErrorReportAssertionResult.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.metersphere.dto; - -import lombok.Data; - -@Data -public class ErrorReportAssertionResult extends ResponseAssertionResult{ - private String errorReportMessage; - - public ErrorReportAssertionResult(String errorReportMessage){ - this.errorReportMessage = errorReportMessage; - this.setMessage(errorReportMessage); - } -} diff --git a/framework/sdk-parent/jmeter/src/main/java/io/metersphere/dto/RequestResult.java b/framework/sdk-parent/jmeter/src/main/java/io/metersphere/dto/RequestResult.java index daf37b31bc..d886698de7 100644 --- a/framework/sdk-parent/jmeter/src/main/java/io/metersphere/dto/RequestResult.java +++ b/framework/sdk-parent/jmeter/src/main/java/io/metersphere/dto/RequestResult.java @@ -53,4 +53,6 @@ public class RequestResult { this.passAssertions++; } + private String fakeErrorMessage; + } diff --git a/framework/sdk-parent/jmeter/src/main/java/io/metersphere/jmeter/JMeterBase.java b/framework/sdk-parent/jmeter/src/main/java/io/metersphere/jmeter/JMeterBase.java index 98ad0feebf..f82cfd176a 100644 --- a/framework/sdk-parent/jmeter/src/main/java/io/metersphere/jmeter/JMeterBase.java +++ b/framework/sdk-parent/jmeter/src/main/java/io/metersphere/jmeter/JMeterBase.java @@ -85,6 +85,7 @@ public class JMeterBase { requestResult.setSuccess(result.isSuccessful()); requestResult.setError(result.getErrorCount()); requestResult.setScenario(result.getScenario()); + requestResult.setFakeErrorMessage(result.getFakeError()); if (result instanceof HTTPSampleResult) { HTTPSampleResult res = (HTTPSampleResult) result; requestResult.setCookies(res.getCookies()); @@ -143,13 +144,7 @@ public class JMeterBase { } private static ResponseAssertionResult getResponseAssertionResult(AssertionResult assertionResult) { - ResponseAssertionResult responseAssertionResult = null; - - if (StringUtils.startsWith(assertionResult.getName(), "ErrorReportAssertion")) { - responseAssertionResult = new ErrorReportAssertionResult(assertionResult.getFailureMessage()); - } else { - responseAssertionResult = new ResponseAssertionResult(); - } + ResponseAssertionResult responseAssertionResult = new ResponseAssertionResult(); responseAssertionResult.setName(assertionResult.getName()); if (StringUtils.isNotEmpty(assertionResult.getName()) && assertionResult.getName().indexOf("split==") != -1) { diff --git a/framework/sdk-parent/jmeter/src/main/java/org/apache/jmeter/samplers/SampleResult.java b/framework/sdk-parent/jmeter/src/main/java/org/apache/jmeter/samplers/SampleResult.java index 8742f6748d..f2cd19ac36 100644 --- a/framework/sdk-parent/jmeter/src/main/java/org/apache/jmeter/samplers/SampleResult.java +++ b/framework/sdk-parent/jmeter/src/main/java/org/apache/jmeter/samplers/SampleResult.java @@ -44,7 +44,6 @@ import java.util.concurrent.TimeUnit; /** * This is a nice packaging for the various information returned from taking a * sample of an entry. - * */ public class SampleResult implements Serializable, Cloneable, Searchable { @@ -143,6 +142,8 @@ public class SampleResult implements Serializable, Cloneable, Searchable { private static final String NULL_FILENAME = "NULL"; + public static final String MS_FAKE_ERROR = "MS-FAKE-ERROR"; + static { if (START_TIMESTAMP) { log.info("Note: Sample TimeStamps are START times"); @@ -208,10 +209,14 @@ public class SampleResult implements Serializable, Cloneable, Searchable { private String label = "";// Never return null - /** Filename used by ResultSaver */ + /** + * Filename used by ResultSaver + */ private String resultFileName = ""; - /** The data used by the sampler */ + /** + * The data used by the sampler + */ private String samplerData; private String threadName = ""; // Never return null @@ -234,13 +239,21 @@ public class SampleResult implements Serializable, Cloneable, Searchable { private long idleTime = 0;// Allow for non-sample time - /** Start of pause (if any) */ + /** + * Start of pause (if any) + */ private long pauseTime = 0; private List assertionResults; private List subResults; + private String fakeError; + + public String getFakeError() { + return this.fakeError; + } + /** * The data type of the sample * @@ -266,25 +279,39 @@ public class SampleResult implements Serializable, Cloneable, Searchable { private String contentType = ""; // e.g. text/html; charset=utf-8 - /** elapsed time */ + /** + * elapsed time + */ private long elapsedTime = 0; - /** time to first response */ + /** + * time to first response + */ private long latency = 0; - /** time to end connecting */ + /** + * time to end connecting + */ private long connectTime = 0; - /** Way to signal what to do on Test */ + /** + * Way to signal what to do on Test + */ private TestLogicalAction testLogicalAction = TestLogicalAction.CONTINUE; - /** Should thread terminate? */ + /** + * Should thread terminate? + */ private boolean stopThread = false; - /** Should test terminate? */ + /** + * Should test terminate? + */ private boolean stopTest = false; - /** Should test terminate abruptly? */ + /** + * Should test terminate abruptly? + */ private boolean stopTestNow = false; private int sampleCount = 1; @@ -295,10 +322,14 @@ public class SampleResult implements Serializable, Cloneable, Searchable { private long bodySize = 0; - /** Currently active threads in this thread group */ + /** + * Currently active threads in this thread group + */ private volatile int groupThreads = 0; - /** Currently active threads in all thread groups */ + /** + * Currently active threads in all thread groups + */ private volatile int allThreads = 0; private final long nanoTimeOffset; @@ -342,6 +373,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { this.samplerId = sampler.getPropertyAsString("MS-ID"); this.resourceId = sampler.getPropertyAsString("MS-RESOURCE-ID"); this.scenario = sampler.getPropertyAsString("MS-SCENARIO"); + this.fakeError = sampler.getPropertyAsString(MS_FAKE_ERROR); } } @@ -399,13 +431,11 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Create a sample with a specific elapsed time but don't allow the times to * be changed later - * + *

* (only used by HTTPSampleResult) * - * @param elapsed - * time - * @param atend - * create the sample finishing now, else starting now + * @param elapsed time + * @param atend create the sample finishing now, else starting now */ protected SampleResult(long elapsed, boolean atend) { this(); @@ -442,8 +472,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { } /** - * @param propertiesToSave - * The propertiesToSave to set. + * @param propertiesToSave The propertiesToSave to set. */ public void setSaveConfig(SampleSaveConfiguration propertiesToSave) { this.saveConfig = propertiesToSave; @@ -460,13 +489,11 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Create a sample with specific start and end times for test purposes, but * don't allow the times to be changed later - * + *

* (used by StatVisualizerModel.Test) * - * @param start - * start time in milliseconds since unix epoch - * @param end - * end time in milliseconds since unix epoch + * @param start start time in milliseconds since unix epoch + * @param end end time in milliseconds since unix epoch * @return sample with given start and end time */ public static SampleResult createTestSample(long start, long end) { @@ -480,8 +507,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * Create a sample with a specific elapsed time for test purposes, but don't * allow the times to be changed later * - * @param elapsed - * - desired elapsed time in milliseconds + * @param elapsed - desired elapsed time in milliseconds * @return sample that starts 'now' and ends elapsed milliseconds later */ public static SampleResult createTestSample(long elapsed) { @@ -497,9 +523,8 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * Helper method to get 1 ms resolution timing. * * @return the current time in milliseconds - * @throws RuntimeException - * when useNanoTime is true but - * nanoTimeOffset is not set + * @throws RuntimeException when useNanoTime is true but + * nanoTimeOffset is not set */ public long currentTimeInMillis() { if (useNanoTime) { @@ -527,13 +552,10 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * For use by SaveService only. * - * @param stamp - * this may be a start time or an end time (both in milliseconds) - * @param elapsed - * time in milliseconds - * @throws RuntimeException - * when startTime or endTime has been - * set already + * @param stamp this may be a start time or an end time (both in milliseconds) + * @param elapsed time in milliseconds + * @throws RuntimeException when startTime or endTime has been + * set already */ public void setStampAndTime(long stamp, long elapsed) { if (startTime != 0 || endTime != 0) { @@ -558,7 +580,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Set response code to OK, i.e. "200" - * */ public void setResponseCodeOK() { responseCode = OK_CODE; @@ -609,10 +630,9 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Get the sample timestamp, which may be either the start time or the end time. * + * @return timeStamp in milliseconds * @see #getStartTime() * @see #getEndTime() - * - * @return timeStamp in milliseconds */ public long getTimeStamp() { return timeStamp; @@ -652,7 +672,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * Gets the assertion results associated with this sample. * * @return an array containing the assertion results for this sample. - * Returns empty array if there are no assertion results. + * Returns empty array if there are no assertion results. */ public AssertionResult[] getAssertionResults() { if (assertionResults == null) { @@ -664,8 +684,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Add a subresult and adjust the parent byte count and end-time. * - * @param subResult - * the {@link SampleResult} to be added + * @param subResult the {@link SampleResult} to be added */ public void addSubResult(SampleResult subResult) { addSubResult(subResult, isRenameSampleLabel()); @@ -673,6 +692,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * see https://bz.apache.org/bugzilla/show_bug.cgi?id=63055 + * * @return true if TestPlan is in functional mode or property subresults.disable_renaming is true */ public static boolean isRenameSampleLabel() { @@ -682,8 +702,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Add a subresult and adjust the parent byte count and end-time. * - * @param subResult - * the {@link SampleResult} to be added + * @param subResult the {@link SampleResult} to be added * @param renameSubResults boolean do we rename subResults based on position */ public void addSubResult(SampleResult subResult, boolean renameSubResults) { @@ -711,8 +730,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Add a subresult to the collection without updating any parent fields. * - * @param subResult - * the {@link SampleResult} to be added + * @param subResult the {@link SampleResult} to be added */ public void addRawSubResult(SampleResult subResult) { storeSubResult(subResult, isRenameSampleLabel()); @@ -721,8 +739,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Add a subresult to the collection without updating any parent fields. * - * @param subResult - * the {@link SampleResult} to be added + * @param subResult the {@link SampleResult} to be added */ private void addRawSubResult(SampleResult subResult, boolean renameSubResults) { storeSubResult(subResult, renameSubResults); @@ -735,8 +752,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * addSubResult(SampleResult)}, except that the fields don't need to be * accumulated * - * @param subResult - * the {@link SampleResult} to be added + * @param subResult the {@link SampleResult} to be added */ public void storeSubResult(SampleResult subResult) { storeSubResult(subResult, isRenameSampleLabel()); @@ -749,8 +765,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * addSubResult(SampleResult)}, except that the fields don't need to be * accumulated * - * @param subResult - * the {@link SampleResult} to be added + * @param subResult the {@link SampleResult} to be added * @param renameSubResults boolean do we rename subResults based on position */ public void storeSubResult(SampleResult subResult, boolean renameSubResults) { @@ -768,7 +783,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * Gets the subresults associated with this sample. * * @return an array containing the subresults for this sample. Returns an - * empty array if there are no subresults. + * empty array if there are no subresults. */ public SampleResult[] getSubResults() { if (subResults == null) { @@ -779,12 +794,11 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Sets the responseData attribute of the SampleResult object. - * + *

* If the parameter is null, then the responseData is set to an empty byte array. * This ensures that getResponseData() can never be null. * - * @param response - * the new responseData value + * @param response the new responseData value */ public void setResponseData(byte[] response) { responseDataAsString = null; @@ -795,9 +809,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * Sets the responseData attribute of the SampleResult object. * Should only be called after setting the dataEncoding (if necessary) * - * @param response - * the new responseData value (String) - * + * @param response the new responseData value (String) * @deprecated - only intended for use from BeanShell code */ @Deprecated @@ -816,7 +828,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * * @param response the new responseData value (String) * @param encoding the encoding to set and then use (if null, use platform default) - * */ public void setResponseData(final String response, final String encoding) { responseDataAsString = null; @@ -837,9 +848,10 @@ public class SampleResult implements Serializable, Cloneable, Searchable { *

* Note that some samplers may not store all the data, in which case * getResponseData().length will be incorrect. - * + *

* Instead, always use {@link #getBytes()} to obtain the sample result byte count. *

+ * * @return the responseData value (cannot be null) */ public byte[] getResponseData() { @@ -875,7 +887,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * Get the time it took this sample to occur. * * @return elapsed time in milliseconds - * */ public long getTime() { return elapsedTime; @@ -887,6 +898,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Sets the data type of the sample. + * * @param dataType String containing {@link #BINARY} or {@link #TEXT} * @see #BINARY * @see #TEXT @@ -909,9 +921,9 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Extract and save the DataEncoding and DataType from the parameter provided. * Does not save the full content Type. - * @see #setContentType(String) which should be used to save the full content-type string * * @param ct - content type (may be null) + * @see #setContentType(String) which should be used to save the full content-type string */ public void setEncodingAndType(String ct) { if (ct != null) { @@ -966,8 +978,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Sets the successful attribute of the SampleResult object. * - * @param success - * the new successful value + * @param success the new successful value */ public void setSuccessful(boolean success) { this.success = success; @@ -1007,6 +1018,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Returns the dataEncoding. May be null or the empty String. + * * @return the value of the dataEncoding */ public String getDataEncodingNoDefault() { @@ -1016,8 +1028,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Sets the dataEncoding. * - * @param dataEncoding - * the dataEncoding to set, e.g. ISO-8895-1, UTF-8 + * @param dataEncoding the dataEncoding to set, e.g. ISO-8895-1, UTF-8 */ public void setDataEncoding(String dataEncoding) { this.dataEncoding = dataEncoding; @@ -1072,7 +1083,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * @param string - - * request headers + * request headers */ public void setRequestHeaders(String string) { requestHeaders = string; @@ -1080,7 +1091,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * @param string - - * response headers + * response headers */ public void setResponseHeaders(String string) { responseHeaders = string; @@ -1095,6 +1106,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Get the media type from the Content Type + * * @return the media type - e.g. text/html (without charset, if any) */ public String getMediaType() { @@ -1103,9 +1115,9 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Stores the content-type string, e.g. text/xml; charset=utf-8 - * @see #setEncodingAndType(String) which can be used to extract the charset. * * @param string the content-type to be set + * @see #setEncodingAndType(String) which can be used to extract the charset. */ public void setContentType(String string) { contentType = string; @@ -1160,6 +1172,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Set idle time pause. * For use by SampleResultConverter/CSVSaveService. + * * @param idle long */ public void setIdleTime(long idle) { @@ -1173,7 +1186,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Record the start time of a sample - * */ public void sampleStart() { if (startTime == 0) { @@ -1185,7 +1197,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Record the end time of a sample and calculate the elapsed time - * */ public void sampleEnd() { if (endTime == 0) { @@ -1197,7 +1208,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Pause a sample - * */ public void samplePause() { if (pauseTime != 0) { @@ -1208,7 +1218,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Resume a sample - * */ public void sampleResume() { if (pauseTime == 0) { @@ -1221,9 +1230,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * When a Sampler is working as a monitor * - * @param monitor - * flag whether this sampler is working as a monitor - * + * @param monitor flag whether this sampler is working as a monitor * @deprecated since 3.2 NOOP */ @Deprecated @@ -1265,7 +1272,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * Returns the count of errors. * * @return 0 - or 1 if the sample failed - * + *

* TODO do we need allow for nested samples? */ public int getErrorCount() { @@ -1299,8 +1306,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * still want to calculate the throughput. The bytes are the bytes of the * response data. * - * @param length - * the number of bytes of the response data for this sample + * @param length the number of bytes of the response data for this sample */ public void setBytes(long length) { bytes = length; @@ -1311,8 +1317,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { * still want to calculate the throughput. The bytes are the bytes of the * response data. * - * @param length - * the number of bytes of the response data for this sample + * @param length the number of bytes of the response data for this sample * @deprecated use setBytes(long) */ @Deprecated @@ -1321,7 +1326,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { } /** - * * @param sentBytesCount long sent bytes */ public void setSentBytes(long sentBytesCount) { @@ -1365,7 +1369,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Set the time to the first response - * */ public void latencyEnd() { latency = currentTimeInMillis() - startTime - idleTime; @@ -1374,8 +1377,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * This is only intended for use by SampleResultConverter! * - * @param latency - * The latency to set. + * @param latency The latency to set. */ public void setLatency(long latency) { this.latency = latency; @@ -1407,8 +1409,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * This is only intended for use by SampleResultConverter! * - * @param timeStamp - * The timeStamp to set. + * @param timeStamp The timeStamp to set. */ public void setTimeStamp(long timeStamp) { this.timeStamp = timeStamp; @@ -1440,8 +1441,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { } /** - * @param parent - * The parent to set. + * @param parent The parent to set. */ public void setParent(SampleResult parent) { this.parent = parent; @@ -1472,6 +1472,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { } // Bug 47394 + /** * Allow custom SampleSenders to drop unwanted assertionResults */ @@ -1489,8 +1490,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { /** * Set the headers size in bytes * - * @param size - * the number of bytes of the header + * @param size the number of bytes of the header */ public void setHeadersSize(int size) { this.headersSize = size; @@ -1549,7 +1549,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable { public void run() { // Wait longer than a clock pulse (generally 10-15ms) getOffset(30L); // Catch an early clock pulse to reduce slop. - while(true) { + while (true) { getOffset(NANOTHREAD_SLEEP); // Can now afford to wait a bit longer between checks } } @@ -1577,12 +1577,12 @@ public class SampleResult implements Serializable, Cloneable, Searchable { } /** - * @deprecated use SampleResult#setTestLogicalAction(TestLogicalAction) * @param startNextThreadLoop the startNextLoop to set + * @deprecated use SampleResult#setTestLogicalAction(TestLogicalAction) */ @Deprecated public void setStartNextThreadLoop(boolean startNextThreadLoop) { - if(startNextThreadLoop) { + if (startNextThreadLoop) { testLogicalAction = TestLogicalAction.START_NEXT_ITERATION_OF_THREAD; } else { testLogicalAction = TestLogicalAction.CONTINUE;