feat(测试跟踪): 测试计划-性能测试用例添加串行并行执行模式
This commit is contained in:
parent
1bf00efb41
commit
b6b649b679
|
@ -6,7 +6,6 @@ import lombok.Data;
|
|||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.beans.Beans;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -17,6 +16,7 @@ public class HttpConfig {
|
|||
private String protocol = "https";
|
||||
private String defaultCondition;
|
||||
private int port;
|
||||
private boolean isMock;
|
||||
private List<HttpConfigCondition> conditions;
|
||||
private List<KeyValue> headers;
|
||||
|
||||
|
|
|
@ -798,7 +798,7 @@ public class ApiAutomationService {
|
|||
group.setOnSampleError(request.getConfig().isOnSampleError());
|
||||
}
|
||||
// 批量执行的结果直接存储为报告
|
||||
if (isFirst) {
|
||||
if (isFirst && StringUtils.isNotEmpty(request.getId())) {
|
||||
group.setName(request.getId());
|
||||
}
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
@ -848,11 +848,11 @@ public class ApiAutomationService {
|
|||
testPlan.getHashTree().add(group);
|
||||
isFirst = false;
|
||||
}
|
||||
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
|
||||
} catch (Exception ex) {
|
||||
MSException.throwException(ex.getMessage());
|
||||
}
|
||||
|
||||
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
|
||||
return jmeterHashTree;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.metersphere.track.dto.TestPlanLoadCaseDTO;
|
|||
import io.metersphere.track.request.testplan.LoadCaseReportBatchRequest;
|
||||
import io.metersphere.track.request.testplan.LoadCaseReportRequest;
|
||||
import io.metersphere.track.request.testplan.LoadCaseRequest;
|
||||
import io.metersphere.track.request.testplan.RunBatchTestPlanRequest;
|
||||
import io.metersphere.track.service.TestPlanLoadCaseService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
@ -51,6 +52,11 @@ public class TestPlanLoadCaseController {
|
|||
return testPlanLoadCaseService.run(request);
|
||||
}
|
||||
|
||||
@PostMapping("/run/batch")
|
||||
public void runBatch(@RequestBody RunBatchTestPlanRequest request) {
|
||||
testPlanLoadCaseService.runBatch(request);
|
||||
}
|
||||
|
||||
@PostMapping("/report/exist")
|
||||
public Boolean isExistReport(@RequestBody LoadCaseReportRequest request) {
|
||||
return testPlanLoadCaseService.isExistReport(request);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.track.request.testplan;
|
||||
|
||||
import io.metersphere.api.dto.automation.RunModeConfig;
|
||||
import io.metersphere.performance.request.RunTestPlanRequest;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class RunBatchTestPlanRequest {
|
||||
private List<RunTestPlanRequest> requests;
|
||||
private RunModeConfig config;
|
||||
private String userId;
|
||||
}
|
|
@ -7,6 +7,7 @@ import io.metersphere.base.mapper.TestPlanLoadCaseMapper;
|
|||
import io.metersphere.base.mapper.TestPlanMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper;
|
||||
import io.metersphere.commons.constants.TestPlanStatus;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.controller.request.OrderRequest;
|
||||
import io.metersphere.performance.request.RunTestPlanRequest;
|
||||
import io.metersphere.performance.service.PerformanceTestService;
|
||||
|
@ -14,6 +15,9 @@ import io.metersphere.track.dto.TestPlanLoadCaseDTO;
|
|||
import io.metersphere.track.request.testplan.LoadCaseReportBatchRequest;
|
||||
import io.metersphere.track.request.testplan.LoadCaseReportRequest;
|
||||
import io.metersphere.track.request.testplan.LoadCaseRequest;
|
||||
import io.metersphere.track.request.testplan.RunBatchTestPlanRequest;
|
||||
import io.metersphere.track.service.utils.ParallelExecTask;
|
||||
import io.metersphere.track.service.utils.SerialExecTask;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
|
@ -26,6 +30,9 @@ import javax.annotation.Resource;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
|
@ -121,6 +128,38 @@ public class TestPlanLoadCaseService {
|
|||
return reportId;
|
||||
}
|
||||
|
||||
public void runBatch(RunBatchTestPlanRequest request) {
|
||||
if (request.getConfig() != null && request.getConfig().getMode().equals("serial")) {
|
||||
try {
|
||||
serialRun(request);
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(request.getRequests().size());
|
||||
request.getRequests().forEach(item -> {
|
||||
executorService.submit(new ParallelExecTask(performanceTestService, testPlanLoadCaseMapper, item));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void serialRun(RunBatchTestPlanRequest request) throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(request.getRequests().size());
|
||||
for (RunTestPlanRequest runTestPlanRequest : request.getRequests()) {
|
||||
Future<LoadTestReportWithBLOBs> future = executorService.submit(new SerialExecTask(performanceTestService, testPlanLoadCaseMapper, loadTestReportMapper, runTestPlanRequest, request.getConfig()));
|
||||
LoadTestReportWithBLOBs report = future.get();
|
||||
// 如果开启失败结束执行,则判断返回结果状态
|
||||
if (request.getConfig().isOnSampleError()) {
|
||||
TestPlanLoadCaseExample example = new TestPlanLoadCaseExample();
|
||||
example.createCriteria().andLoadReportIdEqualTo(report.getId());
|
||||
List<TestPlanLoadCase> cases = testPlanLoadCaseMapper.selectByExample(example);
|
||||
if (CollectionUtils.isEmpty(cases) || !cases.get(0).getStatus().equals("success")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean isExistReport(LoadCaseReportRequest request) {
|
||||
String reportId = request.getReportId();
|
||||
String testPlanLoadCaseId = request.getTestPlanLoadCaseId();
|
||||
|
@ -159,11 +198,11 @@ public class TestPlanLoadCaseService {
|
|||
testPlanLoadCaseMapper.deleteByExample(example);
|
||||
}
|
||||
|
||||
public void batchDelete(LoadCaseReportBatchRequest request){
|
||||
public void batchDelete(LoadCaseReportBatchRequest request) {
|
||||
List<String> ids = request.getIds();
|
||||
if(request.getCondition()!=null && request.getCondition().isSelectAll()){
|
||||
if (request.getCondition() != null && request.getCondition().isSelectAll()) {
|
||||
ids = this.selectTestPlanLoadCaseIds(request.getCondition());
|
||||
if(request.getCondition().getUnSelectIds()!=null){
|
||||
if (request.getCondition().getUnSelectIds() != null) {
|
||||
ids.removeAll(request.getCondition().getUnSelectIds());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,8 +111,10 @@ public class TestPlanScenarioCaseService {
|
|||
|
||||
RunScenarioRequest request = new RunScenarioRequest();
|
||||
request.setIds(scenarioIds);
|
||||
request.setReportId(testPlanScenarioRequest.getId());
|
||||
request.setScenarioTestPlanIdMap(scenarioIdApiScarionMap);
|
||||
request.setRunMode(ApiRunMode.SCENARIO_PLAN.name());
|
||||
request.setId(testPlanScenarioRequest.getId());
|
||||
return apiAutomationService.run(request);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package io.metersphere.track.service.utils;
|
||||
|
||||
import io.metersphere.base.domain.TestPlanLoadCase;
|
||||
import io.metersphere.base.mapper.TestPlanLoadCaseMapper;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.performance.request.RunTestPlanRequest;
|
||||
import io.metersphere.performance.service.PerformanceTestService;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ParallelExecTask<T> implements Callable<T> {
|
||||
private RunTestPlanRequest request;
|
||||
private PerformanceTestService performanceTestService;
|
||||
private TestPlanLoadCaseMapper testPlanLoadCaseMapper;
|
||||
|
||||
public ParallelExecTask(PerformanceTestService performanceTestService, TestPlanLoadCaseMapper testPlanLoadCaseMapper, RunTestPlanRequest request) {
|
||||
this.performanceTestService = performanceTestService;
|
||||
this.testPlanLoadCaseMapper = testPlanLoadCaseMapper;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T call() {
|
||||
try {
|
||||
String reportId = performanceTestService.run(request);
|
||||
TestPlanLoadCase testPlanLoadCase = new TestPlanLoadCase();
|
||||
testPlanLoadCase.setId(request.getTestPlanLoadId());
|
||||
testPlanLoadCase.setLoadReportId(reportId);
|
||||
testPlanLoadCaseMapper.updateByPrimaryKeySelective(testPlanLoadCase);
|
||||
return (T) reportId;
|
||||
|
||||
} catch (Exception ex) {
|
||||
LogUtil.error(ex.getMessage());
|
||||
MSException.throwException(ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package io.metersphere.track.service.utils;
|
||||
|
||||
import io.metersphere.api.dto.automation.RunModeConfig;
|
||||
import io.metersphere.base.domain.LoadTestReportWithBLOBs;
|
||||
import io.metersphere.base.domain.TestPlanLoadCase;
|
||||
import io.metersphere.base.mapper.LoadTestReportMapper;
|
||||
import io.metersphere.base.mapper.TestPlanLoadCaseMapper;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.performance.request.RunTestPlanRequest;
|
||||
import io.metersphere.performance.service.PerformanceTestService;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class SerialExecTask<T> implements Callable<T> {
|
||||
private RunTestPlanRequest request;
|
||||
private RunModeConfig config;
|
||||
private PerformanceTestService performanceTestService;
|
||||
private TestPlanLoadCaseMapper testPlanLoadCaseMapper;
|
||||
private LoadTestReportMapper loadTestReportMapper;
|
||||
|
||||
public SerialExecTask(PerformanceTestService performanceTestService, TestPlanLoadCaseMapper testPlanLoadCaseMapper,LoadTestReportMapper loadTestReportMapper, RunTestPlanRequest request, RunModeConfig config) {
|
||||
this.performanceTestService = performanceTestService;
|
||||
this.testPlanLoadCaseMapper = testPlanLoadCaseMapper;
|
||||
this.loadTestReportMapper = loadTestReportMapper;
|
||||
this.request = request;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T call() {
|
||||
try {
|
||||
// 串行,开启轮询等待
|
||||
String reportId = performanceTestService.run(request);
|
||||
TestPlanLoadCase testPlanLoadCase = new TestPlanLoadCase();
|
||||
testPlanLoadCase.setId(request.getTestPlanLoadId());
|
||||
testPlanLoadCase.setLoadReportId(reportId);
|
||||
testPlanLoadCaseMapper.updateByPrimaryKeySelective(testPlanLoadCase);
|
||||
LoadTestReportWithBLOBs report = null;
|
||||
// 轮询查看报告状态,最多200次,防止死循环
|
||||
int index = 1;
|
||||
while (index < 200) {
|
||||
Thread.sleep(3000);
|
||||
index++;
|
||||
report = loadTestReportMapper.selectByPrimaryKey(reportId);
|
||||
if (report != null && (report.getStatus().equals("Completed") || report.getStatus().equals("Error") || report.getStatus().equals("Saved"))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (T) report;
|
||||
|
||||
} catch (Exception ex) {
|
||||
LogUtil.error(ex.getMessage());
|
||||
MSException.throwException(ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -463,7 +463,6 @@ export default {
|
|||
this.$success(this.$t('test_track.cancel_relevance_success'));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -482,9 +481,7 @@ export default {
|
|||
},
|
||||
singleRun(row) {
|
||||
this.runData = [];
|
||||
|
||||
this.rowLoading = row.id;
|
||||
|
||||
this.$get('/api/testcase/get/' + row.caseId, (response) => {
|
||||
let apiCase = response.data;
|
||||
let request = JSON.parse(apiCase.request);
|
||||
|
@ -504,6 +501,8 @@ export default {
|
|||
return new Promise((resolve) => {
|
||||
let index = 1;
|
||||
this.runData = [];
|
||||
// 按照列表顺序排序
|
||||
this.orderBySelectRows(this.selectRows);
|
||||
this.selectRows.forEach(row => {
|
||||
this.$get('/api/testcase/get/' + row.caseId, (response) => {
|
||||
let apiCase = response.data;
|
||||
|
@ -512,7 +511,7 @@ export default {
|
|||
request.id = row.id;
|
||||
request.useEnvironment = row.environmentId;
|
||||
this.runData.unshift(request);
|
||||
if (this.selectRows.size === index) {
|
||||
if (this.selectRows.length === index) {
|
||||
resolve();
|
||||
}
|
||||
index++;
|
||||
|
@ -558,12 +557,37 @@ export default {
|
|||
// 批量修改其它
|
||||
}
|
||||
},
|
||||
handleBatchExecute() {
|
||||
this.getData().then(() => {
|
||||
if (this.runData && this.runData.length > 0) {
|
||||
this.$refs.runMode.open();
|
||||
orderBySelectRows(rows){
|
||||
let selectIds = Array.from(rows).map(row => row.id);
|
||||
let array = [];
|
||||
for(let i in this.tableData){
|
||||
if(selectIds.indexOf(this.tableData[i].id)!==-1){
|
||||
array.push(this.tableData[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.selectRows = array;
|
||||
},
|
||||
handleBatchExecute() {
|
||||
if(this.condition != null && this.condition.selectAll) {
|
||||
this.$alert(this.$t('commons.option_cannot_spread_pages'), '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.getData().then(() => {
|
||||
if (this.runData && this.runData.length > 0) {
|
||||
this.$refs.runMode.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
}else {
|
||||
this.getData().then(() => {
|
||||
if (this.runData && this.runData.length > 0) {
|
||||
this.$refs.runMode.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
handleRunBatch(config) {
|
||||
let testPlan = new TestPlan();
|
||||
|
|
|
@ -294,9 +294,31 @@ export default {
|
|||
})
|
||||
},
|
||||
handleBatchExecute() {
|
||||
this.$refs.runMode.open();
|
||||
if(this.condition != null && this.condition.selectAll) {
|
||||
this.$alert(this.$t('commons.option_cannot_spread_pages'), '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.$refs.runMode.open();
|
||||
}
|
||||
}
|
||||
})
|
||||
}else {
|
||||
this.$refs.runMode.open();
|
||||
}
|
||||
},
|
||||
orderBySelectRows(rows){
|
||||
let selectIds = Array.from(rows).map(row => row.id);
|
||||
let array = [];
|
||||
for(let i in this.tableData){
|
||||
if(selectIds.indexOf(this.tableData[i].id)!==-1){
|
||||
array.push(this.tableData[i]);
|
||||
}
|
||||
}
|
||||
this.selectRows = array;
|
||||
},
|
||||
handleRunBatch(config){
|
||||
this.orderBySelectRows(this.selectRows);
|
||||
if (this.reviewId) {
|
||||
let param = {config : config,planCaseIds:[]};
|
||||
this.selectRows.forEach(row => {
|
||||
|
@ -320,6 +342,7 @@ export default {
|
|||
execute(row) {
|
||||
this.infoDb = false;
|
||||
let param ={planCaseIds: []};
|
||||
this.reportId = "";
|
||||
this.buildExecuteParam(param,row);
|
||||
if (this.planId) {
|
||||
this.$post("/test/plan/scenario/case/run", param, response => {
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
</el-card>
|
||||
<ms-plan-run-mode @handleRunBatch="runBatch" ref="runMode"/>
|
||||
|
||||
<load-case-report :report-id="reportId" ref="loadCaseReport" @refresh="initTable"/>
|
||||
</div>
|
||||
|
@ -162,6 +163,7 @@ import {Test_Plan_Load_Case, Track_Test_Case} from "@/business/components/common
|
|||
import {getCurrentUser} from "@/common/js/utils";
|
||||
import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate";
|
||||
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
|
||||
import MsPlanRunMode from "../../../common/PlanRunMode";
|
||||
|
||||
export default {
|
||||
name: "TestPlanLoadCaseList",
|
||||
|
@ -174,7 +176,8 @@ export default {
|
|||
MsTablePagination,
|
||||
MsPerformanceTestStatus,
|
||||
MsTableOperatorButton,
|
||||
MsTableHeaderSelectPopover
|
||||
MsTableHeaderSelectPopover,
|
||||
MsPlanRunMode
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -225,8 +228,6 @@ export default {
|
|||
created() {
|
||||
this.initTable();
|
||||
this.refreshStatus();
|
||||
|
||||
|
||||
},
|
||||
watch: {
|
||||
selectProjectId() {
|
||||
|
@ -237,6 +238,60 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
orderBySelectRows(rows){
|
||||
let selectIds = Array.from(rows).map(row => row.id);
|
||||
let array = [];
|
||||
for(let i in this.tableData){
|
||||
if(selectIds.indexOf(this.tableData[i].id)!==-1){
|
||||
array.push(this.tableData[i]);
|
||||
}
|
||||
}
|
||||
this.selectRows = array;
|
||||
},
|
||||
runBatch(config){
|
||||
this.orderBySelectRows(this.selectRows);
|
||||
if(this.condition != null && this.condition.selectAll){
|
||||
this.$alert(this.$t('commons.option_cannot_spread_pages'), '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let runArr = [];
|
||||
this.selectRows.forEach(loadCase => {
|
||||
runArr.push( {
|
||||
id: loadCase.loadCaseId,
|
||||
testPlanLoadId: loadCase.id,
|
||||
triggerMode: 'CASE'
|
||||
})
|
||||
});
|
||||
let obj = {config:config,requests:runArr,userId:getCurrentUser().id};
|
||||
this._runBatch(obj);
|
||||
this.refreshStatus();
|
||||
}
|
||||
}
|
||||
});
|
||||
}else {
|
||||
let runArr = [];
|
||||
this.selectRows.forEach(loadCase => {
|
||||
runArr.push( {
|
||||
id: loadCase.loadCaseId,
|
||||
testPlanLoadId: loadCase.id,
|
||||
triggerMode: 'CASE'
|
||||
})
|
||||
});
|
||||
let obj = {config:config,requests:runArr,userId:getCurrentUser().id};
|
||||
this._runBatch(obj);
|
||||
this.initTable();
|
||||
this.refreshStatus();
|
||||
}
|
||||
},
|
||||
_runBatch(loadCases) {
|
||||
this.$post('/test/plan/load/case/run/batch',loadCases, response => {
|
||||
});
|
||||
this.$success(this.$t('test_track.plan.load_case.exec'));
|
||||
this.initTable();
|
||||
this.refreshStatus();
|
||||
},
|
||||
|
||||
customHeader() {
|
||||
this.$refs.headerCustom.open(this.tableLabel)
|
||||
},
|
||||
|
@ -338,24 +393,7 @@ export default {
|
|||
})
|
||||
},
|
||||
handleRunBatch() {
|
||||
if(this.condition != null && this.condition.selectAll){
|
||||
this.$alert(this.$t('commons.option_cannot_spread_pages'), '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.selectRows.forEach(loadCase => {
|
||||
this._run(loadCase);
|
||||
});
|
||||
this.refreshStatus();
|
||||
}
|
||||
}
|
||||
});
|
||||
}else {
|
||||
this.selectRows.forEach(loadCase => {
|
||||
this._run(loadCase);
|
||||
});
|
||||
this.refreshStatus();
|
||||
}
|
||||
this.$refs.runMode.open();
|
||||
},
|
||||
run(loadCase) {
|
||||
this._run(loadCase);
|
||||
|
|
Loading…
Reference in New Issue