com.hpe.application.automation.tools.results.RunResultRecorder.java Source code

Java tutorial

Introduction

Here is the source code for com.hpe.application.automation.tools.results.RunResultRecorder.java

Source

/*
 *  Copyright 2013 EntIT Software LLC
 *  Certain versions of software and/or documents (Material?) accessible here may contain branding from
 *  Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company.  As of September 1, 2017,
 *  the Material is now offered by Micro Focus, a separately owned and operated company.  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 (c) 2018 Micro Focus Company, L.P.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * ___________________________________________________________________
 *
 */

package com.hpe.application.automation.tools.results;

import com.hpe.application.automation.tools.common.RuntimeUtils;
import com.hpe.application.automation.tools.model.EnumDescription;
import com.hpe.application.automation.tools.model.ResultsPublisherModel;
import com.hpe.application.automation.tools.results.projectparser.performance.AvgTransactionResponseTime;
import com.hpe.application.automation.tools.results.projectparser.performance.JobLrScenarioResult;
import com.hpe.application.automation.tools.results.projectparser.performance.LrJobResults;
import com.hpe.application.automation.tools.results.projectparser.performance.LrTest;
import com.hpe.application.automation.tools.results.projectparser.performance.PercentileTransactionWholeRun;
import com.hpe.application.automation.tools.results.projectparser.performance.TimeRange;
import com.hpe.application.automation.tools.results.projectparser.performance.TimeRangeResult;
import com.hpe.application.automation.tools.results.projectparser.performance.WholeRunResult;
import com.hpe.application.automation.tools.run.PcBuilder;
import com.hpe.application.automation.tools.run.RunFromAlmBuilder;
import com.hpe.application.automation.tools.run.RunFromFileBuilder;
import com.hpe.application.automation.tools.run.SseBuilder;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.matrix.MatrixAggregatable;
import hudson.matrix.MatrixAggregator;
import hudson.matrix.MatrixBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Project;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Builder;
import hudson.tasks.Publisher;
import hudson.tasks.Recorder;
import hudson.tasks.junit.CaseResult;
import hudson.tasks.junit.JUnitResultArchiver;
import hudson.tasks.junit.SuiteResult;
import hudson.tasks.junit.TestResult;
import hudson.tasks.junit.TestResultAction;
import hudson.tasks.test.TestResultAggregator;
import jenkins.tasks.SimpleBuildStep;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.kohsuke.stapler.DataBoundConstructor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.annotation.Nonnull;
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;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import static com.hpe.application.automation.tools.results.projectparser.performance.XmlParserUtil.getNode;
import static com.hpe.application.automation.tools.results.projectparser.performance.XmlParserUtil.getNodeAttr;

/**
 * using {@link JUnitResultArchiver};
 *
 * @author Thomas Maurel
 */
public class RunResultRecorder extends Recorder implements Serializable, MatrixAggregatable, SimpleBuildStep {

    public static final String REPORT_NAME_FIELD = "report";
    public static final int SECS_IN_DAY = 86400;
    public static final int SECS_IN_HOUR = 3600;
    public static final int SECS_IN_MINUTE = 60;
    private static final long serialVersionUID = 1L;
    private static final String PERFORMANCE_REPORT_FOLDER = "PerformanceReport";
    private static final String IE_REPORT_FOLDER = "IE";
    private static final String HTML_REPORT_FOLDER = "HTML";
    private static final String INDEX_HTML_NAME = "index.html";
    private static final String REPORT_INDEX_NAME = "report.index";
    private static final String REPORTMETADATE_XML = "report_metadata.xml";
    private static final String TRANSACTION_SUMMARY_FOLDER = "TransactionSummary";
    private static final String TRANSACTION_REPORT_NAME = "Report3";
    private static final String SLA_ACTUAL_VALUE_LABEL = "ActualValue";
    private static final String SLA_GOAL_VALUE_LABEL = "GoalValue";
    public static final String SLA_ULL_NAME = "FullName";
    public static final String ARCHIVING_TEST_REPORTS_FAILED_DUE_TO_XML_PARSING_ERROR = "Archiving test reports failed due to xml parsing error: ";
    private final ResultsPublisherModel _resultsPublisherModel;
    private List<FilePath> runReportList;

    /**
     * Instantiates a new Run result recorder.
     *
     * @param archiveTestResultsMode the archive test results mode
     */
    @DataBoundConstructor
    public RunResultRecorder(String archiveTestResultsMode) {

        _resultsPublisherModel = new ResultsPublisherModel(archiveTestResultsMode);

    }

    @Override
    public DescriptorImpl getDescriptor() {

        return (DescriptorImpl) super.getDescriptor();
    }

    /**
     * Pipeline perform.
     *
     * @param build              the build
     * @param workspace          the workspace
     * @param launcher           the launcher
     * @param listener           the listener
     * @param builderResultNames the builder result names
     * @throws IOException          the io exception
     * @throws InterruptedException the interrupted exception
     */
    // temporary solution to deal with lack of support in builder list when Project is replaced by job type in pipeline.
    // Should be dealt with general refactoring - making this a job property or change folder structure to scan instead
    // of passing file name from builder.
    @SuppressWarnings("squid:S1160")
    public void pipelinePerform(@Nonnull Run<?, ?> build, @Nonnull FilePath workspace, @Nonnull Launcher launcher,
            @Nonnull TaskListener listener, @Nonnull Map<String, String> builderResultNames)
            throws IOException, InterruptedException {
        final List<String> mergedResultNames = new ArrayList<String>();
        runReportList = new ArrayList<FilePath>();
        final List<String> fileSystemResultNames = new ArrayList<String>();
        fileSystemResultNames.add(builderResultNames.get(RunFromFileBuilder.class.getName()));

        mergedResultNames.addAll(builderResultNames.values());

        if (mergedResultNames.isEmpty()) {
            listener.getLogger().println("RunResultRecorder: no results xml File provided");
            return;
        }

        recordRunResults(build, workspace, launcher, listener, mergedResultNames, fileSystemResultNames);
        return;
    }

