org.apache.ddlutils.TestSummaryCreatorTask.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ddlutils.TestSummaryCreatorTask.java

Source

package org.apache.ddlutils;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.xml.sax.InputSource;

/**
 * Creates a test summary snippet that can be put onto the DdlUtils web site.
 * 
 * @version $Revision: $
 */
public class TestSummaryCreatorTask extends Task {
    /** The DdlUtils version. */
    private String _version;
    /** The file to write the snippet to. */
    private File _outputFile;
    /** The input files. */
    private ArrayList _fileSets = new ArrayList();

    /**
     * Set the DdlUtils version used to run the tests.
     *
     * @param version The version
     */
    public void setVersion(String version) {
        _version = version;
    }

    /**
     * Set the output file.
     *
     * @param outputFile The output file
     */
    public void setOutputFile(File outputFile) {
        _outputFile = outputFile;
    }

    /**
     * Adds a fileset.
     * 
     * @param fileset The additional input files
     */
    public void addConfiguredFileset(FileSet fileset) {
        _fileSets.add(fileset);
    }

    /**
     * Returns the list of input files ({@link java.io.File} objects) to process.
     * Note that this does not check that the file is a valid and useful XML file.
     * 
     * @return The input files
     */
    private List getInputFiles() {
        ArrayList result = new ArrayList();

        for (Iterator it = _fileSets.iterator(); it.hasNext();) {
            FileSet fileSet = (FileSet) it.next();
            File fileSetDir = fileSet.getDir(getProject());
            DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
            String[] files = scanner.getIncludedFiles();

            for (int idx = 0; (files != null) && (idx < files.length); idx++) {
                File file = new File(fileSetDir, files[idx]);

                if (file.isFile() && file.canRead()) {
                    result.add(file);
                }
            }
        }
        return result;
    }

    /**
     * Processes all input files and gathers the relevant data.
     * 
     * @return The Dom4j document object containing the summary
     */
    private Document processInputFiles() throws IOException {
        Document summaryDoc = DocumentHelper.createDocument();

        summaryDoc.addElement("summary");

        for (Iterator it = getInputFiles().iterator(); it.hasNext();) {
            processInputFile(summaryDoc, (File) it.next());
        }
        return summaryDoc;
    }

    /**
     * Returns the value of the specified attribute of the node determined by the given xpath.
     * 
     * @param doc      The XML document
     * @param xpath    The xpath selecting the node whose attribute we want
     * @param attrName The name of the attribute
     * @return The attribute value
     */
    private String getAttrValue(Document doc, String xpath, String attrName) {
        Node node = doc.selectSingleNode(xpath);

        if (node instanceof Attribute) {
            // we ignore the attribute name then
            return ((Attribute) node).getValue();
        } else if (node != null) {
            return node.valueOf("@" + attrName);
        } else {
            return null;
        }
    }

