You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by cu...@apache.org on 2001/02/23 22:04:45 UTC
cvs commit: xml-xalan/test/java/src/org/apache/qetest/xsl StylesheetDatalet.java StylesheetTestlet.java StylesheetTestletDriver.java
curcuru 01/02/23 13:04:45
Added: test/java/src/org/apache/qetest/xsl StylesheetDatalet.java
StylesheetTestlet.java StylesheetTestletDriver.java
Log:
Greatly improved conformance testing engine: replaces
XSLDirectoryIterator, and has a generic XSL/Stylesheet
Testlet and Datalet; makes it much easier to iterate over
trees of conf, contrib, perf, whatever stylesheet tests and
can replace the Testlet algorithim easily
Revision Changes Path
1.1 xml-xalan/test/java/src/org/apache/qetest/xsl/StylesheetDatalet.java
Index: StylesheetDatalet.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
*
* StylesheetDatalet.java
*
*/
package org.apache.qetest.xsl;
import org.apache.qetest.Datalet;
import java.util.Hashtable;
import java.util.StringTokenizer;
/**
* Datalet for conformance testing of xsl stylesheet files.
* Should serve as a base class for other XSLT related Datalets.
* @author Shane_Curcuru@lotus.com
* @version $Id: StylesheetDatalet.java,v 1.1 2001/02/23 21:04:45 curcuru Exp $
*/
public class StylesheetDatalet implements Datalet
{
/** URL of the stylesheet; default:.../identity.xsl. */
public String inputName = "tests/api/trax/identity.xsl";
/** URL of the xml document; default:.../identity.xml. */
public String xmlName = "tests/api/trax/identity.xml";
/** URL to put output into; default:StylesheetDatalet.out. */
public String outputName = "StylesheetDatalet.out";
/** URL of the a gold file or data; default:.../identity.out. */
public String goldName = "tests/api-gold/trax/identity.out";
/** Flavor of a ProcessorWrapper to use; default:trax. */
public String flavor = "trax"; //@todo should be ProcessorWrapper.DEFAULT_FLAVOR
/**
* If we should force any local path\filenames to URLs.
* Note: This is not really the best place for this, but
* since it works with Xerces and Crimson and Xalan, it's
* good enough for now.
* Not currently settable by user; default:true
*/
public boolean useURL = true;
/** Description of what this Datalet tests. */
protected String description = "StylesheetDatalet: String inputName, String xmlName, String outputName, String goldName, String flavor";
/**
* No argument constructor is a no-op.
*/
public StylesheetDatalet() { /* no-op */ }
/**
* Initialize this datalet from a string, perhaps from
* a command line.
* We will parse the command line with whitespace and fill
* in our member variables in order:
* <pre>inputName, xmlName, outputName, goldName, flavor</pre>,
* if there are too few tokens, remaining variables will default.
*/
public StylesheetDatalet(String args)
{
load(args);
}
/**
* Accesor method for a brief description of this Datalet.
*
* @return String describing the specific set of data
* this Datalet contains (can often be used as the description
* of any check() calls made from the Testlet).
*/
public String getDescription()
{
return description;
}
/**
* Accesor method for a brief description of this Datalet.
*
* @param s description to use for this Datalet.
*/
public void setDescription(String s)
{
description = s;
}
/**
* Load fields of this Datalet from a Hashtable.
* Caller must provide data for all of our fields.
*
* @param Hashtable to load
*/
public void load(Hashtable h)
{
if (null == h)
return; //@todo should this have a return val or exception?
inputName = (String)h.get("inputName");
xmlName = (String)h.get("xmlName");
outputName = (String)h.get("outputName");
goldName = (String)h.get("goldName");
flavor = (String)h.get("flavor");
}
/**
* Load fields of this Datalet from an array.
* Order: inputName, xmlName, outputName, goldName, flavor
* If too few args, then fields at end of list are left at default value.
* @param args array of Strings
*/
public void load(String[] args)
{
if (null == args)
return; //@todo should this have a return val or exception?
try
{
inputName = args[0];
xmlName = args[1];
outputName = args[2];
goldName = args[3];
flavor = args[4];
}
catch (ArrayIndexOutOfBoundsException aioobe)
{
// No-op, leave remaining items as default
}
}
/**
* Load fields of this Datalet from a whitespace-delimited String.
* Order: inputName, xmlName, outputName, goldName, flavor
* If too few args, then fields at end of list are left at default value.
* @param args array of Strings
*/
public void load(String str)
{
if (null == str)
return; //@todo should this have a return val or exception?
StringTokenizer st = new StringTokenizer(str);
// Fill in as many items as we can; leave as default otherwise
// Note that order is important!
if (st.hasMoreTokens())
{
inputName = st.nextToken();
if (st.hasMoreTokens())
{
xmlName = st.nextToken();
if (st.hasMoreTokens())
{
outputName = st.nextToken();
if (st.hasMoreTokens())
{
goldName = st.nextToken();
if (st.hasMoreTokens())
{
flavor = st.nextToken();
}
}
}
}
}
}
} // end of class StylesheetDatalet
1.1 xml-xalan/test/java/src/org/apache/qetest/xsl/StylesheetTestlet.java
Index: StylesheetTestlet.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
*
* StylesheetTestlet.java
*
*/
package org.apache.qetest.xsl;
import org.apache.qetest.CheckService;
import org.apache.qetest.Datalet;
import org.apache.qetest.Logger;
import org.apache.qetest.QetestUtils;
import org.apache.qetest.TestletImpl;
import org.apache.qetest.xslwrapper.ProcessorWrapper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Hashtable;
/**
* Testlet for conformance testing of xsl stylesheet files.
*
* @author Shane_Curcuru@lotus.com
* @version $Id: StylesheetTestlet.java,v 1.1 2001/02/23 21:04:45 curcuru Exp $
*/
public class StylesheetTestlet extends TestletImpl
{
// Initialize our classname for TestletImpl's main() method
static { thisClassName = "org.apache.qetest.xsl.StylesheetTestlet"; }
// Initialize our defaultDatalet
{ defaultDatalet = (Datalet)new StylesheetDatalet(); }
/**
* Accesor method for a brief description of this test.
*
* @return String describing what this StylesheetTestlet does.
*/
public String getDescription()
{
return "StylesheetTestlet";
}
/**
* Run this StylesheetTestlet: execute it's test and return.
*
* @param Datalet to use as data point for the test.
*/
public void execute(Datalet d)
{
StylesheetDatalet datalet = null;
try
{
datalet = (StylesheetDatalet)d;
}
catch (ClassCastException e)
{
logger.checkErr("Datalet provided is not a StylesheetDatalet; cannot continue");
return;
}
//@todo validate our Datalet - ensure it has valid
// and/or existing files available.
// Cleanup outputName - delete the file on disk
// Ensure other files or previous test runs don't
// interfere with our results
try
{
File outFile = new File(datalet.outputName);
boolean btmp = outFile.delete();
}
catch (SecurityException se)
{
logger.logMsg(Logger.WARNINGMSG, "Deleting OutFile of:" + datalet.outputName
+ " threw: " + se.toString());
// But continue anyways...
}
// Test our supplied input file, and compare with gold
try
{
// Create a new ProcessorWrapper of appropriate flavor
// null arg is unused liaison for ProcessorWrapper
//@todo allow user to pass in pre-created
// ProcessorWrapper so we don't have lots of objects
// created and destroyed for every file
ProcessorWrapper processorWrapper = ProcessorWrapper.getWrapper(datalet.flavor);
if (null == processorWrapper.createNewProcessor(null))
{
logger.checkErr("ERROR: could not create processorWrapper, aborting.");
return;
}
// Store local copies of XSL, XML references for
// potential change to URLs
String inputName = datalet.inputName;
String xmlName = datalet.xmlName;
if (datalet.useURL)
{
// inputName may not exist if it's an embedded test
if (null != inputName)
inputName = QetestUtils.filenameToURL(inputName);
xmlName = QetestUtils.filenameToURL(xmlName);
}
//@todo do we really want to log all of this out here?
logger.logMsg(Logger.TRACEMSG, "executing with: inputName=" + inputName
+ " xmlName=" + xmlName + " outputName=" + datalet.outputName
+ " goldName=" + datalet.goldName + " flavor=" + datalet.flavor);
// Simply have the wrapper do all the transforming
// or processing for us - we handle either normal .xsl
// stylesheet tests or just .xml embedded tests
long retVal = ProcessorWrapper.ERROR;
if (null == datalet.inputName)
{
// presume it's an embedded test
retVal = processorWrapper.processEmbeddedToFile(xmlName, datalet.outputName);
}
else
{
// presume it's a normal stylesheet test
retVal = processorWrapper.processToFile(xmlName, inputName, datalet.outputName);
}
if (ProcessorWrapper.ERROR == retVal)
{
//@todo Should validate potential expectedExceptions here
logger.checkFail(getDescription() + " " + datalet.getDescription()
+ "unexpected processToFile problem");
return;
}
//@todo report out timing data for overall use?
}
catch (FileNotFoundException fnfe)
{
logger.checkFail(getDescription() + " " + datalet.getDescription()
+ " threw: " + fnfe.toString());
// Don't bother logging the stacktrace here; not worth it
return;
}
catch (Throwable t)
{
logger.checkFail(getDescription() + " " + datalet.getDescription()
+ " threw: " + t.toString());
logThrowable(t, getDescription() + " " + datalet.getDescription());
return;
}
// If we get here, attempt to validate the contents of
// the last outputFile created
//@todo allow passing in of preexisting checkService
CheckService fileChecker = new XHTFileCheckService();
if (Logger.PASS_RESULT
!= fileChecker.check(logger,
new File(datalet.outputName),
new File(datalet.goldName),
getDescription() + " " + datalet.getDescription())
)
{
// Log a custom element with all the file refs first
// Closely related to viewResults.xsl select='fileref"
//@todo check that these links are valid when base
// paths are either relative or absolute!
Hashtable attrs = new Hashtable();
attrs.put("idref", (new File(datalet.inputName)).getName());
attrs.put("inputName", datalet.inputName);
attrs.put("xmlName", datalet.xmlName);
attrs.put("outputName", datalet.outputName);
attrs.put("goldName", datalet.goldName);
logger.logElement(Logger.STATUSMSG, "fileref", attrs, "Conformance test file references");
// Then log the failure reason
logger.logArbitrary(Logger.STATUSMSG, (new File(datalet.inputName)).getName()
+ " failure reason: " + fileChecker.getExtendedInfo());
}
}
/**
* Logs out throwable.toString() and stack trace to our Logger.
* //@todo Copied from Reporter; should probably be moved into Logger.
* @param throwable thrown throwable/exception to log out.
* @param msg description of the throwable.
*/
protected void logThrowable(Throwable throwable, String msg)
{
StringWriter sWriter = new StringWriter();
sWriter.write(msg + "\n");
PrintWriter pWriter = new PrintWriter(sWriter);
throwable.printStackTrace(pWriter);
logger.logArbitrary(Logger.STATUSMSG, sWriter.toString());
}
} // end of class StylesheetTestlet
1.1 xml-xalan/test/java/src/org/apache/qetest/xsl/StylesheetTestletDriver.java
Index: StylesheetTestletDriver.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
*
* StylesheetTestletDriver.java
*
*/
package org.apache.qetest.xsl;
// Support for test reporting and harness classes
import org.apache.qetest.*;
import org.apache.qetest.xslwrapper.ProcessorWrapper;
// java classes
import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
//-------------------------------------------------------------------------
/**
* Test driver for XSLT stylesheet Testlets.
* @author shane_curcuru@lotus.com
* @version $Id: StylesheetTestletDriver.java,v 1.1 2001/02/23 21:04:45 curcuru Exp $
*/
public class StylesheetTestletDriver extends XSLProcessorTestBase
{
//-----------------------------------------------------
//-------- Constants for common input params --------
//-----------------------------------------------------
/**
* Parameter: Run a specific list of files, instead of
* iterating over directories.
* <p>Default: null, do normal iteration.</p>
*/
public static final String OPT_FILELIST = "fileList";
/** Name of fileList file to read in to get test definitions from. */
protected String fileList = null;
/**
* Parameter: FQCN or simple classname of Testlet to use.
* <p>User may pass in either a FQCN or just a base classname,
* and we will attempt to look it up in any of the most common
* Xalan-testing packages. See QetestUtils.testClassForName().</p>
* <p>Default: null, use StylesheetTestlet.</p>
*/
public static final String OPT_TESTLET = "testlet";
/** Classname of Testlet to use. */
protected String testlet = null;
/**
* Parameter: FQCN or simple classname of FilenameFilter for
* directories under testDir we will process.
* If fileList is not set, we simply go to our inputDir, and
* then use this filter to iterate through directories returned.
* <p>Default: null, use ConformanceDirRules.</p>
*/
public static final String OPT_DIRFILTER = "dirFilter";
/** Classname of FilenameFilter to use for dirs. */
protected String dirFilter = null;
/**
* Parameter: FQCN or simple classname of FilenameFilter for
* files within subdirs we will process.
* If fileList is not set, we simply go through all directories
* specified by directoryFilter, and then use this filter to
* find all stylesheet test files in that directory to test.
* Note that this does <b>not</b> handle embedded tests, where
* the XML document has an xml-stylesheet PI that defines the
* stylesheet to use to process it.
* <p>Default: null, use ConformanceFileRules.</p>
*/
public static final String OPT_FILEFILTER = "fileFilter";
/** Classname of FilenameFilter to use for files. */
protected String fileFilter = null;
/** Convenience constant: .xml extension for input data file. */
public static final String XML_EXTENSION = ".xml";
/** Convenience constant: .xsl extension for stylesheet file. */
public static final String XSL_EXTENSION = ".xsl";
/** Convenience constant: .out extension for output result file. */
public static final String OUT_EXTENSION = ".out";
/** Just initialize test name, comment; numTestCases is not used. */
public StylesheetTestletDriver()
{
testName = "StylesheetTestletDriver";
testComment = "Test driver for XSLT stylesheet Testlets";
}
/**
* Initialize this test - fill in parameters.
* Simply fills in convenience variables from user parameters.
*
* @param p unused
* @return true
*/
public boolean doTestFileInit(Properties p)
{
// Copy any of our parameters from testProps to
// our local convenience variables
testlet = testProps.getProperty(OPT_TESTLET);
dirFilter = testProps.getProperty(OPT_DIRFILTER);
fileFilter = testProps.getProperty(OPT_FILEFILTER);
fileList = testProps.getProperty(OPT_FILELIST);
return true;
}
/**
* Run through the directory given to us and run tests found
* in subdirs; or run through our fileList.
*
* @param p Properties block of options to use - unused
* @return true if OK, false if we should abort
*/
public boolean runTestCases(Properties p)
{
// Grab the (potential) fileList from our properties
// block, in case it hasn't been read yet
//@todo better specify how this is set!
if (null == fileList)
{
fileList = testProps.getProperty(OPT_FILELIST);
}
// First log out any other runtime information, like the
// actual flavor of ProcessorWrapper, etc.
try
{
Hashtable runtimeProps = new Hashtable(4);
runtimeProps.put("actual.ProcessorWrapper",
ProcessorWrapper.getWrapper(flavor).getDescription());
runtimeProps.put("actual.testlet", getTestlet());
runtimeProps.put("actual.dirFilter", getDirFilter());
runtimeProps.put("actual.fileFilter", getFileFilter());
reporter.logHashtable(Logger.CRITICALMSG, runtimeProps,
"actual.runtime information");
}
catch (Exception e)
{
reporter.logWarningMsg("Logging actual.runtime threw: " + e.toString());
reporter.logThrowable(Logger.WARNINGMSG, e, "Logging actual.runtime threw");
}
// Now either run a list of specific tests the user specified,
// or do the default of iterating over a set of directories
if (null != fileList)
{
// Process the specific list of tests the user supplied
String desc = "User-supplied fileList"; // provide default value
Vector datalets = readFileList(fileList, desc);
// Actually process the specified files in a testCase
processFileList(datalets, desc);
}
else
{
// Do the default, which is to iterate over the inputDir
// Note that this calls the testCaseInit/testCaseClose
// logging methods itself
processInputDir();
}
return true;
}
/**
* Do the default: test all stylesheets found in subdirs
* of our inputDir, using FilenameFilters for dirs and files.
* This only goes down one level in the tree, eg:
* <ul>inputDir = tests/conf
* <li>tests/conf - not tested</li>
* <li>tests/conf/boolean - test all boolean*.xsl files</li>
* <li>tests/conf/copy - test all copy*.xsl files</li>
* <li>tests/conf/copy/foo - not tested</li>
* <li>tests/conf/xmanual - not tested, since default
* ConformanceDirRules excludes dirs starting with 'x|X'</li>
* <li>tests/whitespace - test all whitespace*.xsl files</li>
* <li>etc.</li>
* </ul>
*/
public void processInputDir()
{
// Ensure the inputDir is there - we must have a valid location for input files
File testDirectory = new File(inputDir);
if (!testDirectory.exists())
{
// Try a default inputDir
String oldInputDir = inputDir; // cache for potential error message
testDirectory = new File((inputDir = getDefaultInputDir()));
if (!testDirectory.exists())
{
// No inputDir, can't do any tests!
// @todo check if this is the best way to express this
reporter.checkErr("inputDir(" + oldInputDir
+ ", or " + inputDir + ") does not exist, aborting!");
return;
}
}
reporter.logInfoMsg("inputDir(" + testDirectory.getPath()
+ ") looking for subdirs with: " + dirFilter);
// Use our filter to get a list of directories to process
String subdirs[] = testDirectory.list(getDirFilter());
// Validate that we have some valid directories to process
if ((null == subdirs) || (subdirs.length <= 0))
{
reporter.checkErr("inputDir(" + testDirectory.getPath()
+ ") no valid subdirs found!");
return;
}
int numSubdirs = subdirs.length;
// For every subdirectory, check if we should run tests in it
for (int i = 0; i < numSubdirs; i++)
{
File subTestDir = new File(testDirectory, subdirs[i]);
if ((null == subTestDir) || (!subTestDir.exists()))
{
// Just log it and continue; presumably we'll find
// other directories to test
reporter.logWarningMsg("subTestDir(" + subTestDir.getPath()
+ ") does not exist, skipping!");
continue;
}
// Construct matching directories for outputs and golds
File subOutDir = new File(outputDir, subdirs[i]);
File subGoldDir = new File(goldDir, subdirs[i]);
// Validate that each of the specified dirs exists
// Returns directory references like so:
// testDirectory = 0, outDirectory = 1, goldDirectory = 2
File[] dirs = validateDirs(new File[] { subTestDir },
new File[] { subOutDir, subGoldDir });
if (null == dirs) // also ensures that dirs[0] is non-null
{
// Just log it and continue; presumably we'll find
// other directories to test
reporter.logWarningMsg("subTestDir(" + subTestDir.getPath()
+ ") or associated dirs does not exist, skipping!");
continue;
}
// Call worker method to process the individual directory
// and get a list of .xsl files to test
Vector files = getFilesFromDir(subTestDir, getFileFilter(), embedded);
// 'Transform' the list of individual test files into a
// list of Datalets with all fields filled in
Vector datalets = buildDatalets(files, subTestDir, subOutDir, subGoldDir);
if ((null == datalets) || (0 == datalets.size()))
{
// Just log it and continue; presumably we'll find
// other directories to test
reporter.logWarningMsg("subTestDir(" + subTestDir.getPath()
+ ") did not contain any tests, skipping!");
continue;
}
processFileList(datalets, "Conformance test of: " + subdirs[i]);
} // end of for...
}
/**
* Run a list of stylesheet tests through a Testlet.
* The file names are assumed to be fully specified, and we assume
* the corresponding directories exist.
* Each fileList is turned into a testcase.
*
* @param testlet StylesheetTestlet or subclass to use to test
* with the corresponding Datalets
* @param vector of StylesheetDatalet objects to pass in
* @param desc String to use as testCase description
*/
public void processFileList(Vector datalets, String desc)
{
// Validate arguments
if ((null == datalets) || (0 == datalets.size()))
{
// Bad arguments, report it as an error
// Note: normally, this should never happen, since
// this class normally validates these arguments
// before calling us
reporter.checkErr("Testlet or datalets are null/blank, nothing to test!");
return;
}
// Put everything else into a testCase
// This is not necessary, but feels a lot nicer to
// break up large test sets
reporter.testCaseInit(desc);
// Now just go through the list and process each set
int numDatalets = datalets.size();
reporter.logInfoMsg("processFileList() with " + numDatalets
+ " potential tests");
// Iterate over every datalet and test it
for (int ctr = 0; ctr < numDatalets; ctr++)
{
try
{
// Create a Testlet to execute a test with this
// next datalet - the Testlet will log all info
// about the test, including calling check*()
getTestlet().execute((Datalet)datalets.elementAt(ctr));
}
catch (Throwable t)
{
// Log any exceptions as fails and keep going
//@todo improve the below to output more useful info
reporter.checkFail("Datalet num " + ctr + " threw: " + t.toString());
reporter.logThrowable(Logger.ERRORMSG, t, "Datalet threw");
}
} // of while...
reporter.testCaseClose();
}
/**
* Use the supplied filter on given directory to return a list
* of stylesheet tests to be run.
* Uses the normal filter for variations of *.xsl files, and
* also constructs names for any -embedded tests found (which
* may be .xml with xml-stylesheet PI's, not just .xsl)
*
* @param dir directory to scan
* @param filter to use on this directory; if null, uses default
* @param embeddedFiles special list of embedded files to find
* @return Vector of local path\filenames of tests to run;
* the tests themselves will exist; null if error
*/
public Vector getFilesFromDir(File dir, FilenameFilter filter, String embeddedFiles)
{
// Validate arguments
if ((null == dir) || (!dir.exists()))
{
// Bad arguments, report it as an error
// Note: normally, this should never happen, since
// this class normally validates these arguments
// before calling us
reporter.logWarningMsg("getFilesFromDir(" + dir.toString() + ") dir null or does not exist");
return null;
}
// Get the list of 'normal' test files
String[] files = dir.list(filter);
Vector v = new Vector(files.length);
for (int i = 0; i < files.length; i++)
{
v.addElement(files[i]);
}
reporter.logTraceMsg("getFilesFromDir(" + dir.toString() + ") found " + v.size() + " xsl files to test");
// Also get a list of any embedded test files here
// Optimization: only look for embedded files when likely to find them
if ((null != embeddedFiles) && (embeddedFiles.indexOf(dir.getName()) > -1))
{
// OK, presumably we have an embedded file in the current dir,
// add that name
StringTokenizer st = new StringTokenizer(embeddedFiles, ";");//@todo resource ;
while (st.hasMoreTokens())
{
String embeddedName = st.nextToken();
// Check if it's in our dir...
if (embeddedName.startsWith(dir.getName()))
{
// ...and that it exists
if ((new File(dir.getPath() + File.separator + embeddedName)).exists())
{
v.addElement(embeddedName);
}
else
{
reporter.logWarningMsg("Requested embedded file " + dir.getPath() + File.separator + embeddedName
+ " does not exist, skipping");
}
}
}
}
reporter.logTraceMsg("getFilesFromDir(" + dir.toString() + ") found " + v.size() + " total files to test");
return v;
}
/**
* Transform a vector of individual test names into a Vector
* of filled-in datalets to be tested
*
* @param files Vector of local path\filenames to be tested
* @param testLocation File denoting directory where all
* .xml/.xsl tests are found
* @param outLocation File denoting directory where all
* output files should be put
* @param goldLocation File denoting directory where all
* gold files are found
* @return Vector of StylesheetDatalets that are fully filled in,
* i.e. outputName, goldName, etc are filled in respectively
* to inputName
*/
public Vector buildDatalets(Vector files, File testLocation,
File outLocation, File goldLocation)
{
// Validate arguments
if ((null == files) || (files.size() < 1))
{
// Bad arguments, report it as an error
// Note: normally, this should never happen, since
// this class normally validates these arguments
// before calling us
reporter.logWarningMsg("buildDatalets null or empty file vector");
return null;
}
Vector v = new Vector(files.size());
// For every file in the vector, construct the matching
// out, gold, and xml/xsl files
for (Enumeration enum = files.elements();
enum.hasMoreElements(); /* no increment portion */ )
{
String file = null;
try
{
file = (String)enum.nextElement();
}
catch (ClassCastException cce)
{
// Just skip this entry
//@todo log an error
continue;
}
// Check if it's a normal .xsl file, or a .xml file
// (we assume .xml files are embedded tests!)
if (file.endsWith(XML_EXTENSION))
{
StylesheetDatalet d = new StylesheetDatalet();
d.xmlName = testLocation.getPath() + File.separator + file;
String fileNameRoot = file.substring(0, file.indexOf(XML_EXTENSION));
d.inputName = null;
d.outputName = outLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
d.goldName = goldLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
d.setDescription(file);
v.addElement(d);
}
else if (file.endsWith(XSL_EXTENSION))
{
StylesheetDatalet d = new StylesheetDatalet();
d.inputName = testLocation.getPath() + File.separator + file;
String fileNameRoot = file.substring(0, file.indexOf(XSL_EXTENSION));
d.xmlName = testLocation.getPath() + File.separator + fileNameRoot + XML_EXTENSION;
d.outputName = outLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
d.goldName = goldLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
d.setDescription(file);
v.addElement(d);
}
else
{
// Hmmm - I'm not sure what we should do here
reporter.logWarningMsg("Unexpected test file found, skipping: " + file);
}
}
return v; //@todo FIXME
}
/**
* Read in a file specifying a list of files to test.
* <p>File format is pretty simple:</p>
* <ul>
* <li># first line of comments is copied into desc</li>
* <li># beginning a line is a comment</li>
* <li># rest of lines are whitespace delimited filenames and options</li>
* <li>inputName xmlName outName goldName flavor options...</li>
* <li><b>Note:</b> see {@link StylesheetDatalet} for
* details on how the file lines are parsed!</li>
* </ul>
* <p>Most items are optional, but not having them may result
* in validation oddities</p>
*
* @param fileName String; name of the file
* @param desc description; caller's copy changed
* @return Vector of StylesheetDatalets, or null if error
*/
public Vector readFileList(String fileName, String desc)
{
final String COMMENT_CHAR = "#";
final String ABSOLUTE = "absolute";
final String RELATIVE = "relative";
// Verify the file is there
File f = new File(fileName);
if (!f.exists())
{
reporter.logErrorMsg("readFileList: " + fileName + " does not exist!");
return null;
}
Vector vec = new Vector();
BufferedReader br = null;
String line = null;
try
{
br = new BufferedReader(new FileReader(f));
line = br.readLine(); // read just first line
}
catch (IOException ioe)
{
reporter.logErrorMsg("readFileList: " + fileName + " threw: "
+ ioe.toString());
return null;
}
// Verify the first line
if (line == null)
{
reporter.logErrorMsg("readFileList: " + fileName
+ " appears to be blank!");
return null;
}
// Check if the first line is a comment
if (line.startsWith(COMMENT_CHAR))
{
// Save it as the description
desc = line;
}
// Load each line into a StylesheetDatalet
for (;;)
{
// Skip any lines beginning with # comment char or that are blank
if ((!line.startsWith(COMMENT_CHAR)) && (line.length() > 0))
{
// Create a Datalet and initialize with the line's contents
StylesheetDatalet d = new StylesheetDatalet(line);
//@todo Avoid spurious passes when output & gold not specified
// needs to detect when StylesheetDatalet doesn't
// properly have outputName and goldName set
// Add it to our vector
vec.addElement(d);
}
// Read next line and loop
try
{
line = br.readLine();
}
catch (IOException ioe2)
{
// Just force us out of the loop; if we've already
// read part of the file, fine
reporter.logWarningMsg("readFileList: " + fileName
+ " threw: " + ioe2.toString());
break;
}
if (line == null)
break;
} // end of for (;;)
if (vec.size() == 0)
{
reporter.logErrorMsg("readFileList: " + fileName
+ " did not have any non-comment lines!");
return null;
}
return vec;
}
/**
* Validate existence of or create various directories needed.
* <p>If any optionalDir cannot be created, it's array entry
* will be null.</p>
*
* @param requiredDirs array of directories that must previously
* exist; if none do, will return null; if none passed, return null
* @param optionalDirs array of optional directories; if they do
* not exist they'll be created
* @return array of file objects, null if any error; all
* required dirs are first, in order; then all optionalDirs
*/
public File[] validateDirs(File[] requiredDirs, File[] optionalDirs)
{
if ((null == requiredDirs) || (0 == requiredDirs.length))
{
return null;
}
File[] dirs = new File[(requiredDirs.length + optionalDirs.length)];
int ctr = 0;
try
{
// Validate requiredDirs exist first
for (int ir = 0; ir < requiredDirs.length; ir++)
{
if (!requiredDirs[ir].exists())
{
reporter.logErrorMsg("validateDirs("
+ requiredDirs[ir]
+ ") requiredDir did not exist!");
return null;
}
dirs[ctr] = requiredDirs[ir];
ctr++;
}
// Create any optionalDirs needed
for (int iopt = 0; iopt < optionalDirs.length; iopt++)
{
if (!optionalDirs[iopt].exists())
{
if (!optionalDirs[iopt].mkdirs())
{
reporter.logWarningMsg("validateDirs("
+ optionalDirs[iopt]
+ ") optionalDir could not be created");
dirs[ctr] = null;
}
else
{
reporter.logTraceMsg("validateDirs("
+ optionalDirs[iopt]
+ ") optionalDir was created");
dirs[ctr] = optionalDirs[iopt];
}
}
else
{
// It does previously exist, so copy it over
dirs[ctr] = optionalDirs[iopt];
}
ctr++;
}
}
catch (Exception e)
{
reporter.logThrowable(Logger.ERRORMSG, e, "validateDirs threw: " + e.toString());
return null;
}
return dirs;
}
/**
* List of common packages that Xalan testing classes are in.
* Note that Xalan-J 2.x packages are listed before Xalan-J 1.x
* packages, and there is an inherent danger in the ordering
* when two classes have the same name.
*/
protected String[] testPackages =
{
"org.apache.qetest.xsl",
"org.apache.qetest.trax",
"org.apache.qetest.xalanj2",
"org.apache.qetest.xalanj1",
"org.apache.qetest"
};
/** Default FilenameFilter for directories. */
protected String defaultDirFilter = "org.apache.qetest.xsl.ConformanceDirRules";
/** Default FilenameFilter for files. */
protected String defaultFileFilter = "org.apache.qetest.xsl.ConformanceFileRules";
/** Default Testlet for executing stylesheet tests. */
protected String defaultTestlet = "org.apache.qetest.xsl.StylesheetTestlet";
/**
* Convenience method to get a Testlet to use.
* Attempts to return one as specified by our testlet parameter,
* otherwise returns a default StylesheetTestlet.
*
* @return Testlet for use in this test; null if error
*/
public Testlet getTestlet()
{
// Find a Testlet class to use
Class clazz = QetestUtils.testClassForName(testlet,
testPackages,
defaultTestlet);
try
{
// Create it and set our reporter into it
Testlet t = (Testlet)clazz.newInstance();
t.setLogger((Logger)reporter);
return (Testlet)t;
}
catch (Exception e)
{
// Ooops, none found! This should be very rare, since
// we know the defaultTestlet should be found
return null;
}
}
/**
* Convenience method to get a default filter for directories.
* Uses category member variable if set.
*
* @return FilenameFilter using ConformanceDirRules(category).
*/
public FilenameFilter getDirFilter()
{
// Find a Testlet class to use
Class clazz = QetestUtils.testClassForName(dirFilter,
testPackages,
defaultDirFilter);
try
{
// Create it, optionally with a category
if ((null != category) && (category.length() > 1)) // Arbitrary check for non-null, non-blank string
{
Class[] parameterTypes = { java.lang.String.class };
Constructor ctor = clazz.getConstructor(parameterTypes);
Object[] ctorArgs = { category };
return (FilenameFilter)ctor.newInstance(ctorArgs);
}
else
{
return (FilenameFilter)clazz.newInstance();
}
}
catch (Exception e)
{
// Ooops, none found!
return null;
}
}
/**
* Convenience method to get a default filter for files.
* Uses excludes member variable if set.
*
* @return FilenameFilter using ConformanceFileRules(excludes).
*/
public FilenameFilter getFileFilter()
{
// Find a Testlet class to use
Class clazz = QetestUtils.testClassForName(fileFilter,
testPackages,
defaultFileFilter);
try
{
// Create it, optionally with a category
if ((null != excludes) && (excludes.length() > 1)) // Arbitrary check for non-null, non-blank string
{
Class[] parameterTypes = { java.lang.String.class };
Constructor ctor = clazz.getConstructor(parameterTypes);
Object[] ctorArgs = { excludes };
return (FilenameFilter) ctor.newInstance(ctorArgs);
}
else
{
return (FilenameFilter)clazz.newInstance();
}
}
catch (Exception e)
{
// Ooops, none found!
return null;
}
}
/**
* Convenience method to get a default inputDir when none or
* a bad one was given.
* @return String pathname of default inputDir "tests\conf".
*/
public String getDefaultInputDir()
{
return "tests" + File.separator + "conf";
}
/**
* Convenience method to print out usage information - update if needed.
* @return String denoting usage of this test class
*/
public String usage()
{
return ("Common [optional] options supported by StylesheetTestletDriver:\n"
+ " -" + OPT_FILELIST
+ " <name of listfile of tests to run>\n"
+ " -" + OPT_DIRFILTER
+ " <classname of FilenameFilter for dirs>\n"
+ " -" + OPT_FILEFILTER
+ " <classname of FilenameFilter for files>\n"
+ " -" + OPT_TESTLET
+ " <classname of Testlet to execute tests with>\n"
+ super.usage()); // Grab our parent classes usage as well
}
/**
* Main method to run test from the command line - can be left alone.
* @param args command line argument array
*/
public static void main(String[] args)
{
StylesheetTestletDriver app = new StylesheetTestletDriver();
app.doMain(args);
}
}