    /**
     * Records LR test results copied in JUnit format
     *
     * @param build
     * @param workspace
     * @param launcher
     * @param listener
     * @param mergedResultNames
     * @param fileSystemResultNames
     * @throws InterruptedException
     * @throws IOException
     */
    private void recordRunResults(@Nonnull Run<?, ?> build, @Nonnull FilePath workspace, @Nonnull Launcher launcher,
            @Nonnull TaskListener listener, List<String> mergedResultNames, List<String> fileSystemResultNames)
            throws InterruptedException, IOException {

        for (String resultFile : mergedResultNames) {
            JUnitResultArchiver jUnitResultArchiver = new JUnitResultArchiver(resultFile);
            jUnitResultArchiver.setKeepLongStdio(true);
            jUnitResultArchiver.setAllowEmptyResults(true);
            jUnitResultArchiver.perform(build, workspace, launcher, listener);
        }

        final TestResultAction tempAction = build.getAction(TestResultAction.class);
        if (tempAction == null || tempAction.getResult() == null) {
            listener.getLogger().println("RunResultRecorder: didn't find any test results to record");
            return;
        }

        TestResult result = tempAction.getResult();

        try {
            archiveTestsReport(build, listener, fileSystemResultNames, result, workspace);
        } catch (ParserConfigurationException | SAXException e) {
            listener.error(ARCHIVING_TEST_REPORTS_FAILED_DUE_TO_XML_PARSING_ERROR + e);
        }

        if ((runReportList != null) && !(runReportList.isEmpty())) {
            LrJobResults jobDataSet = null;
            try {
                jobDataSet = buildJobDataset(listener);
            } catch (ParserConfigurationException | SAXException e) {
                listener.error(ARCHIVING_TEST_REPORTS_FAILED_DUE_TO_XML_PARSING_ERROR + e);
            }

            if ((jobDataSet != null && !jobDataSet.getLrScenarioResults().isEmpty())) {
                PerformanceJobReportAction performanceJobReportAction = build
                        .getAction(PerformanceJobReportAction.class);
                if (performanceJobReportAction != null) {
                    performanceJobReportAction.mergeResults(jobDataSet);
                } else {
                    performanceJobReportAction = new PerformanceJobReportAction(build, jobDataSet);
                }
                build.replaceAction(performanceJobReportAction);
            }
        }
        publishLrReports(build);
    }

    /**
     * Adds the html reports actions to the left side menu.
     *
     * @param build
     */
    private void publishLrReports(@Nonnull Run<?, ?> build) throws IOException {
        File reportDirectory = new File(build.getRootDir(), PERFORMANCE_REPORT_FOLDER);
        if (reportDirectory.exists()) {
            File htmlIndexFile = new File(reportDirectory, INDEX_HTML_NAME);
            if (htmlIndexFile.exists()) {
                build.replaceAction(new PerformanceReportAction(build));
            }
        }

        File summaryDirectory = new File(build.getRootDir(), TRANSACTION_SUMMARY_FOLDER);
        if (summaryDirectory.exists()) {
            File htmlIndexFile = new File(summaryDirectory, INDEX_HTML_NAME);
            if (htmlIndexFile.exists()) {
                build.replaceAction(new TransactionSummaryAction(build));
            }
        }
    }

