From badeee9f20d7899228b91b6a003db7919a5386fd Mon Sep 17 00:00:00 2001 From: song-tianyang Date: Mon, 6 Dec 2021 18:04:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E6=89=A7=E8=A1=8C):=20=E6=9B=B4=E6=94=B9=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E6=89=A7=E8=A1=8C=E7=9A=84=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更改测试计划执行时的线程顺序为先开启监听线程再开启执行线程、测试计划列表展现时增加结果状态检查 --- .../api/service/ApiScenarioReportService.java | 46 ++++---- .../track/service/TestPlanReportService.java | 105 +++++++++++++++--- .../track/service/TestPlanService.java | 10 +- 3 files changed, 121 insertions(+), 40 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java index 93b1fce686..48a2c41dfa 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -322,7 +322,6 @@ public class ApiScenarioReportService { } ApiScenarioReport report = editReport(scenarioResult, startTime); - if(report != null){ TestResult newResult = createTestResult(result.getTestId(), scenarioResult); newResult.setConsole(result.getConsole()); @@ -346,26 +345,37 @@ public class ApiScenarioReportService { } else { planScenarioId = report.getScenarioId(); } + + String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError())); + TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(planScenarioId); - report.setScenarioId(testPlanApiScenario.getApiScenarioId()); - report.setTestPlanScenarioId(planScenarioId); + if(testPlanApiScenario != null){ + //更新测试计划场景相关状态 + report.setScenarioId(testPlanApiScenario.getApiScenarioId()); + report.setTestPlanScenarioId(planScenarioId); + + if (scenarioResult.getError() > 0) { + scenarioAndErrorMap.put(testPlanApiScenario.getId(), TestPlanApiExecuteStatus.FAILD.name()); + testPlanApiScenario.setLastResult(ScenarioStatus.Fail.name()); + testPlanApiScenario.setPassRate(passRate); + } else { + scenarioAndErrorMap.put(testPlanApiScenario.getId(), TestPlanApiExecuteStatus.SUCCESS.name()); + testPlanApiScenario.setLastResult(ScenarioStatus.Success.name()); + } + testPlanApiScenario.setReportId(report.getId()); + testPlanApiScenario.setUpdateTime(System.currentTimeMillis()); + testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario); + scenarioIdList.add(testPlanApiScenario.getApiScenarioId()); + }else { + LogUtil.info("TestPlanReport_Id is null. scenario report id : ["+report.getId()+"]; planScenarioIdArr:["+report.getScenarioId()+"] DATA:"+JSON.toJSONString(scenarioResult)); + } + report.setEndTime(System.currentTimeMillis()); apiScenarioReportMapper.updateByPrimaryKeySelective(report); planScenarioReportMap.put(planScenarioId, report.getId()); - if (scenarioResult.getError() > 0) { - scenarioAndErrorMap.put(testPlanApiScenario.getId(), TestPlanApiExecuteStatus.FAILD.name()); - testPlanApiScenario.setLastResult(ScenarioStatus.Fail.name()); - } else { - scenarioAndErrorMap.put(testPlanApiScenario.getId(), TestPlanApiExecuteStatus.SUCCESS.name()); - testPlanApiScenario.setLastResult(ScenarioStatus.Success.name()); - } - - String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError())); - testPlanApiScenario.setPassRate(passRate); // 报告详情内容 ApiScenarioReportDetail detail = new ApiScenarioReportDetail(); - detail.setContent(JSON.toJSONString(newResult).getBytes(StandardCharsets.UTF_8)); detail.setReportId(report.getId()); detail.setProjectId(report.getProjectId()); @@ -374,15 +384,11 @@ public class ApiScenarioReportService { } apiScenarioReportDetailMapper.insert(detail); - testPlanApiScenario.setReportId(report.getId()); - report.setEndTime(System.currentTimeMillis()); - testPlanApiScenario.setUpdateTime(System.currentTimeMillis()); - testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario); - scenarioIdList.add(testPlanApiScenario.getApiScenarioId()); + scenarioNames.append(report.getName()).append(","); // 更新场景状态 - ApiScenario scenario = apiScenarioMapper.selectByPrimaryKey(testPlanApiScenario.getApiScenarioId()); + ApiScenario scenario = apiScenarioMapper.selectByPrimaryKey(report.getScenarioId()); if (scenario != null) { if (scenarioResult.getError() > 0) { scenario.setLastResult("Fail"); diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java index 862864495c..6065d8f9f1 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java @@ -65,6 +65,8 @@ public class TestPlanReportService { @Resource ApiScenarioReportMapper apiScenarioReportMapper; @Resource + ApiDefinitionExecResultMapper apiDefinitionExecResultMapper; + @Resource TestPlanReportDataMapper testPlanReportDataMapper; @Resource ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper; @@ -126,6 +128,7 @@ public class TestPlanReportService { } } list = extTestPlanReportMapper.list(request); + this.checkReportStatus(list); return list; } @@ -612,8 +615,8 @@ public class TestPlanReportService { testPlanReport.setEndTime(endTime); testPlanReport.setUpdateTime(endTime); } - TestPlanReportExecuteCatch.remove(testPlanReport.getId()); + testPlanLog.info("Task is finish. Remove listener:" + testPlanReport.getId()); } testPlanReportContentMapper.updateByPrimaryKeySelective(parseReportDaoToReportContent(reportDTO, reportContent)); @@ -1057,7 +1060,9 @@ public class TestPlanReportService { public synchronized TestPlanReport updateExecuteApis(String planReportId) { TestPlanExecuteInfo executeInfo = TestPlanReportExecuteCatch.getTestPlanExecuteInfo(planReportId); - + if (executeInfo == null) { + return null; + } Map executeApiCaseIdMap = executeInfo.getApiCaseExecInfo(); Map executeScenarioCaseIdMap = executeInfo.getApiScenarioCaseExecInfo(); Map executePerformanceIdMap = executeInfo.getLoadCaseExecInfo(); @@ -1102,17 +1107,11 @@ public class TestPlanReportService { } public void countReport(String planReportId) { - TestPlanExecuteInfo executeInfo = TestPlanReportExecuteCatch.getTestPlanExecuteInfo(planReportId); - int unFinishNum = executeInfo.countUnFinishedNum(); - if (unFinishNum > 0) { - //如果间隔超过5分钟没有案例执行完成,则把执行结果变成false - long lastCountTime = executeInfo.getLastFinishedNumCountTime(); - long nowTime = System.currentTimeMillis(); - if (nowTime - lastCountTime > 1800000) { - TestPlanReportExecuteCatch.finishAllTask(planReportId); - } + boolean isTimeOut = this.checkTestPlanReportIsTimeOut(planReportId); + if (isTimeOut) { + //判断是否超时。超时时强行停止任务 + TestPlanReportExecuteCatch.finishAllTask(planReportId); } - this.updateExecuteApis(planReportId); } @@ -1172,26 +1171,55 @@ public class TestPlanReportService { private void updateReportExecResult(TestPlanReportContentWithBLOBs testPlanReportContent) { boolean isUpdate = false; + boolean isTaskRunning = false; + boolean reportHasData = false; + if (StringUtils.isNotBlank(testPlanReportContent.getApiAllCases())) { + reportHasData = true; + List allCases = JSONObject.parseArray(testPlanReportContent.getApiAllCases(), TestPlanFailureApiDTO.class); + for (TestPlanFailureApiDTO dto : allCases) { + String status = dto.getExecResult(); + if (StringUtils.equalsAnyIgnoreCase(status, "Running", "Waiting")) { + isUpdate = true; + ApiDefinitionExecResult definitionExecResult = apiDefinitionExecResultMapper.selectByPrimaryKey(dto.getReportId()); + if (definitionExecResult != null) { + dto.setExecResult(definitionExecResult.getStatus()); + } + } + + if (StringUtils.equalsAnyIgnoreCase(dto.getExecResult(), "Running", "Waiting")) { + isTaskRunning = true; + } + } + testPlanReportContent.setApiAllCases(JSONArray.toJSONString(allCases)); + } + if (StringUtils.isNotBlank(testPlanReportContent.getScenarioAllCases())) { + reportHasData = true; List allCases = JSONObject.parseArray(testPlanReportContent.getScenarioAllCases(), TestPlanFailureScenarioDTO.class); for (TestPlanFailureScenarioDTO dto : allCases) { - if (StringUtils.equalsAnyIgnoreCase("Underway",dto.getStatus(), dto.getLastResult())) { + String lastResult = dto.getLastResult(); + if (StringUtils.equalsAnyIgnoreCase(lastResult, "Running", "Waiting", "Underway")) { isUpdate = true; ApiScenarioReport apiReport = apiScenarioReportMapper.selectByPrimaryKey(dto.getReportId()); if (apiReport != null) { dto.setLastResult(apiReport.getStatus()); dto.setStatus(apiReport.getStatus()); } - }else if (StringUtils.equalsAnyIgnoreCase("Error",dto.getStatus(), dto.getLastResult())) { + } else if (StringUtils.equalsAnyIgnoreCase("Error", lastResult)) { isUpdate = true; dto.setLastResult("Fail"); dto.setStatus("Fail"); } + + if (StringUtils.equalsAnyIgnoreCase(dto.getLastResult(), "Running", "Waiting", "Underway")) { + isTaskRunning = true; + } } testPlanReportContent.setScenarioAllCases(JSONArray.toJSONString(allCases)); } if (StringUtils.isNotBlank(testPlanReportContent.getLoadAllCases())) { + reportHasData = true; List allCases = JSONObject.parseArray(testPlanReportContent.getLoadAllCases(), TestPlanLoadCaseDTO.class); for (TestPlanLoadCaseDTO dto : allCases) { if (StringUtils.equalsIgnoreCase(dto.getStatus(), "run")) { @@ -1201,12 +1229,20 @@ public class TestPlanReportService { dto.setStatus(report.getStatus()); } } + + if (StringUtils.equalsAnyIgnoreCase("Underway", dto.getStatus(), dto.getStatus())) { + isTaskRunning = true; + } } testPlanReportContent.setLoadAllCases(JSONArray.toJSONString(allCases)); } if (isUpdate) { testPlanReportContentMapper.updateByPrimaryKeyWithBLOBs(testPlanReportContent); } + + if (!isTaskRunning && reportHasData) { + this.finishTestPlanReport(testPlanReportContent.getTestPlanReportId()); + } } public void finishReport(TestPlanReport testPlanReport) { @@ -1215,6 +1251,45 @@ public class TestPlanReportService { testPlanReport.setUpdateTime(endTime); testPlanReport.setStatus(TestPlanReportStatus.FAILED.name()); - testPlanReportMapper.updateByPrimaryKey(testPlanReport); + testPlanReportMapper.updateByPrimaryKeySelective(testPlanReport); } + + private void checkReportStatus(List list) { + String errorStatus = "FAILED"; + for (TestPlanReportDTO dto : list) { + if (StringUtils.equalsIgnoreCase(dto.getStatus(), "Running")) { + if (!TestPlanReportExecuteCatch.containsReport(dto.getId())) { + //测试计划报告处于Running状态,且监听中不存在此ID,更改Running的运行状态。一般在测试计划执行结束之前服务关闭时会出现这种情况 + TestPlanReport report = new TestPlanReport(); + report.setId(dto.getId()); + this.finishReport(report); + dto.setStatus(errorStatus); + } + } + } + } + + private boolean checkTestPlanReportIsTimeOut(String planReportId) { + TestPlanExecuteInfo executeInfo = TestPlanReportExecuteCatch.getTestPlanExecuteInfo(planReportId); + int unFinishNum = executeInfo.countUnFinishedNum(); + if (unFinishNum > 0) { + //15分钟没有案例执行结果更新,则定位超时 + long lastCountTime = executeInfo.getLastFinishedNumCountTime(); + long nowTime = System.currentTimeMillis(); + if (nowTime - lastCountTime > 9000) { + return true; + } + } + return false; + } + + private void finishTestPlanReport(String planReportId) { + TestPlanReport testPlanReport = testPlanReportMapper.selectByPrimaryKey(planReportId); + if (testPlanReport != null && StringUtils.equalsIgnoreCase("Running", testPlanReport.getStatus())) { + this.finishReport(testPlanReport); + testPlanLog.info("结束测试计划报告:[" + planReportId + "]"); + } + TestPlanReportExecuteCatch.remove(planReportId); + } + } diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index 6e414116cb..128324fdf6 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -190,7 +190,7 @@ public class TestPlanService { @Resource private TestPlanFollowMapper testPlanFollowMapper; - private final ExecutorService executorService = Executors.newFixedThreadPool(20, new NamedThreadFactory("TestPlanService")); + private final ExecutorService executorService = Executors.newFixedThreadPool(40, new NamedThreadFactory("TestPlanService")); public synchronized TestPlan addTestPlan(AddTestPlanRequest testPlan) { if (getTestPlanByName(testPlan.getName()).size() > 0) { @@ -1125,11 +1125,13 @@ public class TestPlanService { extTestPlanMapper.updateActualEndTimeIsNullById(testPlanID); String planReportId = testPlanReport.getId(); testPlanLog.info("ReportId[" + planReportId + "] created. TestPlanID:[" + testPlanID + "]. " + "API Run Config:【" + apiRunConfig + "】"); + //开启测试计划执行状态的监听 + this.listenTaskExecuteStatus(planReportId); + //不同任务的执行ID Map executePerformanceIdMap = new HashMap<>(); Map executeApiCaseIdMap = new HashMap<>(); Map executeScenarioCaseIdMap = new HashMap<>(); - //执行性能测试任务 Map performaneReportIDMap = new LinkedHashMap<>(); Map performaneThreadIDMap = new LinkedHashMap<>(); @@ -1193,7 +1195,6 @@ public class TestPlanService { this.executeApiTestCase(triggerMode, planReportId, new ArrayList<>(planApiCaseMap.keySet()), runModeConfig); //执行场景执行任务 this.executeScenarioCase(planReportId, testPlanID, projectID, runModeConfig, triggerMode, userId, planScenarioIdsMap); - this.listenTaskExecuteStatus(planReportId); return testPlanReport.getId(); } @@ -1207,6 +1208,7 @@ public class TestPlanService { Thread.sleep(10000); } } catch (InterruptedException e) { + TestPlanReportExecuteCatch.remove(planReportId); e.printStackTrace(); } }); @@ -1215,8 +1217,6 @@ public class TestPlanService { private void executeApiTestCase(String triggerMode, String planReportId, List planCaseIds, RunModeConfig runModeConfig) { executorService.submit(() -> { BatchRunDefinitionRequest request = new BatchRunDefinitionRequest(); -// List planIdList = new ArrayList<>(1); -// planIdList.add(testPlanId); if (StringUtils.equals(triggerMode, ReportTriggerMode.API.name())) { request.setTriggerMode(ApiRunMode.JENKINS_API_PLAN.name()); } else if (StringUtils.equals(triggerMode, ReportTriggerMode.MANUAL.name())) {