perflab.LoadrunnerWrapper.java Source code

Java tutorial

Introduction

Here is the source code for perflab.LoadrunnerWrapper.java

Source

package perflab;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */

import java.io.*;
import java.util.ArrayList;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;

import java.io.File;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

/**
 * Goal which executes loadrunner scenario, analyze it and extract short summary
 *
 * @goal run-lr
 * @phase process-sources
 */
public class LoadrunnerWrapper extends AbstractMojo {
    /**
     * @parameter property="run-lr.scenario" default-value=""
     */
    private String scenario;

    /**
     * @parameter property="run-lr.htmlReportFolder" default-value=""
     */
    private String htmlReportFolder;

    /**
     * @parameter property="run-lr.resultsFolder" default-value=""
     */
    private String resultsFolder;

    /**
     * @parameter property="run-lr.summaryFile" default-value=""
     */
    private String summaryFile;

    /**
     * @parameter property="run-lr.loadRunnerBin" default-value="c:\Program Files (x86)\HP\LoadRunner\bin"
     */
    private String loadRunnerBin;

    /**
     * @parameter property="run-lr.analysisTemplateName" default-value="DefaultTemplate1"
     */
    private String analysisTemplateName;

    /**
     * @parameter property="run-lr.additionalAttributes" default-value=""
     */
    private String additionalAttributes;

    private HashMap<String, ConfigurationValue> LRSFlags;
    private HashMap<String, ConfigurationValue> AnalysisTemplatesFlags;

    /**
     *
     */
    private ArrayList<LoadRunnerTransaction> transactions = new ArrayList<LoadRunnerTransaction>();

    /* (non-Javadoc)
     * @see org.apache.maven.plugin.AbstractMojo#execute()
     */
    public void execute() throws MojoExecutionException, MojoFailureException {
        getLog().info("run scenario = " + scenario);
        getLog().info("results-folder = " + resultsFolder);
        getLog().info("html-results-folder = " + htmlReportFolder);
        getLog().info("summary-file = " + summaryFile);
        getLog().info("analysisTemplateName = " + analysisTemplateName);
        getLog().info("loadRunnerBin = " + loadRunnerBin);
        getLog().info("additionalAttributes = " + additionalAttributes);

        setConfigurationFlags();

        boolean okay = true;

        getLog().info(
                "======================= Checking Scenario and Analysis template configurations ======================= ");
        getLog().info("");

        /* check for files exists */
        //Check if lrs exists
        boolean lrsExists = checkIfScenarioExists(scenario);
        if (!lrsExists) {
            getLog().error("Scenario file " + scenario + " was not found on slave. Aborting job");
            okay = false;
        }

        //Check if LRS configured correctly
        if (!isFileWellConfigured(scenario, LRSFlags)) {
            okay = false;
        }

        //Check if Analysis template exists
        boolean analysisTemplateExists = checkIfTemplateExists(analysisTemplateName);
        if (!analysisTemplateExists) {
            getLog().error("Template file " + analysisTemplateName + " was not found on slave. Aborting job");
            okay = false;
        }

        if (!isFileWellConfigured(System.getenv("LR_PATH") + "\\AnalysisTemplates\\" + analysisTemplateName + "\\"
                + analysisTemplateName + ".tem", AnalysisTemplatesFlags)) {
            okay = false;
        }

        getLog().info("");
        getLog().info(
                "====================================================================================================== ");

        if (!okay) {
            throw new MojoExecutionException("Scenario and/or template are not configured well");
        }

        /*Build controller command line*/
        StringBuilder sb = new StringBuilder("\"").append(loadRunnerBin).append("\\").append("Wlrun.exe")
                .append("\"").append(" -Run ").append(" -TestPath ").append("\"").append(scenario).append("\"")
                .append(" -ResultName ").append("\"").append(resultsFolder).append("\"");

        if (additionalAttributes != null && !additionalAttributes.isEmpty()) {
            sb.append(" ").append(additionalAttributes);
        }

        String controllerCommand = sb.toString();
        //getLog().info("Run controller " + controllerCommand);

        runCommand("taskkill /f /im Wlrun.exe");

        /* Run controller */
        int controllerRC = runCommand(controllerCommand);

        if (controllerRC == 1) {
            /* Run Analysis if controller return code is okay */

            String resultsFile = getResultsFile(resultsFolder);

            if (resultsFile.isEmpty()) {
                getLog().error(
                        "Analysis session file (.lrr) was not  found in " + resultsFile + " folder. Aborting job");
                okay = false;
            }

            /*Build Analysis command line*/
            String analysisCommand = "\"" + loadRunnerBin + "\\AnalysisUI.exe\" -RESULTPATH " + resultsFile;
            if (analysisTemplateName != null && !analysisTemplateName.isEmpty()) {
                analysisCommand = analysisCommand + " -TEMPLATENAME " + analysisTemplateName;
            }

            int analysisRC = runCommand(analysisCommand);

            /* Parse analysis results and extract short report if analysis return code is okay */
            if (analysisRC == 0) {
                extractKPIs(resultsFolder, htmlReportFolder);
            }

        } else {
            getLog().error("Controller failed. Exit code is: " + controllerRC);
            throw new MojoExecutionException("Controller failed. Exit code is: " + controllerRC);
        }

    }