    /**
     * copies, archives and creates the Test reports of LR and UFT runs.
     *
     * @param build
     * @param listener
     * @param resultFiles
     * @param testResult
     * @param runWorkspace
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws IOException
     * @throws InterruptedException
     */
    @SuppressWarnings({ "squid:S134", "squid:S135" })
    private void archiveTestsReport(Run<?, ?> build, TaskListener listener, List<String> resultFiles,
            TestResult testResult, FilePath runWorkspace)
            throws ParserConfigurationException, SAXException, IOException, InterruptedException {

        if ((resultFiles == null) || (resultFiles.isEmpty())) {
            return;
        }

        ArrayList<String> zipFileNames = new ArrayList<String>();
        ArrayList<FilePath> reportFolders = new ArrayList<FilePath>();
        List<String> reportNames = new ArrayList<String>();

        listener.getLogger()
                .println("Report archiving mode is set to: " + _resultsPublisherModel.getArchiveTestResultsMode());

        // if user specified not to archive report
        if (_resultsPublisherModel.getArchiveTestResultsMode()
                .equals(ResultsPublisherModel.dontArchiveResults.getValue()))
            return;

        FilePath projectWS = runWorkspace;

        // get the artifacts directory where we will upload the zipped report
        // folder
        File artifactsDir = new File(build.getRootDir(), "archive");
        artifactsDir.mkdirs();

        // read each result.xml
        /*
         * The structure of the result file is: <testsuites> <testsuite>
         * <testcase.........report="path-to-report"/>
         * <testcase.........report="path-to-report"/>
         * <testcase.........report="path-to-report"/>
         * <testcase.........report="path-to-report"/> </testsuite>
         * </testsuites>
         */

        // add previous report names for aggregation when using pipelines.
        PerformanceJobReportAction performanceJobReportAction = build.getAction(PerformanceJobReportAction.class);
        if (performanceJobReportAction != null) {
            reportNames
                    .addAll(performanceJobReportAction.getLrResultBuildDataset().getLrScenarioResults().keySet());
        }

        for (String resultsFilePath : resultFiles) {
            FilePath resultsFile = projectWS.child(resultsFilePath);

            List<ReportMetaData> ReportInfoToCollect = new ArrayList<ReportMetaData>();

            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();

            Document doc = dBuilder.parse(resultsFile.read());
            doc.getDocumentElement().normalize();

            Node testSuiteNode = doc.getElementsByTagName("testsuite").item(0);
            Element testSuiteElement = (Element) testSuiteNode;
            if (testSuiteElement.hasAttribute("name") && testSuiteElement.getAttribute("name").endsWith(".lrs")) { // LR test
                NodeList testSuiteNodes = doc.getElementsByTagName("testsuite");
                for (int i = 0; i < testSuiteNodes.getLength(); i++) {
                    testSuiteNode = testSuiteNodes.item(i);
                    testSuiteElement = (Element) testSuiteNode;
                    if (!testSuiteElement.hasAttribute("name")) {
                        continue;
                    }
                    String testFolderPath = testSuiteElement.getAttribute("name");
                    int testPathArr = testFolderPath.lastIndexOf('\\');
                    String testName = testFolderPath.substring(testPathArr + 1);
                    reportNames.add(testName);
                    String testStatus = ("0".equals(testSuiteElement.getAttribute("failures"))) ? "pass" : "fail";

                    Node testCaseNode = testSuiteElement.getElementsByTagName("testcase").item(0);
                    if (testCaseNode == null) {
                        listener.getLogger().println("No report folder was found in results");
                        return;
                    }
                    if (testCaseNode.getNodeType() == Node.ELEMENT_NODE) {

                        Element testCaseElement = (Element) testCaseNode;

                        if (!testCaseElement.hasAttribute(REPORT_NAME_FIELD)) {
                            continue;
                        }

                        String reportFolderPath = testCaseElement.getAttribute(REPORT_NAME_FIELD);
                        FilePath reportFolder = new FilePath(projectWS.getChannel(), reportFolderPath);
                        reportFolders.add(reportFolder);

                        FilePath testFolder = new FilePath(projectWS.getChannel(), testFolderPath);
                        String zipFileName = getUniqueZipFileNameInFolder(zipFileNames, testFolder.getName());
                        FilePath archivedFile = new FilePath(new FilePath(artifactsDir), zipFileName);

                        if (archiveFolder(reportFolder, testStatus, archivedFile, listener)) {
                            zipFileNames.add(zipFileName);
                        }

                        createHtmlReport(reportFolder, testFolderPath, artifactsDir, reportNames, testResult);
                        createTransactionSummary(reportFolder, testFolderPath, artifactsDir, reportNames,
                                testResult);
                        try {
                            FilePath testSla = copyRunReport(reportFolder, build.getRootDir(),
                                    testFolder.getName());
                            if (testSla == null) {
                                listener.getLogger().println("no RunReport.xml file was created");
                            } else {
                                runReportList.add(testSla);
                            }
                        } catch (IOException | InterruptedException e) {
                            listener.getLogger().println(e);
                        }
                    }
                }
            } else { // UFT Test
                boolean reportIsHtml = false;
                NodeList testCasesNodes = ((Element) testSuiteNode).getElementsByTagName("testcase");
                for (int i = 0; i < testCasesNodes.getLength(); i++) {

                    Node nNode = testCasesNodes.item(i);

                    if (nNode.getNodeType() == Node.ELEMENT_NODE) {

                        Element eElement = (Element) nNode;

                        if (!eElement.hasAttribute(REPORT_NAME_FIELD)) {
                            continue;
                        }

                        String reportFolderPath = eElement.getAttribute(REPORT_NAME_FIELD); // e.g. "C:\UFTTest\GuiTest1\Report"
                        String testFolderPath = eElement.getAttribute("name"); // e.g. "C:\UFTTest\GuiTest1"
                        String testStatus = eElement.getAttribute("status"); // e.g. "pass"

                        Node nodeSystemInfo = eElement.getElementsByTagName("system-out").item(0);
                        String sysinfo = nodeSystemInfo.getFirstChild().getNodeValue();
                        String testDateTime = sysinfo.substring(0, 19);

                        FilePath reportFolder = new FilePath(projectWS.getChannel(), reportFolderPath);

                        reportFolders.add(reportFolder);

                        String archiveTestResultMode = _resultsPublisherModel.getArchiveTestResultsMode();
                        boolean archiveTestResult = false;

                        //check for the new html report
                        FilePath htmlReport = new FilePath(reportFolder, "run_results.html");
                        FilePath rrvReport = new FilePath(reportFolder, "Results.xml");
                        if (htmlReport.exists()) {
                            reportIsHtml = true;
                            String htmlReportDir = reportFolder.getRemote();

                            ReportMetaData reportMetaData = new ReportMetaData();
                            reportMetaData.setFolderPath(htmlReportDir);
                            reportMetaData.setIsHtmlReport(true);
                            reportMetaData.setDateTime(testDateTime);
                            reportMetaData.setStatus(testStatus);

                            File testFileFullName = new File(testFolderPath);
                            String testName = org.apache.commons.io.FilenameUtils
                                    .getName(testFileFullName.getPath());
                            String resourceUrl = "artifact/UFTReport/" + testName;
                            reportMetaData.setResourceURL(resourceUrl);
                            reportMetaData.setDisPlayName(testName); // use the name, not the full path
                            //don't know reportMetaData's URL path yet, we will generate it later.
                            ReportInfoToCollect.add(reportMetaData);

                            listener.getLogger().println(
                                    "add html report info to ReportInfoToCollect: " + "[date]" + testDateTime);
                        }

                        archiveTestResult = isArchiveTestResult(testStatus, archiveTestResultMode);

                        if (archiveTestResult && rrvReport.exists()) {

                            if (reportFolder.exists()) {

                                FilePath testFolder = new FilePath(projectWS.getChannel(), testFolderPath);

                                String zipFileName = getUniqueZipFileNameInFolder(zipFileNames,
                                        testFolder.getName());
                                zipFileNames.add(zipFileName);

                                listener.getLogger().println("Zipping report folder: " + reportFolderPath);

                                ByteArrayOutputStream outstr = new ByteArrayOutputStream();

                                //don't use FileFilter for zip, or it will cause bug when files are on slave
                                reportFolder.zip(outstr);

                                /*
                                         * I did't use copyRecursiveTo or copyFrom due to
                                 * bug in
                                 * jekins:https://issues.jenkins-ci.org/browse
                                 * /JENKINS-9189 //(which is cleaimed to have been
                                 * fixed, but not. So I zip the folder to stream and
                                 * copy it to the master.
                                 */

                                ByteArrayInputStream instr = new ByteArrayInputStream(outstr.toByteArray());

                                FilePath archivedFile = new FilePath(new FilePath(artifactsDir), zipFileName);
                                archivedFile.copyFrom(instr);
                                listener.getLogger().println("copy from slave to master: " + archivedFile);
                                outstr.close();
                                instr.close();

                                // add to Report list
                                ReportMetaData reportMetaData = new ReportMetaData();
                                reportMetaData.setIsHtmlReport(false);
                                // reportMetaData.setFolderPath(htmlReportDir); //no need for RRV
                                File testFileFullName = new File(testFolderPath);
                                String testName = testFileFullName.getName();
                                reportMetaData.setDisPlayName(testName); // use the name, not the full path
                                String zipFileUrlName = "artifact/" + zipFileName;
                                reportMetaData.setUrlName(zipFileUrlName); // for RRV, the file url and resource url are the same.
                                reportMetaData.setResourceURL(zipFileUrlName);
                                reportMetaData.setDateTime(testDateTime);
                                reportMetaData.setStatus(testStatus);
                                ReportInfoToCollect.add(reportMetaData);

                            } else {
                                listener.getLogger().println("No report folder was found in: " + reportFolderPath);
                            }
                        }

                    }
                }

                if (reportIsHtml && !ReportInfoToCollect.isEmpty()) {

                    listener.getLogger().println("begin to collectAndPrepareHtmlReports");
                    collectAndPrepareHtmlReports(build, listener, ReportInfoToCollect, runWorkspace);
                }

                if (!ReportInfoToCollect.isEmpty()) {
                    // serialize report metadata
                    File reportMetaDataXmlFile = new File(artifactsDir.getParent(), REPORTMETADATE_XML);
                    String reportMetaDataXml = reportMetaDataXmlFile.getAbsolutePath();
                    writeReportMetaData2XML(ReportInfoToCollect, reportMetaDataXml, listener);

                    // Add UFT report action
                    try {
                        listener.getLogger().println("Adding a report action to the current build.");
                        HtmlBuildReportAction reportAction = new HtmlBuildReportAction(build);
                        build.addAction(reportAction);

                    } catch (IOException | SAXException | ParserConfigurationException ex) {
                        listener.getLogger().println("a problem adding action: " + ex);
                    }
                }
            }
        }
    }

