com.servicelibre.jxsl.scenario.test.xspec.XspecTestScenarioRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.servicelibre.jxsl.scenario.test.xspec.XspecTestScenarioRunner.java

Source

/**
 * Java XSL code library
 *
 * Copyright (C) 2010 Benoit Mercier <info@servicelibre.com>  All rights reserved.
 *
 * This file is part of jxsl.
 *
 * jxsl is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3.
 *
 * jxsl is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with jxsl.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.servicelibre.jxsl.scenario.test.xspec;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathFactoryConfigurationException;

import net.sf.saxon.lib.NamespaceConstant;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.xml.SimpleNamespaceContext;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.servicelibre.jxsl.dstest.Document;
import com.servicelibre.jxsl.scenario.RunReport;
import com.servicelibre.jxsl.scenario.XslScenario;
import com.servicelibre.jxsl.scenario.test.FailureReport;
import com.servicelibre.jxsl.scenario.test.TestReport;
import com.servicelibre.jxsl.scenario.test.XslTestScenarioRunner;

/**
 * Run XML testdoc => generate Xspec XSL to apply on the fly then apply!
 */
public class XspecTestScenarioRunner implements XslTestScenarioRunner {

    private static final String JXSL_TEST_DOCUMENT_PLACEHOLDER = "file:/jxslTestDocument";
    private static final String JXSL_TEST_HREF_PLACEHOLDER = "href=\"" + JXSL_TEST_DOCUMENT_PLACEHOLDER + "\"";
    private static final String JXSL_TEST_SELECT_PLACEHOLDER = "doc\\('" + JXSL_TEST_DOCUMENT_PLACEHOLDER + "'\\)";

    static Logger logger = LoggerFactory.getLogger(XspecTestScenarioRunner.class);

    static {
        System.setProperty("javax.xml.transform.TransformerFactory", XslScenario.SAXON_TRANSFORMER_FACTORY_FQCN);
        System.setProperty("javax.xml.xpath.XPathFactory:" + NamespaceConstant.OBJECT_MODEL_SAXON,
                "net.sf.saxon.xpath.XPathFactoryImpl");

    }

    private File outputDir = new File(System.getProperty("java.io.tmpdir"));
    private XslScenario xspecTestsGeneratorScenario;
    private XslScenario xspecResultHtmlConvertorScenario;

    private boolean storeResultsInSubDir = true;
    private boolean resultsSubDirWithTimeStamp = true;
    private XPathExpression successXpath;
    private XPathExpression testFailedCount;
    private XPathExpression testCount;
    private TestReport lastRunReport;

    private DocumentBuilder xmlBuilder;
    private File currentXspecFile;
    private XslScenario currentXspecTestsScenario;

    public XspecTestScenarioRunner(File xspecTestsGeneratorFile) {
        super();
        this.xspecTestsGeneratorScenario = new XslScenario(xspecTestsGeneratorFile);
        init();
    }

    public XspecTestScenarioRunner(XslScenario xspecTestsGeneratorScenario) {
        super();
        this.xspecTestsGeneratorScenario = xspecTestsGeneratorScenario;
        init();
    }

    private void init() {

        XPath xpath = null;
        try {
            xpath = XPathFactory.newInstance(NamespaceConstant.OBJECT_MODEL_SAXON).newXPath();
        } catch (XPathFactoryConfigurationException e1) {
            logger.error("Error while creating XPathFactory", e1);
            return;
        }

        SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
        namespaceContext.bindNamespaceUri("x", "http://www.jenitennison.com/xslt/xspec");
        xpath.setNamespaceContext(namespaceContext);

        try {

            successXpath = xpath.compile("count(//x:test[@successful ='false'] ) = 0");
            testFailedCount = xpath.compile("count(//x:test[@successful ='false'] )");
            testCount = xpath.compile("count(//x:test)");
        } catch (XPathExpressionException e) {
            logger.error("Error while initializing {}.", this.getClass().getName(), e);
        }

        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        docFactory.setNamespaceAware(true);

        try {
            xmlBuilder = docFactory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            logger.error("Error while configuring XML parser", e);
        }

    }

    public void reset() {
        xspecTestsGeneratorScenario.getTransformer().reset();
    }

    public void setOutputDir(File outputDir) {
        this.outputDir = outputDir;

    }

    public File getOutputDir() {
        return outputDir;
    }

    public TestReport getLastRunReport() {
        return this.lastRunReport;
    }

    @Override
    public TestReport run(File xspecFile) {
        return run(xspecFile, outputDir, null);
    }

    @Override
    public TestReport run(File xspecFile, File testOutputDir) {
        return run(xspecFile, testOutputDir, null);
    }

    @Override
    public TestReport run(File xspecFile, Document xmlDoc) {
        return run(xspecFile, outputDir, xmlDoc);
    }

