/* * 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. */ /* * $Id$ */ package org.apache.qetest.xsl; import java.io.File; import java.io.FilenameFilter; 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; import org.apache.qetest.Datalet; import org.apache.qetest.FileBasedTest; import org.apache.qetest.Logger; import org.apache.qetest.QetestUtils; import org.apache.qetest.Testlet; import org.apache.qetest.xslwrapper.TransformWrapper; import org.apache.qetest.xslwrapper.TransformWrapperFactory; /** * Test driver for XSLT stylesheet Testlets. * * This is a generic driver for XSLT-oriented Testlets, and * supports iterating over either a user-supplied, specific list * of files to test or over a directory tree of test files. * Note there are a number of design decisions made that are * just slightly specific to stylesheet testing, although this * would be a good model for a completely generic TestletDriver. * * @author shane_curcuru@lotus.com * @version $Id$ */ public class StylesheetTestletDriver extends FileBasedTest { //----------------------------------------------------- //-------- Constants for common input params -------- //----------------------------------------------------- /** * Parameter: Run a specific list of files, instead of * iterating over directories. *
Default: null, do normal iteration.
*/ 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. *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().
*Default: null, use StylesheetTestlet.
*/ 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. *Default: null, use ConformanceDirRules.
*/ 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 not handle embedded tests, where * the XML document has an xml-stylesheet PI that defines the * stylesheet to use to process it. *Default: null, use ConformanceFileRules.
*/ public static final String OPT_FILEFILTER = "fileFilter"; /** Classname of FilenameFilter to use for files. */ protected String fileFilter = null; /** * Parameter: What flavor of TransformWrapper to use: trax.sax|trax.stream|other? *Default: trax.
*/ public static final String OPT_FLAVOR = "flavor"; /** Parameter: What flavor of TransformWrapper to use: trax.sax|trax.stream|other? */ protected String flavor = "trax"; /** * Parameter: Are there any embedded stylesheets in XML files? *Default: null (no embedded tests; otherwise specify * semicolon delimited list of bare filenames something like * 'axes02.xml;bool98.xml').
*/ public static final String OPT_EMBEDDED = "embedded"; /** Parameter: Are there any embedded stylesheets in XML files? */ protected String embedded = null; /** * Parameter: Is there any preference to which gold file to use? *Default: null (use standard .out file) * name of processor, could be XalanJ-I, XalanJ-C, or XalanC *
*/ public static final String OPT_PROCESSOR = "processor"; /** * Parameter: Is there any parameters for XSL to use? *Default: none *
*/ public static final String OPT_PARAM = "param"; /** Parameter: What processor is being used? */ protected String processor = null; /** * Parameter: Is trace mode on? *Default: null (no trace) * if on, non-null *
*/ public static final String OPT_TRACE = "trace"; /** Parameter: Are we tracing? */ protected String traceMode = null; /** * Parameter: Name of test (Conf, Accept) as StylesheetTestletDriver * can be used for more than one bucket *Default: null (use StylesheetTestletDriver) *
*/ public static final String OPT_TESTNAME = "testName"; /** Unique runId for each specific invocation of this test driver. */ protected String runId = null; /** Convenience constant: .xml extension for input data file. */ public static final String XML_EXTENSION = ".xml"; /** Convenience constant: .param extension for input data file. */ public static final String PARAM_EXTENSION = ".param"; /** 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"; /** Convenience constant: .log extension for log file. */ public static final String LOG_EXTENSION = ".log"; /** 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, testlet); dirFilter = testProps.getProperty(OPT_DIRFILTER, dirFilter); fileFilter = testProps.getProperty(OPT_FILEFILTER, fileFilter); fileList = testProps.getProperty(OPT_FILELIST, fileList); flavor = testProps.getProperty(OPT_FLAVOR, flavor); embedded = testProps.getProperty(OPT_EMBEDDED, embedded); processor = testProps.getProperty(OPT_PROCESSOR, processor); testName = testProps.getProperty(OPT_TESTNAME, testName); traceMode = testProps.getProperty(OPT_TRACE, traceMode); // Grab a unique runid for logging out with our tests // Used in results reporting stylesheets to differentiate // between different test runs runId = QetestUtils.createRunId(testProps.getProperty("runId")); testProps.put("runId", runId); // put back in the properties // for later use return true; } /** * Run through the directory given to us and run tests found * in subdirs; or run through our fileList. * * This method logs some basic runtime data (like the actual * testlet and ProcessorWrapper implementations used) and * then decides to either run a user-specified fileList or to * use our dirFilter to iterate over the inputDir. * * @param p Properties block of options to use - unused * @return true if OK, false if we should abort */ public boolean runTestCases(Properties p) { // First log out any other runtime information, like the // actual flavor of ProcessorWrapper, etc. try { // Note that each of these calls actually force the // creation of an actual object of each type: this is // required since we may default the types or our call // to QetestUtils.testClassForName() may return a // different classname than the user actually specified // Care should be taken that the construction of objects // here does not affect our testing later on // Just grab all the info from the TransformWrapper... Properties runtimeProps = TransformWrapperFactory.newWrapper(flavor).getProcessorInfo(); // ... and add a few extra things ourselves 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: " + fileList; // provide default value // Use static worker class to process the list Vector datalets = StylesheetDataletManager.readFileList(reporter, fileList, desc, testProps); // 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: *If any optionalDir cannot be created, it's array entry * will be null.
* * @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; } /** Default FilenameFilter FQCN for directories. */ protected String defaultDirFilter = "org.apache.qetest.xsl.ConformanceDirRules"; /** Default FilenameFilter FQCN for files. */ protected String defaultFileFilter = "org.apache.qetest.xsl.ConformanceFileRules"; /** Default GoldFilenameFilter FQCN for files. */ protected String defaultGoldFileFilter = "org.apache.qetest.xsl.GoldFileRules"; /** Default Testlet FQCN for executing stylesheet tests. */ protected String defaultTestlet = "org.apache.qetest.xsl.StylesheetTestlet"; /** Cached Testlet Class; used for life of this test. */ protected Class cachedTestletClazz = null; /** * 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 if we haven't already if (null == cachedTestletClazz) { cachedTestletClazz = QetestUtils.testClassForName(testlet, QetestUtils.defaultPackages, defaultTestlet); } try { // Create it and set our reporter into it Testlet t = (Testlet)cachedTestletClazz.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 FilenameFilter class to use Class clazz = QetestUtils.testClassForName(dirFilter, QetestUtils.defaultPackages, defaultDirFilter); try { // Create it, optionally with a category String category = testProps.getProperty(OPT_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 FilenameFilter class to use Class clazz = QetestUtils.testClassForName(fileFilter, QetestUtils.defaultPackages, defaultFileFilter); try { // Create it, optionally with excludes String excludes = testProps.getProperty(OPT_EXCLUDES); 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 filter for files. * Uses excludes member variable if set. * * @return FilenameFilter using ConformanceFileRules(excludes). */ public FilenameFilter getGoldFileFilter() { // Find a FilenameFilter class to use Class clazz = QetestUtils.testClassForName(fileFilter, QetestUtils.defaultPackages, defaultGoldFileFilter); try { // Create it, optionally with excludes String excludes = testProps.getProperty(OPT_EXCLUDES); 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 ("Additional options supported by StylesheetTestletDriver:\n" + " -" + OPT_FILELIST + "