src/main/java/com/microfocus/application/automation/tools/results/LrGraphUtils.java
/*
* Certain versions of software accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company.
* This software was acquired by Micro Focus on September 1, 2017, and is now offered by OpenText.
* Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.
* __________________________________________________________________
* MIT License
*
* Copyright 2012-2024 Open Text
*
* The only warranties for products and services of Open Text and
* its affiliates and licensors ("Open Text") are as may be set forth
* in the express warranty statements accompanying such products and services.
* Nothing herein should be construed as constituting an additional warranty.
* Open Text shall not be liable for technical or editorial errors or
* omissions contained herein. The information contained herein is subject
* to change without notice.
*
* Except as specifically indicated otherwise, this document contains
* confidential information and a valid license is required for possession,
* use or copying. If this work is provided to the U.S. Government,
* consistent with FAR 12.211 and 12.212, Commercial Computer Software,
* Computer Software Documentation, and Technical Data for Commercial Items are
* licensed to the U.S. Government under vendor's standard commercial license.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ___________________________________________________________________
*/
package com.microfocus.application.automation.tools.results;
import com.microfocus.application.automation.tools.results.projectparser.performance.AvgTransactionResponseTime;
import com.microfocus.application.automation.tools.results.projectparser.performance.LrProjectScenarioResults;
import com.microfocus.application.automation.tools.results.projectparser.performance.PercentileTransactionWholeRun;
import com.microfocus.application.automation.tools.results.projectparser.performance.TimeRangeResult;
import com.microfocus.application.automation.tools.results.projectparser.performance.WholeRunResult;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* The type Lr graph utils.
*/
public final class LrGraphUtils {
/**
* The constant X_AXIS_TITLE.
*/
private static final String X_AXIS_TITLE = "x_axis_title";
/**
* The constant Y_AXIS_TITLE.
*/
private static final String Y_AXIS_TITLE = "y_axis_title";
/**
* The constant DESCRIPTION.
*/
private static final String DESCRIPTION = "description";
/**
* The constant TITLE.
*/
private static final String TITLE = "title";
/**
* The constant LABELS.
*/
private static final String LABELS = "labels";
/**
* The constant BUILD_NUMBER.
*/
private static final String BUILD_NUMBER = "Build number";
/**
* The constant PERCENTILE_TRANSACTION_RESPONSE_TIME.
*/
private static final String PERCENTILE_TRANSACTION_RESPONSE_TIME = "Percentile Transaction Response Time";
/**
* The constant TRANSACTIONS_RESPONSE_TIME_SECONDS.
*/
private static final String TRANSACTIONS_RESPONSE_TIME_SECONDS = "Time (Sec)";
private static final String PRECENTILE_GRAPH_DESCRIPTION =
"Displays the average time taken to perform transactions during each second of the load test." +
" This graph helps you determine whether the performance of the server is within " +
"acceptable minimum and maximum transaction performance time ranges defined for your " +
"system.";
public static final String SERIES = "series";
private LrGraphUtils() {
}
/**
* creates dataset for Percentile transaction graph
*
* @param scenarioResults the relative scenario results to create the graph
* @param scenarioGraphData the target graph data set
*/
static void constructPercentileTransactionGraph(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
Map<Integer, TreeMap<String, PercentileTransactionWholeRun>> percentileTransactionResults =
scenarioResults.getValue().getPercentileTransactionResults();
JSONObject percentileTransactionResultsGraphSet =
extractPercentileTransactionSet(percentileTransactionResults,
scenarioResults.getValue().getTransactions());
if (!percentileTransactionResultsGraphSet.getJSONArray(LABELS).isEmpty()) {
percentileTransactionResultsGraphSet
.put(TITLE, PERCENTILE_TRANSACTION_RESPONSE_TIME);
percentileTransactionResultsGraphSet
.put(X_AXIS_TITLE, BUILD_NUMBER);
percentileTransactionResultsGraphSet.put(Y_AXIS_TITLE,
TRANSACTIONS_RESPONSE_TIME_SECONDS);
percentileTransactionResultsGraphSet
.put(DESCRIPTION, PRECENTILE_GRAPH_DESCRIPTION);
scenarioGraphData.put("percentileTransaction", percentileTransactionResultsGraphSet);
}
}
private static JSONObject extractPercentileTransactionSet(
Map<Integer, TreeMap<String, PercentileTransactionWholeRun>> graphData, HashSet<String> transactions) {
JSONObject graphDataSet = new JSONObject();
JSONArray labels = new JSONArray();
HashMap<String, ArrayList<Number>> percentileTrtData = new HashMap<String, ArrayList<Number>>(0);
for (String transaction : transactions) {
percentileTrtData.put(transaction, new ArrayList<Number>(0));
}
for (Map.Entry<Integer, TreeMap<String, PercentileTransactionWholeRun>> result : graphData.entrySet()) {
labels.add(result.getKey());
for (String transaction : transactions) {
if (!result.getValue().containsKey(transaction)) {
percentileTrtData.get(transaction).add(null);
// TODO:change to null
continue;
}
percentileTrtData.get(transaction).add((result.getValue()).get(transaction).getActualValue());
}
}
graphDataSet.put(LABELS, labels);
graphDataSet.put(SERIES, createGraphDatasets(percentileTrtData));
return graphDataSet;
}
private static JSONArray createGraphDatasets(Map<String, ArrayList<Number>> datasets) {
JSONArray graphSeries = new JSONArray();
for (Map.Entry<String, ArrayList<Number>> transactionData : datasets.entrySet()) {
JSONObject dataset = new JSONObject();
dataset.put("name", transactionData.getKey());
JSONArray data = new JSONArray();
data.addAll(transactionData.getValue());
dataset.put("data", data);
graphSeries.add(dataset);
}
return graphSeries;
}
/**
* Construct avg transaction graph.
*
* @param scenarioResults the scenario results
* @param scenarioGraphData the scenario graph data
*/
static void constructAvgTransactionGraph(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
Map<Integer, TreeMap<String, AvgTransactionResponseTime>> avgTransactionResponseTimeResults =
scenarioResults.getValue().getAvgTransactionResponseTimeResults();
JSONObject avgTransactionResponseTimeGraphSet =
extractAvgTrtData(avgTransactionResponseTimeResults, scenarioResults.getValue().getTransactions());
if (!avgTransactionResponseTimeGraphSet.getJSONArray(LABELS).isEmpty()) {
avgTransactionResponseTimeGraphSet.put(TITLE, "Average Transaction Response Time");
avgTransactionResponseTimeGraphSet.put(X_AXIS_TITLE, "Build number");
avgTransactionResponseTimeGraphSet
.put(Y_AXIS_TITLE, "Time (Sec.)");
avgTransactionResponseTimeGraphSet.put(DESCRIPTION,
"Displays the average time taken to perform transactions during each second of the load test." +
" This graph helps you determine whether the performance of the server is within " +
"acceptable minimum and maximum transaction performance time ranges defined for your " +
"system.");
scenarioGraphData.put("averageTransactionResponseTime", avgTransactionResponseTimeGraphSet);
}
}
private static JSONObject extractAvgTrtData(Map<Integer, TreeMap<String, AvgTransactionResponseTime>> graphData,
HashSet<String> transactions) {
HashMap<String, ArrayList<Number>> averageTRTData = new HashMap<String, ArrayList<Number>>(0);
JSONObject graphDataSet = new JSONObject();
JSONArray labels = new JSONArray();
for (String transaction : transactions) {
averageTRTData.put(transaction, new ArrayList<Number>(0));
}
for (Map.Entry<Integer, TreeMap<String, AvgTransactionResponseTime>> result : graphData.entrySet()) {
labels.add(result.getKey());
for (String transaction : transactions) {
if (!result.getValue().containsKey(transaction)) {
averageTRTData.get(transaction).add(null);
// TODO:change to null
continue;
}
averageTRTData.get(transaction).add((result.getValue()).get(transaction).getActualValueAvg());
}
}
graphDataSet.put(LABELS, labels);
JSONArray datasets = createGraphDatasets(averageTRTData);
graphDataSet.put(SERIES, datasets);
return graphDataSet;
}
/**
* Construct error graph.
*
* @param scenarioResults the scenario results
* @param scenarioGraphData the scenario graph data
*/
static void constructErrorGraph(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
Map<Integer, TimeRangeResult> errPerSecResults = scenarioResults.getValue().getErrPerSecResults();
JSONObject errPerSecResultsResultsGraphSet =
extractTimeRangeResult(errPerSecResults);
if (!errPerSecResultsResultsGraphSet.getJSONArray(LABELS).isEmpty()) {
errPerSecResultsResultsGraphSet.put(TITLE, "Total errors per second");
errPerSecResultsResultsGraphSet.put(X_AXIS_TITLE, "Build number");
errPerSecResultsResultsGraphSet.put(Y_AXIS_TITLE, "Errors");
errPerSecResultsResultsGraphSet.put(DESCRIPTION, "");
scenarioGraphData.put("errorPerSecResults", errPerSecResultsResultsGraphSet);
}
}
private static JSONObject extractTimeRangeResult(Map<Integer, TimeRangeResult> graphData) {
JSONObject graphDataSet;
graphDataSet = new JSONObject();
JSONArray labels = new JSONArray();
JSONArray datasets = new JSONArray();
JSONArray data = new JSONArray();
for (Map.Entry<Integer, TimeRangeResult> result : graphData.entrySet()) {
if (result.getValue().getTimeRanges().isEmpty()) {
labels.add(result.getKey());
data.add(result.getValue().getActualValueAvg());
}
}
graphDataSet.put(LABELS, labels);
datasets.add(data);
graphDataSet.put(SERIES, datasets);
return graphDataSet;
}
/**
* Construct average throughput.
*
* @param scenarioResults the scenario results
* @param scenarioGraphData the scenario graph data
*/
static void constructAverageThroughput(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
Map<Integer, WholeRunResult> averageThroughputResults = scenarioResults.getValue().getAverageThroughputResults();
JSONObject averageThroughputResultsGraphSet =
extractWholeRunSlaResult(averageThroughputResults, "Bytes/Sec");
if (!averageThroughputResultsGraphSet.getJSONArray(LABELS).isEmpty()) {
averageThroughputResultsGraphSet.put(TITLE, "Average Throughput per second");
averageThroughputResultsGraphSet.put(X_AXIS_TITLE, "Build number");
averageThroughputResultsGraphSet.put(Y_AXIS_TITLE, "Bytes");
averageThroughputResultsGraphSet.put(DESCRIPTION,
" Displays the amount of throughput (in bytes) on the Web server during the load test. " +
"Throughput represents the amount of data that the Vusers received from the server at" +
" any given second. This graph helps you to evaluate the amount of load Vusers " +
"generate, in terms of server throughput.\n");
scenarioGraphData.put("averageThroughput", averageThroughputResultsGraphSet);
}
}
/**
* @param graphData
* @return
*/
private static JSONObject extractWholeRunSlaResult(Map<Integer, WholeRunResult> graphData, String seriesName) {
JSONObject graphDataSet;
graphDataSet = new JSONObject();
JSONObject wholeRunSlaResults = new JSONObject();
JSONArray labels = new JSONArray();
JSONArray datasets = new JSONArray();
JSONArray data = new JSONArray();
for (Map.Entry<Integer, WholeRunResult> result : graphData.entrySet()) {
labels.add(result.getKey());
data.add(result.getValue().getActualValue());
}
graphDataSet.put(LABELS, labels);
wholeRunSlaResults.put("name",seriesName);
wholeRunSlaResults.put("data",data);
datasets.add(wholeRunSlaResults);
graphDataSet.put(SERIES, datasets);
return graphDataSet;
}
/**
* Construct total throughput graph.
*
* @param scenarioResults the scenario results
* @param scenarioGraphData the scenario graph data
*/
static void constructTotalThroughputGraph(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
Map<Integer, WholeRunResult> totalThroughputResults = scenarioResults.getValue().getTotalThroughtputResults();
JSONObject totalThroughputResultsGraphSet =
extractWholeRunSlaResult(totalThroughputResults, "Bytes");
if (!totalThroughputResultsGraphSet.getJSONArray(LABELS).isEmpty()) {
totalThroughputResultsGraphSet.put(TITLE, "Total Throughput");
totalThroughputResultsGraphSet.put(X_AXIS_TITLE, "Build number");
totalThroughputResultsGraphSet.put(Y_AXIS_TITLE, "Bytes");
totalThroughputResultsGraphSet.put(DESCRIPTION,
" Displays the amount of throughput (in bytes) on the Web server during the load test. " +
"Throughput represents the amount of data that the Vusers received from the server at" +
" any given second. This graph helps you to evaluate the amount of load Vusers " +
"generate, in terms of server throughput.\n");
scenarioGraphData.put("totalThroughput", totalThroughputResultsGraphSet);
}
}
/**
* Construct avg hits graph.
*
* @param scenarioResults the scenario results
* @param scenarioGraphData the scenario graph data
*/
static void constructAvgHitsGraph(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
Map<Integer, WholeRunResult> avgHitsPerSec = scenarioResults.getValue().getAverageHitsPerSecondResults();
JSONObject avgHitsPerSecGraphSet = extractWholeRunSlaResult(avgHitsPerSec, "Hits/Sec");
if (!avgHitsPerSecGraphSet.getJSONArray(LABELS).isEmpty()) {
avgHitsPerSecGraphSet.put(TITLE, "Average Hits per Second");
avgHitsPerSecGraphSet.put(X_AXIS_TITLE, "Build number");
avgHitsPerSecGraphSet.put(Y_AXIS_TITLE, "Hits");
avgHitsPerSecGraphSet.put(DESCRIPTION,
"Displays the number of hits made on the Web server by Vusers " +
"during each second of the load test. This graph helps you evaluate the amount of load " +
"Vusers" +
" " +
"generate, in terms of the number of hits.");
scenarioGraphData.put("avgHitsPerSec", avgHitsPerSecGraphSet);
}
}
/**
* Construct total hits graph.
*
* @param scenarioResults the scenario results
* @param scenarioGraphData the scenario graph data
*/
static void constructTotalHitsGraph(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
Map<Integer, WholeRunResult> totalHitsResults = scenarioResults.getValue().getTotalHitsResults();
JSONObject totalHitsGraphSet = extractWholeRunSlaResult(totalHitsResults, "Hits");
if (!totalHitsGraphSet.getJSONArray(LABELS).isEmpty()) {
totalHitsGraphSet.put(TITLE, "Total Hits");
totalHitsGraphSet.put(X_AXIS_TITLE, "Build number");
totalHitsGraphSet.put(Y_AXIS_TITLE, "Hits");
totalHitsGraphSet.put(DESCRIPTION,
"Displays the number of hits made on the Web server by Vusers " +
"during each second of the load test. This graph helps you evaluate the amount of load " +
"Vusers" +
" " +
"generate, in terms of the number of hits.");
scenarioGraphData.put("totalHits", totalHitsGraphSet);
}
}
private static JSONObject extractVuserResult(Map<Integer, TreeMap<String, Integer>> graphData) {
JSONObject graphDataSet;
graphDataSet = new JSONObject();
JSONArray labels = new JSONArray();
HashMap<String, ArrayList<Number>> vUserState = new HashMap<String, ArrayList<Number>>(0);
vUserState.put("Passed", new ArrayList<Number>(0));
vUserState.put("Failed", new ArrayList<Number>(0));
vUserState.put("Stopped", new ArrayList<Number>(0));
vUserState.put("Error", new ArrayList<Number>(0));
for(Map.Entry<Integer, TreeMap<String, Integer>> run : graphData.entrySet())
{
Number tempVUserCount = run.getValue().get("Count");
if(tempVUserCount != null && tempVUserCount.intValue() > 0)
{
labels.add(run.getKey());
vUserState.get("Passed").add(run.getValue().get("Passed"));
vUserState.get("Failed").add(run.getValue().get("Failed"));
vUserState.get("Stopped").add(run.getValue().get("Stopped"));
vUserState.get("Error").add(run.getValue().get("Error"));
}
}
graphDataSet.put(LABELS, labels);
graphDataSet.put(SERIES, createGraphDatasets(vUserState));
return graphDataSet;
}
static void constructVuserGraph(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
Map<Integer, TreeMap<String, Integer>> vUserResults = scenarioResults.getValue().getvUserPerRun();
JSONObject vUserGraphSet = extractVuserResult(vUserResults);
if (!vUserGraphSet.getJSONArray(LABELS).isEmpty()) {
vUserGraphSet.put(TITLE, "VUser");
vUserGraphSet.put(X_AXIS_TITLE, "Build number");
vUserGraphSet.put(Y_AXIS_TITLE, "Vuser count");
vUserGraphSet.put(DESCRIPTION, "");
scenarioGraphData.put("VUser", vUserGraphSet);
}
}
static void constructConnectionsGraph(Map.Entry<String, LrProjectScenarioResults> scenarioResults,
JSONObject scenarioGraphData) {
SortedMap<Integer, Integer> connectionsResults = scenarioResults.getValue().getMaxConnectionsCount();
JSONObject maxConnectionsGraphSet = extractConnectionResults(connectionsResults);
if (!maxConnectionsGraphSet.getJSONArray(LABELS).isEmpty()) {
maxConnectionsGraphSet.put(TITLE, "Connections");
maxConnectionsGraphSet.put(X_AXIS_TITLE, "Build number");
maxConnectionsGraphSet.put(Y_AXIS_TITLE, "Connection count");
maxConnectionsGraphSet.put(DESCRIPTION, "");
scenarioGraphData.put("Connections", maxConnectionsGraphSet);
}
}
private static JSONObject extractConnectionResults(SortedMap<Integer, Integer> connectionsResults) {
JSONObject graphDataSet;
graphDataSet = new JSONObject();
JSONArray labels = new JSONArray();
JSONArray data = new JSONArray();
JSONArray dataSets = new JSONArray();
JSONObject maxConnections = new JSONObject();
for(Map.Entry<Integer, Integer> runConnectionMax: connectionsResults.entrySet())
{
if(runConnectionMax.getValue()!= null && runConnectionMax.getValue() > 0)
{
labels.add(runConnectionMax.getKey());
data.add(runConnectionMax.getValue().toString());
}
}
maxConnections.put("name", "Max connections");
maxConnections.put("data", data);
dataSets.add(maxConnections);
graphDataSet.put(LABELS, labels);
graphDataSet.put(SERIES, dataSets);
return graphDataSet;
}
static void constructVuserSummary(SortedMap<String, Integer> vUserResults,
JSONObject scenarioStats, int size){
JSONObject vUserSummary = new JSONObject();
int vUserCount = vUserResults.get("Count");
if(vUserCount != 0) {
double passedVuserPercentile = ((double) vUserResults.get("Passed") / vUserCount) * 100;
vUserSummary.put("PassedVuserPercentile", passedVuserPercentile);
double failedVuserPercentile = ((double) vUserResults.get("Failed") / vUserCount) * 100;
vUserSummary.put("FailedVuserPercentile", failedVuserPercentile);
double errorVuserPercentile = ((double) vUserResults.get("Error") / vUserCount) * 100;
vUserSummary.put("ErroredVuserPercentile", errorVuserPercentile);
int avgMaxVuser = vUserResults.get("MaxVuserRun") / size;
vUserSummary.put("AvgMaxVuser", avgMaxVuser);
scenarioStats.put("VUserSummary", vUserSummary);
}
}
static void constructConnectionSummary(SortedMap<Integer, Integer> maxConnectionPerRun,
JSONObject scenarioStats){
JSONObject maxConnectionsSummary = new JSONObject();
int connectionSum = 0;
for(Integer runConnectionMax: maxConnectionPerRun.values())
{
if((runConnectionMax > 0))
{
connectionSum += runConnectionMax;
continue;
}
}
int connectionMaxAverage = connectionSum / maxConnectionPerRun.size();
maxConnectionsSummary.put("AvgMaxConnection", connectionMaxAverage);
scenarioStats.put("AvgMaxConnections", maxConnectionsSummary);
}
static void constructTransactionSummary(SortedMap<String, Integer> transactionSummary,
JSONObject scenarioStats, int size){
JSONObject transactionSum = new JSONObject();
for(SortedMap.Entry<String, Integer> transaction : transactionSummary.entrySet()){
transactionSum.put(transaction.getKey(), (transaction.getValue().intValue() / size ));
}
scenarioStats.put("TransactionSummary", transactionSum);
}
static void constructDurationSummary(SortedMap<Integer, Long> durationData, JSONObject scenarioStats) {
JSONObject durationSummary = new JSONObject();
long runDurationSum = 0;
for(Long runDuration: durationData.values())
{
if((runDuration > 0))
{
runDurationSum += runDuration;
continue;
}
}
double scenarioDurationAverage = -1;
if(!durationData.isEmpty())
{
scenarioDurationAverage = (double) runDurationSum / durationData.size();
}
durationSummary.put("AvgDuration", scenarioDurationAverage);
scenarioStats.put("AvgScenarioDuration", durationSummary);
}
}