diff --git a/backend/src/main/java/io/metersphere/controller/PerformanceReportController.java b/backend/src/main/java/io/metersphere/controller/PerformanceReportController.java index 9c6dde50fa..662ffab951 100644 --- a/backend/src/main/java/io/metersphere/controller/PerformanceReportController.java +++ b/backend/src/main/java/io/metersphere/controller/PerformanceReportController.java @@ -8,12 +8,7 @@ import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; import io.metersphere.controller.request.ReportRequest; import io.metersphere.dto.ReportDTO; -import io.metersphere.report.base.ChartsData; -import io.metersphere.report.base.Errors; -import io.metersphere.report.base.ReportTimeInfo; -import io.metersphere.report.base.TestOverview; -import io.metersphere.report.dto.ErrorsTop5DTO; -import io.metersphere.report.dto.RequestStatisticsDTO; +import io.metersphere.report.base.*; import io.metersphere.service.ReportService; import io.metersphere.user.SessionUtils; import org.apache.shiro.authz.annotation.Logical; @@ -59,7 +54,7 @@ public class PerformanceReportController { } @GetMapping("/content/{reportId}") - public RequestStatisticsDTO getReportContent(@PathVariable String reportId) { + public List getReportContent(@PathVariable String reportId) { return reportService.getReport(reportId); } @@ -69,7 +64,7 @@ public class PerformanceReportController { } @GetMapping("/content/errors_top5/{reportId}") - public ErrorsTop5DTO getReportErrorsTop5(@PathVariable String reportId) { + public List getReportErrorsTop5(@PathVariable String reportId) { return reportService.getReportErrorsTOP5(reportId); } diff --git a/backend/src/main/java/io/metersphere/controller/PerformanceTestController.java b/backend/src/main/java/io/metersphere/controller/PerformanceTestController.java index a046035ed0..60b8c2c59e 100644 --- a/backend/src/main/java/io/metersphere/controller/PerformanceTestController.java +++ b/backend/src/main/java/io/metersphere/controller/PerformanceTestController.java @@ -21,6 +21,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.util.List; +import java.util.Map; @RestController @RequestMapping(value = "performance") @@ -88,6 +89,11 @@ public class PerformanceTestController { performanceTestService.run(request); } + @GetMapping("/log/{testId}") + public Map stop(@PathVariable String testId) { + return performanceTestService.log(testId); + } + @GetMapping("/file/metadata/{testId}") public List getFileMetadata(@PathVariable String testId) { return fileService.getFileMetadataByTestId(testId); diff --git a/backend/src/main/java/io/metersphere/controller/TestPlanTestCaseController.java b/backend/src/main/java/io/metersphere/controller/TestPlanTestCaseController.java index 8819e5d113..d46fd3cd4d 100644 --- a/backend/src/main/java/io/metersphere/controller/TestPlanTestCaseController.java +++ b/backend/src/main/java/io/metersphere/controller/TestPlanTestCaseController.java @@ -29,6 +29,11 @@ public class TestPlanTestCaseController { return PageUtils.setPageInfo(page, testPlanTestCaseService.getTestPlanCases(request)); } + @PostMapping("/list/all") + public List getTestPlanCases(@RequestBody QueryTestPlanCaseRequest request){ + return testPlanTestCaseService.getTestPlanCases(request); + } + @PostMapping("/edit") public void editTestCase(@RequestBody TestPlanTestCase testPlanTestCase){ testPlanTestCaseService.editTestCase(testPlanTestCase); diff --git a/backend/src/main/java/io/metersphere/engine/docker/DockerTestEngine.java b/backend/src/main/java/io/metersphere/engine/docker/DockerTestEngine.java index a898fb7027..aca39565fe 100644 --- a/backend/src/main/java/io/metersphere/engine/docker/DockerTestEngine.java +++ b/backend/src/main/java/io/metersphere/engine/docker/DockerTestEngine.java @@ -10,7 +10,6 @@ import io.metersphere.dto.NodeDTO; import io.metersphere.engine.AbstractEngine; import io.metersphere.engine.EngineContext; import io.metersphere.engine.EngineFactory; -import io.metersphere.engine.docker.request.BaseRequest; import io.metersphere.engine.docker.request.TestRequest; import io.metersphere.i18n.Translator; import org.springframework.web.client.RestTemplate; @@ -91,14 +90,13 @@ public class DockerTestEngine extends AbstractEngine { public void stop() { // TODO 停止运行测试 String testId = loadTest.getId(); - BaseRequest request = new BaseRequest(); this.resourceList.forEach(r -> { NodeDTO node = JSON.parseObject(r.getConfiguration(), NodeDTO.class); String ip = node.getIp(); Integer port = node.getPort(); String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port); - restTemplate.postForObject(uri, request, String.class); + restTemplate.getForObject(uri, String.class); }); } @@ -106,14 +104,13 @@ public class DockerTestEngine extends AbstractEngine { public Map log() { String testId = loadTest.getId(); Map logs = new HashMap<>(); - BaseRequest request = new BaseRequest(); this.resourceList.forEach(r -> { NodeDTO node = JSON.parseObject(r.getConfiguration(), NodeDTO.class); String ip = node.getIp(); Integer port = node.getPort(); String uri = String.format(BASE_URL + "/jmeter/container/log/" + testId, ip, port); - String log = restTemplate.postForObject(uri, request, String.class); + String log = restTemplate.getForObject(uri, String.class); logs.put(node.getIp(), log); }); return logs; diff --git a/backend/src/main/java/io/metersphere/engine/kubernetes/KubernetesTestEngine.java b/backend/src/main/java/io/metersphere/engine/kubernetes/KubernetesTestEngine.java index 7f74c6dfab..eec1302b09 100644 --- a/backend/src/main/java/io/metersphere/engine/kubernetes/KubernetesTestEngine.java +++ b/backend/src/main/java/io/metersphere/engine/kubernetes/KubernetesTestEngine.java @@ -133,8 +133,10 @@ public class KubernetesTestEngine extends AbstractEngine { ClientCredential clientCredential = JSON.parseObject(configuration, ClientCredential.class); KubernetesProvider provider = new KubernetesProvider(JSON.toJSONString(clientCredential)); provider.confirmNamespace(loadTest.getProjectId()); - String joblog = provider.getKubernetesClient().batch().jobs().inNamespace(loadTest.getProjectId()).withName("job-" + loadTest.getId()).getLog(); - logs.put(clientCredential.getMasterUrl(), joblog); + try (KubernetesClient client = provider.getKubernetesClient()) { + String joblog = client.batch().jobs().inNamespace(loadTest.getProjectId()).withName("job-" + loadTest.getId()).getLog(); + logs.put(clientCredential.getMasterUrl(), joblog); + } } catch (Exception e) { MSException.throwException(e); } diff --git a/backend/src/main/java/io/metersphere/report/GenerateReport.java b/backend/src/main/java/io/metersphere/report/GenerateReport.java index 870b4b6673..9cd66713ab 100644 --- a/backend/src/main/java/io/metersphere/report/GenerateReport.java +++ b/backend/src/main/java/io/metersphere/report/GenerateReport.java @@ -4,32 +4,26 @@ import com.opencsv.bean.CsvToBean; import com.opencsv.bean.CsvToBeanBuilder; import com.opencsv.bean.HeaderColumnNameMappingStrategy; import io.metersphere.report.base.*; -import io.metersphere.report.dto.ErrorsTop5DTO; -import io.metersphere.report.dto.RequestStatisticsDTO; import io.metersphere.report.parse.ResultDataParse; -import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.report.processor.ErrorsSummaryConsumer; +import org.apache.jmeter.report.processor.StatisticsSummaryConsumer; +import org.apache.jmeter.report.processor.Top5ErrorsBySamplerConsumer; import org.apache.jmeter.report.processor.graph.impl.ActiveThreadsGraphConsumer; import org.apache.jmeter.report.processor.graph.impl.HitsPerSecondGraphConsumer; import org.apache.jmeter.report.processor.graph.impl.ResponseTimeOverTimeGraphConsumer; import java.io.Reader; import java.io.StringReader; +import java.math.BigDecimal; import java.text.DecimalFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; -import java.util.stream.Collectors; public class GenerateReport { - private static final Integer ERRORS_TOP_SIZE = 5; - private static final String DATE_TIME_PATTERN = "yyyy/MM/dd HH:mm:ss"; - private static final String TIME_PATTERN = "HH:mm:ss"; - private static List resolver(String jtlString) { HeaderColumnNameMappingStrategy ms = new HeaderColumnNameMappingStrategy<>(); ms.setType(Metric.class); @@ -47,291 +41,54 @@ public class GenerateReport { return null; } - public static RequestStatisticsDTO getRequestStatistics(String jtlString) { - List allElapseTimeList = new ArrayList<>(); - List requestStatisticsList = new ArrayList<>(); - DecimalFormat decimalFormat = new DecimalFormat("0.00"); - - List totalMetricList = resolver(jtlString); - Map> jtlLabelMap = totalMetricList.stream().collect(Collectors.groupingBy(Metric::getLabel)); - Iterator>> iterator = jtlLabelMap.entrySet().iterator(); - - int totalElapsedTime = 0; - float totalBytes = 0f; - - while (iterator.hasNext()) { - Map.Entry> entry = iterator.next(); - String label = entry.getKey(); - List metricList = entry.getValue(); - List elapsedList = new ArrayList<>(); - - int jtlSamplesSize = 0, oneLineElapsedTime = 0, failSize = 0; - float oneLineBytes = 0f; - - for (int i = 0; i < metricList.size(); i++) { - try { - Metric row = metricList.get(i); - String elapsed = row.getElapsed(); - oneLineElapsedTime += Integer.parseInt(elapsed); - totalElapsedTime += Integer.parseInt(elapsed); - elapsedList.add(Integer.valueOf(elapsed)); - allElapseTimeList.add(Integer.valueOf(elapsed)); - - String isSuccess = row.getSuccess(); - if (!"true".equals(isSuccess)) { - failSize++; - } - String bytes = row.getBytes(); - oneLineBytes += Float.parseFloat(bytes); - totalBytes += Float.parseFloat(bytes); - jtlSamplesSize++; - } catch (Exception e) { - System.out.println("exception i:" + i); - } - } - - Collections.sort(elapsedList); - - int tp90 = elapsedList.size() * 90 / 100; - int tp95 = elapsedList.size() * 95 / 100; - int tp99 = elapsedList.size() * 99 / 100; - - metricList.sort(Comparator.comparing(t0 -> Long.valueOf(t0.getTimestamp()))); - long time = Long.parseLong(metricList.get(metricList.size() - 1).getTimestamp()) - Long.parseLong(metricList.get(0).getTimestamp()) - + Long.parseLong(metricList.get(metricList.size() - 1).getElapsed()); - - RequestStatistics requestStatistics = new RequestStatistics(); - requestStatistics.setRequestLabel(label); - requestStatistics.setSamples(jtlSamplesSize); - - String average = decimalFormat.format((float) oneLineElapsedTime / jtlSamplesSize); - requestStatistics.setAverage(average); - - /* - * TP90的计算 - * 1,把一段时间内全部的请求的响应时间,从小到大排序,获得序列A - * 2,总的请求数量,乘以90%,获得90%对应的请求个数C - * 3,从序列A中找到第C个请求,它的响应时间,即为TP90的值 - * 其余相似的指标还有TP95, TP99 - */ - // todo tp90 - requestStatistics.setTp90(elapsedList.get(tp90) + ""); - requestStatistics.setTp95(elapsedList.get(tp95) + ""); - requestStatistics.setTp99(elapsedList.get(tp99) + ""); - - double avgHits = (double) metricList.size() / (time * 1.0 / 1000); - requestStatistics.setAvgHits(decimalFormat.format(avgHits)); - - requestStatistics.setMin(elapsedList.get(0) + ""); - requestStatistics.setMax(elapsedList.get(jtlSamplesSize - 1) + ""); - requestStatistics.setErrors(decimalFormat.format(failSize * 100.0 / jtlSamplesSize) + "%"); - requestStatistics.setKo(failSize); - /* - * 所有的相同请求的bytes总和 / 1024 / 请求持续运行的时间=sum(bytes)/1024/total time - * total time = 最大时间戳 - 最小时间戳 + 最后请求的响应时间 - */ - requestStatistics.setKbPerSec(decimalFormat.format(oneLineBytes * 1.0 / 1024 / (time * 1.0 / 1000))); - requestStatisticsList.add(requestStatistics); - } - - Collections.sort(allElapseTimeList); - int totalTP90 = allElapseTimeList.size() * 90 / 100; - int totalTP95 = allElapseTimeList.size() * 95 / 100; - int totalTP99 = allElapseTimeList.size() * 99 / 100; - - Integer min = allElapseTimeList.get(0); - Integer max = allElapseTimeList.get(allElapseTimeList.size() - 1); - - int allSamples = requestStatisticsList.stream().mapToInt(RequestStatistics::getSamples).sum(); - int failSize = requestStatisticsList.stream().mapToInt(RequestStatistics::getKo).sum(); - - double errors = (double) failSize / allSamples * 100; - String totalErrors = decimalFormat.format(errors); - double average = (double) totalElapsedTime / allSamples; - String totalAverage = decimalFormat.format(average); - - RequestStatisticsDTO statisticsDTO = new RequestStatisticsDTO(); - statisticsDTO.setRequestStatisticsList(requestStatisticsList); - statisticsDTO.setTotalLabel("Total"); - statisticsDTO.setTotalSamples(String.valueOf(allSamples)); - statisticsDTO.setTotalErrors(totalErrors + "%"); - statisticsDTO.setTotalAverage(totalAverage); - statisticsDTO.setTotalMin(String.valueOf(min)); - statisticsDTO.setTotalMax(String.valueOf(max)); - statisticsDTO.setTotalTP90(String.valueOf(allElapseTimeList.get(totalTP90))); - statisticsDTO.setTotalTP95(String.valueOf(allElapseTimeList.get(totalTP95))); - statisticsDTO.setTotalTP99(String.valueOf(allElapseTimeList.get(totalTP99))); - - totalMetricList.sort(Comparator.comparing(t0 -> Long.valueOf(t0.getTimestamp()))); - - long ms = Long.parseLong(totalMetricList.get(totalMetricList.size() - 1).getTimestamp()) - Long.parseLong(totalMetricList.get(0).getTimestamp()) - + Long.parseLong(totalMetricList.get(totalMetricList.size() - 1).getElapsed()); - double avgThroughput = (double) totalMetricList.size() / (ms * 1.0 / 1000); - - statisticsDTO.setTotalAvgHits(decimalFormat.format(avgThroughput)); - statisticsDTO.setTotalAvgBandwidth(decimalFormat.format(totalBytes * 1.0 / 1024 / (ms * 1.0 / 1000))); - - return statisticsDTO; - } - public static List getErrorsList(String jtlString) { - List totalMetricList = resolver(jtlString); - List errorsList = new ArrayList<>(); - DecimalFormat decimalFormat = new DecimalFormat("0.00"); - - List falseList = new ArrayList<>(); - for (Metric metric : totalMetricList) { - if (StringUtils.equals("false", metric.getSuccess())) { - falseList.add(metric); - } - } - - Map> jtlMap = falseList.stream().collect(Collectors.groupingBy(GenerateReport::getResponseCodeAndFailureMessage)); - - for (Map.Entry> next : jtlMap.entrySet()) { - String key = next.getKey(); - List metricList = next.getValue(); - Errors errors = new Errors(); - errors.setErrorType(key); - errors.setErrorNumber(String.valueOf(metricList.size())); - int errorSize = metricList.size(); - int errorAllSize = falseList.size(); - int allSamples = totalMetricList.size(); - errors.setPrecentOfErrors(decimalFormat.format((double) errorSize / errorAllSize * 100) + "%"); - errors.setPrecentOfAllSamples(decimalFormat.format((double) errorSize / allSamples * 100) + "%"); - errorsList.add(errors); - } - - return errorsList; + Map statisticsDataMap = ResultDataParse.getSummryDataMap(jtlString, new ErrorsSummaryConsumer()); + return ResultDataParse.summaryMapParsing(statisticsDataMap, Errors.class); } - private static String getResponseCodeAndFailureMessage(Metric metric) { - return metric.getResponseCode() + "/" + metric.getResponseMessage(); + public static List getErrorsTop5List(String jtlString) { + Map statisticsDataMap = ResultDataParse.getSummryDataMap(jtlString, new Top5ErrorsBySamplerConsumer()); + return ResultDataParse.summaryMapParsing(statisticsDataMap, ErrorsTop5.class); } - public static ErrorsTop5DTO getErrorsTop5DTO(String jtlString) { - List totalMetricList = resolver(jtlString); - ErrorsTop5DTO top5DTO = new ErrorsTop5DTO(); - List errorsTop5s = new ArrayList<>(); - - List falseList = Objects.requireNonNull(totalMetricList).stream() - .filter(metric -> StringUtils.equals("false", metric.getSuccess())) - .collect(Collectors.toList()); - - Map> collect = falseList.stream() - .collect(Collectors.groupingBy(GenerateReport::getResponseCodeAndFailureMessage)); - - for (Map.Entry> next : collect.entrySet()) { - String key = next.getKey(); - List metricList = next.getValue(); - List list = new ArrayList<>(); - for (Metric metric : totalMetricList) { - if (StringUtils.equals(metric.getLabel(), metricList.get(0).getLabel())) { - list.add(metric); - } - } - - ErrorsTop5 errorsTop5 = new ErrorsTop5(); - errorsTop5.setSamples(String.valueOf(list.size())); - errorsTop5.setSample(metricList.get(0).getLabel()); - errorsTop5.setErrors(String.valueOf(metricList.size())); - errorsTop5.setErrorsAllSize(metricList.size()); - errorsTop5.setError(key); - errorsTop5s.add(errorsTop5); - } - - errorsTop5s.sort((t0, t1) -> t1.getErrorsAllSize().compareTo(t0.getErrorsAllSize())); - - if (errorsTop5s.size() >= ERRORS_TOP_SIZE) { - errorsTop5s = errorsTop5s.subList(0, ERRORS_TOP_SIZE); - } - - top5DTO.setLabel("Total"); - top5DTO.setErrorsTop5List(errorsTop5s); - top5DTO.setTotalSamples(String.valueOf(totalMetricList.size())); - top5DTO.setTotalErrors(String.valueOf(falseList.size())); - int size = errorsTop5s.size(); - // Total行 信息 - top5DTO.setError1(size > 0 ? errorsTop5s.get(0).getError() : null); - top5DTO.setError1Size(size > 0 ? errorsTop5s.get(0).getErrors() : null); - top5DTO.setError2(size > 1 ? errorsTop5s.get(1).getError() : null); - top5DTO.setError2Size(size > 1 ? errorsTop5s.get(1).getErrors() : null); - top5DTO.setError3(size > 2 ? errorsTop5s.get(2).getError() : null); - top5DTO.setError3Size(size > 2 ? errorsTop5s.get(2).getErrors() : null); - top5DTO.setError4(size > 3 ? errorsTop5s.get(3).getError() : null); - top5DTO.setError4Size(size > 3 ? errorsTop5s.get(3).getErrors() : null); - top5DTO.setError5(size > 4 ? errorsTop5s.get(4).getError() : null); - top5DTO.setError5Size(size > 4 ? errorsTop5s.get(4).getErrors() : null); - - return top5DTO; + public static List getRequestStatistics(String jtlString) { + Map statisticsDataMap = ResultDataParse.getSummryDataMap(jtlString, new StatisticsSummaryConsumer()); + return ResultDataParse.summaryMapParsing(statisticsDataMap, Statistics.class); } public static TestOverview getTestOverview(String jtlString) { - TestOverview testOverview = new TestOverview(); DecimalFormat decimalFormat = new DecimalFormat("0.00"); - List totalLineList = GenerateReport.resolver(jtlString); - // todo 修改测试概览的数值 - List totalLineList2 = GenerateReport.resolver(jtlString); - // 时间戳转时间 - for (Metric metric : totalLineList2) { - metric.setTimestamp(stampToDate(DATE_TIME_PATTERN, metric.getTimestamp())); - } + Map activeDataMap = ResultDataParse.getGraphDataMap(jtlString, new ActiveThreadsGraphConsumer()); + List usersList = ResultDataParse.graphMapParsing(activeDataMap, "users"); + Optional max = usersList.stream().max(Comparator.comparing(ChartsData::getyAxis)); + int maxUser = max.get().getyAxis().setScale(0, BigDecimal.ROUND_UP).intValue(); - Map> collect2 = Objects.requireNonNull(totalLineList2).stream().collect(Collectors.groupingBy(Metric::getTimestamp)); - List>> entries = new ArrayList<>(collect2.entrySet()); - int maxUsers = 0; - for (Map.Entry> entry : entries) { - List metrics = entry.getValue(); - Map> metricsMap = metrics.stream().collect(Collectors.groupingBy(Metric::getThreadName)); - if (metricsMap.size() > maxUsers) { - maxUsers = metricsMap.size(); - } - } + Map hitsDataMap = ResultDataParse.getGraphDataMap(jtlString, new HitsPerSecondGraphConsumer()); + List hitsList = ResultDataParse.graphMapParsing(hitsDataMap, "hits"); + double hits = hitsList.stream().map(ChartsData::getyAxis) + .mapToDouble(BigDecimal::doubleValue) + .average().orElse(0); - Map> collect = totalLineList.stream().collect(Collectors.groupingBy(Metric::getTimestamp)); - Iterator>> iterator = collect.entrySet().iterator(); + Map errorDataMap = ResultDataParse.getSummryDataMap(jtlString, new StatisticsSummaryConsumer()); + List statisticsList = ResultDataParse.summaryMapParsing(errorDataMap, Statistics.class); + Optional error = statisticsList.stream().map(item -> Double.parseDouble(item.getError())).reduce(Double::sum); - int totalElapsed = 0; - float totalBytes = 0f; + Map responseDataMap = ResultDataParse.getGraphDataMap(jtlString, new ResponseTimeOverTimeGraphConsumer()); + List responseDataList = ResultDataParse.graphMapParsing(responseDataMap, "response"); + double responseTime = responseDataList.stream().map(ChartsData::getyAxis) + .mapToDouble(BigDecimal::doubleValue) + .average().orElse(0); - while (iterator.hasNext()) { - Map.Entry> entry = iterator.next(); - List metricList = entry.getValue(); - - for (Metric metric : metricList) { - String elapsed = metric.getElapsed(); - totalElapsed += Integer.parseInt(elapsed); - String bytes = metric.getBytes(); - totalBytes += Float.parseFloat(bytes); - } - } - - totalLineList.sort(Comparator.comparing(t0 -> Long.valueOf(t0.getTimestamp()))); - - testOverview.setMaxUsers(String.valueOf(maxUsers)); - - List list90 = totalLineList.subList(0, totalLineList.size() * 9 / 10); - long sum = list90.stream().mapToLong(metric -> Long.parseLong(metric.getElapsed())).sum(); - double avg90 = (double) sum / 1000 / list90.size(); - testOverview.setResponseTime90(decimalFormat.format(avg90)); - - long timesStampStart = Long.parseLong(totalLineList.get(0).getTimestamp()); - long timesStampEnd = Long.parseLong(totalLineList.get(totalLineList.size() - 1).getTimestamp()); - long time = timesStampEnd - timesStampStart + Long.parseLong(totalLineList.get(totalLineList.size() - 1).getElapsed()); - double avgThroughput = (double) totalLineList.size() / (time * 1.0 / 1000); - testOverview.setAvgThroughput(decimalFormat.format(avgThroughput)); - - List falseList = totalLineList.stream().filter(metric -> StringUtils.equals("false", metric.getSuccess())).collect(Collectors.toList()); - double errors = falseList.size() * 1.0 / totalLineList.size() * 100; - testOverview.setErrors(decimalFormat.format(errors)); - - double avg = totalElapsed * 1.0 / totalLineList.size() / 1000; - testOverview.setAvgResponseTime(decimalFormat.format(avg)); - - double bandwidth = totalBytes * 1.0 / time; - testOverview.setAvgBandwidth(decimalFormat.format(bandwidth)); + TestOverview testOverview = new TestOverview(); + testOverview.setMaxUsers(String.valueOf(maxUser)); + testOverview.setAvgThroughput(decimalFormat.format(hits)); + testOverview.setErrors(decimalFormat.format(error.get())); + testOverview.setAvgResponseTime(decimalFormat.format(responseTime / 1000)); + // todo + testOverview.setResponseTime90("0"); + testOverview.setAvgBandwidth("0"); return testOverview; } @@ -354,7 +111,6 @@ public class GenerateReport { } public static ReportTimeInfo getReportTimeInfo(String jtlString) { - ReportTimeInfo reportTimeInfo = new ReportTimeInfo(); List totalLineList = GenerateReport.resolver(jtlString); totalLineList.sort(Comparator.comparing(t0 -> Long.valueOf(t0.getTimestamp()))); @@ -365,35 +121,15 @@ public class GenerateReport { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); String startTime = dtf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(startTimeStamp)), ZoneId.systemDefault())); String endTime = dtf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(endTimeStamp)), ZoneId.systemDefault())); - reportTimeInfo.setStartTime(startTime); - reportTimeInfo.setEndTime(endTime); - - Date startDate = new Date(Long.parseLong(startTimeStamp)); - Date endDate = new Date(Long.parseLong(endTimeStamp)); - long timestamp = endDate.getTime() - startDate.getTime(); - reportTimeInfo.setDuration(String.valueOf(timestamp * 1.0 / 1000 / 60)); // todo 时间问题 long seconds = Duration.between(Instant.ofEpochMilli(Long.parseLong(startTimeStamp)), Instant.ofEpochMilli(Long.parseLong(endTimeStamp))).getSeconds(); + ReportTimeInfo reportTimeInfo = new ReportTimeInfo(); + reportTimeInfo.setStartTime(startTime); + reportTimeInfo.setEndTime(endTime); reportTimeInfo.setDuration(String.valueOf(seconds)); return reportTimeInfo; } - private static String stampToDate(String pattern, String timeStamp) { - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); - LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(timeStamp)), ZoneId.systemDefault()); - return localDateTime.format(dateTimeFormatter); - } - - /** - * @param "yyyy-MM-dd HH:mm:ss" - * @return "HH:mm:ss" - */ - private static String formatDate(String dateString) throws ParseException { - SimpleDateFormat before = new SimpleDateFormat(DATE_TIME_PATTERN); - SimpleDateFormat after = new SimpleDateFormat(TIME_PATTERN); - return after.format(before.parse(dateString)); - } - } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/report/base/ErrorsTop5.java b/backend/src/main/java/io/metersphere/report/base/ErrorsTop5.java index 311e1112fc..9e3ac456c5 100644 --- a/backend/src/main/java/io/metersphere/report/base/ErrorsTop5.java +++ b/backend/src/main/java/io/metersphere/report/base/ErrorsTop5.java @@ -4,9 +4,17 @@ public class ErrorsTop5 { private String sample; private String samples; - private Integer errorsAllSize; - private String error; - private String errors; + private String errorsAllSize; + private String error1; + private String error1Size; + private String error2; + private String error2Size; + private String error3; + private String error3Size; + private String error4; + private String error4Size; + private String error5; + private String error5Size; public String getSample() { return sample; @@ -24,27 +32,91 @@ public class ErrorsTop5 { this.samples = samples; } - public Integer getErrorsAllSize() { + public String getErrorsAllSize() { return errorsAllSize; } - public void setErrorsAllSize(Integer errorsAllSize) { + public void setErrorsAllSize(String errorsAllSize) { this.errorsAllSize = errorsAllSize; } - public String getError() { - return error; + public String getError1() { + return error1; } - public void setError(String error) { - this.error = error; + public void setError1(String error1) { + this.error1 = error1; } - public String getErrors() { - return errors; + public String getError1Size() { + return error1Size; } - public void setErrors(String errors) { - this.errors = errors; + public void setError1Size(String error1Size) { + this.error1Size = error1Size; + } + + public String getError2() { + return error2; + } + + public void setError2(String error2) { + this.error2 = error2; + } + + public String getError2Size() { + return error2Size; + } + + public void setError2Size(String error2Size) { + this.error2Size = error2Size; + } + + public String getError3() { + return error3; + } + + public void setError3(String error3) { + this.error3 = error3; + } + + public String getError3Size() { + return error3Size; + } + + public void setError3Size(String error3Size) { + this.error3Size = error3Size; + } + + public String getError4() { + return error4; + } + + public void setError4(String error4) { + this.error4 = error4; + } + + public String getError4Size() { + return error4Size; + } + + public void setError4Size(String error4Size) { + this.error4Size = error4Size; + } + + public String getError5() { + return error5; + } + + public void setError5(String error5) { + this.error5 = error5; + } + + public String getError5Size() { + return error5Size; + } + + public void setError5Size(String error5Size) { + this.error5Size = error5Size; } } diff --git a/backend/src/main/java/io/metersphere/report/base/RequestStatistics.java b/backend/src/main/java/io/metersphere/report/base/RequestStatistics.java deleted file mode 100644 index 1600cf342d..0000000000 --- a/backend/src/main/java/io/metersphere/report/base/RequestStatistics.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.metersphere.report.base; - -public class RequestStatistics { - - /**请求标签*/ - private String requestLabel; - - /**压测请求数*/ - private Integer samples; - - /**平均响应时间*/ - private String average; - - /**平均点击率*/ - private String avgHits; - - /**90% Line*/ - private String tp90; - - /**95% Line*/ - private String tp95; - - /**99% Line*/ - private String tp99; - - /**最小请求时间 Min Response Time /ms */ - private String min; - - /**最大请求时间 Max Response Time /ms */ - private String max; - - /**吞吐量 KB/sec*/ - private String kbPerSec; - - /**错误率 Error Percentage */ - private String errors; - - /**错误个数*/ - private Integer ko; - - public String getRequestLabel() { - return requestLabel; - } - - public void setRequestLabel(String requestLabel) { - this.requestLabel = requestLabel; - } - - public Integer getSamples() { - return samples; - } - - public void setSamples(Integer samples) { - this.samples = samples; - } - - public String getAverage() { - return average; - } - - public void setAverage(String average) { - this.average = average; - } - - public String getAvgHits() { - return avgHits; - } - - public void setAvgHits(String avgHits) { - this.avgHits = avgHits; - } - - public String getTp90() { - return tp90; - } - - public void setTp90(String tp90) { - this.tp90 = tp90; - } - - public String getTp95() { - return tp95; - } - - public void setTp95(String tp95) { - this.tp95 = tp95; - } - - public String getTp99() { - return tp99; - } - - public void setTp99(String tp99) { - this.tp99 = tp99; - } - - public String getMin() { - return min; - } - - public void setMin(String min) { - this.min = min; - } - - public String getMax() { - return max; - } - - public void setMax(String max) { - this.max = max; - } - - public String getKbPerSec() { - return kbPerSec; - } - - public void setKbPerSec(String kbPerSec) { - this.kbPerSec = kbPerSec; - } - - public String getErrors() { - return errors; - } - - public void setErrors(String errors) { - this.errors = errors; - } - - public Integer getKo() { - return ko; - } - - public void setKo(Integer ko) { - this.ko = ko; - } -} diff --git a/backend/src/main/java/io/metersphere/report/base/Statistics.java b/backend/src/main/java/io/metersphere/report/base/Statistics.java new file mode 100644 index 0000000000..821b45d270 --- /dev/null +++ b/backend/src/main/java/io/metersphere/report/base/Statistics.java @@ -0,0 +1,134 @@ +package io.metersphere.report.base; + +public class Statistics { + + private String label; + + private String samples; + + private String ko; + + private String error; + + private String average; + + private String min; + + private String max; + + private String tp90; + + private String tp95; + + private String tp99; + + private String transactions; + + private String received; + + private String sent; + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getSamples() { + return samples; + } + + public void setSamples(String samples) { + this.samples = samples; + } + + public String getKo() { + return ko; + } + + public void setKo(String ko) { + this.ko = ko; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public String getAverage() { + return average; + } + + public void setAverage(String average) { + this.average = average; + } + + public String getMin() { + return min; + } + + public void setMin(String min) { + this.min = min; + } + + public String getMax() { + return max; + } + + public void setMax(String max) { + this.max = max; + } + + public String getTp90() { + return tp90; + } + + public void setTp90(String tp90) { + this.tp90 = tp90; + } + + public String getTp95() { + return tp95; + } + + public void setTp95(String tp95) { + this.tp95 = tp95; + } + + public String getTp99() { + return tp99; + } + + public void setTp99(String tp99) { + this.tp99 = tp99; + } + + public String getTransactions() { + return transactions; + } + + public void setTransactions(String transactions) { + this.transactions = transactions; + } + + public String getSent() { + return sent; + } + + public void setSent(String sent) { + this.sent = sent; + } + + public String getReceived() { + return received; + } + + public void setReceived(String received) { + this.received = received; + } +} diff --git a/backend/src/main/java/io/metersphere/report/base/SummaryData.java b/backend/src/main/java/io/metersphere/report/base/SummaryData.java new file mode 100644 index 0000000000..ccd8958d83 --- /dev/null +++ b/backend/src/main/java/io/metersphere/report/base/SummaryData.java @@ -0,0 +1,16 @@ +package io.metersphere.report.base; + +import java.util.List; + +public class SummaryData { + + private List result; + + public List getResult() { + return result; + } + + public void setResult(List result) { + this.result = result; + } +} diff --git a/backend/src/main/java/io/metersphere/report/dto/ErrorsTop5DTO.java b/backend/src/main/java/io/metersphere/report/dto/ErrorsTop5DTO.java deleted file mode 100644 index 7bcf200855..0000000000 --- a/backend/src/main/java/io/metersphere/report/dto/ErrorsTop5DTO.java +++ /dev/null @@ -1,135 +0,0 @@ -package io.metersphere.report.dto; - -import io.metersphere.report.base.ErrorsTop5; - -import java.util.List; - -public class ErrorsTop5DTO { - - private List errorsTop5List; - private String label; - private String totalSamples; - private String totalErrors; - private String error1; - private String error1Size; - private String error2; - private String error2Size; - private String error3; - private String error3Size; - private String error4; - private String error4Size; - private String error5; - private String error5Size; - - public List getErrorsTop5List() { - return errorsTop5List; - } - - public void setErrorsTop5List(List errorsTop5List) { - this.errorsTop5List = errorsTop5List; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public String getTotalSamples() { - return totalSamples; - } - - public void setTotalSamples(String totalSamples) { - this.totalSamples = totalSamples; - } - - public String getTotalErrors() { - return totalErrors; - } - - public void setTotalErrors(String totalErrors) { - this.totalErrors = totalErrors; - } - - public String getError1() { - return error1; - } - - public void setError1(String error1) { - this.error1 = error1; - } - - public String getError1Size() { - return error1Size; - } - - public void setError1Size(String error1Size) { - this.error1Size = error1Size; - } - - public String getError2() { - return error2; - } - - public void setError2(String error2) { - this.error2 = error2; - } - - public String getError2Size() { - return error2Size; - } - - public void setError2Size(String error2Size) { - this.error2Size = error2Size; - } - - public String getError3() { - return error3; - } - - public void setError3(String error3) { - this.error3 = error3; - } - - public String getError3Size() { - return error3Size; - } - - public void setError3Size(String error3Size) { - this.error3Size = error3Size; - } - - public String getError4() { - return error4; - } - - public void setError4(String error4) { - this.error4 = error4; - } - - public String getError4Size() { - return error4Size; - } - - public void setError4Size(String error4Size) { - this.error4Size = error4Size; - } - - public String getError5() { - return error5; - } - - public void setError5(String error5) { - this.error5 = error5; - } - - public String getError5Size() { - return error5Size; - } - - public void setError5Size(String error5Size) { - this.error5Size = error5Size; - } -} diff --git a/backend/src/main/java/io/metersphere/report/dto/RequestStatisticsDTO.java b/backend/src/main/java/io/metersphere/report/dto/RequestStatisticsDTO.java deleted file mode 100644 index 1ea442e54f..0000000000 --- a/backend/src/main/java/io/metersphere/report/dto/RequestStatisticsDTO.java +++ /dev/null @@ -1,128 +0,0 @@ -package io.metersphere.report.dto; - -import io.metersphere.report.base.RequestStatistics; - -import java.util.List; - -public class RequestStatisticsDTO extends RequestStatistics { - - private List requestStatisticsList; - - private String totalLabel; - - private String totalSamples; - - private String totalErrors; - - private String totalAverage; - - private String totalMin; - - private String totalMax; - - private String totalTP90; - - private String totalTP95; - - private String totalTP99; - - private String totalAvgBandwidth; - - private String totalAvgHits; - - public List getRequestStatisticsList() { - return requestStatisticsList; - } - - public void setRequestStatisticsList(List requestStatisticsList) { - this.requestStatisticsList = requestStatisticsList; - } - - public String getTotalLabel() { - return totalLabel; - } - - public void setTotalLabel(String totalLabel) { - this.totalLabel = totalLabel; - } - - public String getTotalSamples() { - return totalSamples; - } - - public void setTotalSamples(String totalSamples) { - this.totalSamples = totalSamples; - } - - public String getTotalErrors() { - return totalErrors; - } - - public void setTotalErrors(String totalErrors) { - this.totalErrors = totalErrors; - } - - public String getTotalAverage() { - return totalAverage; - } - - public void setTotalAverage(String totalAverage) { - this.totalAverage = totalAverage; - } - - public String getTotalMin() { - return totalMin; - } - - public void setTotalMin(String totalMin) { - this.totalMin = totalMin; - } - - public String getTotalMax() { - return totalMax; - } - - public void setTotalMax(String totalMax) { - this.totalMax = totalMax; - } - - public String getTotalTP90() { - return totalTP90; - } - - public void setTotalTP90(String totalTP90) { - this.totalTP90 = totalTP90; - } - - public String getTotalTP95() { - return totalTP95; - } - - public void setTotalTP95(String totalTP95) { - this.totalTP95 = totalTP95; - } - - public String getTotalTP99() { - return totalTP99; - } - - public void setTotalTP99(String totalTP99) { - this.totalTP99 = totalTP99; - } - - public String getTotalAvgBandwidth() { - return totalAvgBandwidth; - } - - public void setTotalAvgBandwidth(String totalAvgBandwidth) { - this.totalAvgBandwidth = totalAvgBandwidth; - } - - public String getTotalAvgHits() { - return totalAvgHits; - } - - public void setTotalAvgHits(String totalAvgHits) { - this.totalAvgHits = totalAvgHits; - } -} diff --git a/backend/src/main/java/io/metersphere/report/parse/ResultDataParse.java b/backend/src/main/java/io/metersphere/report/parse/ResultDataParse.java index 016fbc0855..527bfc51a1 100644 --- a/backend/src/main/java/io/metersphere/report/parse/ResultDataParse.java +++ b/backend/src/main/java/io/metersphere/report/parse/ResultDataParse.java @@ -7,6 +7,7 @@ import org.apache.jmeter.report.core.SampleMetadata; import org.apache.jmeter.report.dashboard.JsonizerVisitor; import org.apache.jmeter.report.processor.*; import org.apache.jmeter.report.processor.graph.AbstractOverTimeGraphConsumer; +import java.lang.reflect.Field; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -14,16 +15,49 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; +import java.util.*; public class ResultDataParse { private static final String DATE_TIME_PATTERN = "yyyy/MM/dd HH:mm:ss"; private static final String TIME_PATTERN = "HH:mm:ss"; + public static List summaryMapParsing(Map map, Class clazz) { + List list = new ArrayList<>(); + for (String key : map.keySet()) { + MapResultData mapResultData = (MapResultData) map.get(key); + ListResultData items = (ListResultData) mapResultData.getResult("items"); + if (items.getSize() > 0) { + for (int i = 0; i < items.getSize(); i++) { + MapResultData resultData = (MapResultData) items.get(i); + ListResultData data = (ListResultData) resultData.getResult("data"); + int size = data.getSize(); + String[] strArray = new String[size]; + if (size > 0) { + T t = null; + for (int j = 0; j < size; j++) { + ValueResultData valueResultData = (ValueResultData) data.get(j); + if (valueResultData.getValue() == null) { + strArray[j] = ""; + } else { + String accept = valueResultData.accept(new JsonizerVisitor()); + strArray[j] = accept.replace("\\", ""); + } + } + + try { + t = setParam(clazz, strArray); + } catch (Exception e) { + e.printStackTrace(); + } + list.add(t); + } + } + } + } + return list; + } + public static List graphMapParsing(Map map, String seriesName) { List list = new ArrayList<>(); // ThreadGroup @@ -136,4 +170,21 @@ public class ResultDataParse { SimpleDateFormat after = new SimpleDateFormat(TIME_PATTERN); return after.format(before.parse(dateString)); } + + private static T setParam(Class clazz, Object[] args) + throws Exception { + if (clazz == null || args == null) { + throw new IllegalArgumentException(); + } + T t = clazz.newInstance(); + Field[] fields = clazz.getDeclaredFields(); + if (fields == null || fields.length > args.length) { + throw new IndexOutOfBoundsException(); + } + for (int i = 0; i < fields.length; i++) { + fields[i].setAccessible(true); + fields[i].set(t, args[i]); + } + return t; + } } diff --git a/backend/src/main/java/io/metersphere/service/PerformanceTestService.java b/backend/src/main/java/io/metersphere/service/PerformanceTestService.java index ef3ce7006f..e3c032bf81 100644 --- a/backend/src/main/java/io/metersphere/service/PerformanceTestService.java +++ b/backend/src/main/java/io/metersphere/service/PerformanceTestService.java @@ -24,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.IOException; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -229,6 +230,23 @@ public class PerformanceTestService { } } + public Map log(String testId) { + final LoadTestWithBLOBs loadTest = loadTestMapper.selectByPrimaryKey(testId); + if (loadTest == null) { + MSException.throwException(Translator.get("test_not_found") + testId); + } + + if (!StringUtils.equals(loadTest.getStatus(), PerformanceTestStatus.Running.name())) { + MSException.throwException(Translator.get("test_not_running")); + } + + Engine engine = EngineFactory.createEngine(loadTest); + if (engine == null) { + MSException.throwException(String.format("Engine is null,test ID:%s", testId)); + } + return engine.log(); + } + public List recentTestPlans(QueryTestPlanRequest request) { // 查询最近的测试计划 request.setRecent(true); @@ -260,4 +278,5 @@ public class PerformanceTestService { example.createCriteria().andTestResourcePoolIdEqualTo(resourcePoolId); return loadTestMapper.selectByExampleWithBLOBs(example); } + } diff --git a/backend/src/main/java/io/metersphere/service/ReportService.java b/backend/src/main/java/io/metersphere/service/ReportService.java index a833a5a07a..754a3d6e2d 100644 --- a/backend/src/main/java/io/metersphere/service/ReportService.java +++ b/backend/src/main/java/io/metersphere/service/ReportService.java @@ -12,16 +12,10 @@ import io.metersphere.dto.ReportDTO; import io.metersphere.engine.Engine; import io.metersphere.engine.EngineFactory; import io.metersphere.report.GenerateReport; -import io.metersphere.report.base.ChartsData; -import io.metersphere.report.base.Errors; -import io.metersphere.report.base.ReportTimeInfo; -import io.metersphere.report.base.TestOverview; -import io.metersphere.report.dto.ErrorsTop5DTO; -import io.metersphere.report.dto.RequestStatisticsDTO; +import io.metersphere.report.base.*; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - import javax.annotation.Resource; import java.util.List; @@ -86,12 +80,11 @@ public class ReportService { return extLoadTestReportMapper.getReportTestAndProInfo(reportId); } - public RequestStatisticsDTO getReport(String id) { + public List getReport(String id) { checkReportStatus(id); LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(id); String content = loadTestReport.getContent(); - RequestStatisticsDTO requestStatistics = GenerateReport.getRequestStatistics(content); - return requestStatistics; + return GenerateReport.getRequestStatistics(content); } public List getReportErrors(String id) { @@ -102,12 +95,12 @@ public class ReportService { return errors; } - public ErrorsTop5DTO getReportErrorsTOP5(String id) { + public List getReportErrorsTOP5(String id) { checkReportStatus(id); LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(id); String content = loadTestReport.getContent(); - ErrorsTop5DTO errors = GenerateReport.getErrorsTop5DTO(content); - return errors; + List errorsTop5 = GenerateReport.getErrorsTop5List(content); + return errorsTop5; } public TestOverview getTestOverview(String id) { @@ -119,7 +112,6 @@ public class ReportService { } public ReportTimeInfo getReportTimeInfo(String id) { - checkReportStatus(id); LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(id); String content = loadTestReport.getContent(); ReportTimeInfo reportTimeInfo = GenerateReport.getReportTimeInfo(content); diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 22315563d4..a29e9abee7 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -19,4 +19,6 @@ duplicate_node_ip=Duplicate IPs only_one_k8s=Only one K8s can be added organization_id_is_null=Organization ID cannot be null max_thread_insufficient=The number of concurrent users exceeds -cannot_edit_load_test_running=Cannot modify the running test \ No newline at end of file +cannot_edit_load_test_running=Cannot modify the running test +test_not_found=Test cannot be found: +test_not_running=Test is not running \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 822254cf47..f5fe455fac 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -19,4 +19,6 @@ duplicate_node_ip=节点 IP 重复 only_one_k8s=只能添加一个 K8s organization_id_is_null=组织 ID 不能为空 max_thread_insufficient=并发用户数超额 -cannot_edit_load_test_running=不能修改正在运行的测试 \ No newline at end of file +cannot_edit_load_test_running=不能修改正在运行的测试 +test_not_found=测试不存在: +test_not_running=测试未运行 \ No newline at end of file diff --git a/backend/src/test/java/io/metersphere/JtlTest.java b/backend/src/test/java/io/metersphere/JtlTest.java index 1a8cfdacde..cf701cfa55 100644 --- a/backend/src/test/java/io/metersphere/JtlTest.java +++ b/backend/src/test/java/io/metersphere/JtlTest.java @@ -1,10 +1,8 @@ package io.metersphere; -import com.alibaba.fastjson.JSONObject; import com.opencsv.bean.CsvToBean; import com.opencsv.bean.CsvToBeanBuilder; import com.opencsv.bean.HeaderColumnNameMappingStrategy; -import io.metersphere.report.base.RequestStatistics; import org.junit.Test; import java.io.Reader; import java.io.StringReader; @@ -201,73 +199,6 @@ public class JtlTest { List metrics = beanBuilderExample(jtlString); // 根据label分组,label作为map的key Map> map = metrics.stream().collect(Collectors.groupingBy(Metric::getLabel)); - getOneRpsResult(map); } - private void getOneRpsResult(Map> map){ - Iterator>> iterator = map.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry> entry = iterator.next(); - String label = entry.getKey(); - List list = entry.getValue(); - List timestampList = list.stream().map(Metric::getTimestamp).collect(Collectors.toList()); - int index=0; - //总的响应时间 - int sumElapsed=0; - Integer failSize = 0; - Integer totalBytes = 0; - // 响应时间的列表排序之后 用于计算90%line、95%line、99line - List elapsedList = new ArrayList<>(); - - for (int i = 0; i < list.size(); i++) { - try { - Metric row = list.get(i); - //响应时间 - String elapsed = row.getElapsed(); - sumElapsed += Integer.valueOf(elapsed); - elapsedList.add(Integer.valueOf(elapsed)); - //成功与否 - String success = row.getSuccess(); - if (!"true".equals(success)){ - failSize++; - } - //字节 - String bytes = row.getBytes(); - totalBytes += Integer.valueOf(bytes); - - index++; - }catch (Exception e){ - System.out.println("exception i:"+i); - } - } - - Collections.sort(elapsedList, new Comparator() { - public int compare(Integer o1, Integer o2) { - return o1-o2; - } - }); - - Integer tp90 = elapsedList.size()*9/10; - Integer tp95 = elapsedList.size()*95/100; - Integer tp99 = elapsedList.size()*99/100; - - Long l = Long.valueOf(timestampList.get(index-1)) - Long.valueOf(timestampList.get(0)); - - RequestStatistics sceneResult = new RequestStatistics(); - sceneResult.setRequestLabel(label); - sceneResult.setSamples(index); -// sceneResult.setAverage(sumElapsed/index); - sceneResult.setTp90(elapsedList.get(tp90)+""); - sceneResult.setTp95(elapsedList.get(tp95)+""); - sceneResult.setTp99(elapsedList.get(tp99)+""); - sceneResult.setMin(elapsedList.get(0)+""); - sceneResult.setMax(elapsedList.get(index-1)+""); - sceneResult.setErrors(String.format("%.2f",failSize*100.0/index)+"%"); - sceneResult.setKbPerSec(String.format("%.2f",totalBytes*1.0/1024/(l*1.0/1000))); - System.out.println(JSONObject.toJSONString(sceneResult)); - - System.out.println(); - } - - } } diff --git a/backend/src/test/java/io/metersphere/ResultDataParseTest.java b/backend/src/test/java/io/metersphere/ResultDataParseTest.java new file mode 100644 index 0000000000..10f3eeadce --- /dev/null +++ b/backend/src/test/java/io/metersphere/ResultDataParseTest.java @@ -0,0 +1,36 @@ +package io.metersphere; + +import io.metersphere.report.base.Statistics; +import org.junit.Test; + +import java.lang.reflect.Field; + +public class ResultDataParseTest { + + String[] s = {"1","2","3","4","5","6","7","8","9","10","11","12","13"}; + + public static T setParam(Class clazz, Object[] args) + throws Exception { + if (clazz == null || args == null) { + throw new IllegalArgumentException(); + } + T t = clazz.newInstance(); + Field[] fields = clazz.getDeclaredFields(); + if (fields == null || fields.length > args.length) { + throw new IndexOutOfBoundsException(); + } + for (int i = 0; i < fields.length; i++) { + fields[i].setAccessible(true); + fields[i].set(t, args[i]); + } + return t; + } + + @Test + public void test() throws Exception { + Statistics statistics = setParam(Statistics.class, s); + System.out.println(statistics.toString()); + } + + +} diff --git a/frontend/src/business/components/common/components/MsTableSearchBar.vue b/frontend/src/business/components/common/components/MsTableSearchBar.vue new file mode 100644 index 0000000000..dfa1584494 --- /dev/null +++ b/frontend/src/business/components/common/components/MsTableSearchBar.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/frontend/src/business/components/common/components/MsTipButton.vue b/frontend/src/business/components/common/components/MsTipButton.vue index 8656306245..e27c6e3b66 100644 --- a/frontend/src/business/components/common/components/MsTipButton.vue +++ b/frontend/src/business/components/common/components/MsTipButton.vue @@ -1,11 +1,9 @@ @@ -79,15 +86,18 @@ startTime: '0', endTime: '0', minutes: '0', - seconds: '0' + seconds: '0', + title: 'Logging', + testLogging: null, + showTestLogging: false, } }, methods: { initBreadcrumb() { - if(this.reportId){ + if (this.reportId) { this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => { let data = res.data; - if(data){ + if (data) { this.reportName = data.name; this.testName = data.testName; this.projectName = data.projectName; @@ -96,10 +106,10 @@ } }, initReportTimeInfo() { - if(this.reportId){ + if (this.reportId) { this.result = this.$get("/performance/report/content/report_time/" + this.reportId, res => { let data = res.data; - if(data){ + if (data) { this.startTime = data.startTime; this.endTime = data.endTime; let duration = data.duration; @@ -108,23 +118,37 @@ } }) } + }, + getLog(testId) { + this.result = this.$get('/performance/log/' + testId, response => { + this.testLogging = response.data; + }) } }, mounted() { this.reportId = this.$route.path.split('/')[4]; - this.$get("/performance/report/" + this.reportId, res => { + this.result = this.$get("/performance/report/" + this.reportId, res => { let data = res.data; this.status = data.status; - if (data.status === "Error") { - this.$message({ - type: 'warning', - message: "报告生成错误,无法查看!" - }); - } else if (data.status === "Starting") { - this.$message({ - type: 'info', - message: "报告生成中...." - }); + switch (data.status) { + case 'Error': + this.$message({ + type: 'warning', + message: "报告生成错误,无法查看!" + }); + break; + case 'Starting': + this.$message({ + type: 'info', + message: "报告生成中...." + }); + break; + case 'Running': + this.showTestLogging = true; + this.getLog(data.testId); + break; + default: + break; } }) this.initBreadcrumb(); @@ -133,10 +157,10 @@ watch: { '$route'(to) { let reportId = to.path.split('/')[4]; - if(reportId){ + if (reportId) { this.$get("/performance/report/test/pro/info/" + reportId, response => { let data = response.data; - if(data){ + if (data) { this.reportName = data.name; this.testName = data.testName; this.projectName = data.projectName; @@ -144,7 +168,7 @@ }); this.result = this.$get("/performance/report/content/report_time/" + this.reportId, res => { let data = res.data; - if(data){ + if (data) { this.startTime = data.startTime; this.endTime = data.endTime; let duration = data.duration; @@ -170,4 +194,10 @@ display: block; color: #5C7878; } + + .logging-content { + white-space: pre-line; + height: calc(100vh - 450px); + overflow: auto; + } diff --git a/frontend/src/business/components/performance/report/components/ErrorLog.vue b/frontend/src/business/components/performance/report/components/ErrorLog.vue index 31b5140719..2691b0c3e9 100644 --- a/frontend/src/business/components/performance/report/components/ErrorLog.vue +++ b/frontend/src/business/components/performance/report/components/ErrorLog.vue @@ -39,7 +39,6 @@ stripe style="width: 100%" show-summary - :summary-method="getSummaries" > + + + @@ -128,22 +130,7 @@ name: "ErrorLog", data() { return { - tableData: [{},{},{},{},{}], - errorTotal: { - label: '', - totalSamples: '', - totalErrors: '', - error1: '', - error1Size: '', - error2: '', - error2Size: '', - error3: '', - error3Size: '', - error4: '', - error4Size: '', - error5: '', - error5Size: '' - }, + tableData: [], errorTop5: [] } }, @@ -153,33 +140,14 @@ this.tableData = res.data; }) this.$get("/performance/report/content/errors_top5/" + this.id, res => { - this.errorTotal = res.data - this.errorTop5 = res.data.errorsTop5List; + this.errorTop5 = res.data; }) - }, - getSummaries () { - const sums = [] - sums[0] = this.errorTotal.label; - sums[1] = this.errorTotal.totalSamples; - sums[2] = this.errorTotal.totalErrors; - sums[3] = this.errorTotal.error1; - sums[4] = this.errorTotal.error1Size; - sums[5] = this.errorTotal.error2; - sums[6] = this.errorTotal.error2Size; - sums[7] = this.errorTotal.error3; - sums[8] = this.errorTotal.error3Size; - sums[9] = this.errorTotal.error4; - sums[10] = this.errorTotal.error4Size; - sums[11] = this.errorTotal.error5; - sums[12] = this.errorTotal.error5Size; - return sums; } }, watch: { status() { if ("Completed" === this.status) { this.initTableData() - this.getSummaries() } } }, diff --git a/frontend/src/business/components/performance/report/components/RequestStatistics.vue b/frontend/src/business/components/performance/report/components/RequestStatistics.vue index 6b96b27379..48a4d0f7aa 100644 --- a/frontend/src/business/components/performance/report/components/RequestStatistics.vue +++ b/frontend/src/business/components/performance/report/components/RequestStatistics.vue @@ -6,12 +6,11 @@ border style="width: 100%" show-summary - :summary-method="getSummaries" :default-sort = "{prop: 'samples', order: 'descending'}" > @@ -25,7 +24,13 @@ /> + + @@ -58,18 +63,29 @@ /> - + + + + + + + + - @@ -79,50 +95,20 @@ name: "RequestStatistics", data() { return { - tableData: [{},{},{},{},{}], - totalInfo: { - totalLabel: '', - totalSamples: '', - totalErrors: '', - totalAverage: '', - totalMin: '', - totalMax: '', - totalTP90: '', - totalTP95: '', - totalTP99: '', - totalAvgHits: '', - totalAvgBandwidth: '' - } + tableData: [{},{},{},{},{}] } }, methods: { initTableData() { this.$get("/performance/report/content/" + this.id, res => { - this.tableData = res.data.requestStatisticsList; - this.totalInfo = res.data; + this.tableData = res.data; }) }, - getSummaries () { - const sums = [] - sums[0] = this.totalInfo.totalLabel; - sums[1] = this.totalInfo.totalSamples; - sums[2] = this.totalInfo.totalErrors; - sums[3] = this.totalInfo.totalAverage; - sums[4] = this.totalInfo.totalMin; - sums[5] = this.totalInfo.totalMax; - sums[6] = this.totalInfo.totalTP90; - sums[7] = this.totalInfo.totalTP95; - sums[8] = this.totalInfo.totalTP99; - sums[9] = this.totalInfo.totalAvgHits; - sums[10] = this.totalInfo.totalAvgBandwidth; - return sums; - } }, watch: { status() { if ("Completed" === this.status) { this.initTableData() - this.getSummaries() } } }, diff --git a/frontend/src/business/components/track/case/TestCase.vue b/frontend/src/business/components/track/case/TestCase.vue index bac8398700..e0b4e9f14c 100644 --- a/frontend/src/business/components/track/case/TestCase.vue +++ b/frontend/src/business/components/track/case/TestCase.vue @@ -9,16 +9,19 @@ @dataChange="changeProject"> - + v-loading="result.loading" + @nodeSelectEvent="nodeChange" + @refresh="refresh" + :tree-nodes="treeNodes" + :type="'edit'" + ref="nodeTree"/> - - + @@ -216,13 +208,8 @@ margin-left: 0; } - .main-content { - /*background: white;*/ - } - .test-case-list { padding: 15px; } - diff --git a/frontend/src/business/components/track/case/components/TestCaseEdit.vue b/frontend/src/business/components/track/case/components/TestCaseEdit.vue index 742e2210bc..157cc741e5 100644 --- a/frontend/src/business/components/track/case/components/TestCaseEdit.vue +++ b/frontend/src/business/components/track/case/components/TestCaseEdit.vue @@ -167,18 +167,11 @@ + @@ -188,126 +181,163 @@ diff --git a/frontend/src/business/components/track/common/NodeEdit.vue b/frontend/src/business/components/track/common/NodeEdit.vue new file mode 100644 index 0000000000..54a69c2a6f --- /dev/null +++ b/frontend/src/business/components/track/common/NodeEdit.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/frontend/src/business/components/track/case/components/NodeTree.vue b/frontend/src/business/components/track/common/NodeTree.vue similarity index 51% rename from frontend/src/business/components/track/case/components/NodeTree.vue rename to frontend/src/business/components/track/common/NodeTree.vue index 34b7b9ecc8..82007f9360 100644 --- a/frontend/src/business/components/track/case/components/NodeTree.vue +++ b/frontend/src/business/components/track/common/NodeTree.vue @@ -3,7 +3,7 @@
-