    /**
     * Processes the given input file.
     * 
     * @param summaryDoc The document object to add data to
     * @param inputFile  The input file
     */
    private void processInputFile(Document summaryDoc, File inputFile) throws IOException {
        try {
            // First we check whether it is an XML file
            SAXReader reader = new SAXReader();
            Document testDoc = reader.read(new InputSource(new FileReader(inputFile)));

            // Then we check whether it is one that we're interested in
            if (testDoc.selectSingleNode("/testsuite") == null) {
                return;
            }

            // Ok, it is, so lets extract the data that we want:
            Element generalElement = (Element) summaryDoc.selectSingleNode("//general");
            int totalNumTests = 0;
            int totalNumErrors = 0;
            int totalNumFailures = 0;

            if (generalElement == null) {
                generalElement = summaryDoc.getRootElement().addElement("general");

                // General run info (we only need this once)
                generalElement.addAttribute("ddlUtilsVersion", _version);
                generalElement.addAttribute("date", getAttrValue(testDoc, "//property[@name='TODAY']", "value"));
                generalElement.addAttribute("lang", getAttrValue(testDoc, "//property[@name='env.LANG']", "value"));
                generalElement.addAttribute("jre",
                        getAttrValue(testDoc, "//property[@name='java.runtime.name']", "value") + " "
                                + getAttrValue(testDoc, "//property[@name='java.runtime.version']", "value") + " ("
                                + getAttrValue(testDoc, "//property[@name='java.vendor']", "value") + ")");
                generalElement.addAttribute("os",
                        getAttrValue(testDoc, "//property[@name='os.name']", "value") + " "
                                + getAttrValue(testDoc, "//property[@name='os.version']", "value") + ", arch "
                                + getAttrValue(testDoc, "//property[@name='os.arch']", "value"));
                addTargetDatabaseInfo(generalElement,
                        getAttrValue(testDoc, "//property[@name='jdbc.properties.file']", "value"));
            } else {
                totalNumTests = Integer.parseInt(generalElement.attributeValue("tests"));
                totalNumErrors = Integer.parseInt(generalElement.attributeValue("errors"));
                totalNumFailures = Integer.parseInt(generalElement.attributeValue("failures"));
            }

            int numTests = Integer.parseInt(getAttrValue(testDoc, "/testsuite", "tests"));
            int numErrors = Integer.parseInt(getAttrValue(testDoc, "/testsuite", "errors"));
            int numFailures = Integer.parseInt(getAttrValue(testDoc, "/testsuite", "failures"));

            totalNumTests += numTests;
            totalNumErrors += numErrors;
            totalNumFailures += numFailures;

            generalElement.addAttribute("tests", String.valueOf(totalNumTests));
            generalElement.addAttribute("errors", String.valueOf(totalNumErrors));
            generalElement.addAttribute("failures", String.valueOf(totalNumFailures));

            if ((numErrors > 0) || (numFailures > 0)) {
                Element testSuiteNode = (Element) testDoc.selectSingleNode("/testsuite");
                String testSuiteName = testSuiteNode.attributeValue("name");

                // since tests have failed, we add it to the summary
                for (Iterator it = testSuiteNode.selectNodes("testcase[failure or error]").iterator(); it
                        .hasNext();) {
                    Element failedTestElement = (Element) it.next();
                    Element newTestElement = summaryDoc.getRootElement().addElement("failedTest");

                    // Test setup failure, so the test was not actually run ?
                    if (!failedTestElement.attributeValue("classname").startsWith("junit.framework.TestSuite")) {
                        newTestElement.addAttribute("name", failedTestElement.attributeValue("classname") + "#"
                                + failedTestElement.attributeValue("name"));
                    }
                    newTestElement.addAttribute("testsuite", testSuiteName);
                }
            }
        } catch (DocumentException ex) {
            // No, apparently it's not an XML document, so we ignore it
        }
    }