    private void writeReportMetaData2XML(List<ReportMetaData> htmlReportsInfo, String xmlFile,
            TaskListener _logger) {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = null;
        try {
            builder = dbf.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            _logger.error("Failed creating xml doc report: " + e);
            return;
        }
        Document doc = builder.newDocument();
        Element root = doc.createElement("reports_data");
        doc.appendChild(root);

        for (ReportMetaData htmlReportInfo : htmlReportsInfo) {
            String disPlayName = htmlReportInfo.getDisPlayName();
            String urlName = htmlReportInfo.getUrlName();
            String resourceURL = htmlReportInfo.getResourceURL();
            String dateTime = htmlReportInfo.getDateTime();
            String status = htmlReportInfo.getStatus();
            String isHtmlReport = htmlReportInfo.getIsHtmlReport() ? "true" : "false";
            Element elmReport = doc.createElement(REPORT_NAME_FIELD);
            elmReport.setAttribute("disPlayName", disPlayName);
            elmReport.setAttribute("urlName", urlName);
            elmReport.setAttribute("resourceURL", resourceURL);
            elmReport.setAttribute("dateTime", dateTime);
            elmReport.setAttribute("status", status);
            elmReport.setAttribute("isHtmlreport", isHtmlReport);
            root.appendChild(elmReport);

        }

        try {
            write2XML(doc, xmlFile);
        } catch (TransformerException e) {
            _logger.error("Failed transforming xml file: " + e);
        } catch (FileNotFoundException e) {
            _logger.error("Failed to find " + xmlFile + ": " + e);
        }
    }

    private void write2XML(Document document, String filename) throws TransformerException, FileNotFoundException {
        document.normalize();

        TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer = tFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        DOMSource source = new DOMSource(document);
        PrintWriter pw = new PrintWriter(new FileOutputStream(filename));
        StreamResult result = new StreamResult(pw);
        transformer.transform(source, result);

    }

    private Boolean collectAndPrepareHtmlReports(Run build, TaskListener listener,
            List<ReportMetaData> htmlReportsInfo, FilePath runWorkspace) throws IOException, InterruptedException {
        File reportDir = new File(new File(build.getRootDir(), "archive"), "UFTReport");

        FilePath rootTarget = new FilePath(reportDir);

        try {
            for (ReportMetaData htmlReportInfo : htmlReportsInfo) {

                // make sure it's a html report
                if (!htmlReportInfo.getIsHtmlReport()) {
                    continue;
                }
                String htmlReportDir = htmlReportInfo.getFolderPath(); // C:\UFTTest\GuiTest1\Report

                listener.getLogger().println("collectAndPrepareHtmlReports, collecting:" + htmlReportDir);
                listener.getLogger().println("workspace: " + runWorkspace);

                // copy to the subdirs of master
                FilePath source = new FilePath(runWorkspace, htmlReportDir);
                listener.getLogger().println("source: " + source);
                String testName = htmlReportInfo.getDisPlayName(); // like "GuiTest1"
                String dest = testName;
                FilePath targetPath = new FilePath(rootTarget, dest); // target path is something like "C:\Program Files
                // (x86)\Jenkins\jobs\testAction\builds\35\archive\UFTReport\GuiTest1"

                // zip copy and unzip
                ByteArrayOutputStream outstr = new ByteArrayOutputStream();

                // don't use FileFilter for zip, or it will cause bug when files are on slave
                source.zip(outstr);
                ByteArrayInputStream instr = new ByteArrayInputStream(outstr.toByteArray());

                String zipFileName = "UFT_Report_HTML_tmp.zip";
                FilePath archivedFile = new FilePath(rootTarget, zipFileName);

                archivedFile.copyFrom(instr);

                listener.getLogger().println("copy from slave to master: " + archivedFile);
                outstr.close();
                instr.close();

                // unzip
                archivedFile.unzip(rootTarget);
                archivedFile.delete();

                // now,all the files are in the C:\Program Files (x86)
                // \Jenkins\jobs\testAction\builds\35\archive\UFTReport\Report
                // we need to rename the above path to targetPath.
                // So at last we got files in C:\Program Files (x86)
                // \Jenkins\jobs\testAction\builds\35\archive\UFTReport\GuiTest

                String unzippedFileName = org.apache.commons.io.FilenameUtils.getName(htmlReportDir);
                FilePath unzippedFolderPath = new FilePath(rootTarget, unzippedFileName); // C:\Program Files
                // (x86)\Jenkins\jobs\testAction\builds\35\archive\UFTReport\Report
                // FilePath unzippedFolderPath = new FilePath(rootTarget, source.getName()); //C:\Program Files
                // (x86)\Jenkins\jobs\testAction\builds\35\archive\UFTReport\Report
                unzippedFolderPath.renameTo(targetPath);
                listener.getLogger()
                        .println("UnzippedFolderPath is: " + unzippedFolderPath + " targetPath is: " + targetPath);
                // end zip copy and unzip

                // fill in the urlName of this report. we need a network path not a FS path
                String resourceUrl = htmlReportInfo.getResourceURL();
                String urlName = resourceUrl + "/run_results.html"; // like artifact/UFTReport/GuiTest1/run_results.html

                listener.getLogger().println("set the report urlName to " + urlName);
                htmlReportInfo.setUrlName(urlName);

            }
        } catch (Exception ex) {
            listener.getLogger().println("catch exception in collectAndPrepareHtmlReports: " + ex);
        }

        return true;
    }

    /**
     * Copies the run report from the executing node to the Jenkins master for processing.
     * @param reportFolder
     * @param buildDir
     * @param scenarioName
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    private FilePath copyRunReport(FilePath reportFolder, File buildDir, String scenarioName)
            throws IOException, InterruptedException {
        FilePath slaReportFilePath = new FilePath(reportFolder, "RunReport.xml");
        if (slaReportFilePath.exists()) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            slaReportFilePath.zip(baos);
            File slaDirectory = new File(buildDir, "RunReport");
            if (!slaDirectory.exists()) {
                slaDirectory.mkdir();
            }
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            FilePath slaDirectoryFilePath = new FilePath(slaDirectory);
            FilePath tmpZipFile = new FilePath(slaDirectoryFilePath, "runReport.zip");
            tmpZipFile.copyFrom(bais);
            bais.close();
            baos.close();
            tmpZipFile.unzip(slaDirectoryFilePath);
            FilePath slaFile = new FilePath(slaDirectoryFilePath, "RunReport.xml");
            slaFile.getBaseName();
            slaFile.renameTo(new FilePath(slaDirectoryFilePath, scenarioName + ".xml"));

            slaFile = new FilePath(slaDirectoryFilePath, scenarioName + ".xml");

            return slaFile;
        }
        return null;
    }

    private boolean archiveFolder(FilePath reportFolder, String testStatus, FilePath archivedFile,
            TaskListener listener) throws IOException, InterruptedException {
        String archiveTestResultMode = _resultsPublisherModel.getArchiveTestResultsMode();
        boolean archiveTestResult;

        archiveTestResult = isArchiveTestResult(testStatus, archiveTestResultMode);

        if (archiveTestResult) {

            if (reportFolder.exists()) {

                listener.getLogger().println("Zipping report folder: " + reportFolder);

                ByteArrayOutputStream outstr = new ByteArrayOutputStream();
                reportFolder.zip(outstr);

                /*
                 * I did't use copyRecursiveTo or copyFrom due to
                 * bug in
                 * jekins:https://issues.jenkins-ci.org/browse
                 * /JENKINS-9189 //(which is cleaimed to have been
                 * fixed, but not. So I zip the folder to stream and
                 * copy it to the master.
                 */