    /*Set LRS and LRR configuration flags*/
    private void setConfigurationFlags() {

        if (LRSFlags == null && AnalysisTemplatesFlags == null) {
            LRSFlags = new HashMap<String, ConfigurationValue>();
            AnalysisTemplatesFlags = new HashMap<String, ConfigurationValue>();

            LRSFlags.put("AutoSetResults", new ConfigurationValue("0",
                    "Please clear 'Automatically create a results directory for each scenario execution' option at Controller->Results->Results Settings..."));
            LRSFlags.put("AutoOverwriteResults", new ConfigurationValue("0",
                    "Please clear 'Automatically overwrite existing results directory without prompting for confirmation' option at Controller->Results->Results Settings..."));

            AnalysisTemplatesFlags.put("AutoHtml", new ConfigurationValue("1",
                    "Please set 'Generate the following automatic HTML report' option at Analysis->Tools->Templates->"
                            + analysisTemplateName));
            AnalysisTemplatesFlags.put("AutoSave",
                    new ConfigurationValue("1",
                            "Please set 'Automatically save the session at' option at Analysis->Tools->Templates->"
                                    + analysisTemplateName));
            AnalysisTemplatesFlags.put("AutoClose", new ConfigurationValue("1",
                    "Please set 'Automatically close Analysis after saving session' option at Analysis->Tools->Templates->"
                            + analysisTemplateName));
        }
    }

    /**
     * check if loadrunner scenario exists
     *
     * @return - true if LRS file exists
     */
    private boolean checkIfScenarioExists(String scenario) {
        boolean okay = false;

        File lrs = new File(scenario);
        if (lrs.exists() && !lrs.isDirectory()) {
            okay = true;
        }

        return okay;
    }

