blob: 18131260c25f93ba398052bd0e48f5290ff511b5 [file] [log] [blame]
/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package org.apache.batik.test.xml;
import java.io.File;
import java.io.StringWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.net.MalformedURLException;
import java.lang.reflect.Constructor;
import java.util.Vector;
import org.apache.batik.test.DefaultTestSuite;
import org.apache.batik.test.TestReport;
import org.apache.batik.test.TestSuite;
import org.apache.batik.test.Test;
import org.apache.batik.test.TestException;
import org.apache.batik.test.TestReportProcessor;
import org.apache.batik.dom.util.DocumentFactory;
import org.apache.batik.dom.util.SAXDocumentFactory;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.xml.sax.InputSource;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* This class can be used to build and run a <tt>TestSuite</tt> from
* an XML description following the "XML Test Suite" format, whose
* constants are defined in the<tt>XTSConstants</tt> interface.
*
* In summary, this class builds a <tt>TestSuite</tt> which is
* an aggregation of individual <tt>Test</tt> and <tt>TestSuite</tt>
* and requests a set of <tt>TestResultProcessor</tt> to
* process the <tt>TestResult</tt> that are produced by the
* <tt>Test</tt> and <tt>TestSuite</tt> objects.
*
* @author <a href="mailto:vhardy@apache.org">Vincent Hardy</a>
* @version $Id$
*/
public class XMLTestSuiteRunner implements XTSConstants{
/**
* An error happened while processing a <tt>Test</tt>
* description.
* {0} : the <test> "className" attribute value
* {1} : exception's class name
* {2} : exception's message
* {3} : exception's stack trace
*/
public static final String CANNOT_CREATE_TEST
= "xml.XMLTestSuiteRunner.error.cannot.create.test";
/**
* An error happened while processing a <tt>TestreportProcessor</tt>
* description.
* {0} : the <testReportProcessor> "className" attribute value
* {1} : exception's class name
* {2} : exception's message
* {3} : exception's stack trace
*/
public static final String CANNOT_CREATE_TEST_REPORT_PROCESSOR
= "xml.XMLTestSuiteRunner.error.cannot.create.test.report.processor";
/**
* An error happened while running the <tt>TestSuite</tt>
* {0} : <tt>TestSuite</tt> name
* {1} : <tt>TestSuite</tt> class name.
* {1} : exception's class name.
* {2} : exception's message
* {3} : exception's stack trace.
*/
public static final String TEST_SUITE_EXCEPTION
= "xml.XMLTestSuiteRunner.test.suite.exception";
/**
* An error happened while processing the <tt>TestReport</tt>
* generated by the <tt>TestSuite</tt>
* {0} : <tt>TestReportProcessor</tt> class name.
* {1} : exception's class name.
* {2} : exception's message
* {3} : exception's stack trace.
*/
public static final String TEST_REPORT_PROCESSING_EXCEPTION
= "xml.XMLTestSuiteRunner.error.test.report.processing.exception";
/**
* Builds an array of <tt>TestReportProcessor</tt> from the input
* element, assuming the input element is a <testSuite> instance,
*/
protected TestReportProcessor[] extractTestReportProcessor(Element element)
throws TestException
{
Vector processors = new Vector();
NodeList children = element.getChildNodes();
if(children != null && children.getLength() > 0){
int n = children.getLength();
for(int i=0; i<n; i++){
Node child = children.item(i);
if(child.getNodeType() == Node.ELEMENT_NODE){
Element childElement = (Element)child;
String tagName = childElement.getTagName().intern();
if(tagName == XTS_TEST_REPORT_PROCESSOR_TAG){
processors.addElement(buildProcessor(childElement));
}
}
}
}
TestReportProcessor[] p = null;
if(processors.size() > 0){
p = new TestReportProcessor[processors.size()];
processors.copyInto(p);
}
return p;
}
/**
* Builds a <tt>TestResultProcessor</tt> from an element.
*/
protected TestReportProcessor buildProcessor(Element element)
throws TestException {
String className
= element.getAttributeNS(null,
XTS_CLASS_ATTRIBUTE);
try{
return (TestReportProcessor)buildObject(className, element);
}catch(Exception e){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
throw new TestException(CANNOT_CREATE_TEST_REPORT_PROCESSOR,
new Object[] { className,
e.getClass().getName(),
e.getMessage(),
sw.toString() },
e);
}
}
/**
* Builds a <tt>TestSuite</tt> from an input element.
* This method assumes that element is a <testSuite>
* instance, as the input document should have been
* validated when loaded.
*/
protected TestSuite buildTestSuite(Element element)
throws TestException {
DefaultTestSuite testSuite
= new DefaultTestSuite();
String suiteName
= element.getAttributeNS(null,
XTS_NAME_ATTRIBUTE);
testSuite.setName(suiteName + "--" + testSuite.getName());
NodeList children = element.getChildNodes();
if(children != null && children.getLength() > 0){
int n = children.getLength();
for(int i=0; i<n; i++){
Node child = children.item(i);
if(child.getNodeType() == Node.ELEMENT_NODE){
Element childElement = (Element)child;
String tagName = childElement.getTagName().intern();
if(tagName == XTS_TEST_TAG){
Test t = buildTest(childElement);
testSuite.addTest(t);
}
}
}
}
return testSuite;
}
protected Test buildTest(Element element) throws TestException {
String className
= element.getAttributeNS(null,
XTS_CLASS_ATTRIBUTE);
try{
return (Test)buildObject(className, element);
}catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
throw new TestException(CANNOT_CREATE_TEST,
new Object[] { className,
e.getClass().getName(),
e.getMessage(),
sw.toString() },
e);
}
}
/**
* Implementation helper: builds a generic object
*/
public Object buildObject(String className,
Element element) throws Exception {
Class cl = Class.forName(className);
Object[] argsArray = null;
Class[] argsClasses = null;
NodeList children = element.getChildNodes();
if(children != null && children.getLength() > 0){
int n = children.getLength();
Vector args = new Vector();
for(int i=0; i<n; i++){
Node child = children.item(i);
if(child.getNodeType() == Node.ELEMENT_NODE){
Element childElement = (Element)child;
String tagName = childElement.getTagName().intern();
if(tagName == XTS_ARG_TAG){
Object arg = buildArgument(childElement);
args.addElement(arg);
}
}
}
if(args.size() > 0){
argsArray = new Object[args.size()];
args.copyInto(argsArray);
argsClasses = new Class[args.size()];
for(int i=0; i<args.size(); i++){
argsClasses[i] = argsArray[i].getClass();
}
}
}
Constructor constructor
= getDeclaredConstructor(cl, argsClasses);
return constructor.newInstance(argsArray);
}
/**
* Returns a constructor that has can be used for the input class
* types.
*/
protected Constructor getDeclaredConstructor(Class cl,
Class[] argClasses){
Constructor[] cs = cl.getDeclaredConstructors();
for(int i=0; i<cs.length; i++){
Class[] reqArgClasses = cs[i].getParameterTypes();
if(reqArgClasses.length == argClasses.length){
int j=0;
for(; j<argClasses.length; j++){
if(!reqArgClasses[j].isAssignableFrom(argClasses[j])){
break;
}
}
if(j == argClasses.length){
return cs[i];
}
}
}
return null;
}
/**
* Limitation: Arguments *must* have a String based
* constructor. Or be an object that takes a set of string
* based arguments.
*/
public Object buildArgument(Element element) throws Exception {
String classAttr = element.getAttributeNS(null,
XTS_CLASS_ATTRIBUTE);
if(!element.hasChildNodes()){
String value = element.getAttributeNS(null,
XTS_VALUE_ATTRIBUTE);
// String based argument
Class cl = Class.forName(classAttr);
Constructor constructor
= cl.getDeclaredConstructor(new Class[] { String.class });
return constructor.newInstance(new Object[] {value});
}
else{
return buildObject(classAttr, element);
}
}
/**
* Runs the test suite described by the input
* Document object.
*/
public void run(Document doc)
throws TestException {
//
// First, extract the TestSuite to run
// the actual tests.
//
TestSuite testSuite
= buildTestSuite(doc.getDocumentElement());
//
// Now, get the set of TestReportProcessors
// that can use the data
//
TestReportProcessor[] processors
= extractTestReportProcessor(doc.getDocumentElement());
//
// Run the test
//
TestReport report = null;
try{
report = testSuite.run();
}catch(Exception e){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
throw new TestException(TEST_SUITE_EXCEPTION,
new Object[] { testSuite.getName(),
testSuite.getClass().getName(),
e.getClass().getName(),
e.getMessage(),
sw.toString() },
e);
}
//
// Process the report
//
int n = processors.length;
int i=0;
try{
for(; i<n; i++){
processors[i].processReport(report);
}
}catch(Exception e){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
throw new TestException(TEST_REPORT_PROCESSING_EXCEPTION,
new Object[] { processors[i].getClass().getName(),
e.getClass().getName(),
e.getMessage(),
sw.toString() },
e);
}
}
/**
* Displayed when the user passes no arguments to the command line.
*/
public static final String USAGE
= "XMLTestSuiteRunner.messages.error.usage";
/**
* Displayed when the input argument does not represent an existing
* file to notify the user that the argument is going to be
* interpreted as a URI.
*/
public static final String NOT_A_FILE_TRY_URI
= "XMLTestSuiteRunner.messages.error.not.a.file.try.uri";
/**
* Displayed when the input file name cannot be turned into a URL
*/
public static final String COULD_NOT_CONVERT_FILE_NAME_TO_URI
= "XMLTestSuiteRunner.messages.error.could.not.convert.file.name.to.uri";
/**
* Displayed when the input argument does not represent a valid
* URI
*/
public static final String INVALID_URI
= "XMLTestSuiteRunner.messages.error.invalid.uri";
/**
* Displayed when the input document cannot be parsed.
* {0} : uri of the invalid document.
* {1} : exception generated while parsing
* {2} : exception message
*/
public static final String INVALID_DOCUMENT
= "XMLTestSuiteRunner.messages.error.invalid.document";
/**
* Error displayed when an error occurs while running the
* test suite
*/
public static final String ERROR_RUNNING_TEST_SUITE
= "XMLTestSuiteRunner.messages.error.running.test.suite";
/**
* Configuration parameter
*/
public static final String XML_PARSER =
"XMLTestSuiteRunner.config.xml.parser";
public static void main(String[] args) {
if(args.length < 1){
System.err.println(Messages.formatMessage(USAGE, null));
System.exit(0);
}
String uriStr = args[0];
File file = new File(uriStr);
URL url = null;
if(file.exists()){
try {
url = file.toURL();
}catch(MalformedURLException e){
System.err.println(Messages.formatMessage(COULD_NOT_CONVERT_FILE_NAME_TO_URI,
new Object[]{uriStr}));
System.exit(0);
}
}
else {
System.err.println(Messages.formatMessage(NOT_A_FILE_TRY_URI,
new Object[]{uriStr}));
try{
url = new URL(uriStr);
}catch(MalformedURLException e){
System.err.println(Messages.formatMessage(INVALID_URI,
new Object[]{uriStr}));
System.exit(0);
}
}
DocumentFactory df
= new SAXDocumentFactory(SVGDOMImplementation.getDOMImplementation(),
Messages.formatMessage(XML_PARSER, null));
Document doc = null;
try{
doc = df.createDocument(null,
XTS_TEST_SUITE_TAG,
url.toString(),
url.openStream());
}catch(Exception e){
e.printStackTrace();
System.err.println(Messages.formatMessage(INVALID_DOCUMENT,
new Object[] { uriStr,
e.getClass().getName(),
e.getMessage() }));
System.exit(0);
}
try{
XMLTestSuiteRunner r = new XMLTestSuiteRunner();
r.run(doc);
}catch(TestException e){
System.err.println(Messages.formatMessage(ERROR_RUNNING_TEST_SUITE,
new Object[] { e.getMessage() }));
System.exit(0);
}
System.exit(1);
}
}