refactor(接口测试): 优化误报逻辑

--story=1011259 --user=王孝刚
【Bug转需求】误报机制和失败重试机制冲突导致问题“【测试跟踪】测试计划失败重试,所有的接口 / 场景步骤 都重试了“
https://www.tapd.cn/55049933/s/1344889
This commit is contained in:
wxg0103 2023-03-02 13:45:06 +08:00 committed by fit2-zhao
parent 1db32ded82
commit 5561193ae9
19 changed files with 290 additions and 536 deletions

View File

@ -11,12 +11,12 @@ import java.util.List;
@Getter @Getter
@Setter @Setter
public class ErrorReportLibraryParseDTO { public class FakeErrorLibraryDTO {
private List<String> errorCodeList; private List<String> errorCodeList;
private RequestResult result; private RequestResult result;
private String requestStatus; private String requestStatus;
public ErrorReportLibraryParseDTO() { public FakeErrorLibraryDTO() {
this.errorCodeList = new ArrayList<>(); this.errorCodeList = new ArrayList<>();
} }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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.ElementConstants;
import io.metersphere.commons.constants.PropertyConstant; import io.metersphere.commons.constants.PropertyConstant;
import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.ErrorReportLibraryUtil;
import io.metersphere.plugin.core.MsParameter; import io.metersphere.plugin.core.MsParameter;
import io.metersphere.plugin.core.MsTestElement; import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.definition.ApiDefinitionService; import io.metersphere.service.definition.ApiDefinitionService;
@ -105,24 +104,19 @@ public class MsAssertions extends MsTestElement {
} }
private ResponseAssertion responseAssertion(MsAssertionRegex assertionRegex) { private ResponseAssertion responseAssertion(MsAssertionRegex assertionRegex) {
ResponseAssertion assertion = null; ResponseAssertion assertion = new ResponseAssertion();
boolean isErrorReportAssertion = false; assertion.setEnabled(this.isEnable());
if (StringUtils.startsWith(this.getName(), "ErrorReportAssertion:")) { if (StringUtils.isNotEmpty(assertionRegex.getDescription())) {
assertion = new ErrorReportAssertion(); assertion.setName(StringUtils.join(this.getName(), delimiter, assertionRegex.getDescription()));
isErrorReportAssertion = true;
} else { } else {
assertion = new ResponseAssertion(); assertion.setName(StringUtils.join(this.getName(), delimiter, "AssertionRegex"));
} }
assertion.setEnabled(this.isEnable()); assertion.setEnabled(this.isEnable());
if (StringUtils.isNotEmpty(assertionRegex.getDescription())) { if (StringUtils.isNotEmpty(assertionRegex.getDescription())) {
if (!isErrorReportAssertion) { assertion.setName(StringUtils.join(this.getName(), delimiter, assertionRegex.getDescription()));
//正常断言要在desc增加匹配信息用于接受结果后和误报断言进行匹配
assertionRegex.setDescription(assertionRegex.getDescription() + ErrorReportLibraryUtil.ASSERTION_CONTENT_REGEX_DELIMITER + assertionRegex.getSubject() + ":" + assertionRegex.getExpression());
}
assertion.setName(this.getName() + delimiter + assertionRegex.getDescription());
} else { } 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.TEST_CLASS, ResponseAssertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("AssertionGui")); assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("AssertionGui"));

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.definition.request.sampler; 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.ElementUtil;
import io.metersphere.api.dto.definition.request.ParameterConfig; import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.definition.request.assertions.MsAssertions; import io.metersphere.api.dto.definition.request.assertions.MsAssertions;
@ -31,12 +32,14 @@ import io.metersphere.service.definition.ApiTestCaseService;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.KeystoreConfig; import org.apache.jmeter.config.KeystoreConfig;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.http.util.HTTPArgument; import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.protocol.http.util.HTTPConstants; import org.apache.jmeter.protocol.http.util.HTTPConstants;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.save.SaveService; import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
@ -213,10 +216,8 @@ public class MsHTTPSamplerProxy extends MsTestElement {
//根据配置增加全局前后至脚本 //根据配置增加全局前后至脚本
JMeterScriptUtil.setScriptByHttpConfig(httpConfig, httpSamplerTree, config, useEnvironment, this.getEnvironmentId(), false); JMeterScriptUtil.setScriptByHttpConfig(httpConfig, httpSamplerTree, config, useEnvironment, this.getEnvironmentId(), false);
//增加误报断言 //增加误报断言
if (!config.isOperating() && CollectionUtils.isNotEmpty(httpConfig.getErrorReportAssertions())) { if (ObjectUtils.isNotEmpty(httpConfig.getFakeError())) {
for (MsAssertions assertion : httpConfig.getErrorReportAssertions()) { sampler.setProperty(SampleResult.MS_FAKE_ERROR, JSONUtil.toJSONString(httpConfig.getFakeError()));
assertion.toHashTree(httpSamplerTree, assertion.getHashTree(), config);
}
} }
if (CollectionUtils.isNotEmpty(httpConfig.getAssertions())) { if (CollectionUtils.isNotEmpty(httpConfig.getAssertions())) {
for (MsAssertions assertion : httpConfig.getAssertions()) { for (MsAssertions assertion : httpConfig.getAssertions()) {
@ -335,7 +336,11 @@ public class MsHTTPSamplerProxy extends MsTestElement {
httpConfig.setAssertions(ElementUtil.copyAssertion(environmentConfig.getAssertions())); httpConfig.setAssertions(ElementUtil.copyAssertion(environmentConfig.getAssertions()));
} }
if (environmentConfig.isUseErrorCode()) { 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; return httpConfig;
} }

View File

@ -126,7 +126,7 @@ public class MsJDBCSampler extends MsTestElement {
//添加csv //添加csv
ElementUtil.addApiVariables(config, tree, this.getProjectId()); 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(); String environmentId = this.getEnvironmentId();
if (environmentId == null) { if (environmentId == null) {

View File

@ -3,7 +3,6 @@ package io.metersphere.api.dto.definition.request.sampler;
import io.metersphere.api.dto.automation.TcpTreeTableDataStruct; import io.metersphere.api.dto.automation.TcpTreeTableDataStruct;
import io.metersphere.api.dto.definition.request.ElementUtil; import io.metersphere.api.dto.definition.request.ElementUtil;
import io.metersphere.api.dto.definition.request.ParameterConfig; 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.definition.request.processors.pre.MsJSR223PreProcessor;
import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
@ -140,13 +139,14 @@ public class MsTCPSampler extends MsTestElement {
ElementUtil.addApiVariables(config, tree, this.getProjectId()); ElementUtil.addApiVariables(config, tree, this.getProjectId());
final HashTree samplerHashTree = new ListedHashTree(); final HashTree samplerHashTree = new ListedHashTree();
samplerHashTree.add(tcpConfig()); samplerHashTree.add(tcpConfig());
tree.set(tcpSampler(config), samplerHashTree); TCPSampler tcpSampler = tcpSampler(config);
tree.set(tcpSampler, samplerHashTree);
setUserParameters(samplerHashTree); setUserParameters(samplerHashTree);
if (tcpPreProcessor != null && StringUtils.isNotBlank(tcpPreProcessor.getScript())) { if (tcpPreProcessor != null && StringUtils.isNotBlank(tcpPreProcessor.getScript())) {
samplerHashTree.add(tcpPreProcessor.getShellProcessor()); samplerHashTree.add(tcpPreProcessor.getShellProcessor());
} }
//增加误报全局断言 //增加误报全局断言
HashTreeUtil.addPositive(envConfig, samplerHashTree, config, this.getProjectId()); HashTreeUtil.addPositive(envConfig, samplerHashTree, config, this.getProjectId(), tcpSampler);
//处理全局前后置脚本(步骤内) //处理全局前后置脚本(步骤内)
String environmentId = this.getEnvironmentId(); String environmentId = this.getEnvironmentId();
if (environmentId == null) { if (environmentId == null) {
@ -154,15 +154,9 @@ public class MsTCPSampler extends MsTestElement {
} }
//根据配置将脚本放置在私有脚本之前 //根据配置将脚本放置在私有脚本之前
JMeterScriptUtil.setScriptByEnvironmentConfig(envConfig, samplerHashTree, GlobalScriptFilterRequest.TCP.name(), environmentId, config, false); JMeterScriptUtil.setScriptByEnvironmentConfig(envConfig, samplerHashTree, GlobalScriptFilterRequest.TCP.name(), environmentId, config, false);
HashTreeUtil hashTreeUtil = new HashTreeUtil();
if (CollectionUtils.isNotEmpty(hashTree)) { if (CollectionUtils.isNotEmpty(hashTree)) {
hashTree = ElementUtil.order(hashTree); hashTree = ElementUtil.order(hashTree);
EnvironmentConfig finalEnvConfig = envConfig;
hashTree.forEach(el -> { hashTree.forEach(el -> {
if (el instanceof MsAssertions && finalEnvConfig != null) {
//断言设置需要和全局断言误报进行去重
el = hashTreeUtil.duplicateRegexInAssertions(ElementUtil.copyAssertion(finalEnvConfig.getAssertions()), (MsAssertions) el);
}
el.toHashTree(samplerHashTree, el.getHashTree(), config); el.toHashTree(samplerHashTree, el.getHashTree(), config);
}); });
} }

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.scenario; 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.assertions.MsAssertions;
import io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor; import io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor;
import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor; import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor;
@ -30,6 +31,7 @@ public class HttpConfig {
private List<MsAssertions> assertions; private List<MsAssertions> assertions;
private List<MsAssertions> errorReportAssertions; private List<MsAssertions> errorReportAssertions;
private String description; private String description;
private FakeError fakeError;
public HttpConfig initHttpConfig(HttpConfigCondition configCondition) { public HttpConfig initHttpConfig(HttpConfigCondition configCondition) {

View File

@ -1,9 +1,11 @@
package io.metersphere.api.jmeter; package io.metersphere.api.jmeter;
import io.metersphere.api.dto.MsgDTO; import io.metersphere.api.dto.MsgDTO;
import io.metersphere.api.dto.RequestResultExpandDTO;
import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.KafkaTopicConstants; import io.metersphere.commons.constants.KafkaTopicConstants;
import io.metersphere.commons.utils.*; import io.metersphere.commons.utils.*;
import io.metersphere.dto.RequestResult;
import io.metersphere.service.ApiExecutionQueueService; import io.metersphere.service.ApiExecutionQueueService;
import io.metersphere.service.RedisTemplateService; import io.metersphere.service.RedisTemplateService;
import io.metersphere.service.TestResultService; import io.metersphere.service.TestResultService;
@ -42,6 +44,9 @@ public class MsKafkaListener {
@Resource @Resource
private RedisTemplateService redisTemplateService; private RedisTemplateService redisTemplateService;
@Resource
private ApiDefinitionEnvService apiDefinitionEnvService;
private final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( private final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
CORE_POOL_SIZE, CORE_POOL_SIZE,
MAX_POOL_SIZE, MAX_POOL_SIZE,
@ -76,9 +81,19 @@ public class MsKafkaListener {
LoggerUtil.info("接收到执行结果:", record.key()); LoggerUtil.info("接收到执行结果:", record.key());
if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) { if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) {
MsgDTO dto = JSONUtil.parseObject(record.value(), MsgDTO.class); MsgDTO dto = JSONUtil.parseObject(record.value(), MsgDTO.class);
if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name()) && dto.getContent().startsWith("result_")) { if (StringUtils.isNotBlank(dto.getContent()) && dto.getContent().startsWith("result_")) {
ApiDefinitionEnvService apiDefinitionEnvService = CommonBeanFactory.getBean(ApiDefinitionEnvService.class); String content = dto.getContent().substring(7);
apiDefinitionEnvService.setEnvAndPoolName(dto); 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); WebSocketUtil.sendMessageSingle(dto);
} }

View File

@ -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<ResponseAssertionResult> errorReportAssertionList = assertionParserResult.errorReportAssertionList;
Map<String, List<ResponseAssertionResult>> successAssertionMessageMap = assertionParserResult.successAssertionMessageMap;
Map<String, List<ResponseAssertionResult>> errorAssertionMessageMap = assertionParserResult.errorAssertionMessageMap;
boolean higherThanSuccess = assertionParserResult.higherThanSuccess;
boolean higherThanError = assertionParserResult.higherThanError;
/*
默认优先级(higherThanSuccess=false,higherThanError=false):
1.如果一个请求同时有 成功误报失败 三个断言断言规则不一样那么请求最终结果处理成失败展示匹配到的误报断言
2.如果一个请求同时有 失败误报断言规则不一样那么处理成失败展示匹配到的误报断言
3.如果一个请求同时有 成功误报断言规则不一样那么处理成误报展示匹配到的误报断言
4.如果一个请求同时有 成功误报断言规则一样那么处理成成功不展示匹配到的误报断言
能和误报断言匹配上的成功断言都转为成功断言
如果含有失败断言则结果处理成失败展示剩下的误报断言
如果还有误报断言那么结果处理成误报展示剩下的误报断言
最后就是成功
如果配置的误报与失败的优先级 / 误报与成功的优先级 与上述出现偏差则对应的结果也会改变
*/
if (CollectionUtils.isNotEmpty(errorReportAssertionList)) {
List<ResponseAssertionResult> unMatchErrorReportAssertions = new ArrayList<>();
Map<String, List<ResponseAssertionResult>> 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<ResponseAssertionResult> 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<ResponseAssertionResult> allAssertions = result.getResponseResult().getAssertions();
List<ResponseAssertionResult> 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<String, List<ResponseAssertionResult>> errorReportAssertionMap,
Map<String, List<ResponseAssertionResult>> successAssertionMap,
Map<String, List<ResponseAssertionResult>> errorAssertionMap,
boolean resultIsSuccess, boolean higherThanSuccess, boolean higherThanError) {
AssertionFilterResult result = new AssertionFilterResult();
if (MapUtils.isNotEmpty(errorReportAssertionMap)) {
List<ResponseAssertionResult> removedSuccessList = removeAssertions(errorReportAssertionMap, successAssertionMap, higherThanSuccess);
if (CollectionUtils.isNotEmpty(removedSuccessList)) {
result.filteredSuccessAssertionList.addAll(removedSuccessList);
}
List<ResponseAssertionResult> removedErrorList = removeAssertions(errorReportAssertionMap, errorAssertionMap, higherThanError);
if (CollectionUtils.isNotEmpty(removedErrorList)) {
if (higherThanError) {
result.filteredErrorAssertionList.addAll(removedErrorList);
} else {
result.filteredSuccessAssertionList.addAll(removedErrorList);
}
}
for (List<ResponseAssertionResult> 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<ResponseAssertionResult> removeAssertions(Map<String, List<ResponseAssertionResult>> firstMap, Map<String, List<ResponseAssertionResult>> secondMap, boolean removeDataInSecondMap) {
List<ResponseAssertionResult> 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<ResponseAssertionResult> 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<ResponseAssertionResult> 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<ResponseAssertionResult> list = new ArrayList<>();
list.add(assertion);
result.errorAssertionMessageMap.put(regexString, list);
}
}
}
}
}
return result;
}
}
class AssertionParserResult {
boolean higherThanSuccess = false;
boolean higherThanError = false;
List<ResponseAssertionResult> errorReportAssertionList = new ArrayList<>();
Map<String, List<ResponseAssertionResult>> successAssertionMessageMap = new HashMap<>();
Map<String, List<ResponseAssertionResult>> errorAssertionMessageMap = new HashMap<>();
}
class AssertionFilterResult {
String requestStatus;
List<String> errorCodeList = new ArrayList<>();
List<ResponseAssertionResult> filteredSuccessAssertionList = new ArrayList<>();
List<ResponseAssertionResult> filteredErrorAssertionList = new ArrayList<>();
}

View File

@ -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<ErrorReportLibraryWithBLOBs> bloBs = service.selectByExampleWithBLOBs(example);
List<MsRegexDTO> regexList = new ArrayList<>();
bloBs.forEach(item -> {
if (StringUtils.isNotEmpty(item.getContent())) {
try {
Map<String, Object> 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<String> 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);
}
}

View File

@ -1,9 +1,9 @@
package io.metersphere.commons.utils; package io.metersphere.commons.utils;
import io.metersphere.api.dto.RunningParamKeys; 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.ElementUtil;
import io.metersphere.api.dto.definition.request.ParameterConfig; 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.definition.request.assertions.MsAssertions;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
@ -15,7 +15,6 @@ import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.environment.service.BaseEnvironmentService; import io.metersphere.environment.service.BaseEnvironmentService;
import io.metersphere.metadata.service.FileMetadataService; import io.metersphere.metadata.service.FileMetadataService;
import io.metersphere.request.BodyFile; import io.metersphere.request.BodyFile;
import io.metersphere.service.ExtErrorReportLibraryService;
import io.metersphere.utils.LoggerUtil; import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils; 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.extractor.JSR223PostProcessor;
import org.apache.jmeter.modifiers.JSR223PreProcessor; import org.apache.jmeter.modifiers.JSR223PreProcessor;
import org.apache.jmeter.protocol.java.sampler.JSR223Sampler; 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.apache.jorphan.collections.HashTree;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
@ -182,53 +185,19 @@ public class HashTreeUtil {
return execute_env_param_dataMap; return execute_env_param_dataMap;
} }
public MsAssertions duplicateRegexInAssertions(List<MsAssertions> compareList, MsAssertions target) { public static void addPositive(EnvironmentConfig envConfig, HashTree samplerHashTree, ParameterConfig config, String projectId, AbstractTestElement sample) {
if (target != null && CollectionUtils.isNotEmpty(target.getRegex())) {
List<MsAssertionRegex> compareRegexList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(compareList)) {
for (MsAssertions assertions : compareList) {
if (assertions != null && CollectionUtils.isNotEmpty(assertions.getRegex())) {
compareRegexList.addAll(assertions.getRegex());
}
}
}
List<MsAssertionRegex> 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<MsAssertions> 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) {
if (envConfig == null) { if (envConfig == null) {
return; return;
} }
if (!config.isOperating() && envConfig.isUseErrorCode()) { if (!config.isOperating() && envConfig.isUseErrorCode()) {
List<MsAssertions> errorReportAssertion = HashTreeUtil.getErrorReportByProjectId(projectId, envConfig.isHigherThanSuccess(), envConfig.isHigherThanError()); FakeError fakeError = new FakeError();
for (MsAssertions assertion : errorReportAssertion) { fakeError.setHigherThanError(envConfig.isHigherThanError());
assertion.toHashTree(samplerHashTree, assertion.getHashTree(), config); 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())) { if (CollectionUtils.isNotEmpty(envConfig.getAssertions())) {

View File

@ -3,7 +3,7 @@ package io.metersphere.commons.utils;
import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper; 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.api.dto.RequestResultExpandDTO;
import io.metersphere.commons.enums.ApiReportStatus; import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.enums.ResponseFormatType; import io.metersphere.commons.enums.ResponseFormatType;
@ -24,7 +24,7 @@ public class ResponseUtil {
//根据responseheader的信息来处理返回数据 //根据responseheader的信息来处理返回数据
baseResult = ResponseUtil.parseResponseBodyByHeader(baseResult); baseResult = ResponseUtil.parseResponseBodyByHeader(baseResult);
//解析是否含有误报库信息 //解析是否含有误报库信息
ErrorReportLibraryParseDTO errorCodeDTO = ErrorReportLibraryUtil.parseAssertions(baseResult); FakeErrorLibraryDTO errorCodeDTO = FakeErrorParse.parseAssertions(baseResult);
RequestResult requestResult = errorCodeDTO.getResult(); RequestResult requestResult = errorCodeDTO.getResult();
RequestResultExpandDTO expandDTO = new RequestResultExpandDTO(); RequestResultExpandDTO expandDTO = new RequestResultExpandDTO();
BeanUtils.copyBean(expandDTO, requestResult); BeanUtils.copyBean(expandDTO, requestResult);

View File

@ -2,8 +2,7 @@ package io.metersphere.commons.utils;
import io.metersphere.api.dto.ApiScenarioReportBaseInfoDTO; import io.metersphere.api.dto.ApiScenarioReportBaseInfoDTO;
import io.metersphere.api.dto.ErrorReportLibraryParseDTO; import io.metersphere.api.dto.FakeErrorLibraryDTO;
import io.metersphere.base.domain.ApiScenarioReportResult;
import io.metersphere.base.domain.ApiScenarioReportResultWithBLOBs; import io.metersphere.base.domain.ApiScenarioReportResultWithBLOBs;
import io.metersphere.commons.enums.ApiReportStatus; import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.dto.RequestResult; import io.metersphere.dto.RequestResult;
@ -12,27 +11,13 @@ import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
public class ResultConversionUtil { public class ResultConversionUtil {
public static List<ApiScenarioReportResult> getApiScenarioReportResults(String reportId, List<RequestResult> requestResults) {
//解析误报内容
List<ApiScenarioReportResult> 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) { public static ApiScenarioReportResultWithBLOBs getApiScenarioReportResult(String reportId, RequestResult requestResult) {
//解析误报内容 //解析误报内容
ErrorReportLibraryParseDTO errorCodeDTO = ErrorReportLibraryUtil.parseAssertions(requestResult); FakeErrorLibraryDTO errorCodeDTO = FakeErrorParse.parseAssertions(requestResult);
RequestResult result = errorCodeDTO.getResult(); RequestResult result = errorCodeDTO.getResult();
String resourceId = result.getResourceId(); String resourceId = result.getResourceId();

View File

@ -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<MsAssertions> getAssertionByProjectIdAndStatusIsOpen(String projectId, boolean higherThanSuccess, boolean higherThanError) {
ErrorReportLibraryService service = CommonBeanFactory.getBean(ErrorReportLibraryService.class);
if (service == null) {
return new ArrayList<>();
}
List<MsAssertions> returnList = new ArrayList<>();
ErrorReportLibraryExample example = new ErrorReportLibraryExample();
example.createCriteria().andProjectIdEqualTo(projectId).andStatusEqualTo(true);
List<ErrorReportLibraryWithBLOBs> bloBs = service.selectByExampleWithBLOBs(example);
bloBs.forEach(item -> {
if (StringUtils.isNotEmpty(item.getContent())) {
try {
MsAssertions assertions = new MsAssertions();
Map<String, Object> assertionMap = JSON.parseObject(item.getContent(), Map.class);
if (assertionMap != null) {
MsAssertionDuration duration = JSONUtil.parseObject(JSONUtil.toJSONString(assertionMap.get("duration")), MsAssertionDuration.class);
List<MsAssertionRegex> 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;
}
}

View File

@ -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);
}
}

View File

@ -53,4 +53,6 @@ public class RequestResult {
this.passAssertions++; this.passAssertions++;
} }
private String fakeErrorMessage;
} }

View File

@ -85,6 +85,7 @@ public class JMeterBase {
requestResult.setSuccess(result.isSuccessful()); requestResult.setSuccess(result.isSuccessful());
requestResult.setError(result.getErrorCount()); requestResult.setError(result.getErrorCount());
requestResult.setScenario(result.getScenario()); requestResult.setScenario(result.getScenario());
requestResult.setFakeErrorMessage(result.getFakeError());
if (result instanceof HTTPSampleResult) { if (result instanceof HTTPSampleResult) {
HTTPSampleResult res = (HTTPSampleResult) result; HTTPSampleResult res = (HTTPSampleResult) result;
requestResult.setCookies(res.getCookies()); requestResult.setCookies(res.getCookies());
@ -143,13 +144,7 @@ public class JMeterBase {
} }
private static ResponseAssertionResult getResponseAssertionResult(AssertionResult assertionResult) { private static ResponseAssertionResult getResponseAssertionResult(AssertionResult assertionResult) {
ResponseAssertionResult responseAssertionResult = null; ResponseAssertionResult responseAssertionResult = new ResponseAssertionResult();
if (StringUtils.startsWith(assertionResult.getName(), "ErrorReportAssertion")) {
responseAssertionResult = new ErrorReportAssertionResult(assertionResult.getFailureMessage());
} else {
responseAssertionResult = new ResponseAssertionResult();
}
responseAssertionResult.setName(assertionResult.getName()); responseAssertionResult.setName(assertionResult.getName());
if (StringUtils.isNotEmpty(assertionResult.getName()) && assertionResult.getName().indexOf("split==") != -1) { if (StringUtils.isNotEmpty(assertionResult.getName()) && assertionResult.getName().indexOf("split==") != -1) {

View File

@ -44,7 +44,6 @@ import java.util.concurrent.TimeUnit;
/** /**
* This is a nice packaging for the various information returned from taking a * This is a nice packaging for the various information returned from taking a
* sample of an entry. * sample of an entry.
*
*/ */
public class SampleResult implements Serializable, Cloneable, Searchable { 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"; private static final String NULL_FILENAME = "NULL";
public static final String MS_FAKE_ERROR = "MS-FAKE-ERROR";
static { static {
if (START_TIMESTAMP) { if (START_TIMESTAMP) {
log.info("Note: Sample TimeStamps are START times"); 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 private String label = "";// Never return null
/** Filename used by ResultSaver */ /**
* Filename used by ResultSaver
*/
private String resultFileName = ""; private String resultFileName = "";
/** The data used by the sampler */ /**
* The data used by the sampler
*/
private String samplerData; private String samplerData;
private String threadName = ""; // Never return null 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 private long idleTime = 0;// Allow for non-sample time
/** Start of pause (if any) */ /**
* Start of pause (if any)
*/
private long pauseTime = 0; private long pauseTime = 0;
private List<AssertionResult> assertionResults; private List<AssertionResult> assertionResults;
private List<SampleResult> subResults; private List<SampleResult> subResults;
private String fakeError;
public String getFakeError() {
return this.fakeError;
}
/** /**
* The data type of the sample * 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 private String contentType = ""; // e.g. text/html; charset=utf-8
/** elapsed time */ /**
* elapsed time
*/
private long elapsedTime = 0; private long elapsedTime = 0;
/** time to first response */ /**
* time to first response
*/
private long latency = 0; private long latency = 0;
/** time to end connecting */ /**
* time to end connecting
*/
private long connectTime = 0; 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; private TestLogicalAction testLogicalAction = TestLogicalAction.CONTINUE;
/** Should thread terminate? */ /**
* Should thread terminate?
*/
private boolean stopThread = false; private boolean stopThread = false;
/** Should test terminate? */ /**
* Should test terminate?
*/
private boolean stopTest = false; private boolean stopTest = false;
/** Should test terminate abruptly? */ /**
* Should test terminate abruptly?
*/
private boolean stopTestNow = false; private boolean stopTestNow = false;
private int sampleCount = 1; private int sampleCount = 1;
@ -295,10 +322,14 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
private long bodySize = 0; private long bodySize = 0;
/** Currently active threads in this thread group */ /**
* Currently active threads in this thread group
*/
private volatile int groupThreads = 0; 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 volatile int allThreads = 0;
private final long nanoTimeOffset; private final long nanoTimeOffset;
@ -342,6 +373,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
this.samplerId = sampler.getPropertyAsString("MS-ID"); this.samplerId = sampler.getPropertyAsString("MS-ID");
this.resourceId = sampler.getPropertyAsString("MS-RESOURCE-ID"); this.resourceId = sampler.getPropertyAsString("MS-RESOURCE-ID");
this.scenario = sampler.getPropertyAsString("MS-SCENARIO"); 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 * Create a sample with a specific elapsed time but don't allow the times to
* be changed later * be changed later
* * <p>
* (only used by HTTPSampleResult) * (only used by HTTPSampleResult)
* *
* @param elapsed * @param elapsed time
* time * @param atend create the sample finishing now, else starting now
* @param atend
* create the sample finishing now, else starting now
*/ */
protected SampleResult(long elapsed, boolean atend) { protected SampleResult(long elapsed, boolean atend) {
this(); this();
@ -442,8 +472,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
} }
/** /**
* @param propertiesToSave * @param propertiesToSave The propertiesToSave to set.
* The propertiesToSave to set.
*/ */
public void setSaveConfig(SampleSaveConfiguration propertiesToSave) { public void setSaveConfig(SampleSaveConfiguration propertiesToSave) {
this.saveConfig = 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 * Create a sample with specific start and end times for test purposes, but
* don't allow the times to be changed later * don't allow the times to be changed later
* * <p>
* (used by StatVisualizerModel.Test) * (used by StatVisualizerModel.Test)
* *
* @param start * @param start start time in milliseconds since unix epoch
* start time in milliseconds since unix epoch * @param end end time in milliseconds since unix epoch
* @param end
* end time in milliseconds since unix epoch
* @return sample with given start and end time * @return sample with given start and end time
*/ */
public static SampleResult createTestSample(long start, long end) { 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 * Create a sample with a specific elapsed time for test purposes, but don't
* allow the times to be changed later * allow the times to be changed later
* *
* @param elapsed * @param elapsed - desired elapsed time in milliseconds
* - desired elapsed time in milliseconds
* @return sample that starts 'now' and ends <code>elapsed</code> milliseconds later * @return sample that starts 'now' and ends <code>elapsed</code> milliseconds later
*/ */
public static SampleResult createTestSample(long elapsed) { 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. * Helper method to get 1 ms resolution timing.
* *
* @return the current time in milliseconds * @return the current time in milliseconds
* @throws RuntimeException * @throws RuntimeException when <code>useNanoTime</code> is <code>true</code> but
* when <code>useNanoTime</code> is <code>true</code> but * <code>nanoTimeOffset</code> is not set
* <code>nanoTimeOffset</code> is not set
*/ */
public long currentTimeInMillis() { public long currentTimeInMillis() {
if (useNanoTime) { if (useNanoTime) {
@ -527,13 +552,10 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* For use by SaveService only. * For use by SaveService only.
* *
* @param stamp * @param stamp this may be a start time or an end time (both in milliseconds)
* this may be a start time or an end time (both in milliseconds) * @param elapsed time in milliseconds
* @param elapsed * @throws RuntimeException when <code>startTime</code> or <code>endTime</code> has been
* time in milliseconds * set already
* @throws RuntimeException
* when <code>startTime</code> or <code>endTime</code> has been
* set already
*/ */
public void setStampAndTime(long stamp, long elapsed) { public void setStampAndTime(long stamp, long elapsed) {
if (startTime != 0 || endTime != 0) { if (startTime != 0 || endTime != 0) {
@ -558,7 +580,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Set response code to OK, i.e. "200" * Set response code to OK, i.e. "200"
*
*/ */
public void setResponseCodeOK() { public void setResponseCodeOK() {
responseCode = OK_CODE; 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. * Get the sample timestamp, which may be either the start time or the end time.
* *
* @return timeStamp in milliseconds
* @see #getStartTime() * @see #getStartTime()
* @see #getEndTime() * @see #getEndTime()
*
* @return timeStamp in milliseconds
*/ */
public long getTimeStamp() { public long getTimeStamp() {
return timeStamp; return timeStamp;
@ -652,7 +672,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
* Gets the assertion results associated with this sample. * Gets the assertion results associated with this sample.
* *
* @return an array containing the assertion results for 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() { public AssertionResult[] getAssertionResults() {
if (assertionResults == null) { 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. * Add a subresult and adjust the parent byte count and end-time.
* *
* @param subResult * @param subResult the {@link SampleResult} to be added
* the {@link SampleResult} to be added
*/ */
public void addSubResult(SampleResult subResult) { public void addSubResult(SampleResult subResult) {
addSubResult(subResult, isRenameSampleLabel()); 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 * 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 * @return true if TestPlan is in functional mode or property subresults.disable_renaming is true
*/ */
public static boolean isRenameSampleLabel() { 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. * Add a subresult and adjust the parent byte count and end-time.
* *
* @param subResult * @param subResult the {@link SampleResult} to be added
* the {@link SampleResult} to be added
* @param renameSubResults boolean do we rename subResults based on position * @param renameSubResults boolean do we rename subResults based on position
*/ */
public void addSubResult(SampleResult subResult, boolean renameSubResults) { 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. * Add a subresult to the collection without updating any parent fields.
* *
* @param subResult * @param subResult the {@link SampleResult} to be added
* the {@link SampleResult} to be added
*/ */
public void addRawSubResult(SampleResult subResult) { public void addRawSubResult(SampleResult subResult) {
storeSubResult(subResult, isRenameSampleLabel()); 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. * Add a subresult to the collection without updating any parent fields.
* *
* @param subResult * @param subResult the {@link SampleResult} to be added
* the {@link SampleResult} to be added
*/ */
private void addRawSubResult(SampleResult subResult, boolean renameSubResults) { private void addRawSubResult(SampleResult subResult, boolean renameSubResults) {
storeSubResult(subResult, 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 * addSubResult(SampleResult)}, except that the fields don't need to be
* accumulated * accumulated
* *
* @param subResult * @param subResult the {@link SampleResult} to be added
* the {@link SampleResult} to be added
*/ */
public void storeSubResult(SampleResult subResult) { public void storeSubResult(SampleResult subResult) {
storeSubResult(subResult, isRenameSampleLabel()); 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 * addSubResult(SampleResult)}, except that the fields don't need to be
* accumulated * accumulated
* *
* @param subResult * @param subResult the {@link SampleResult} to be added
* the {@link SampleResult} to be added
* @param renameSubResults boolean do we rename subResults based on position * @param renameSubResults boolean do we rename subResults based on position
*/ */
public void storeSubResult(SampleResult subResult, boolean renameSubResults) { 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. * Gets the subresults associated with this sample.
* *
* @return an array containing the subresults for this sample. Returns an * @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() { public SampleResult[] getSubResults() {
if (subResults == null) { if (subResults == null) {
@ -779,12 +794,11 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Sets the responseData attribute of the SampleResult object. * Sets the responseData attribute of the SampleResult object.
* * <p>
* If the parameter is null, then the responseData is set to an empty byte array. * If the parameter is null, then the responseData is set to an empty byte array.
* This ensures that getResponseData() can never be null. * This ensures that getResponseData() can never be null.
* *
* @param response * @param response the new responseData value
* the new responseData value
*/ */
public void setResponseData(byte[] response) { public void setResponseData(byte[] response) {
responseDataAsString = null; responseDataAsString = null;
@ -795,9 +809,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
* Sets the responseData attribute of the SampleResult object. * Sets the responseData attribute of the SampleResult object.
* Should only be called after setting the dataEncoding (if necessary) * Should only be called after setting the dataEncoding (if necessary)
* *
* @param response * @param response the new responseData value (String)
* the new responseData value (String)
*
* @deprecated - only intended for use from BeanShell code * @deprecated - only intended for use from BeanShell code
*/ */
@Deprecated @Deprecated
@ -816,7 +828,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
* *
* @param response the new responseData value (String) * @param response the new responseData value (String)
* @param encoding the encoding to set and then use (if null, use platform default) * @param encoding the encoding to set and then use (if null, use platform default)
*
*/ */
public void setResponseData(final String response, final String encoding) { public void setResponseData(final String response, final String encoding) {
responseDataAsString = null; responseDataAsString = null;
@ -837,9 +848,10 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
* <p> * <p>
* Note that some samplers may not store all the data, in which case * Note that some samplers may not store all the data, in which case
* getResponseData().length will be incorrect. * getResponseData().length will be incorrect.
* * <p>
* Instead, always use {@link #getBytes()} to obtain the sample result byte count. * Instead, always use {@link #getBytes()} to obtain the sample result byte count.
* </p> * </p>
*
* @return the responseData value (cannot be null) * @return the responseData value (cannot be null)
*/ */
public byte[] getResponseData() { public byte[] getResponseData() {
@ -875,7 +887,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
* Get the time it took this sample to occur. * Get the time it took this sample to occur.
* *
* @return elapsed time in milliseconds * @return elapsed time in milliseconds
*
*/ */
public long getTime() { public long getTime() {
return elapsedTime; return elapsedTime;
@ -887,6 +898,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Sets the data type of the sample. * Sets the data type of the sample.
*
* @param dataType String containing {@link #BINARY} or {@link #TEXT} * @param dataType String containing {@link #BINARY} or {@link #TEXT}
* @see #BINARY * @see #BINARY
* @see #TEXT * @see #TEXT
@ -909,9 +921,9 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Extract and save the DataEncoding and DataType from the parameter provided. * Extract and save the DataEncoding and DataType from the parameter provided.
* Does not save the full content Type. * 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) * @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) { public void setEncodingAndType(String ct) {
if (ct != null) { if (ct != null) {
@ -966,8 +978,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Sets the successful attribute of the SampleResult object. * Sets the successful attribute of the SampleResult object.
* *
* @param success * @param success the new successful value
* the new successful value
*/ */
public void setSuccessful(boolean success) { public void setSuccessful(boolean success) {
this.success = 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. * Returns the dataEncoding. May be null or the empty String.
*
* @return the value of the dataEncoding * @return the value of the dataEncoding
*/ */
public String getDataEncodingNoDefault() { public String getDataEncodingNoDefault() {
@ -1016,8 +1028,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Sets the dataEncoding. * Sets the dataEncoding.
* *
* @param dataEncoding * @param dataEncoding the dataEncoding to set, e.g. ISO-8895-1, UTF-8
* the dataEncoding to set, e.g. ISO-8895-1, UTF-8
*/ */
public void setDataEncoding(String dataEncoding) { public void setDataEncoding(String dataEncoding) {
this.dataEncoding = dataEncoding; this.dataEncoding = dataEncoding;
@ -1072,7 +1083,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* @param string - * @param string -
* request headers * request headers
*/ */
public void setRequestHeaders(String string) { public void setRequestHeaders(String string) {
requestHeaders = string; requestHeaders = string;
@ -1080,7 +1091,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* @param string - * @param string -
* response headers * response headers
*/ */
public void setResponseHeaders(String string) { public void setResponseHeaders(String string) {
responseHeaders = string; responseHeaders = string;
@ -1095,6 +1106,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Get the media type from the Content Type * Get the media type from the Content Type
*
* @return the media type - e.g. text/html (without charset, if any) * @return the media type - e.g. text/html (without charset, if any)
*/ */
public String getMediaType() { public String getMediaType() {
@ -1103,9 +1115,9 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Stores the content-type string, e.g. <code>text/xml; charset=utf-8</code> * Stores the content-type string, e.g. <code>text/xml; charset=utf-8</code>
* @see #setEncodingAndType(String) which can be used to extract the charset.
* *
* @param string the content-type to be set * @param string the content-type to be set
* @see #setEncodingAndType(String) which can be used to extract the charset.
*/ */
public void setContentType(String string) { public void setContentType(String string) {
contentType = string; contentType = string;
@ -1160,6 +1172,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Set idle time pause. * Set idle time pause.
* For use by SampleResultConverter/CSVSaveService. * For use by SampleResultConverter/CSVSaveService.
*
* @param idle long * @param idle long
*/ */
public void setIdleTime(long idle) { public void setIdleTime(long idle) {
@ -1173,7 +1186,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Record the start time of a sample * Record the start time of a sample
*
*/ */
public void sampleStart() { public void sampleStart() {
if (startTime == 0) { 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 * Record the end time of a sample and calculate the elapsed time
*
*/ */
public void sampleEnd() { public void sampleEnd() {
if (endTime == 0) { if (endTime == 0) {
@ -1197,7 +1208,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Pause a sample * Pause a sample
*
*/ */
public void samplePause() { public void samplePause() {
if (pauseTime != 0) { if (pauseTime != 0) {
@ -1208,7 +1218,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Resume a sample * Resume a sample
*
*/ */
public void sampleResume() { public void sampleResume() {
if (pauseTime == 0) { if (pauseTime == 0) {
@ -1221,9 +1230,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* When a Sampler is working as a monitor * When a Sampler is working as a monitor
* *
* @param monitor * @param monitor flag whether this sampler is working as a monitor
* flag whether this sampler is working as a monitor
*
* @deprecated since 3.2 NOOP * @deprecated since 3.2 NOOP
*/ */
@Deprecated @Deprecated
@ -1265,7 +1272,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
* Returns the count of errors. * Returns the count of errors.
* *
* @return 0 - or 1 if the sample failed * @return 0 - or 1 if the sample failed
* * <p>
* TODO do we need allow for nested samples? * TODO do we need allow for nested samples?
*/ */
public int getErrorCount() { 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 * still want to calculate the throughput. The bytes are the bytes of the
* response data. * response data.
* *
* @param length * @param length the number of bytes of the response data for this sample
* the number of bytes of the response data for this sample
*/ */
public void setBytes(long length) { public void setBytes(long length) {
bytes = 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 * still want to calculate the throughput. The bytes are the bytes of the
* response data. * response data.
* *
* @param length * @param length the number of bytes of the response data for this sample
* the number of bytes of the response data for this sample
* @deprecated use setBytes(long) * @deprecated use setBytes(long)
*/ */
@Deprecated @Deprecated
@ -1321,7 +1326,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
} }
/** /**
*
* @param sentBytesCount long sent bytes * @param sentBytesCount long sent bytes
*/ */
public void setSentBytes(long sentBytesCount) { public void setSentBytes(long sentBytesCount) {
@ -1365,7 +1369,6 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Set the time to the first response * Set the time to the first response
*
*/ */
public void latencyEnd() { public void latencyEnd() {
latency = currentTimeInMillis() - startTime - idleTime; latency = currentTimeInMillis() - startTime - idleTime;
@ -1374,8 +1377,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* This is only intended for use by SampleResultConverter! * This is only intended for use by SampleResultConverter!
* *
* @param latency * @param latency The latency to set.
* The latency to set.
*/ */
public void setLatency(long latency) { public void setLatency(long latency) {
this.latency = latency; this.latency = latency;
@ -1407,8 +1409,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* This is only intended for use by SampleResultConverter! * This is only intended for use by SampleResultConverter!
* *
* @param timeStamp * @param timeStamp The timeStamp to set.
* The timeStamp to set.
*/ */
public void setTimeStamp(long timeStamp) { public void setTimeStamp(long timeStamp) {
this.timeStamp = timeStamp; this.timeStamp = timeStamp;
@ -1440,8 +1441,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
} }
/** /**
* @param parent * @param parent The parent to set.
* The parent to set.
*/ */
public void setParent(SampleResult parent) { public void setParent(SampleResult parent) {
this.parent = parent; this.parent = parent;
@ -1472,6 +1472,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
} }
// Bug 47394 // Bug 47394
/** /**
* Allow custom SampleSenders to drop unwanted assertionResults * Allow custom SampleSenders to drop unwanted assertionResults
*/ */
@ -1489,8 +1490,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
/** /**
* Set the headers size in bytes * Set the headers size in bytes
* *
* @param size * @param size the number of bytes of the header
* the number of bytes of the header
*/ */
public void setHeadersSize(int size) { public void setHeadersSize(int size) {
this.headersSize = size; this.headersSize = size;
@ -1549,7 +1549,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
public void run() { public void run() {
// Wait longer than a clock pulse (generally 10-15ms) // Wait longer than a clock pulse (generally 10-15ms)
getOffset(30L); // Catch an early clock pulse to reduce slop. 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 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 * @param startNextThreadLoop the startNextLoop to set
* @deprecated use SampleResult#setTestLogicalAction(TestLogicalAction)
*/ */
@Deprecated @Deprecated
public void setStartNextThreadLoop(boolean startNextThreadLoop) { public void setStartNextThreadLoop(boolean startNextThreadLoop) {
if(startNextThreadLoop) { if (startNextThreadLoop) {
testLogicalAction = TestLogicalAction.START_NEXT_ITERATION_OF_THREAD; testLogicalAction = TestLogicalAction.START_NEXT_ITERATION_OF_THREAD;
} else { } else {
testLogicalAction = TestLogicalAction.CONTINUE; testLogicalAction = TestLogicalAction.CONTINUE;