                ByteArrayInputStream instr = new ByteArrayInputStream(outstr.toByteArray());

                archivedFile.copyFrom(instr);

                outstr.close();
                instr.close();
                return true;
            } else {
                listener.getLogger().println("No report folder was found in: " + reportFolder);
            }
        }

        return false;
    }

    private boolean isArchiveTestResult(String testStatus, String archiveTestResultMode) {
        if (archiveTestResultMode.equals(ResultsPublisherModel.alwaysArchiveResults.getValue())
                || archiveTestResultMode.equals(ResultsPublisherModel.CreateHtmlReportResults.getValue())) {
            return true;
        } else if (archiveTestResultMode.equals(ResultsPublisherModel.ArchiveFailedTestsResults.getValue())) {
            if ("fail".equals(testStatus)) {
                return true;
            } else if (archiveTestResultMode.equals(ResultsPublisherModel.dontArchiveResults.getValue())) {
                return false;
            }
        }
        return false;
    }

    /**
     * Copy Summary Html reports created by LoadRunner
     *
     * @param reportFolder
     * @param testFolderPath
     * @param artifactsDir
     * @param reportNames
     * @param testResult
     * @throws IOException
     * @throws InterruptedException
     */
    @SuppressWarnings("squid:S134")
    private void createHtmlReport(FilePath reportFolder, String testFolderPath, File artifactsDir,
            List<String> reportNames, TestResult testResult) throws IOException, InterruptedException {
        String archiveTestResultMode = _resultsPublisherModel.getArchiveTestResultsMode();
        boolean createReport = archiveTestResultMode
                .equals(ResultsPublisherModel.CreateHtmlReportResults.getValue());

        if (createReport) {
            File testFolderPathFile = new File(testFolderPath);
            FilePath srcDirectoryFilePath = new FilePath(reportFolder, HTML_REPORT_FOLDER);
            if (srcDirectoryFilePath.exists()) {
                FilePath srcFilePath = new FilePath(srcDirectoryFilePath, IE_REPORT_FOLDER);
                if (srcFilePath.exists()) {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    srcFilePath.zip(baos);
                    File reportDirectory = new File(artifactsDir.getParent(), PERFORMANCE_REPORT_FOLDER);
                    if (!reportDirectory.exists()) {
                        reportDirectory.mkdir();
                    }
                    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                    FilePath reportDirectoryFilePath = new FilePath(reportDirectory);
                    FilePath tmpZipFile = new FilePath(reportDirectoryFilePath, "tmp.zip");
                    tmpZipFile.copyFrom(bais);
                    bais.close();
                    baos.close();
                    tmpZipFile.unzip(reportDirectoryFilePath);
                    String newFolderName = org.apache.commons.io.FilenameUtils
                            .getName(testFolderPathFile.getPath());
                    FileUtils.moveDirectory(new File(reportDirectory, IE_REPORT_FOLDER),
                            new File(reportDirectory, newFolderName));
                    tmpZipFile.delete();
                    outputReportFiles(reportNames, reportDirectory, testResult, false);
                }
            }
        }
    }

    /**
     * creates index files as index for the different scenarios.
     *
     * @param reportNames
     * @param reportDirectory
     * @param testResult
     * @param tranSummary
     * @throws IOException
     */
    private void outputReportFiles(List<String> reportNames, File reportDirectory, TestResult testResult,
            boolean tranSummary) throws IOException {

        if (reportNames.isEmpty()) {
            return;
        }
        String title = (tranSummary) ? "Transaction Summary" : "Performance Report";
        String htmlFileName = (tranSummary) ? (TRANSACTION_REPORT_NAME + ".html") : "HTML.html";
        File htmlIndexFile = new File(reportDirectory, INDEX_HTML_NAME);
        BufferedWriter writer = new BufferedWriter(new FileWriter(htmlIndexFile));
        writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>%n");
        writer.write("<HTML><HEAD>%n");
        writer.write("<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">%n");
        writer.write(String.format("<TITLE>%s</TITLE>%n", title));
        writer.write("</HEAD>%n");
        writer.write("<BODY>%n");
        writer.write(
                "<table style=\"font-size:15px;width:100%;max-width:100%;border-left:1px solid #DDD;border-right:1px "
                        + "solid #DDD;border-bottom:1px solid #DDD;\">%n");
        writer.write("<tr style=\"background-color: #F1F1F1;\"><th style=\"padding:8px;line-height:1.42857;"
                + "vertical-align:top;border-top:1px solid #DDD;\">Name</th></tr>%n");
        boolean rolling = true;
        for (String report : reportNames) {
            if (rolling) {
                writer.write(
                        String.format(
                                "<tr style=\"background-color: #FFF;\"><td style=\"padding:8px;line-height:1.42857;"
                                        + "vertical-align:top;border-top:1px solid #DDD;"
                                        + "\"><a href=\"./%s/%s\">%s</a></td></tr>%n",
                                report, htmlFileName, report));
                rolling = false;
            } else {
                writer.write(String.format(
                        "<tr style=\"background-color: #F1F1F1;\"><td style=\"padding:8px;line-height:1.42857;"
                                + "vertical-align:top;border-top:1px solid #DDD;"
                                + "\"><a href=\"./%s/%s\">%s</a></td></tr>%n",
                        report, htmlFileName, report));
                rolling = true;
            }
        }
        writer.write("</table>%n");
        writer.write("</BODY>%n");
        writer.flush();
        writer.close();

        File indexFile = new File(reportDirectory, REPORT_INDEX_NAME);
        try {
            writer = new BufferedWriter(new FileWriter(indexFile));
            Iterator<SuiteResult> resultIterator = null;
            if ((testResult != null) && (!testResult.getSuites().isEmpty())) {
                resultIterator = testResult.getSuites().iterator();//get the first
            }
            for (String report : reportNames) {
                SuiteResult suitResult = null;
                if ((resultIterator != null) && resultIterator.hasNext()) {
                    suitResult = resultIterator.next();
                }
                if (suitResult == null) {
                    writer.write(report + "\t##\t##\t##%n");
                } else {
                    int iDuration = (int) suitResult.getDuration();
                    StringBuilder bld = new StringBuilder();
                    String duration = "";
                    if ((iDuration / SECS_IN_DAY) > 0) {
                        bld.append(String.format("%dday ", iDuration / SECS_IN_DAY));
                        iDuration = iDuration % SECS_IN_DAY;
                    }
                    if ((iDuration / SECS_IN_HOUR) > 0) {
                        bld.append(String.format("%02dhr ", iDuration / SECS_IN_HOUR));
                        iDuration = iDuration % SECS_IN_HOUR;
                    } else if (!duration.isEmpty()) {
                        bld.append("00hr ");
                    }
                    if ((iDuration / SECS_IN_MINUTE) > 0) {
                        bld.append(String.format("%02dmin ", iDuration / SECS_IN_MINUTE));
                        iDuration = iDuration % SECS_IN_MINUTE;
                    } else if (!duration.isEmpty()) {
                        bld.append("00min ");
                    }
                    bld.append(String.format("%02dsec", iDuration));
                    duration = bld.toString();
                    int iPassCount = 0;
                    int iFailCount = 0;
                    for (Iterator i = suitResult.getCases().iterator(); i.hasNext();) {
                        CaseResult caseResult = (CaseResult) i.next();
                        iPassCount += caseResult.getPassCount();
                        iFailCount += caseResult.getFailCount();
                    }
                    writer.write(String.format("%s\t%s\t%d\t%d%n", report, duration, iPassCount, iFailCount));
                }
            }
        } finally {
            writer.flush();
            writer.close();
        }
    }

    /**
     * Copies and creates the transaction summery on the master
     *
     * @param reportFolder
     * @param testFolderPath
     * @param artifactsDir
     * @param reportNames
     * @param testResult
     * @throws IOException
     * @throws InterruptedException
     */
    private void createTransactionSummary(FilePath reportFolder, String testFolderPath, File artifactsDir,
            List<String> reportNames, TestResult testResult) throws IOException, InterruptedException {

        File testFolderPathFile = new File(testFolderPath);
        String subFolder = HTML_REPORT_FOLDER + File.separator + IE_REPORT_FOLDER + File.separator
                + HTML_REPORT_FOLDER;
        FilePath htmlReportPath = new FilePath(reportFolder, subFolder);
        if (htmlReportPath.exists()) {
            File reportDirectory = new File(artifactsDir.getParent(), TRANSACTION_SUMMARY_FOLDER);
            if (!reportDirectory.exists()) {
                reportDirectory.mkdir();
            }
            String newFolderName = org.apache.commons.io.FilenameUtils.getName(testFolderPathFile.getPath());
            File testDirectory = new File(reportDirectory, newFolderName);
            if (!testDirectory.exists()) {
                testDirectory.mkdir();
            }

            FilePath dstReportPath = new FilePath(testDirectory);
            FileFilter reportFileFilter = new WildcardFileFilter(TRANSACTION_REPORT_NAME + ".*");
            List<FilePath> reporFiles = htmlReportPath.list(reportFileFilter);
            for (FilePath fileToCopy : reporFiles) {
                FilePath dstFilePath = new FilePath(dstReportPath, fileToCopy.getName());
                fileToCopy.copyTo(dstFilePath);
            }
            FilePath cssFilePath = new FilePath(htmlReportPath, "Properties.css");
            if (cssFilePath.exists()) {
                FilePath dstFilePath = new FilePath(dstReportPath, cssFilePath.getName());
                cssFilePath.copyTo(dstFilePath);
            }
            FilePath pngFilePath = new FilePath(htmlReportPath, "tbic_toexcel.png");
            if (pngFilePath.exists()) {
                FilePath dstFilePath = new FilePath(dstReportPath, pngFilePath.getName());
                pngFilePath.copyTo(dstFilePath);
            }

            outputReportFiles(reportNames, reportDirectory, testResult, true);

        }

    }

    /*
     * if we have a directory with file name "file.zip" we will return "file_1.zip"
     */
    private String getUniqueZipFileNameInFolder(ArrayList<String> names, String fileName)
            throws IOException, InterruptedException {

        String result = fileName + "_Report.zip";

        int index = 0;

        while (names.indexOf(result) > -1) {
            result = fileName + "_" + (++index) + "_Report.zip";
        }

        return result;
    }

    private LrJobResults buildJobDataset(TaskListener listener)
            throws ParserConfigurationException, SAXException, IOException, InterruptedException {
        listener.getLogger().println("Parsing test run dataset for perfomrance report");
        LrJobResults jobResults = new LrJobResults();

        // read each RunReport.xml
        for (FilePath reportFilePath : runReportList) {
            JobLrScenarioResult jobLrScenarioResult = parseScenarioResults(reportFilePath);
            jobResults.addScenario(jobLrScenarioResult);
        }

        return jobResults;
    }

    private JobLrScenarioResult parseScenarioResults(FilePath slaFilePath)
            throws ParserConfigurationException, SAXException, IOException, InterruptedException {
        JobLrScenarioResult jobLrScenarioResult = new JobLrScenarioResult(slaFilePath.getBaseName());

        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();

        Document doc = dBuilder.parse(slaFilePath.read());

        processSLA(jobLrScenarioResult, doc);
        processLrScenarioStats(jobLrScenarioResult, doc);
        //TODO: add fail / Pass count
        return jobLrScenarioResult;
    }

    private void processLrScenarioStats(JobLrScenarioResult jobLrScenarioResult, Document doc) {

        NodeList rootNodes = doc.getChildNodes();
        Node root = getNode("Runs", rootNodes);
        Element generalNode = (Element) getNode("General", root.getChildNodes());
        NodeList generalNodeChildren = generalNode.getChildNodes();

        extractVUserScenarioReult(jobLrScenarioResult, generalNodeChildren);
        extractTransactionScenarioResult(jobLrScenarioResult, generalNodeChildren);
        extractConnectionsScenarioResult(jobLrScenarioResult, generalNodeChildren);
        extractDuration(jobLrScenarioResult, generalNodeChildren);
    }

    private void extractDuration(JobLrScenarioResult jobLrScenarioResult, NodeList generalNodeChildren) {
        Node ScenrioDurationNode = getNode("Time", generalNodeChildren);
        String scenarioDurationAttr = getNodeAttr("Duration", ScenrioDurationNode);
        jobLrScenarioResult.setScenarioDuration(Long.valueOf(scenarioDurationAttr));
    }

    private void extractConnectionsScenarioResult(JobLrScenarioResult jobLrScenarioResult,
            NodeList generalNodeChildren) {
        Node connections = getNode("Connections", generalNodeChildren);
        jobLrScenarioResult.setConnectionMax(Integer.valueOf(getNodeAttr("MaxCount", connections)));
    }

    private void extractTransactionScenarioResult(JobLrScenarioResult jobLrScenarioResult,
            NodeList generalNodeChildren) {
        int atrrCount;
        Node transactions = getNode("Transactions", generalNodeChildren);
        atrrCount = transactions.getAttributes().getLength();
        for (int atrrIndx = 0; atrrIndx < atrrCount; atrrIndx++) {
            Node vUserAttr = transactions.getAttributes().item(atrrIndx);
            jobLrScenarioResult.transactionSum.put(vUserAttr.getNodeName(),
                    Integer.valueOf(vUserAttr.getNodeValue()));
        }

        NodeList transactionNodes = transactions.getChildNodes();
        int transactionNodesCount = transactionNodes.getLength();
        for (int transIdx = 0; transIdx < transactionNodesCount; transIdx++) {
            if (transactionNodes.item(transIdx).getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            Element transaction = (Element) transactionNodes.item(transIdx);
            TreeMap<String, Integer> transactionData = new TreeMap<String, Integer>();
            transactionData.put("Pass", Integer.valueOf(transaction.getAttribute("Pass")));
            transactionData.put("Fail", Integer.valueOf(transaction.getAttribute("Fail")));
            transactionData.put("Stop", Integer.valueOf(transaction.getAttribute("Stop")));
            jobLrScenarioResult.transactionData.put(transaction.getAttribute("Name"), transactionData);
        }
    }

    private void extractVUserScenarioReult(JobLrScenarioResult jobLrScenarioResult, NodeList generalNodeChildren) {
        Node vUser = getNode("VUsers", generalNodeChildren);
        int atrrCount = vUser.getAttributes().getLength();
        for (int atrrIndx = 0; atrrIndx < atrrCount; atrrIndx++) {
            Node vUserAttr = vUser.getAttributes().item(atrrIndx);
            jobLrScenarioResult.vUserSum.put(vUserAttr.getNodeName(), Integer.valueOf(vUserAttr.getNodeValue()));
        }
    }

    private void processSLA(JobLrScenarioResult jobLrScenarioResult, Document doc) {
        Node slaRuleNode;
        Element slaRuleElement;

        NodeList rootNodes = doc.getChildNodes();
        Node root = getNode("Runs", rootNodes);
        Element slaRoot = (Element) getNode("SLA", root.getChildNodes());
        NodeList slaRuleResults = slaRoot.getChildNodes();

        for (int j = 0; j < slaRuleResults.getLength(); j++) {
            slaRuleNode = slaRuleResults.item(j);
            if (slaRuleNode.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            slaRuleElement = (Element) slaRuleNode;
            //check type by mesurment field:
            LrTest.SLA_GOAL slaGoal = LrTest.SLA_GOAL.checkGoal(slaRuleElement.getAttribute("Measurement"));

            processSlaRule(jobLrScenarioResult, slaRuleElement, slaGoal);
        }

    }

    private void processSlaRule(JobLrScenarioResult jobLrScenarioResult, Element slaRuleElement,
            LrTest.SLA_GOAL slaGoal) {
        switch (slaGoal) {
        case AverageThroughput:
            WholeRunResult averageThroughput = new WholeRunResult();
            averageThroughput.setSlaGoal(LrTest.SLA_GOAL.AverageThroughput);
            averageThroughput.setActualValue(Double.valueOf(slaRuleElement.getAttribute(SLA_ACTUAL_VALUE_LABEL)));
            averageThroughput.setGoalValue(Double.valueOf(slaRuleElement.getAttribute(SLA_GOAL_VALUE_LABEL)));
            averageThroughput.setFullName(slaRuleElement.getAttribute(SLA_ULL_NAME));
            averageThroughput.setStatus(
                    LrTest.SLA_STATUS.checkStatus(slaRuleElement.getLastChild().getTextContent().trim()));
            jobLrScenarioResult.scenarioSlaResults.add(averageThroughput);
            break;
        case TotalThroughput:
            WholeRunResult totalThroughput = new WholeRunResult();
            totalThroughput.setSlaGoal(LrTest.SLA_GOAL.TotalThroughput);
            totalThroughput.setActualValue(Double.valueOf(slaRuleElement.getAttribute(SLA_ACTUAL_VALUE_LABEL)));
            totalThroughput.setGoalValue(Double.valueOf(slaRuleElement.getAttribute(SLA_GOAL_VALUE_LABEL)));
            totalThroughput.setFullName(slaRuleElement.getAttribute(SLA_ULL_NAME));
            totalThroughput.setStatus(
                    LrTest.SLA_STATUS.checkStatus(slaRuleElement.getLastChild().getTextContent().trim()));
            jobLrScenarioResult.scenarioSlaResults.add(totalThroughput);

            break;
        case AverageHitsPerSecond:
            WholeRunResult averageHitsPerSecond = new WholeRunResult();
            averageHitsPerSecond.setSlaGoal(LrTest.SLA_GOAL.AverageHitsPerSecond);
            averageHitsPerSecond
                    .setActualValue(Double.valueOf(slaRuleElement.getAttribute(SLA_ACTUAL_VALUE_LABEL)));
            averageHitsPerSecond.setGoalValue(Double.valueOf(slaRuleElement.getAttribute(SLA_GOAL_VALUE_LABEL)));
            averageHitsPerSecond.setFullName(slaRuleElement.getAttribute(SLA_ULL_NAME));
            averageHitsPerSecond.setStatus(
                    LrTest.SLA_STATUS.checkStatus(slaRuleElement.getLastChild().getTextContent().trim()));
            jobLrScenarioResult.scenarioSlaResults.add(averageHitsPerSecond);

            break;
        case TotalHits:
            WholeRunResult totalHits = new WholeRunResult();
            totalHits.setSlaGoal(LrTest.SLA_GOAL.TotalHits);
            totalHits.setActualValue(Double.valueOf(slaRuleElement.getAttribute(SLA_ACTUAL_VALUE_LABEL)));
            totalHits.setGoalValue(Double.valueOf(slaRuleElement.getAttribute(SLA_GOAL_VALUE_LABEL)));
            totalHits.setFullName(slaRuleElement.getAttribute(SLA_ULL_NAME));
            totalHits.setStatus(
                    LrTest.SLA_STATUS.checkStatus(slaRuleElement.getLastChild().getTextContent().trim()));
            jobLrScenarioResult.scenarioSlaResults.add(totalHits);

            break;
        case ErrorsPerSecond:
            TimeRangeResult errPerSec = new AvgTransactionResponseTime();
            errPerSec.setSlaGoal(LrTest.SLA_GOAL.ErrorsPerSecond);
            errPerSec.setFullName(slaRuleElement.getAttribute(SLA_ULL_NAME));
            errPerSec.setLoadThrashold(slaRuleElement.getAttribute("SLALoadThresholdValue"));
            errPerSec.setStatus(
                    LrTest.SLA_STATUS.checkStatus(slaRuleElement.getLastChild().getTextContent().trim())); //Might not work due to time ranges
            addTimeRanges(errPerSec, slaRuleElement);
            jobLrScenarioResult.scenarioSlaResults.add(errPerSec);

            break;
        case PercentileTRT:
            PercentileTransactionWholeRun percentileTransactionWholeRun = new PercentileTransactionWholeRun();
            percentileTransactionWholeRun.setSlaGoal(LrTest.SLA_GOAL.PercentileTRT);
            percentileTransactionWholeRun.setName(slaRuleElement.getAttribute("TransactionName"));
            percentileTransactionWholeRun
                    .setActualValue(Double.valueOf(slaRuleElement.getAttribute(SLA_ACTUAL_VALUE_LABEL)));
            percentileTransactionWholeRun
                    .setGoalValue(Double.valueOf(slaRuleElement.getAttribute(SLA_GOAL_VALUE_LABEL)));
            percentileTransactionWholeRun.setFullName(slaRuleElement.getAttribute(SLA_ULL_NAME));
            percentileTransactionWholeRun.setPrecentage(Double.valueOf(slaRuleElement.getAttribute("Percentile")));
            percentileTransactionWholeRun.setStatus(
                    LrTest.SLA_STATUS.checkStatus(slaRuleElement.getLastChild().getTextContent().trim()));
            jobLrScenarioResult.scenarioSlaResults.add(percentileTransactionWholeRun);

            break;
        case AverageTRT:
            AvgTransactionResponseTime transactionTimeRange = new AvgTransactionResponseTime();
            transactionTimeRange.setSlaGoal(LrTest.SLA_GOAL.AverageTRT);
            transactionTimeRange.setName(slaRuleElement.getAttribute("TransactionName"));
            transactionTimeRange.setFullName(slaRuleElement.getAttribute(SLA_ULL_NAME));
            transactionTimeRange.setLoadThrashold(slaRuleElement.getAttribute("SLALoadThresholdValue"));
            transactionTimeRange.setStatus(
                    LrTest.SLA_STATUS.checkStatus(slaRuleElement.getLastChild().getTextContent().trim())); //Might not work due to time ranges
            addTimeRanges(transactionTimeRange, slaRuleElement);
            jobLrScenarioResult.scenarioSlaResults.add(transactionTimeRange);
            break;
        case Bad:
            break;
        }
    }

    private static void addTimeRanges(TimeRangeResult transactionTimeRange, Element slaRuleElement) {
        Node timeRangeNode;
        Element timeRangeElement;
        NodeList timeRanges = slaRuleElement.getElementsByTagName("TimeRangeInfo");
        if (timeRanges == null || timeRanges.getLength() == 0) {
            return;
        }
        //Taking the goal per transaction -
        double generalGoalValue = Double
                .parseDouble(((Element) timeRanges.item(0)).getAttribute(SLA_GOAL_VALUE_LABEL));
        transactionTimeRange.setGoalValue(generalGoalValue);

        for (int k = 0; k < timeRanges.getLength(); k++) {
            timeRangeNode = timeRanges.item(k);
            timeRangeElement = (Element) timeRangeNode;
            double actualValue = Double.parseDouble(timeRangeElement.getAttribute(SLA_ACTUAL_VALUE_LABEL));
            double goalValue = Double.parseDouble(timeRangeElement.getAttribute(SLA_GOAL_VALUE_LABEL));
            int loadValue = Integer.parseInt(timeRangeElement.getAttribute("LoadValue"));
            double startTime = Double.parseDouble(timeRangeElement.getAttribute("StartTime"));
            double endTIme = Double.parseDouble(timeRangeElement.getAttribute("EndTime"));
            transactionTimeRange.incActualValue(actualValue);
            LrTest.SLA_STATUS slaStatus = LrTest.SLA_STATUS
                    .checkStatus(timeRangeElement.getFirstChild().getTextContent());
            TimeRange timeRange = new TimeRange(actualValue, goalValue, slaStatus, loadValue, startTime, endTIme);
            transactionTimeRange.getTimeRanges().add(timeRange);
        }
    }

    @Override
    public void perform(@Nonnull Run<?, ?> build, @Nonnull FilePath workspace, @Nonnull Launcher launcher,
            @Nonnull TaskListener listener) throws InterruptedException, IOException {
        final List<String> mergedResultNames = new ArrayList<String>();

        Project<?, ?> project = RuntimeUtils.cast(build.getParent());
        List<Builder> builders = project.getBuilders();
        runReportList = new ArrayList<FilePath>();
        final List<String> almResultNames = new ArrayList<String>();
        final List<String> fileSystemResultNames = new ArrayList<String>();
        final List<String> almSSEResultNames = new ArrayList<String>();
        final List<String> pcResultNames = new ArrayList<String>();

        // Get the TestSet report files names of the current build
        for (Builder builder : builders) {
            if (builder instanceof RunFromAlmBuilder) {
                almResultNames.add(((RunFromAlmBuilder) builder).getRunResultsFileName());
            } else if (builder instanceof RunFromFileBuilder) {
                fileSystemResultNames.add(((RunFromFileBuilder) builder).getRunResultsFileName());
            } else if (builder instanceof SseBuilder) {
                String resultsFileName = ((SseBuilder) builder).getRunResultsFileName();
                if (resultsFileName != null) {
                    almSSEResultNames.add(resultsFileName);
                }
            } else if (builder instanceof PcBuilder) {
                String resultsFileName = ((PcBuilder) builder).getRunResultsFileName();
                if (resultsFileName != null) {
                    pcResultNames.add(resultsFileName);
                }
            }
        }

        mergedResultNames.addAll(almResultNames);
        mergedResultNames.addAll(fileSystemResultNames);
        mergedResultNames.addAll(almSSEResultNames);
        mergedResultNames.addAll(pcResultNames);

        // Has any QualityCenter builder been set up?
        if (mergedResultNames.isEmpty()) {
            listener.getLogger().println("RunResultRecorder: no results xml File provided");
            return;
        }

        recordRunResults(build, workspace, launcher, listener, mergedResultNames, fileSystemResultNames);
        return;
    }

    @Override
    public MatrixAggregator createAggregator(MatrixBuild build, Launcher launcher, BuildListener listener) {

        return new TestResultAggregator(build, launcher, listener);
    }

    @Override
    public BuildStepMonitor getRequiredMonitorService() {

        return BuildStepMonitor.BUILD;
    }

    /**
     * Gets results publisher model.
     *
     * @return the results publisher model
     */
    public ResultsPublisherModel getResultsPublisherModel() {

        return _resultsPublisherModel;
    }

    /**
     * The type Descriptor.
     */
    @Extension
    public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {

        /**
         * Instantiates a new Descriptor.
         */
        public DescriptorImpl() {

            load();
        }

        @Override
        public String getDisplayName() {

            return "Publish Micro Focus tests result";
        }

        @Override
        public boolean isApplicable(@SuppressWarnings("rawtypes") Class<? extends AbstractProject> jobType) {

            return true;
        }

        /**
         * Gets report archive modes.
         *
         * @return the report archive modes
         */
        public List<EnumDescription> getReportArchiveModes() {

            return ResultsPublisherModel.archiveModes;
        }
    }

    /**
     * The type Rrv file filter.
     */
    public class RRVFileFilter implements FileFilter {
        private final String[] excludedFilenames = new String[] { "run_results.xml", "run_results.html",
                "diffcompare", "Resources" };
        private final String[] excludedDirnames = new String[] { "diffcompare", "Resources", "CheckPoints",
                "Snapshots" };

        @Override
        public boolean accept(File file) {
            boolean bRet = true;

            for (String filename : excludedFilenames) {
                if (file.getName().equals(filename)) {
                    bRet = false;
                    break;
                }
            }

            if (bRet) {
                for (String parentname : excludedDirnames) {
                    if (file.getParent().contains(parentname)) {
                        bRet = false;
                        break;
                    }
                }
            }
            return bRet;
        }

    }
}