    private boolean isFileWellConfigured(String fileName, HashMap configurations) {
        boolean okay = true;

        try {
            String fileContent = FileUtils.readFileToString(new File(fileName));

            for (Object key : configurations.keySet()) {
                String keyStr = key.toString();

                ConfigurationValue p = (ConfigurationValue) configurations.get(key);
                String strToFind = keyStr + "=" + p.getValue();

                getLog().info("checking for " + strToFind);

                if (!fileContent.contains(strToFind)) {
                    okay = false;
                    getLog().error(fileName + ":" + strToFind + "  is missing or misconfigured . Aborting job");
                    getLog().error(p.getMessage());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return okay;
    }

    /**
     * check if this.loadRunnerAnalysisTemplateName exists on filesystem
     *
     * @return - true if template file exists
     */
    private boolean checkIfTemplateExists(String analysisTemplateName) {
        boolean okay = false;

        File template = new File(System.getenv("LR_PATH") + "\\AnalysisTemplates\\" + analysisTemplateName);
        if (template.exists() && template.isDirectory()) {
            okay = true;
        }
        return okay;
    }

    /**
     * @param resultsFolder
     * @return name of lrr file name in results folder
     */
    private String getResultsFile(String resultsFolder) {

        getLog().info("Looking for lrr file in " + resultsFolder);

        String lrrFile = findFilebyRegex(resultsFolder, "*.lrr");

        return lrrFile;
    }

    /**
     * @param path
     * @param pattern
     * @return first file according to the pattern
     */
    private String findFilebyRegex(String path, String pattern) {
        String foundFile = "";

        try {

            File dir = new File(path);
            if (dir.exists() && dir.isDirectory()) {
                FileFilter fileFilter = new WildcardFileFilter(pattern);
                File[] files = dir.listFiles(fileFilter);

                getLog().info("Length: " + files.length);

                foundFile = files[0].getAbsolutePath();
            } else if (dir.isFile()) {
                getLog().error(path + " not exists or not a folder...");
            }

        } catch (Exception e) {
            getLog().error("Can't find lrr file " + e.getMessage());
        }
        return foundFile;
    }

    /**
     * @param command - command to execute
     * @return command exit code
     */
    private int runCommand(String command) {
        int exitCode = -1;
        getLog().info("Command to run: " + command);

        try {
            Process p = Runtime.getRuntime().exec(command);
            p.waitFor();
            exitCode = p.exitValue();
        } catch (Exception err) {
            err.printStackTrace();
        }

        getLog().info("Exit value: " + exitCode);
        return exitCode;
    }

    /**
     * Generates report in format expected by https://wiki.jenkins-ci.org/display/JENKINS/PerfPublisher+Plugin
     * examples here: file:///C:/Users/i046774/Downloads/master-s-thesis-designing-and-automating-dynamic-testing-of-software-nightly-builds.pdf
     */
    protected void extractKPIs(String resultsFolder, String htmlReportFolder) {
        parseSummaryFile(htmlReportFolder + "\\summary.html", summaryFile);

        //generatePerfPublisherReport(this.transactions, xmlSummaryFile);
        //generatePlotCSVReport(this.transactions, summaryFile);
        generatejUnitReport(this.transactions, summaryFile);
    }

    /**
     * @param htmlSummaryFile - load runner analysis html report file to parse
     * @param summaryFile     - location of summary file to be generated out of loadrunner html analysis
     */
    protected void parseSummaryFile(String htmlSummaryFile, String summaryFile) {
        try {

            File input = new File(htmlSummaryFile);
            Document document = Jsoup.parse(input, "UTF-8");
            Document parse = Jsoup.parse(document.html());
            Elements table = parse.select("table").select("[summary=Transactions statistics summary table]");
            Elements rows = table.select("tr");

            getLog().info("number of rows in summary file=" + rows.size());

            for (Element row : rows) {

                //getLog().info("table element = " + row.toString());

                String name = row.select("td[headers=LraTransaction Name]").select("span").text();

                if (!name.isEmpty()) {

                    float avgRT = Float.valueOf(row.select("td[headers=LraAverage]").select("span").text());
                    float minRT = Float.valueOf(row.select("td[headers=LraMinimum]").select("span").text());
                    float maxRT = Float.valueOf(row.select("td[headers=LraMaximum]").select("span").text());
                    int passed = Integer.valueOf(row.select("td[headers=LraPass]").select("span").text()
                            .replace(".", "").replace(",", ""));
                    int failed = Integer.valueOf(row.select("td[headers=LraFail]").select("span").text()
                            .replace(".", "").replace(",", ""));
                    int failedPrecentage = failed / (failed + passed) * 100;

                    getLog().info("Saving Transaction [" + name + "]");
                    this.transactions.add(
                            new LoadRunnerTransaction(name, minRT, avgRT, maxRT, passed, failed, failedPrecentage));
                }
            }

        } catch (IOException e) {
            getLog().error("Can't read LoadRunner Analysis html report " + e.getMessage());
        }

    }

    /**
     * @param transactions - ArrayList of LoadRunnerTransaction objects
     * @param summaryFile  - location of xml summary file to be generated out of transaction objects in jUnit format
     * @return
     */
    private void generatejUnitReport(ArrayList<LoadRunnerTransaction> transactions, String summaryFile) {

        getLog().info("Transformation to jUnit XML started ...");

        String stringReport = "";

        try {
            /*
             * http://llg.cubic.org/docs/junit/
             <testsuite tests="3" time="42.5">
               <testcase classname="ZZZ_1" name="ZZZ_1" time="10.1"/>
               <testcase classname="ZZZ_2" name="ZZZ_2" time="11.7"/>
               <testcase classname="ZZZ_3" name="ZZZ_3" time="12.2">
                <!--failure type="NotEnoughFoo"> too slow </failure-->
               </testcase>
            </testsuite>
             */

            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

            // root elements
            org.w3c.dom.Document doc = (org.w3c.dom.Document) docBuilder.newDocument();
            org.w3c.dom.Element testsuiteElement = (org.w3c.dom.Element) doc.createElement("testsuite");
            testsuiteElement.setAttribute("tests", String.valueOf(transactions.size()));
            //testsuiteElement.setAttribute("time", "total test duration");
            doc.appendChild(testsuiteElement);

            ////////////////////////////////////////////////////////////////////////////

            for (LoadRunnerTransaction tr : this.transactions) {

                getLog().info("Dump " + tr.getName());

                org.w3c.dom.Element testcaseElement = doc.createElement("testcase");
                testcaseElement.setAttribute("classname", "load.ResponseTime");
                testcaseElement.setAttribute("name", tr.getName());
                testcaseElement.setAttribute("time", String.valueOf(tr.getAvgRT()));

                testsuiteElement.appendChild(testcaseElement);
            }

            ////////////////////////////////////////////////////////////////////////////
            // write the content into xml file
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

            DOMSource source = new DOMSource(doc);

            File lr_report = new File(summaryFile);
            StreamResult result = new StreamResult(lr_report);

            transformer.transform(source, result);

            getLog().info("File saved in " + lr_report.getAbsolutePath());

        } catch (ParserConfigurationException pce) {
            getLog().error(pce.getMessage());
            pce.printStackTrace();
        } catch (TransformerException tfe) {
            getLog().error(tfe.getMessage());
            tfe.printStackTrace();
        }
    }

    /**
     * @param transactions - ArrayList of LoadRunnerTransaction objects
     * @param summaryFile  - locatiob of SCV summary file to be generated out of transaction objects
     * @return
     */
    private boolean generatePlotCSVReport(ArrayList<LoadRunnerTransaction> transactions, String summaryFile) {

        boolean rc = false;

        getLog().info("Transformation CSV started ...");

        ArrayList<String> headers = new ArrayList<String>();
        ArrayList<String> averages = new ArrayList<String>();

        for (LoadRunnerTransaction tr : this.transactions) {
            headers.add("\"" + tr.getName() + "\"");
            averages.add(String.valueOf(tr.getAvgRT()));
        }

        String scvReport = org.apache.commons.lang3.StringUtils.join(headers, ",")
                + System.getProperty("line.separator") + org.apache.commons.lang3.StringUtils.join(averages, ",");

        try {
            FileUtils.writeStringToFile(new File(summaryFile), scvReport);
            getLog().info(scvReport);
            getLog().info("Report is saved to " + summaryFile);
            rc = true;
        } catch (IOException e) {
            e.printStackTrace();
            rc = false;
            getLog().error("Can't write custom csv report for plotting " + e.getMessage());
        }

        getLog().info("Transformation CSV finished ...");
        return rc;
    }

    /**
     * @param transactions - ArrayList of LoadRunnerTransaction objects
     * @return
     */
    private void generatePerfPublisherReport(ArrayList<LoadRunnerTransaction> transactions, String xmlFile) {
        getLog().info("Transformation to XML started ...");
        try {

            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

            // root elements
            org.w3c.dom.Document doc = (org.w3c.dom.Document) docBuilder.newDocument();
            org.w3c.dom.Element reportElement = (org.w3c.dom.Element) doc.createElement("report");
            doc.appendChild(reportElement);

            ////////////////////////////////////////////////////////////////////////////

            //<categories>
            //   <category name="memory" scale="mb">
            //       <observations>
            //           <observation name="Server 1">100</observation>
            //           <observation name="Server 2">200</observation>
            //       </observations>
            //   </category>

            //   <category name="disk" scale="gb">
            //       <observations>
            //           <observation name="Server 1">41</observation>
            //           <observation name="Server 2">58</observation>
            //       </observations>
            //   </category>
            //</categories>

            // start element
            org.w3c.dom.Element startElement = (org.w3c.dom.Element) doc.createElement("start");
            reportElement.appendChild(startElement);

            // date element
            org.w3c.dom.Element date = (org.w3c.dom.Element) doc.createElement("date");
            startElement.appendChild(date);

            date.setAttribute("format", "YYYYMMDD");
            date.setAttribute("val", "20100922");

            // time element
            org.w3c.dom.Element time = (org.w3c.dom.Element) doc.createElement("date");
            startElement.appendChild(time);

            time.setAttribute("format", "HHMMSS");
            time.setAttribute("val", "215935");

            ////////////////////////////////////////////////////////////////////////////

            //<test name="Smoke test" executed="yes" categ="Smoke test">

            //<description>Tests if ATE LAN socket and communication works.</description>

            //<result>
            //   <success passed="yes" state ="100" hasTimedOut="no" />
            //   <compiletime unit="s" mesure="0" isRelevant="yes" />
            //   <performance unit="%" mesure="0" isRelevant="yes" />
            //   <executiontime unit="s" mesure="12" isRelevant="yes" />
            //   <metrics>
            //      <006_My_Benefits unit="sec" mesure="0.115" isRelevant="yes"/>
            //      <007_My_Timesheet unit="sec" mesure="1.247" isRelevant="yes"/>
            //   </metrics>
            //</result>
            //</test>
            //</report>

            ////////////////////////////////////////////////////////////////////////////
            // test element
            org.w3c.dom.Element testElement = doc.createElement("test");
            reportElement.appendChild(testElement);

            testElement.setAttribute("name", "Smoke test");
            testElement.setAttribute("executed", "yes");
            testElement.setAttribute("categ", "Smoke test");

            // description element
            org.w3c.dom.Element descriptionElement = doc.createElement("description");
            descriptionElement.appendChild(doc.createTextNode("This is the best Load test ever executed..."));
            reportElement.appendChild(descriptionElement);

            ////////////////////////////////////////////////////////////////////////////
            // result
            org.w3c.dom.Element resultElement = doc.createElement("result");
            reportElement.appendChild(resultElement);

            org.w3c.dom.Element successElement = doc.createElement("success");
            resultElement.appendChild(successElement);

            org.w3c.dom.Element compiletimeElement = doc.createElement("compiletime");
            resultElement.appendChild(compiletimeElement);

            org.w3c.dom.Element performanceElement = doc.createElement("performance");
            resultElement.appendChild(performanceElement);

            org.w3c.dom.Element executiontimeElement = doc.createElement("executiontime");
            resultElement.appendChild(executiontimeElement);

            org.w3c.dom.Element metricsElement = doc.createElement("metrics");
            resultElement.appendChild(metricsElement);

            ////////////////////////////////////////////////////////////////////////////

            for (LoadRunnerTransaction tr : this.transactions) {
                //<006_My_Benefits unit="sec" mesure="0.115" isRelevant="yes"/>
                String trName = "tr_" + tr.getName();
                getLog().info("Dump " + trName);

                org.w3c.dom.Element trElement = doc.createElement(trName);
                trElement.setAttribute("unit", "sec");
                trElement.setAttribute("mesure", String.valueOf(tr.getAvgRT()));
                trElement.setAttribute("isRelevant", "yes");
                metricsElement.appendChild(trElement);
            }

            ////////////////////////////////////////////////////////////////////////////
            // write the content into xml file
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

            DOMSource source = new DOMSource(doc);

            File lr_report = new File(xmlFile);
            StreamResult result = new StreamResult(lr_report);

            transformer.transform(source, result);

            getLog().info("File saved in " + lr_report.getAbsolutePath());

        } catch (ParserConfigurationException pce) {
            getLog().error(pce.getMessage());
            pce.printStackTrace();
        } catch (TransformerException tfe) {
            getLog().error(tfe.getMessage());
            tfe.printStackTrace();
        }
    }

    private class ConfigurationValue {
        private String value;
        private String message;

        public ConfigurationValue(String value, String message) {
            this.value = value;
            this.message = message;
        }

        public String getValue() {
            return this.value;
        }

        public String getMessage() {
            return this.message;
        }
    }
}