    /**
     * 
     * TODO add optional Document parameter
     * 
     * // TODO improve performance In order to improve performance, we should
     * not regenerate/recompile xspec xsl. We should instead compile it once and
     * change/set parameter (document URL) at each execution. This implies that
     * we have to keep a compiled version of the Xsl for all runs of the
     * testScenario with the same xspec file.
     */
    @Override
    public TestReport run(File xspecFile, File outputDir, Document xmlDoc) {

        this.outputDir = outputDir;

        RunReport testRunReport = null;
        TestReport testReport = new TestReport();

        // Generate custom test XSL
        RunReport generationReport = generateTestFile(xspecFile);
        File generatedTestFile = generationReport.mainOutputFile;

        if (generatedTestFile != null && generatedTestFile.exists()) {

            // Execute the xspec test
            testRunReport = executeTest(xspecFile, generatedTestFile,
                    generationReport.outputProperties.getProperty("encoding"), xmlDoc);

            // Produce HTML report if transformation scenario provided
            if (xspecResultHtmlConvertorScenario != null) {
                RunReport htmlrunReport = generateHtmlReport(testRunReport.mainOutputFile);

                if (testRunReport != null) {
                    testRunReport.otherOutputFiles.add(htmlrunReport.mainOutputFile);
                }
            }

            if (testRunReport != null)

            {

                testReport.executionTime = testRunReport.executionTime;
                testReport.executionDate = testRunReport.executionDate;

                testReport.success = getSuccess(testRunReport.mainOutputFile);

                if (!testReport.success) {
                    testReport.failureReport = getFailureReport(testRunReport.mainOutputFile);
                }

                if (testRunReport.otherOutputFiles.size() > 0) {
                    try {
                        testReport.reportUrl = testRunReport.otherOutputFiles.get(0).toURI().toURL();
                    } catch (MalformedURLException e) {
                        logger.error("Error while converting test report File to URL.", e);
                    }
                }

                try {

                    org.w3c.dom.Document xspecResultDoc = xmlBuilder.parse(testRunReport.mainOutputFile);

                    testReport.testCount = ((Double) testCount.evaluate(xspecResultDoc, XPathConstants.NUMBER))
                            .intValue();
                    testReport.testFailedCount = ((Double) testFailedCount.evaluate(xspecResultDoc,
                            XPathConstants.NUMBER)).intValue();

                } catch (SAXException e) {
                    logger.error("Error while creating failure report", e);
                } catch (IOException e) {
                    logger.error("Error while creating failure report", e);
                } catch (XPathExpressionException e) {
                    logger.error("Error while evaluating XPath during failure report creation", e);
                }

            } else {
                testReport.success = false;

            }

        } else {
            logger.error("Unable to find Xspec generated test file.");
        }

        return testReport;

    }

    private void addParamToGeneratedTestFile(File generatedTestFile, String encoding) {

        // Ajouter <xsl:param name="docUrl" required="yes"/>
        try {
            String fileString = FileUtils.readFileToString(generatedTestFile, encoding);

            // file:/jxslTestDocument => {$docUrl}
            String newContent = fileString.replaceAll(JXSL_TEST_HREF_PLACEHOLDER, "href=\"\\{\\$docUrl\\}\"");

            newContent = newContent.replaceAll(JXSL_TEST_SELECT_PLACEHOLDER, "doc\\(\\$docUrl\\)");
            newContent = newContent.replace("</xsl:stylesheet>",
                    "<xsl:param name=\"docUrl\" required=\"yes\"/></xsl:stylesheet>");

            String generatedTestFilePath = generatedTestFile.getAbsolutePath();
            File newFile = new File(generatedTestFilePath + ".new");

            FileUtils.writeStringToFile(newFile, newContent, encoding);
            FileUtils.deleteQuietly(generatedTestFile);

            if (!newFile.renameTo(new File(generatedTestFilePath))) {
                logger.error("Error while renaming {} to {}", newFile, generatedTestFilePath);
            }

        } catch (IOException e) {
            logger.error("Error while replacing jxslTestDocument placeholder", e);
        }

    }

    private RunReport generateHtmlReport(File xmlResultFile) {

        RunReport runReport = new RunReport();

        if (xspecResultHtmlConvertorScenario != null) {
            xspecResultHtmlConvertorScenario.getTransformer().reset();
            xspecResultHtmlConvertorScenario.setSaveOutputOnDisk(true);
            xspecResultHtmlConvertorScenario.setMainOutputDir(xmlResultFile.getParentFile());
            xspecResultHtmlConvertorScenario.setStoreResultsInSubDir(false);
            xspecResultHtmlConvertorScenario.setName("htmlConvertor");
            xspecResultHtmlConvertorScenario.setMainOutputName(xmlResultFile.getName().replace(".xml", ".html"));
            xspecResultHtmlConvertorScenario.apply(xmlResultFile);
            runReport = xspecResultHtmlConvertorScenario.getLastRunReport();

        }
        return runReport;
    }

