提交 602ae2eb authored 作者: 黄夏豪's avatar 黄夏豪

feat(base): 优化了socket调用测试用例的代码。并且支持用户关闭socket后再次接入了

feat(base):增加了http执行器,PATH类型的执行
上级 e0f6b694
......@@ -42,7 +42,6 @@ public class CheckPointActuator implements Actuator {
CompleteExpressionUtil completeExpressionUtil ;
public CheckPointActuator(@Value("${baseJsPath}") String baseJs, CompleteExpressionUtil completeExpressionUtil) {
System.out.println("-------------:"+Thread.currentThread().getId());
this.completeExpressionUtil = completeExpressionUtil;
ClassPathResource cpr = new ClassPathResource(baseJs);
try {
......@@ -54,7 +53,6 @@ public class CheckPointActuator implements Actuator {
public CheckPointResult httpCheck(HttpResponseDetail httpResponseDetail, CheckPoint checkPoint,Long envId,Long projectId) {
System.out.println("-------------:"+Thread.currentThread().getId());
CheckPointResult checkPointResult = new CheckPointResult();
//根据checkPoint里的细节数据开始检测
//异常检查点检测
......
......@@ -20,7 +20,6 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.matrix.exception.HttpRequestException;
import org.matrix.socket.LogQueue;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
......@@ -33,6 +32,8 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* HttpClient调用封装
......@@ -41,6 +42,7 @@ import java.util.List;
@Component
public class HttpClientActuator implements Actuator {
final
CloseableHttpClient client;
......@@ -107,7 +109,6 @@ public class HttpClientActuator implements Actuator {
*
*/
public HttpResponseDetail sendHttpRequest(HttpRequestDetail httpRequestDetail,Long envId,Long projectId) {
LogQueue.add(Thread.currentThread().getId(),String.format("[HTTP执行] 目标URL :%S", httpRequestDetail.getUrl()));
completeHttpRequestDetail(httpRequestDetail,envId,projectId);
CloseableHttpResponse response = null;
Date startTime = new Date();
......@@ -250,6 +251,11 @@ public class HttpClientActuator implements Actuator {
}
}
url = uriBuilder.build().toString();
}else if(httpRequestDetail.getRequestType() == HttpRequestType.PATH){
List<RequestBody> requestBodies = httpRequestDetail.getRequestBodies();
Map<String, String> requestMap =
requestBodies.stream().collect(Collectors.toMap(RequestBody::getKey, RequestBody::getValue));
url = completeExpressionUtil.completePathVariable(url,requestMap);
}
}
} catch (URISyntaxException e) {
......
......@@ -9,7 +9,10 @@ import java.util.Map;
* @author huangxiahao
*/
public enum HttpRequestType {
/**
* 将参数拼在URL中,例如:127.0.0.1/1/1
*/
PATH,
/**
* 通过将变量拼接在url中的方式请求,例如:127.0.0.1?a=1&b=2
*/
......
package org.matrix.actuators.usecase;
import com.alibaba.fastjson.JSON;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.matrix.actuators.Actuator;
import org.matrix.actuators.checkpoint.CheckPointActuator;
import org.matrix.actuators.httpclient.HttpClientActuator;
......@@ -11,10 +12,17 @@ import org.matrix.actuators.checkpoint.CheckPointResult;
import org.matrix.actuators.httpclient.HttpRequestDetail;
import org.matrix.actuators.httpclient.HttpResponseDetail;
import org.matrix.database.entity.TestCaseBTO;
import org.matrix.database.entity.TestCaseListDataBto;
import org.matrix.database.entity.TestData;
import org.matrix.socket.LogQueue;
import org.matrix.database.mapper.TestDataMapper;
import org.matrix.socket.LogQueueRuntime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 测试用例执行器
* todo 打印LOG
......@@ -30,6 +38,9 @@ public class CaseActuator implements Actuator {
private final String baseJsPath = "syntaxCheck.js";
@Autowired
TestDataMapper testDataMapper;
public CaseActuator(CheckPointActuator checkPointActuator, HttpClientActuator httpClientActuator) {
this.checkPointActuator = checkPointActuator;
this.httpClientActuator = httpClientActuator;
......@@ -40,42 +51,125 @@ public class CaseActuator implements Actuator {
*/
public TestCaseExecuteResult executeTestCase(TestCaseBTO testCaseBto, Long envId, Long projectId) {
try {
LogQueue.add(ThreadUtil.currentThreadId(),String.format("[用例解析器] 当前线程ID为: %S", ThreadUtil.currentThreadId()));
LogQueueRuntime.addNewLog("[用例执行器] 开始执行单条用例数据!!");
//todo 李迪凡 执行前置动作
//执行测试用例的本体内容
HttpResponseDetail baseTestCaseResponseDetail = null;
TestCase testCase = testCaseBto.getTestCase();
TestData testData = testCaseBto.getTestData();
if (testCase.getType().equals(TestCaseTypeEnum.HTTP.getValue())) {
HttpRequestDetail httpRequestDetail = JSON.parseObject(testData.getDetail(), HttpRequestDetail.class);
baseTestCaseResponseDetail = httpClientActuator.sendHttpRequest(httpRequestDetail,envId,projectId);
}
HttpResponseDetail baseTestCaseResponseDetail = getHttpResponseDetail(
envId,
projectId,
testCaseBto.getTestCase(),
testCaseBto.getTestData());
//todo 李迪凡 执行测试后动作
//进行检验
CheckPointResult checkPointResult = null;
if (testCase.getType().equals(TestCaseTypeEnum.HTTP.getValue())) {
checkPointResult = checkPointActuator.httpCheck(baseTestCaseResponseDetail, getCheckPointEntity(testData),envId,projectId);
CheckPointResult checkPointResult = getCheckPointResult(testCaseBto.getTestCase(),
testCaseBto.getTestData(),
envId,
projectId,
baseTestCaseResponseDetail);
;
//todo 李迪凡 执行后置动作
TestCaseExecuteResult testCaseExecuteResult = new TestCaseExecuteResult(baseTestCaseResponseDetail, checkPointResult);
LogQueueRuntime.addNewLog(JSON.toJSONString(testCaseExecuteResult));
return testCaseExecuteResult;
} catch (Exception e) {
throw e;
} finally {
LogQueueRuntime.remove(ThreadUtil.currentThreadId());
}
}
/**
* todo 执行一条测试用例的多条测试数据
*/
public List<TestCaseExecuteResult> executeTestCases(TestCaseListDataBto testCaseBto, Long envId, Long projectId) {
for (int i = 0; i < 100; i++) {
LogQueueRuntime.addNewLog("[用例执行器] 开始执行用例!!"+i);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
LogQueueRuntime.addNewLog(String.format("[用例执行器] 当前正在执行用例ID: %s 用例名: %s",testCaseBto.getTestCase().getId(),testCaseBto.getTestCase().getName()));
//todo 李迪凡 执行前置动作
//执行测试用例的本体内容
List<HttpResponseDetail> baseTestCaseResponseDetailList = new ArrayList<>();
for (TestData testData : testCaseBto.getTestDataList()) {
//向线程中设置当前正在执行的DataId
LogQueueRuntime.setTestData(testData.getId());
HttpResponseDetail baseTestCaseResponseDetail = getHttpResponseDetail(
envId,
projectId,
testCaseBto.getTestCase(),
testData);
baseTestCaseResponseDetailList.add(baseTestCaseResponseDetail);
}
//将线程中正在执行的DataId清除
LogQueueRuntime.clearTestData();
//todo 李迪凡 执行测试后动作
List<TestCaseExecuteResult> resultList = new ArrayList<>();
for (int i = 0; i < testCaseBto.getTestDataList().size(); i++) {
//向线程中设置当前正在执行的DataId
LogQueueRuntime.setTestData(testCaseBto.getTestDataList().get(i).getId());
CheckPointResult checkPointResult = getCheckPointResult(testCaseBto.getTestCase(),
testCaseBto.getTestDataList().get(i),
envId,
projectId,
baseTestCaseResponseDetailList.get(i));
TestCaseExecuteResult testCaseExecuteResult = new TestCaseExecuteResult(baseTestCaseResponseDetailList.get(i), checkPointResult);
resultList.add(testCaseExecuteResult);
LogQueueRuntime.addNewLog(JSON.toJSONString(testCaseExecuteResult));
}
//将线程中正在执行的DataId清除
LogQueueRuntime.clearTestData();
//todo 李迪凡 执行后置动作
return new TestCaseExecuteResult(baseTestCaseResponseDetail, checkPointResult);
} catch (Exception e){
return resultList;
} catch (Exception e) {
throw e;
}finally {
LogQueue.remove(ThreadUtil.currentThreadId());
} finally {
LogQueueRuntime.remove(ThreadUtil.currentThreadId());
}
}
private CheckPointResult getCheckPointResult(TestCase testCase
, TestData testData
, Long envId
, Long projectId
, HttpResponseDetail baseTestCaseResponseDetail) {
if (testCase.getType().equals(TestCaseTypeEnum.HTTP.getValue())) {
return
checkPointActuator.httpCheck(
baseTestCaseResponseDetail,
getCheckPointEntity(testData),
envId,
projectId
);
}
return null;
}
private HttpResponseDetail getHttpResponseDetail(
Long envId, Long projectId, TestCase testCase, TestData testData) {
if (testCase.getType().equals(TestCaseTypeEnum.HTTP.getValue())) {
HttpRequestDetail httpRequestDetail = JSON.parseObject(testData.getDetail(), HttpRequestDetail.class);
return httpClientActuator.sendHttpRequest(httpRequestDetail, envId, projectId);
}
return null;
}
private CheckPoint getCheckPointEntity(TestData testData) {
CheckPoint checkPoint = new CheckPoint();
if (testData.getAbnormalCheckpoint()==0){
if (testData.getAbnormalCheckpoint() == 0) {
checkPoint.setUseExceptionCheck(false);
}else {
} else {
checkPoint.setUseExceptionCheck(true);
}
if (testData.getNoEmptyCheckpoint()==0){
if (testData.getNoEmptyCheckpoint() == 0) {
checkPoint.setUseNullCheck(false);
}else {
} else {
checkPoint.setUseNullCheck(true);
}
checkPoint.setContainCheckPoint(testData.getContainCheckpoint());
......@@ -85,5 +179,4 @@ public class CaseActuator implements Actuator {
}
}
......@@ -3,12 +3,12 @@ package org.matrix.actuators.util;
import com.alibaba.fastjson.JSON;
import com.jayway.jsonpath.JsonPath;
import org.matrix.actuators.checkpoint.JsonPathCheckPoint;
import org.matrix.actuators.sql.SqlExpActuator;
import org.matrix.util.SpringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
......@@ -28,10 +28,23 @@ public class CompleteExpressionUtil {
public static final String DYNAMIC_VARIABLE_REG = "\\$\\{(((?!pre\\.).)*?)}(\\[(.*?)])?";
public static final String PATH_REG = "\\{(((?!pre\\.).)*?)}(\\[(.*?)])?";
public String completeDynamicVariable(String expression,Long envId,Long projectId) {
String result = expression;
result = regexExpression(result, DYNAMIC_VARIABLE_REG, null, (o, s) -> getSqlExpActuator().parseVarByName(s,envId,projectId));
result = regexExpression(
result, DYNAMIC_VARIABLE_REG,
null, (o, s) ->
getSqlExpActuator().parseVarByName(s,envId,projectId));
return result;
}
public String completePathVariable(String expression, Map<String, String> source) {
String result = expression;
result = regexExpression(
result, PATH_REG,
source, (o, s) ->
((Map<String, String>)o).get(s));
return result;
}
......@@ -42,7 +55,8 @@ public class CompleteExpressionUtil {
*/
public String completeJsonPathExpression(String expression, Object jsonObject) {
String result = expression;
result = regexExpression(result, JSON_PATH_REG, jsonObject, (o, s) -> JSON.toJSONString(JsonPath.read(o, s.substring(1, s.length() - 1))));
result = regexExpression(result, JSON_PATH_REG,
jsonObject, (o, s) -> JSON.toJSONString(JsonPath.read(o, s.substring(1, s.length() - 1))));
return result;
}
......
......@@ -8,6 +8,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.matrix.enums.ExecutionRecType;
import org.matrix.socket.SocketType;
import org.matrix.socket.TestExecuteType;
/**
* @author mry
......@@ -20,26 +22,26 @@ import org.matrix.enums.ExecutionRecType;
@TableName(autoResultMap = true, value = "kt_execution_record")
public class ExecutionRecord extends BaseEntity {
@ApiModelProperty("名称")
private String name;
@ApiModelProperty("测试任务id")
private Long testTaskId;
@ApiModelProperty("用户id")
private Long userId;
@ApiModelProperty("执行id")
private Long performId;
@ApiModelProperty("测试数据Id")
private Long testDataId;
@ApiModelProperty("任务id,如果不是从测试任务执行的记录不会有这个ID")
private Long testJobId;
@ApiModelProperty("用例id")
private Long testCaseId;
@ApiModelProperty("key用来记录执行批次")
private String key;
private String unionKey;
@ApiModelProperty("执行状态,0:关闭,1:开启")
private ExecutionRecType status;
private ExecutionRecType status = ExecutionRecType.RUN;
@ApiModelProperty("类型")
private String type;
private TestExecuteType type;
@ApiModelProperty("日志")
private String log;
......
package org.matrix.database.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* TestCaseBTO. 多条数据的测试用例对象
*
* @author huangxiahao
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TestCaseListDataBto {
/**
* 测试用例
*/
private TestCase testCase;
/**
* 测试用例
*/
private List<TestData> testDataList;
}
......@@ -2,9 +2,41 @@ package org.matrix.database.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.matrix.database.entity.ExecutionRecord;
import org.matrix.enums.ExecutionRecType;
import org.matrix.socket.TestExecuteLog;
import org.matrix.socket.TestExecuteType;
/**
* @author mry
*/
public interface IExecutionRecordService extends IService<ExecutionRecord> {
/**
* 根据List<ExecutionRecord>,拼接出前端能够使用的数据结构
* 出参示例:
* {
* -1:{
* 2:{
* 2:[
* "张三","赵四"
* ]
* }
* }
* }
*/
/**
* 新增执行记录日志
* @param testExecuteLog 用例日志临时存储对象
* @param log 日志内容
*/
void addExecutionRecord(TestExecuteLog testExecuteLog,String log);
/**
* 根据unionKey将对应的执行记录修改为目标状态
* @param unionKey 类似于批次Id的作用,同一批执行拥有相同unionKey
* @param status 目标状态
*/
void setStatusByUnionKey(String unionKey, ExecutionRecType status);
}
package org.matrix.database.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.matrix.database.entity.ExecutionRecord;
import org.matrix.database.mapper.ExecutionRecordMapper;
import org.matrix.database.service.IExecutionRecordService;
import org.matrix.enums.ExecutionRecType;
import org.matrix.exception.GlobalException;
import org.matrix.socket.TestExecuteLog;
import org.matrix.socket.TestExecuteType;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author mry
*/
@Service
public class ExecutionRecordImpl extends ServiceImpl<ExecutionRecordMapper, ExecutionRecord> implements IExecutionRecordService {
@Override
public void addExecutionRecord(TestExecuteLog testExecuteLog,String log) {
ExecutionRecord executionRecord = new ExecutionRecord();
BeanUtils.copyProperties(testExecuteLog,executionRecord);
executionRecord.setLog(log);
this.save(executionRecord);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void setStatusByUnionKey(String unionKey, ExecutionRecType status) {
boolean emptyIfStr = StrUtil.isEmptyIfStr(unionKey);
if (emptyIfStr){
throw new GlobalException("不能根据空的unionKey修改执行状态");
}else {
ExecutionRecord executionRecord = new ExecutionRecord();
executionRecord.setStatus(status);
update(executionRecord,Wrappers
.lambdaUpdate(ExecutionRecord.class)
.eq(ExecutionRecord::getUnionKey,unionKey)
);
}
}
}
......@@ -21,6 +21,7 @@ import java.util.List;
public class TestCaseData {
private TestCase testCase;
private List<TestData> testData;
}
package org.matrix.socket;
import lombok.Data;
import org.matrix.database.entity.TestCaseListDataBto;
import java.util.List;
/**
* 接收前端传入的用例执行信息
*
* @author huangxiahao
*/
@Data
public class CaseExecuteVo extends SocketVo{
public class CaseExecuteVo extends SocketVo {
private List<Long> testDateId;
private TestExecuteType type = TestExecuteType.TEST_CASE;
private Long userId;
private Long projectId;
private Long envId;
private List<TestCaseListDataBto> testCaseListDataBtoList;
}
package org.matrix.socket;
import org.springframework.web.socket.WebSocketSession;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author huangxiahao
*/
public class ExecuteMonitorSocketPool {
private static final ConcurrentHashMap<String, List<WebSocketSession>> SOCKET_MAP = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, List<String>> SOCKET_KEY_MAP = new ConcurrentHashMap<>();
public static void add(String key,WebSocketSession clientSocket){
if (clientSocket != null &key !=null) {
List<WebSocketSession> webSocketSessions = SOCKET_MAP.get(key);
if (webSocketSessions!=null){
webSocketSessions.add(clientSocket);
}else {
webSocketSessions = new ArrayList<>();
webSocketSessions.add(clientSocket);
SOCKET_MAP.put(key,webSocketSessions);
}
addKeyMap(clientSocket.getId(),key);
}
}
public static void addKeyMap(String socketId,String key){
if (socketId != null &key !=null) {
List<String> list = SOCKET_KEY_MAP.get(socketId);
if (list!=null){
list.add(key);
}else {
list = new ArrayList<>();
list.add(key);
SOCKET_KEY_MAP.put(socketId,list);
}
}
}
public static void remove(WebSocketSession clientSocket){
List<String> list = SOCKET_KEY_MAP.get(clientSocket.getId());
if (list!=null){
for (String s : list) {
SOCKET_MAP.get(s).remove(clientSocket);
}
}
}
public static List<WebSocketSession> get(String key){
return SOCKET_MAP.get(key);
}
}
\ No newline at end of file
package org.matrix.socket;
import lombok.Data;
import org.matrix.database.entity.TestCaseListDataBto;
import java.util.List;
/**
* 接收前端传入的用例执行信息
* @author huangxiahao
*/
@Data
public class ExecuteMonitorVo extends SocketVo{
private String unionKey;
}
......@@ -4,23 +4,25 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import org.junit.platform.commons.util.StringUtils;
import org.matrix.actuators.usecase.CaseActuator;
import org.matrix.actuators.usecase.TestCaseExecuteResult;
import org.matrix.database.entity.TestCaseBTO;
import org.matrix.actuators.util.ThreadUtil;
import org.matrix.database.entity.TestCaseListDataBto;
import org.matrix.database.service.ITestDataService;
import org.matrix.exception.GlobalException;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.List;
import java.util.UUID;
/**
* webSocket处理
*
* @author huangxiahao
*/
@Component
public class HttpAuthHandler extends TextWebSocketHandler {
public class HttpAuthHandler extends TextWebSocketHandler {
final CaseActuator caseActuator;
final ITestDataService testDataService;
......@@ -39,31 +41,52 @@ public class HttpAuthHandler extends TextWebSocketHandler {
try {
SocketVo socketVo = JSON.parseObject(payload, SocketVo.class);
//如果客户端发送测试用例调试命令,则进行测试用例的执行
if (socketVo!=null&& StringUtils.isNotBlank(socketVo.getType())){
if (SocketType.TEST_CASE.toString().equals(socketVo.getType())){
//将websocketSession 加入到socket池子中
SocketPool.add(Thread.currentThread().getId(),session);
CaseExecuteVo caseExecuteVo = JSON.parseObject(payload, CaseExecuteVo.class);
List<Long> testDateIds = caseExecuteVo.getTestDateId();
for (Long testDateId : testDateIds) {
try {
TestCaseBTO testCaseBto = testDataService.toCaseBTO(testDateId);
//执行测试用例
TestCaseExecuteResult testCaseExecuteResult = caseActuator.executeTestCase(testCaseBto,1L,1L);
session.sendMessage(new TextMessage(JSON.toJSONString(testCaseExecuteResult)));
}catch (GlobalException e){
session.sendMessage(new TextMessage(e.getMessage()));
}
}
SocketPool.remove(Thread.currentThread().getId());
}else {
if (socketVo != null && socketVo.getSocketType()!=null) {
if (SocketType.TEST_CASE_EXECUTE.equals(socketVo.getSocketType())) {
runTestCase(session, payload);
}else if (SocketType.TEST_CASE_MONITOR.equals(socketVo.getSocketType())){
ExecuteMonitorVo caseExecuteVo = JSON.parseObject(payload, ExecuteMonitorVo.class);
ExecuteMonitorSocketPool.add(caseExecuteVo.getUnionKey(),session);
} else {
session.sendMessage(new TextMessage("入参不符合规定"));
}
}
}catch (JSONException e ){
} catch (JSONException e) {
session.sendMessage(new TextMessage("入参不符合规定"));
}
session.sendMessage(new TextMessage("message execute success"));
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
ExecuteMonitorSocketPool.remove(session);
}
/**
* 执行TestCase,并控制运行态日志池,进行日志的生成。
*/
private void runTestCase(WebSocketSession session, String payload) {
//将websocketSession 加入到socket池子中
Long currentThreadId = ThreadUtil.currentThreadId();
TestCaseExecuteSocketPool.add(currentThreadId, session);
CaseExecuteVo caseExecuteVo = JSON.parseObject(payload, CaseExecuteVo.class);
List<TestCaseListDataBto> testCaseListDataBtoList = caseExecuteVo.getTestCaseListDataBtoList();
String unionKey = UUID.randomUUID().toString();
for (TestCaseListDataBto testCaseListDataBto : testCaseListDataBtoList) {
TestExecuteLog testExecuteLog = LogQueueRuntime
.initTestCaseLog(caseExecuteVo.getUserId()
,testCaseListDataBto.getTestCase().getId()
, caseExecuteVo.getType()
, unionKey
);
LogQueueRuntime.put(currentThreadId, testExecuteLog);
//执行测试用例
caseActuator.executeTestCases(testCaseListDataBto, caseExecuteVo.getEnvId(), caseExecuteVo.getProjectId());
}
//将本次产生的数据清除
TestCaseExecuteSocketPool.remove(Thread.currentThread().getId());
//将本次产生的日志从执行状态设为停止状态
LogQueueRuntime.stopExecutionRecords(unionKey);
}
}
package org.matrix.socket;
import org.matrix.exception.GlobalException;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 日志消息队列
*
* @author huangxiahao
*/
public class LogQueue {
private static final ConcurrentHashMap<Long, List<String>> LOG_MAP = new ConcurrentHashMap<>();
public static void add(Long threadId, String log) {
List<String> logList = LOG_MAP.get(threadId);
if (logList != null) {
logList.add(log);
} else {
logList = new ArrayList<>();
logList.add(log);
LOG_MAP.put(threadId, logList);
}
//将log发送出去
sendMessage(threadId, log);
}
public static void remove(Long threadId) {
LOG_MAP.remove(threadId);
}
public static void sendMessage(Long threadId, String log) {
try {
WebSocketSession webSocketSession = SocketPool.get(threadId);
if (webSocketSession != null) {
webSocketSession.sendMessage(new TextMessage(log));
}
} catch (IOException e) {
throw new GlobalException("发送socket消息失败,socket已经断开连接");
}
}
}
package org.matrix.socket;
import cn.hutool.log.LogFactory;
import cn.hutool.log.level.Level;
import com.alibaba.fastjson.JSONObject;
import org.matrix.actuators.util.ThreadUtil;
import org.matrix.database.service.IExecutionRecordService;
import org.matrix.enums.ExecutionRecType;
import org.matrix.exception.GlobalException;
import org.matrix.util.SpringUtils;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 运行态日志消息队列
*
* @author huangxiahao
*/
public class LogQueueRuntime {
private static final ConcurrentHashMap<Long, TestExecuteLog> LOG_MAP = new ConcurrentHashMap<>();
private static IExecutionRecordService EXECUTION_RECORD_SERVICE;
public static IExecutionRecordService getExecutionRecordService(){
if (EXECUTION_RECORD_SERVICE==null){
EXECUTION_RECORD_SERVICE = SpringUtils.getBean("executionRecordImpl");
}
return EXECUTION_RECORD_SERVICE;
}
public static void addExecutionRecord(TestExecuteLog testExecuteLog,String log){
getExecutionRecordService().addExecutionRecord(testExecuteLog,log);
}
public static void stopExecutionRecords(String unionKey){
getExecutionRecordService().setStatusByUnionKey(unionKey,ExecutionRecType.STOP);
}
/**
* 如果返回false,表示该用例已经在线程中执行了
*/
public static void put(Long threadId, TestExecuteLog testExecuteLog) {
LOG_MAP.put(threadId, testExecuteLog);
}
public static void setTestData(Long dataId){
Long currentThreadId = ThreadUtil.currentThreadId();
LOG_MAP.get(currentThreadId).setTestDataId(dataId);
}
public static void clearTestData(){
Long currentThreadId = ThreadUtil.currentThreadId();
LOG_MAP.get(currentThreadId).setTestDataId(-1L);
}
public static void addNewLog(String log) {
Long currentThreadId = ThreadUtil.currentThreadId();
TestExecuteLog testExecuteLog = LOG_MAP.get(currentThreadId);
if (testExecuteLog !=null){
testExecuteLog.addLog(log);
sendMessage(currentThreadId,testExecuteLog.getUnionKey(),log);
addExecutionRecord(testExecuteLog,log);
}else {
throw new GlobalException("该线程中的TestCaseLog对象未初始化,请先调用put方法初始化对象");
}
}
public static void remove(Long threadId) {
TestCaseExecuteSocketPool.remove(threadId);
}
public static void sendMessage(Long threadId,String unionKey, Object log) {
try {
TestExecuteLog testExecuteLog = LOG_MAP.get(threadId);
// 测试任务ID.测试用例ID.测试数据ID
String logMessage =
String.format(
"%s.%s.%s.%s"
,testExecuteLog.getTestJobId()
,testExecuteLog.getTestCaseId()
,testExecuteLog.getTestDataId()
,JSONObject.toJSONString(log)
);
//如果unionKey中存在监听的socket则向该socket发送消息
List<WebSocketSession> webSocketSessions = ExecuteMonitorSocketPool.get(unionKey);
if (webSocketSessions!=null){
for (WebSocketSession webSocketSession : webSocketSessions) {
webSocketSession.sendMessage(new TextMessage(
logMessage
));
}
}
//如果线程中存在socket则向该socket发送消息
WebSocketSession webSocketSession = TestCaseExecuteSocketPool.get(threadId);
if (webSocketSession != null) {
webSocketSession.sendMessage(new TextMessage(
logMessage
));
}
} catch (Exception e) {
LogFactory.get().log(Level.INFO,"发送socket消息失败,socket已经断开连接");
remove(threadId);
}
}
public static TestExecuteLog initTestCaseLog(Long userId, Long caseId,TestExecuteType type,String unionKey) {
TestExecuteLog testExecuteLog = new TestExecuteLog();
testExecuteLog.setTestCaseId(caseId);
testExecuteLog.setUserId(userId);
testExecuteLog.setType(type);
testExecuteLog.setUnionKey(unionKey);
return testExecuteLog;
}
}
......@@ -9,6 +9,7 @@ public enum SocketType {
/**
* 测试用例类型
*/
TEST_CASE;
TEST_CASE_EXECUTE,
TEST_CASE_MONITOR;
}
......@@ -11,7 +11,7 @@ public class SocketVo {
/**
* 用于区分前端传入的内容的类型
* 只有执行类型为,EST_CASE 时 执行测试用例
* 只有执行类型为,TEST_CASE 时 执行测试用例
*/
private String type ;
private SocketType socketType ;
}
......@@ -7,11 +7,10 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* @author huangxiahao
*/
public class SocketPool {
public class TestCaseExecuteSocketPool {
private static final ConcurrentHashMap<Long, WebSocketSession> SOCKET_MAP = new ConcurrentHashMap<>();
public static void add(Long key,WebSocketSession clientSocket){
if (clientSocket != null &key !=null) {
SOCKET_MAP.put(key, clientSocket);
......
package org.matrix.socket;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 用例日志临时存储对象
* @author huangxiahao
*/
@Data
public class TestExecuteLog {
private Long testJobId = -1L;
/**
* 用于记录执行记录的唯一KEY,同一批次的执行记录应该具有相同的KEY。
*/
private String unionKey;
private TestExecuteType type;
private Long userId;
private Long testCaseId = -1L;
private Long testDataId = -1L;
private List<String> log = new ArrayList<>();
/**
* 前置条件:每个用例下的数据组是单线程执行的
* 由于arrayList是有序的,所以只需要向下新增就好了
*/
public void addLog(String log){
this.log.add(log);
}
}
package org.matrix.socket;
/**
* @author huangxiahao
*/
public enum TestExecuteType {
/**
* 测试用例
*/
TEST_CASE,
/**
* 测试任务
*/
TEST_JOB
;
}
package org.matrix.actuators.sql;
package org.matrix.actuators.http;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.matrix.actuators.httpclient.HttpClientActuator;
import org.matrix.actuators.usecase.CaseActuator;
import org.matrix.actuators.usecase.TestCaseExecuteResult;
import org.matrix.database.entity.ExecutionRecord;
import org.matrix.database.entity.TestCase;
import org.matrix.database.entity.TestCaseBTO;
import org.matrix.database.entity.TestData;
import org.matrix.database.service.IExecutionRecordService;
import org.matrix.database.service.ITestCaseService;
import org.matrix.database.service.ITestDataService;
import org.matrix.socket.SocketType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
class CaseActuatorTest {
......@@ -20,6 +31,52 @@ class CaseActuatorTest {
@Autowired
CaseActuator caseActuator;
@Autowired
IExecutionRecordService executionRecordService;
@Autowired
ITestCaseService iTestCaseService;
@Autowired
ITestDataService iTestDataService;
@Test
void testList(){
// 测试任务ID.测试用例ID.测试数据ID.日志
// String str1 = "-1.2.2.张三";
// String str2 = "-1.2.2.李四";
// String str3 = "-1.2.3.张三";
// String str4 = "-1.2.3.张三";
// List<ExecutionRecord> records = new ArrayList<>();
TestCase byId = iTestCaseService.getById(3);
System.out.println(JSONObject.toJSON(byId));
TestData byId1 = iTestDataService.getById(3);
System.out.println(JSONObject.toJSON(byId1));
// String str1 = "2.张三";
// String str2 = "2.李四";
// String str3 = "3.张三";
// String str4 = "3.张三";
// JSONObject object = new JSONObject();
// JSONArray jsonArray = new JSONArray();
}
void t(JSON json, String str){
JSON newJson ;
//判断str中有没有. 如果没有点则证明为最后一个参数,由于最后一个参数的实际类型为jsonArray并且为递归退出条件所以需要特殊处理
if (str.contains(".")){
//切割获取点前面的字符
}else {
}
}
@Test
void test(){
String json = "{\n" +
......
......@@ -8,6 +8,8 @@ import org.matrix.database.entity.ExecutionRecord;
import org.matrix.database.service.IExecutionRecordService;
import org.matrix.database.vo.CommonResult;
import org.matrix.database.vo.CommonResultObj;
import org.matrix.enums.ExecutionRecType;
import org.matrix.socket.TestExecuteType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
......@@ -34,7 +36,12 @@ public class ExecutionRecordController {
*
* @param pageSize 每页多少条数据
* @param pageNum 当前页数
* @param name 执行记录名称
* @param userId 查询条件:用户ID
* @param testDataId 查询条件:测试数据ID
* @param testCaseId 查询条件:测试用例ID
* @param unionKey 查询条件:唯一批次号
* @param status 查询条件:运行状态
* @param type 查询条件:执行类型
* @return 分页查询的结果, 执行记录
*/
@ApiOperation("分页查询执行记录")
......@@ -42,11 +49,21 @@ public class ExecutionRecordController {
public ResponseEntity<CommonResultObj<Page<ExecutionRecord>>> findPageExecutionRecord(
int pageSize,
int pageNum,
String name) {
Long userId,
Long testDataId,
Long testCaseId,
String unionKey,
ExecutionRecType status,
TestExecuteType type) {
Page<ExecutionRecord> results = Optional.of(executionRecordService.page(Page.of(pageNum, pageSize)
, Wrappers.lambdaQuery(ExecutionRecord.class)
.like(StringUtils.hasLength(name)
, ExecutionRecord::getName, name))).orElse(new Page<>());
.eq(userId != null, ExecutionRecord::getUserId, userId)
.eq(testDataId != null, ExecutionRecord::getTestDataId, testDataId)
.eq(testCaseId != null, ExecutionRecord::getTestCaseId, testCaseId)
.eq(StringUtils.hasLength(unionKey), ExecutionRecord::getUnionKey, unionKey)
.eq(status != null, ExecutionRecord::getStatus, status)
.eq(type != null, ExecutionRecord::getType, type)
)).orElse(new Page<>());
return results.getRecords().size() != 0
? CommonResult.success(results, "查询成功")
: CommonResult.failed(results, "查询失败或无数据");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论