/*
* Copyright (c) 2010-2012 Grid Dynamics Consulting Services, Inc, All Rights Reserved
* http://www.griddynamics.com
*
* This library is free software; you can redistribute it and/or modify it under the terms of
* the Apache License; either
* version 2.0 of the License, or any later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.griddynamics.jagger.engine.e1.reporting;
import static com.griddynamics.jagger.util.StandardMetricsNamesUtil.extractDisplayNameFromGenerated;
import static com.griddynamics.jagger.util.StandardMetricsNamesUtil.extractIdsFromGeneratedIdForScenarioComponents;
import com.griddynamics.jagger.dbapi.DatabaseService;
import com.griddynamics.jagger.dbapi.dto.MetricNameDto;
import com.griddynamics.jagger.engine.e1.services.DataService;
import com.griddynamics.jagger.engine.e1.services.DefaultDataService;
import com.griddynamics.jagger.engine.e1.services.data.service.MetricEntity;
import com.griddynamics.jagger.engine.e1.services.data.service.MetricSummaryValueEntity;
import com.griddynamics.jagger.engine.e1.services.data.service.SessionEntity;
import com.griddynamics.jagger.engine.e1.services.data.service.TestEntity;
import com.griddynamics.jagger.util.Decision;
import com.griddynamics.jagger.util.FormatCalculator;
import com.griddynamics.jagger.util.MetricNamesRankingProvider;
import com.griddynamics.jagger.util.StandardMetricsNamesUtil;
import com.griddynamics.jagger.util.StandardMetricsNamesUtil.IdContainer;
import org.springframework.beans.factory.annotation.Required;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class SummaryReporter {
private DatabaseService databaseService;
private String sessionId;
private Map<TestEntity, Set<MetricEntity>> metricsPerTest;
private Map<String, List<SummaryDto>> summaryMap = new HashMap<String, List<SummaryDto>>();
private Map<String, List<SummaryDto>> latencyPercentilesMap = new HashMap<String, List<SummaryDto>>();
private Map<String, List<SummaryDto>> validatorsMap = new HashMap<String, List<SummaryDto>>();
private Map<TestEntity, Map<MetricEntity, MetricSummaryValueEntity>> standardMetricsMap = new HashMap<TestEntity, Map<MetricEntity, MetricSummaryValueEntity>>();
private DateFormat dateFormatter = new SimpleDateFormat(FormatCalculator.DATE_FORMAT);
private int numberOfTestGroups;
private boolean isMetricHighlighting;
private List<SummaryTestDto> testSummaryData = new ArrayList<>();
private Map<TestEntity,Map<String,Double>> dataForScalabilityPlots = new HashMap<TestEntity, Map<String, Double>>();
private SessionEntity sessionEntity;
private StatusImageProvider statusImageProvider;
@Required
public void setMetricHighlighting(boolean isMetricHighlighting) {
this.isMetricHighlighting = isMetricHighlighting;
}
@Required
public void setStatusImageProvider(StatusImageProvider statusImageProvider) {
this.statusImageProvider = statusImageProvider;
}
public List<SummaryDto> getSummary(String sessionId, String taskId) {
getData(sessionId);
if (summaryMap.containsKey(taskId)) {
return summaryMap.get(taskId);
} else {
return null;
}
}
public List<SummaryDto> getValidators(String sessionId, String taskId) {
getData(sessionId);
if (validatorsMap.containsKey(taskId)) {
return validatorsMap.get(taskId);
} else {
return null;
}
}
public List<SummaryDto> getLatencyPercentile(String sessionId, String taskId) {
getData(sessionId);
if (latencyPercentilesMap.containsKey(taskId)) {
return latencyPercentilesMap.get(taskId);
} else {
return null;
}
}
public Map<TestEntity, Map<MetricEntity, MetricSummaryValueEntity>> getStandardMetricsPerTest(String sessionId) {
getData(sessionId);
return standardMetricsMap;
}
public int getNumberOfTestGroups(String sessionId) {
getData(sessionId);
return numberOfTestGroups;
}
public List<SummaryTestDto> getTestSummaryData(String sessionId) {
getData(sessionId);
return testSummaryData;
}
public Map<TestEntity,Map<String,Double>> getDataForScalabilityPlots(String sessionId) {
getData(sessionId);
return dataForScalabilityPlots;
}
public SessionEntity getSessionEntity(String sessionId) {
getData(sessionId);
return sessionEntity;
}
private void getData(String sessionId) {
// Remember what session id was set for cashed data
if (this.sessionId == null) {
this.sessionId = sessionId;
}
// Reset data if new session id arrived
if (!sessionId.equals(this.sessionId)) {
sessionEntity = null;
metricsPerTest = null;
this.sessionId = sessionId;
summaryMap.clear();
latencyPercentilesMap.clear();
validatorsMap.clear();
standardMetricsMap.clear();
testSummaryData.clear();
dataForScalabilityPlots.clear();
}
if (metricsPerTest == null) {
Set<String> standardMetricsIds = new HashSet<String>();
standardMetricsIds.add(StandardMetricsNamesUtil.THROUGHPUT_ID);
standardMetricsIds.add(StandardMetricsNamesUtil.SUCCESS_RATE_ID);
standardMetricsIds.add(StandardMetricsNamesUtil.LATENCY_ID);
standardMetricsIds.add(StandardMetricsNamesUtil.LATENCY_STD_DEV_AGG_ID);
standardMetricsIds.add(StandardMetricsNamesUtil.ITERATION_SAMPLES_ID);
standardMetricsIds.add(StandardMetricsNamesUtil.VIRTUAL_USERS_ID);
LocalRankingProvider localRankingProvider = new LocalRankingProvider();
DataService dataService = new DefaultDataService(databaseService);
Set<TestEntity> testEntities = dataService.getTests(sessionId);
sessionEntity = dataService.getSession(sessionId);
metricsPerTest = dataService.getMetricsByTests(testEntities);
Set<Long> testIds = new HashSet<Long>();
for (TestEntity testEntity : testEntities) {
testIds.add(testEntity.getId());
}
numberOfTestGroups = databaseService.getTestGroupIdsByTestIds(testIds).keySet().size();
for (Map.Entry<TestEntity, Set<MetricEntity>> entry : metricsPerTest.entrySet()) {
List<SummaryDto> summaryList = new ArrayList<SummaryDto>();
Map<String, SummaryDto> summaries = new HashMap<>();
List<SummaryDto> latencyPercentilesList = new ArrayList<SummaryDto>();
List<SummaryDto> validatorsList = new ArrayList<SummaryDto>();
Map<MetricEntity, MetricSummaryValueEntity> standardMetricsPerTest = new HashMap<MetricEntity, MetricSummaryValueEntity>();
// Metrics
Map<MetricEntity, MetricSummaryValueEntity> summary = dataService.getMetricSummary(entry.getValue());
for (MetricEntity metricEntity : summary.keySet()) {
SummaryDto value = new SummaryDto();
// All summary
value.setKey(metricEntity.getDisplayName());
MetricSummaryValueEntity metricSummaryValueEntity = summary.get(metricEntity);
Double summaryValue = metricSummaryValueEntity.getValue();
value.setValue(new DecimalFormat(FormatCalculator.getNumberFormat(summaryValue)).format(summaryValue));
if (isMetricHighlighting && metricSummaryValueEntity.getDecision() != null) {
value.setDecision(metricSummaryValueEntity.getDecision().toString());
}
// Validators
if (metricEntity.getMetricNameDto().getOrigin().equals(MetricNameDto.Origin.VALIDATOR)) {
validatorsList.add(value);
}
// Latency percentiles
if (metricEntity.getMetricId().matches("^" + StandardMetricsNamesUtil.LATENCY_PERCENTILE_ID_REGEX)) {
// change key (name) for back compatibility
value.setKey(StandardMetricsNamesUtil.parseLatencyPercentileKey(metricEntity.getMetricId()) + "% - ");
latencyPercentilesList.add(value);
} else {
summaries.put(metricEntity.getMetricId(), value);
}
// Standard metrics
if (standardMetricsIds.contains(metricEntity.getMetricId())) {
standardMetricsPerTest.put(metricEntity, summary.get(metricEntity));
}
}
if (summaries.keySet().stream().anyMatch(StandardMetricsNamesUtil::isBelongingToScenario)) {
summaryList.addAll(ScenarioSummaryExtractor.extractScenarioSummary(summaries));
} else {
summaryList.addAll(summaries.values());
localRankingProvider.sortSummaryDto(summaryList);
}
localRankingProvider.sortSummaryDto(validatorsList);
localRankingProvider.sortSummaryDto(latencyPercentilesList);
// Test info
SummaryDto description = new SummaryDto();
description.setKey("Test description");
description.setValue(entry.getKey().getDescription());
summaryList.add(0, description);
SummaryDto startTime = new SummaryDto();
startTime.setKey("Start time");
startTime.setValue(dateFormatter.format(entry.getKey().getStartDate()));
summaryList.add(0, startTime);
SummaryDto termination = new SummaryDto();
termination.setKey("Termination");
termination.setValue(entry.getKey().getTerminationStrategy());
summaryList.add(0, termination);
SummaryDto load = new SummaryDto();
load.setKey("Load");
load.setValue(entry.getKey().getLoad());
summaryList.add(0, load);
summaryMap.put(entry.getKey().getId().toString(), summaryList);
latencyPercentilesMap.put(entry.getKey().getId().toString(), latencyPercentilesList);
validatorsMap.put(entry.getKey().getId().toString(), validatorsList);
standardMetricsMap.put(entry.getKey(), standardMetricsPerTest);
}
fillTestSummaryAndScalabilityData(this.sessionId);
}
}
private void fillTestSummaryAndScalabilityData(String sessionId) {
for (TestEntity testEntity : standardMetricsMap.keySet()) {
dataForScalabilityPlots.put(testEntity, new HashMap<String, Double>());
String testStatusComment = "";
Decision testStatus = Decision.OK;
// Errors during workload configuration
Decision testExecutionStatus = testEntity.getTestExecutionStatus();
if (testExecutionStatus.ordinal() > testStatus.ordinal()) {
testStatusComment = "Test status is based on test execution status. There were errors during test execution (f.e. timeouts)";
testStatus = testExecutionStatus;
}
// Limits based decision
Decision testDecisionBasedOLimits = testEntity.getDecision();
if (testDecisionBasedOLimits != null) {
if (testDecisionBasedOLimits.ordinal() > testStatus.ordinal()) {
testStatusComment = "Test status is based on comparison of summary values to limits";
testStatus = testDecisionBasedOLimits;
}
}
SummaryTestDto reportData = new SummaryTestDto();
reportData.setSessionId(sessionId);
reportData.setNumber(testEntity.getTestGroupIndex().toString());
reportData.setId(testEntity.getId().toString());
reportData.setTestStatus(testStatus);
reportData.setStatusImage(statusImageProvider.getImageByDecision(testStatus));
reportData.setTestName(testEntity.getName() + "\n\n\n" + testStatusComment);
testSummaryData.add(reportData);
}
Collections.sort(testSummaryData, new Comparator<SummaryTestDto>() {
@Override
public int compare(final SummaryTestDto result1, final SummaryTestDto result2) {
int val1 = Integer.parseInt(result1.getNumber());
int val2 = Integer.parseInt(result2.getNumber());
return val1 > val2 ? 1 : val1 < val2 ? -1 : 0;
}
} );
}
private class LocalRankingProvider extends MetricNamesRankingProvider {
public void sortSummaryDto(List<SummaryDto> list) {
Collections.sort(list, new Comparator<SummaryDto>() {
@Override
public int compare(SummaryDto o, SummaryDto o2) {
return LocalRankingProvider.compare(o.getKey(), o2.getKey());
}
});
}
}
@Required
public void setDatabaseService(DatabaseService databaseService) {
this.databaseService = databaseService;
}
}