blob: f45f2b2839459a307bf089350a8eb3e4c1a5c226 [file] [log] [blame]
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);
}
}
}