    /**
     * Adds the data from the test jdbc propertis file to the document.
     * 
     * @param element            The element to add the relevant database properties to
     * @param jdbcPropertiesFile The path of the properties file
     */
    protected void addTargetDatabaseInfo(Element element, String jdbcPropertiesFile)
            throws IOException, BuildException {
        if (jdbcPropertiesFile == null) {
            return;
        }

        Properties props = readProperties(jdbcPropertiesFile);
        Connection conn = null;
        DatabaseMetaData metaData = null;

        try {
            String dataSourceClass = props.getProperty(
                    TestAgainstLiveDatabaseBase.DATASOURCE_PROPERTY_PREFIX + "class",
                    BasicDataSource.class.getName());
            DataSource dataSource = (DataSource) Class.forName(dataSourceClass).newInstance();

            for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
                Map.Entry entry = (Map.Entry) it.next();
                String propName = (String) entry.getKey();

                if (propName.startsWith(TestAgainstLiveDatabaseBase.DATASOURCE_PROPERTY_PREFIX)
                        && !propName.equals(TestAgainstLiveDatabaseBase.DATASOURCE_PROPERTY_PREFIX + "class")) {
                    BeanUtils.setProperty(dataSource,
                            propName.substring(TestAgainstLiveDatabaseBase.DATASOURCE_PROPERTY_PREFIX.length()),
                            entry.getValue());
                }
            }

            String platformName = props.getProperty(TestAgainstLiveDatabaseBase.DDLUTILS_PLATFORM_PROPERTY);

            if (platformName == null) {
                platformName = new PlatformUtils().determineDatabaseType(dataSource);
                if (platformName == null) {
                    throw new BuildException(
                            "Could not determine platform from datasource, please specify it in the jdbc.properties via the ddlutils.platform property");
                }
            }

            element.addAttribute("platform", platformName);
            element.addAttribute("dataSourceClass", dataSourceClass);

            conn = dataSource.getConnection();
            metaData = conn.getMetaData();

            try {
                element.addAttribute("dbProductName", metaData.getDatabaseProductName());
            } catch (Throwable ex) {
                // we ignore it
            }
            try {
                element.addAttribute("dbProductVersion", metaData.getDatabaseProductVersion());
            } catch (Throwable ex) {
                // we ignore it
            }
            try {
                int databaseMajorVersion = metaData.getDatabaseMajorVersion();
                int databaseMinorVersion = metaData.getDatabaseMinorVersion();

                element.addAttribute("dbVersion", databaseMajorVersion + "." + databaseMinorVersion);
            } catch (Throwable ex) {
                // we ignore it
            }
            try {
                element.addAttribute("driverName", metaData.getDriverName());
            } catch (Throwable ex) {
                // we ignore it
            }
            try {
                element.addAttribute("driverVersion", metaData.getDriverVersion());
            } catch (Throwable ex) {
                // we ignore it
            }
            try {
                int jdbcMajorVersion = metaData.getJDBCMajorVersion();
                int jdbcMinorVersion = metaData.getJDBCMinorVersion();

                element.addAttribute("jdbcVersion", jdbcMajorVersion + "." + jdbcMinorVersion);
            } catch (Throwable ex) {
                // we ignore it
            }
        } catch (Exception ex) {
            throw new BuildException(ex);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException ex) {
                    // we ignore it
                }
            }
        }
    }

    /**
     * Reads the database properties from the used properties file.
     * 
     * @param jdbcPropertiesFile The path of the properties file
     * @return The properties
     */
    private Properties readProperties(String jdbcPropertiesFile) {
        Properties props = new Properties();
        InputStream propStream = null;

        try {
            propStream = TestSummaryCreatorTask.class.getResourceAsStream(jdbcPropertiesFile);
            if (propStream == null) {
                // not on the classpath ? let's try a file
                File baseDir = getProject().getBaseDir();
                File propFile = new File(baseDir, jdbcPropertiesFile);

                if (propFile.exists() && propFile.isFile() && propFile.canRead()) {
                    propStream = new FileInputStream(propFile);
                } else {
                    throw new BuildException("Cannot load test jdbc properties from file " + jdbcPropertiesFile);
                }
            }
            props.load(propStream);
        } catch (BuildException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new BuildException("Cannot load test jdbc properties from file " + jdbcPropertiesFile, ex);
        } finally {
            if (propStream != null) {
                try {
                    propStream.close();
                } catch (IOException ex) {
                    log("Could not close the stream used to read the test jdbc properties", Project.MSG_WARN);
                }
            }
        }
        return props;
    }

    /**
     * {@inheritDoc}
     */
    public void execute() throws BuildException {
        try {
            log("Processing test results", Project.MSG_INFO);

            Document doc = processInputFiles();
            XMLWriter writer = null;

            if (_outputFile != null) {
                writer = new XMLWriter(new FileWriter(_outputFile), OutputFormat.createPrettyPrint());
            } else {
                writer = new XMLWriter(System.out, OutputFormat.createPrettyPrint());
            }

            writer.write(doc);
            writer.close();

            log("Processing finished", Project.MSG_INFO);
        } catch (Exception ex) {
            throw new BuildException("Error while processing the test results: " + ex.getLocalizedMessage(), ex);
        }
    }
}