    /**
     * Execute xspec test
     * 
     * @param xspecFile
     * @param generatedTestFile
     * @param generatedTestFileEncoding
     * @param xmlDoc
     * @return
     */
    private RunReport executeTest(File xspecFile, File generatedTestFile, String generatedTestFileEncoding,
            Document xmlDoc) {

        if (currentXspecFile == null || !currentXspecFile.getAbsolutePath().equals(xspecFile.getAbsolutePath())) {

            if (xmlDoc != null) {
                addParamToGeneratedTestFile(generatedTestFile, generatedTestFileEncoding);
            }

            currentXspecTestsScenario = new XslScenario(generatedTestFile);
            currentXspecFile = xspecFile;
        }

        if (xmlDoc != null) {
            currentXspecTestsScenario.setParameter("docUrl", xmlDoc.getFile().getAbsolutePath());
        }

        currentXspecTestsScenario.getTransformer().reset();
        currentXspecTestsScenario.setSaveOutputOnDisk(true);
        currentXspecTestsScenario.setMainOutputDir(generatedTestFile.getParentFile());
        currentXspecTestsScenario.setStoreResultsInSubDir(false);
        currentXspecTestsScenario.setSaveRunReport(true);
        currentXspecTestsScenario.setSaveXmlSource(true);
        currentXspecTestsScenario.setMainOutputName(xspecFile.getName().replace(".xspec", "-result.xml"));
        currentXspecTestsScenario.setInitialTemplate("{http://www.jenitennison.com/xslt/xspec}main");
        currentXspecTestsScenario.setName(xspecFile.getName().replaceAll(".xspec", "_xspec"));

        // FIXME xspecFile could be omitted since initialTemplate has been
        // set? Create an apply() method without arg?
        currentXspecTestsScenario.apply(xspecFile);

        return currentXspecTestsScenario.getLastRunReport();
    }

    /**
     * Generate test XSL
     * 
     * @param xspecFile
     * @return
     */
    private RunReport generateTestFile(File xspecFile) {

        xspecTestsGeneratorScenario.getTransformer().reset();

        xspecTestsGeneratorScenario.setMainOutputDir(outputDir);

        xspecTestsGeneratorScenario.setStoreResultsInSubDir(storeResultsInSubDir);

        xspecTestsGeneratorScenario.setResultsSubDirWithTimeStamp(resultsSubDirWithTimeStamp);

        xspecTestsGeneratorScenario.setName(xspecFile.getName().replace(".xspec", ""));

        xspecTestsGeneratorScenario.setMainOutputName(xspecFile.getName().replace(".xspec", ".xslt"));

        xspecTestsGeneratorScenario.setSaveOutputOnDisk(true);

        xspecTestsGeneratorScenario.apply(xspecFile);

        return xspecTestsGeneratorScenario.getLastRunReport();
    }

    public XslScenario getXspecTestsGeneratorScenario() {
        return xspecTestsGeneratorScenario;
    }

    public void setXspecTestsGeneratorScenario(XslScenario xspecTestsGeneratorScenario) {
        this.xspecTestsGeneratorScenario = xspecTestsGeneratorScenario;
    }

    public XslScenario getXspecResultHtmlConvertorScenario() {
        return xspecResultHtmlConvertorScenario;
    }

    public void setXspecResultHtmlConvertorScenario(XslScenario xspecResultHtmlConvertorScenario) {
        this.xspecResultHtmlConvertorScenario = xspecResultHtmlConvertorScenario;
    }

    public void cleanOutputDir() {
        try {
            FileUtils.cleanDirectory(outputDir);
        } catch (IOException e) {
            logger.equals(e);
        }

    }

    public boolean isResultsSubDirWithTimeStamp() {
        return resultsSubDirWithTimeStamp;
    }

    public void setResultsSubDirWithTimeStamp(boolean resultsSubDirWithTimeStamp) {
        this.resultsSubDirWithTimeStamp = resultsSubDirWithTimeStamp;
    }

    public boolean isStoreResultsInSubDir() {
        return storeResultsInSubDir;
    }

    public void setStoreResultsInSubDir(boolean storeResultsInSubDir) {
        this.storeResultsInSubDir = storeResultsInSubDir;
    }

    private boolean getSuccess(File mainOutputFile) {
        String success = "false";

        try {
            success = successXpath.evaluate(new StreamSource(mainOutputFile));
        } catch (XPathExpressionException e) {
            logger.error("Error while retrieving success/failure in test report {}", mainOutputFile, e);
        }

        return Boolean.parseBoolean(success);
    }

    // TODO ????
    private FailureReport getFailureReport(File mainOutputFile) {
        FailureReport failureReport = new FailureReport("XSpec Test failed to run.  See error log.");

        return failureReport;
    }

}