• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id$
20  */
21 
22 package org.apache.qetest.xsl;
23 
24 import java.io.File;
25 import java.io.FilenameFilter;
26 import java.lang.reflect.Constructor;
27 import java.util.Enumeration;
28 import java.util.Hashtable;
29 import java.util.Properties;
30 import java.util.StringTokenizer;
31 import java.util.Vector;
32 
33 import org.apache.qetest.Datalet;
34 import org.apache.qetest.FileBasedTest;
35 import org.apache.qetest.Logger;
36 import org.apache.qetest.QetestUtils;
37 import org.apache.qetest.Testlet;
38 import org.apache.qetest.xslwrapper.TransformWrapper;
39 import org.apache.qetest.xslwrapper.TransformWrapperFactory;
40 
41 /**
42  * Test driver for XSLT stylesheet Testlets.
43  *
44  * This is a generic driver for XSLT-oriented Testlets, and
45  * supports iterating over either a user-supplied, specific list
46  * of files to test or over a directory tree of test files.
47  * Note there are a number of design decisions made that are
48  * just slightly specific to stylesheet testing, although this
49  * would be a good model for a completely generic TestletDriver.
50  *
51  * @author shane_curcuru@lotus.com
52  * @version $Id$
53  */
54 public class StylesheetTestletDriver extends FileBasedTest
55 {
56 
57     //-----------------------------------------------------
58     //-------- Constants for common input params --------
59     //-----------------------------------------------------
60 
61     /**
62      * Parameter: Run a specific list of files, instead of
63      * iterating over directories.
64      * <p>Default: null, do normal iteration.</p>
65      */
66     public static final String OPT_FILELIST = "fileList";
67 
68     /** Name of fileList file to read in to get test definitions from.   */
69     protected String fileList = null;
70 
71     /**
72      * Parameter: FQCN or simple classname of Testlet to use.
73      * <p>User may pass in either a FQCN or just a base classname,
74      * and we will attempt to look it up in any of the most common
75      * Xalan-testing packages.  See QetestUtils.testClassForName().</p>
76      * <p>Default: null, use StylesheetTestlet.</p>
77      */
78     public static final String OPT_TESTLET = "testlet";
79 
80     /** Classname of Testlet to use.   */
81     protected String testlet = null;
82 
83     /**
84      * Parameter: FQCN or simple classname of FilenameFilter for
85      * directories under testDir we will process.
86      * If fileList is not set, we simply go to our inputDir, and
87      * then use this filter to iterate through directories returned.
88      * <p>Default: null, use ConformanceDirRules.</p>
89      */
90     public static final String OPT_DIRFILTER = "dirFilter";
91 
92     /** Classname of FilenameFilter to use for dirs.  */
93     protected String dirFilter = null;
94 
95     /**
96      * Parameter: FQCN or simple classname of FilenameFilter for
97      * files within subdirs we will process.
98      * If fileList is not set, we simply go through all directories
99      * specified by directoryFilter, and then use this filter to
100      * find all stylesheet test files in that directory to test.
101      * Note that this does <b>not</b> handle embedded tests, where
102      * the XML document has an xml-stylesheet PI that defines the
103      * stylesheet to use to process it.
104      * <p>Default: null, use ConformanceFileRules.</p>
105      */
106     public static final String OPT_FILEFILTER = "fileFilter";
107 
108     /** Classname of FilenameFilter to use for files.  */
109     protected String fileFilter = null;
110 
111 
112     /**
113      * Parameter: What flavor of TransformWrapper to use: trax.sax|trax.stream|other?
114      * <p>Default: trax.</p>
115      */
116     public static final String OPT_FLAVOR = "flavor";
117 
118     /** Parameter: What flavor of TransformWrapper to use: trax.sax|trax.stream|other?  */
119     protected String flavor = "trax";
120 
121 
122     /**
123      * Parameter: Are there any embedded stylesheets in XML files?
124      * <p>Default: null (no embedded tests; otherwise specify
125      * semicolon delimited list of bare filenames something like
126      * 'axes02.xml;bool98.xml').</p>
127      */
128     public static final String OPT_EMBEDDED = "embedded";
129 
130     /** Parameter: Are there any embedded stylesheets in XML files?  */
131     protected String embedded = null;
132 
133     /**
134      * Parameter: Is there any preference to which gold file to use?
135      * <p>Default: null (use standard .out file)
136      * name of processor, could be XalanJ-I, XalanJ-C, or XalanC
137      * </p>
138      */
139     public static final String OPT_PROCESSOR = "processor";
140 
141     /**
142      * Parameter: Is there any parameters for XSL to use?
143      * <p>Default: none
144      * </p>
145      */
146     public static final String OPT_PARAM = "param";
147 
148     /** Parameter: What processor is being used?  */
149     protected String processor = null;
150 
151     /**
152      * Parameter: Is trace mode on?
153      * <p>Default: null (no trace)
154      * if on, non-null
155      * </p>
156      */
157     public static final String OPT_TRACE = "trace";
158 
159     /** Parameter: Are we tracing? */
160     protected String traceMode = null;
161 
162     /**
163      * Parameter: Name of test (Conf, Accept) as StylesheetTestletDriver
164      * can be used for more than one bucket
165      * <p>Default: null (use StylesheetTestletDriver)
166      * </p>
167      */
168     public static final String OPT_TESTNAME = "testName";
169 
170 
171     /** Unique runId for each specific invocation of this test driver.  */
172     protected String runId = null;
173 
174     /** Convenience constant: .xml extension for input data file.  */
175     public static final String XML_EXTENSION = ".xml";
176 
177     /** Convenience constant: .param extension for input data file.  */
178     public static final String PARAM_EXTENSION = ".param";
179 
180     /** Convenience constant: .xsl extension for stylesheet file.  */
181     public static final String XSL_EXTENSION = ".xsl";
182 
183     /** Convenience constant: .out extension for output result file.  */
184     public static final String OUT_EXTENSION = ".out";
185 
186     /** Convenience constant: .log extension for log file.  */
187     public static final String LOG_EXTENSION = ".log";
188 
189 
190     /** Just initialize test name, comment; numTestCases is not used. */
StylesheetTestletDriver()191     public StylesheetTestletDriver()
192     {
193         testName = "StylesheetTestletDriver";
194         testComment = "Test driver for XSLT stylesheet Testlets";
195     }
196 
197 
198     /**
199      * Initialize this test - fill in parameters.
200      * Simply fills in convenience variables from user parameters.
201      *
202      * @param p unused
203      * @return true
204      */
doTestFileInit(Properties p)205     public boolean doTestFileInit(Properties p)
206     {
207         // Copy any of our parameters from testProps to
208         //  our local convenience variables
209         testlet = testProps.getProperty(OPT_TESTLET, testlet);
210         dirFilter = testProps.getProperty(OPT_DIRFILTER, dirFilter);
211         fileFilter = testProps.getProperty(OPT_FILEFILTER, fileFilter);
212         fileList = testProps.getProperty(OPT_FILELIST, fileList);
213         flavor = testProps.getProperty(OPT_FLAVOR, flavor);
214         embedded = testProps.getProperty(OPT_EMBEDDED, embedded);
215         processor = testProps.getProperty(OPT_PROCESSOR, processor);
216         testName = testProps.getProperty(OPT_TESTNAME, testName);
217         traceMode = testProps.getProperty(OPT_TRACE, traceMode);
218         // Grab a unique runid for logging out with our tests
219         //  Used in results reporting stylesheets to differentiate
220         //  between different test runs
221         runId = QetestUtils.createRunId(testProps.getProperty("runId"));
222         testProps.put("runId", runId);  // put back in the properties
223                                         // for later use
224         return true;
225     }
226 
227 
228     /**
229      * Run through the directory given to us and run tests found
230      * in subdirs; or run through our fileList.
231      *
232      * This method logs some basic runtime data (like the actual
233      * testlet and ProcessorWrapper implementations used) and
234      * then decides to either run a user-specified fileList or to
235      * use our dirFilter to iterate over the inputDir.
236      *
237      * @param p Properties block of options to use - unused
238      * @return true if OK, false if we should abort
239      */
runTestCases(Properties p)240     public boolean runTestCases(Properties p)
241     {
242         // First log out any other runtime information, like the
243         //  actual flavor of ProcessorWrapper, etc.
244         try
245         {
246             // Note that each of these calls actually force the
247             //  creation of an actual object of each type: this is
248             //  required since we may default the types or our call
249             //  to QetestUtils.testClassForName() may return a
250             //  different classname than the user actually specified
251             // Care should be taken that the construction of objects
252             //  here does not affect our testing later on
253             // Just grab all the info from the TransformWrapper...
254             Properties runtimeProps = TransformWrapperFactory.newWrapper(flavor).getProcessorInfo();
255             // ... and add a few extra things ourselves
256             runtimeProps.put("actual.testlet", getTestlet());
257             runtimeProps.put("actual.dirFilter", getDirFilter());
258             runtimeProps.put("actual.fileFilter", getFileFilter());
259             reporter.logHashtable(Logger.CRITICALMSG, runtimeProps,
260                                   "actual.runtime information");
261         }
262         catch (Exception e)
263         {
264             reporter.logWarningMsg("Logging actual.runtime threw: " + e.toString());
265             reporter.logThrowable(Logger.WARNINGMSG, e, "Logging actual.runtime threw");
266         }
267 
268         // Now either run a list of specific tests the user specified,
269         //  or do the default of iterating over a set of directories
270         if (null != fileList)
271         {
272             // Process the specific list of tests the user supplied
273             String desc = "User-supplied fileList: " + fileList; // provide default value
274             // Use static worker class to process the list
275             Vector datalets = StylesheetDataletManager.readFileList(reporter, fileList, desc, testProps);
276 
277             // Actually process the specified files in a testCase
278             processFileList(datalets, desc);
279         }
280         else
281         {
282             // Do the default, which is to iterate over the inputDir
283             // Note that this calls the testCaseInit/testCaseClose
284             //  logging methods itself
285             processInputDir();
286         }
287         return true;
288     }
289 
290 
291     /**
292      * Do the default: test all stylesheets found in subdirs
293      * of our inputDir, using FilenameFilters for dirs and files.
294      * This only goes down one level in the tree, eg:
295      * <ul>inputDir = tests/conf
296      * <li>tests/conf - not tested</li>
297      * <li>tests/conf/boolean - test all boolean*.xsl files</li>
298      * <li>tests/conf/copy - test all copy*.xsl files</li>
299      * <li>tests/conf/copy/foo - not tested</li>
300      * <li>tests/conf/xmanual - not tested, since default
301      * ConformanceDirRules excludes dirs starting with 'x|X'</li>
302      * <li>tests/whitespace - test all whitespace*.xsl files</li>
303      * <li>etc.</li>
304      * </ul>
305      * Parameters: none, uses our internal members inputDir,
306      * outputDir, testlet, etc.
307      */
processInputDir()308     public void processInputDir()
309     {
310         // Ensure the inputDir is there - we must have a valid location for input files
311         File testDirectory = new File(inputDir);
312 
313         if (!testDirectory.exists())
314         {
315             // Try a default inputDir
316             String oldInputDir = inputDir; // cache for potential error message
317             testDirectory = new File((inputDir = getDefaultInputDir()));
318             if (!testDirectory.exists())
319             {
320                 // No inputDir, can't do any tests!
321                 // @todo check if this is the best way to express this
322                 reporter.checkErr("inputDir(" + oldInputDir
323                                   + ", or " + inputDir + ") does not exist, aborting!");
324                 return;
325             }
326         }
327 
328         reporter.logInfoMsg("inputDir(" + testDirectory.getPath()
329                             + ") looking for subdirs with: " + dirFilter);
330 
331         // Use our filter to get a list of directories to process
332         String subdirs[] = testDirectory.list(getDirFilter());
333 
334         // Validate that we have some valid directories to process
335         if ((null == subdirs) || (subdirs.length <= 0))
336         {
337             reporter.checkErr("inputDir(" + testDirectory.getPath()
338                                + ") no valid subdirs found!");
339             return;
340         }
341 
342         int numSubdirs = subdirs.length;
343 
344         // For every subdirectory, check if we should run tests in it
345         for (int i = 0; i < numSubdirs; i++)
346         {
347             File subTestDir = new File(testDirectory, subdirs[i]);
348 
349             if ((null == subTestDir) || (!subTestDir.exists()))
350             {
351                 // Just log it and continue; presumably we'll find
352                 //  other directories to test
353                 reporter.logWarningMsg("subTestDir(" + subTestDir.getPath()
354                                        + ") does not exist, skipping!");
355                 continue;
356             }
357 
358             // Construct matching directories for outputs and golds
359             File subOutDir = new File(outputDir, subdirs[i]);
360             File subGoldDir = new File(goldDir, subdirs[i]);
361 
362             // Validate that each of the specified dirs exists
363             // Returns directory references like so:
364             //  testDirectory = 0, outDirectory = 1, goldDirectory = 2
365             File[] dirs = validateDirs(new File[] { subTestDir },
366                                        new File[] { subOutDir, subGoldDir });
367 
368             if (null == dirs)  // also ensures that dirs[0] is non-null
369             {
370                 // Just log it and continue; presumably we'll find
371                 //  other directories to test
372                 reporter.logWarningMsg("subTestDir(" + subTestDir.getPath()
373                                        + ") or associated dirs does not exist, skipping!");
374                 continue;
375             }
376 
377             // Call worker method to process the individual directory
378             //  and get a list of .xsl files to test
379             Vector files = getFilesFromDir(subTestDir, getFileFilter(), embedded);
380             Hashtable goldFiles = getGoldsFromDir(subGoldDir, getGoldFileFilter(), processor);
381 
382             // 'Transform' the list of individual test files into a
383             //  list of Datalets with all fields filled in
384             //@todo should getFilesFromDir and buildDatalets be combined?
385             Vector datalets = buildDatalets(files, subTestDir, subOutDir, subGoldDir, goldFiles);
386 
387             if ((null == datalets) || (0 == datalets.size()))
388             {
389                 // Just log it and continue; presumably we'll find
390                 //  other directories to test
391                 reporter.logWarningMsg("subTestDir(" + subTestDir.getPath()
392                                        + ") did not contain any tests, skipping!");
393                 continue;
394             }
395 
396             // Now process the list of files found in this dir
397             processFileList(datalets, "Conformance test of: " + subdirs[i]);
398         } // end of for...
399     }
400 
401 
402     /**
403      * Run a list of stylesheet tests through a Testlet.
404      * The file names are assumed to be fully specified, and we assume
405      * the corresponding directories exist.
406      * Each fileList is turned into a testcase.
407      *
408      * @param vector of Datalet objects to pass in
409      * @param desc String to use as testCase description
410      */
processFileList(Vector datalets, String desc)411     public void processFileList(Vector datalets, String desc)
412     {
413         // Validate arguments
414         if ((null == datalets) || (0 == datalets.size()))
415         {
416             // Bad arguments, report it as an error
417             // Note: normally, this should never happen, since
418             //  this class normally validates these arguments
419             //  before calling us
420             reporter.checkErr("Testlet or datalets are null/blank, nothing to test!");
421             return;
422         }
423 
424         // Put everything else into a testCase
425         //  This is not necessary, but feels a lot nicer to
426         //  break up large test sets
427         reporter.testCaseInit(desc);
428 
429         // Now just go through the list and process each set
430         int numDatalets = datalets.size();
431         reporter.logInfoMsg("processFileList() with " + numDatalets
432                             + " potential tests");
433         // Iterate over every datalet and test it
434         for (int ctr = 0; ctr < numDatalets; ctr++)
435         {
436             try
437             {
438                 // Create a Testlet to execute a test with this
439                 //  next datalet - the Testlet will log all info
440                 //  about the test, including calling check*()
441                 getTestlet().execute((Datalet)datalets.elementAt(ctr));
442             }
443             catch (Throwable t)
444             {
445                 // Log any exceptions as fails and keep going
446                 //@todo improve the below to output more useful info
447                 reporter.checkFail("Datalet num " + ctr + " threw: " + t.toString());
448                 reporter.logThrowable(Logger.ERRORMSG, t, "Datalet threw");
449             }
450         }  // of while...
451         reporter.testCaseClose();
452     }
453 
454 
455     /**
456      * Use the supplied filter on given directory to return a list
457      * of stylesheet tests to be run.
458      * Uses the normal filter for variations of *.xsl files, and
459      * also constructs names for any -embedded tests found (which
460      * may be .xml with xml-stylesheet PI's, not just .xsl)
461      *
462      * @param dir directory to scan
463      * @param filter to use on this directory; if null, uses default
464      * @param embeddedFiles special list of embedded files to find
465      * @return Vector of local path\filenames of tests to run;
466      * the tests themselves will exist; null if error
467      */
getFilesFromDir(File dir, FilenameFilter filter, String embeddedFiles)468     public Vector getFilesFromDir(File dir, FilenameFilter filter, String embeddedFiles)
469     {
470         // Validate arguments
471         if ((null == dir) || (!dir.exists()))
472         {
473             // Bad arguments, report it as an error
474             // Note: normally, this should never happen, since
475             //  this class normally validates these arguments
476             //  before calling us
477             reporter.logWarningMsg("getFilesFromDir(" + dir.toString() + ") dir null or does not exist");
478             return null;
479         }
480         // Get the list of 'normal' test files
481         String[] files = dir.list(filter);
482         Vector v = new Vector(files.length);
483         for (int i = 0; i < files.length; i++)
484         {
485             v.addElement(files[i]);
486         }
487         reporter.logTraceMsg("getFilesFromDir(" + dir.toString() + ") found " + v.size() + " xsl files to test");
488 
489         // Also get a list of any embedded test files here
490         //  Optimization: only look for embedded files when likely to find them
491         if ((null != embeddedFiles) && (embeddedFiles.indexOf(dir.getName()) > -1))
492         {
493             // OK, presumably we have an embedded file in the current dir,
494             //  add that name
495             StringTokenizer st = new StringTokenizer(embeddedFiles, ";");//@todo resource ;
496             while (st.hasMoreTokens())
497             {
498                 String embeddedName = st.nextToken();
499                 // Check if it's in our dir...
500                 if (embeddedName.startsWith(dir.getName()))
501                 {
502                     // ...and that it exists
503                     if ((new File(dir.getPath() + File.separator + embeddedName)).exists())
504                     {
505                         v.addElement(embeddedName);
506                     }
507                     else
508                     {
509                         reporter.logWarningMsg("Requested embedded file " + dir.getPath() + File.separator + embeddedName
510                                                + " does not exist, skipping");
511                     }
512                 }
513             }
514         }
515         reporter.logTraceMsg("getFilesFromDir(" + dir.toString() + ") found " + v.size() + " total files to test");
516         return v;
517     }
518 
519     /**
520      * Use the supplied filter on given directory to return a list
521      * of stylesheet tests to be run.
522      * Uses the normal filter for variations of *.xsl files, and
523      * also constructs names for any -embedded tests found (which
524      * may be .xml with xml-stylesheet PI's, not just .xsl)
525      *
526      * @param dir directory to scan
527      * @param filter to use on this directory; if null, uses default
528      * @param embeddedFiles special list of embedded files to find
529      * @return Vector of local path\filenames of tests to run;
530      * the tests themselves will exist; null if error
531      */
getGoldsFromDir(File dir, FilenameFilter filter, String processor)532     public Hashtable getGoldsFromDir(File dir, FilenameFilter filter, String processor)
533     {
534         // Validate arguments
535         if ((null == dir) || (!dir.exists()))
536         {
537             // Bad arguments, report it as an error
538             // Note: normally, this should never happen, since
539             //  this class normally validates these arguments
540             //  before calling us
541             reporter.logWarningMsg("getGoldsFromDir(" + dir.toString() + ") dir null or does not exist");
542             return null;
543         }
544         // Get the list of 'normal' test files
545         String[] files = dir.list(filter);
546         Hashtable h = new Hashtable();
547         for (int i = 0; i < files.length; i++)
548         {
549         	// Check after the first .
550         	// if "out", assume to be 'default' file
551         	// if name of the processor, override default file.
552         	StringTokenizer strTok = new StringTokenizer(files[i],".");
553         	String testCase = strTok.nextToken();
554         	String procChk = strTok.nextToken();
555         	if (procChk.equals(processor) ||
556         	    (procChk.equals("out") && h.get(testCase) == null)) {
557         	  h.put(testCase,files[i]);
558       	    }
559         }
560         reporter.logTraceMsg("getGoldsFromDir(" + dir.toString() + ") found " + h.size() + " gold files to test");
561         return h;
562     }
563 
564     /**
565      * Transform a vector of individual test names into a Vector
566      * of filled-in datalets to be tested
567      *
568      * This basically just calculates local path\filenames across
569      * the three presumably-parallel directory trees of testLocation
570      * (inputDir), outLocation (outputDir) and goldLocation
571      * (goldDir).  It then stuffs each of these values plus some
572      * generic info like our testProps into each datalet it creates.
573      *
574      * @param files Vector of local path\filenames to be tested
575      * @param testLocation File denoting directory where all
576      * .xml/.xsl tests are found
577      * @param outLocation File denoting directory where all
578      * output files should be put
579      * @param goldLocation File denoting directory where all
580      * gold files are found
581      * @return Vector of StylesheetDatalets that are fully filled in,
582      * i.e. outputName, goldName, etc are filled in respectively
583      * to inputName
584      */
buildDatalets(Vector files, File testLocation, File outLocation, File goldLocation, Hashtable goldFiles)585     public Vector buildDatalets(Vector files, File testLocation,
586                                 File outLocation, File goldLocation,
587                                 Hashtable goldFiles)
588     {
589         // Validate arguments
590         if ((null == files) || (files.size() < 1))
591         {
592             // Bad arguments, report it as an error
593             // Note: normally, this should never happen, since
594             //  this class normally validates these arguments
595             //  before calling us
596             reporter.logWarningMsg("buildDatalets null or empty file vector");
597             return null;
598         }
599         Vector v = new Vector(files.size());
600 
601         // For every file in the vector, construct the matching
602         //  out, gold, and xml/xsl files
603         for (Enumeration elements = files.elements();
604                 elements.hasMoreElements(); /* no increment portion */ )
605         {
606             String file = null;
607             try
608             {
609                 file = (String)elements.nextElement();
610             }
611             catch (ClassCastException cce)
612             {
613                 // Just skip this entry
614                 reporter.logWarningMsg("Bad file element found, skipping: " + cce.toString());
615                 continue;
616             }
617             // Check if it's a normal .xsl file, or a .xml file
618             //  (we assume .xml files are embedded tests!)
619             StylesheetDatalet d = new StylesheetDatalet();
620             if (file.endsWith(XML_EXTENSION))
621             {
622                 d.xmlName = testLocation.getPath() + File.separator + file;
623 
624                 String fileNameRoot = file.substring(0, file.indexOf(XML_EXTENSION));
625                 d.inputName = null;
626 
627                 d.outputName = outLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
628                 if (goldFiles != null && goldFiles.get(fileNameRoot) != null) {
629                   d.goldName = goldLocation.getPath() + File.separator + goldFiles.get(fileNameRoot);
630                 } else {
631                   d.goldName = goldLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
632                 }
633             }
634             else if (file.endsWith(XSL_EXTENSION))
635             {
636                 d.inputName = testLocation.getPath() + File.separator + file;
637 
638                 String fileNameRoot = file.substring(0, file.indexOf(XSL_EXTENSION));
639                 d.paramName = testLocation.getPath() + File.separator + fileNameRoot + PARAM_EXTENSION;
640                 d.xmlName = testLocation.getPath() + File.separator + fileNameRoot + XML_EXTENSION;
641                 d.outputName = outLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
642                 d.goldName = goldLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
643                 if (goldFiles != null && goldFiles.get(fileNameRoot) != null) {
644                   d.goldName = goldLocation.getPath() + File.separator + goldFiles.get(fileNameRoot);
645                 } else {
646                   d.goldName = goldLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
647                 }
648 
649             }
650             else
651             {
652                 // Hmmm - I'm not sure what we should do here
653                 reporter.logWarningMsg("Unexpected test file found, skipping: " + file);
654                 continue;
655             }
656             d.setDescription(file);
657             d.flavor = flavor;
658             // Also copy over our own testProps as it's
659             //  options: this allows for future expansion
660             //  of values in the datalet
661             d.options = new Properties(testProps);
662             // Optimization: put in a copy of our fileChecker, so
663             //  that each testlet doesn't have to create it's own
664             //  fileCheckers should not store state, so this
665             //  shouldn't affect the testing at all
666             d.options.put("fileCheckerImpl", fileChecker);
667             if (traceMode != null) {
668                 d.options.put(TransformWrapper.SET_PROCESSOR_ATTRIBUTES  + "setTraceListener", d.outputName + LOG_EXTENSION);
669             }
670             v.addElement(d);
671         }
672         return v;
673     }
674 
675 
676     /**
677      * Validate existence of or create various directories needed.
678      * <p>If any optionalDir cannot be created, it's array entry
679      * will be null.</p>
680      *
681      * @param requiredDirs array of directories that must previously
682      * exist; if none do, will return null; if none passed, return null
683      * @param optionalDirs array of optional directories; if they do
684      * not exist they'll be created
685      * @return array of file objects, null if any error; all
686      * required dirs are first, in order; then all optionalDirs
687      */
validateDirs(File[] requiredDirs, File[] optionalDirs)688     public File[] validateDirs(File[] requiredDirs, File[] optionalDirs)
689     {
690         if ((null == requiredDirs) || (0 == requiredDirs.length))
691         {
692             return null;
693         }
694 
695         File[] dirs = new File[(requiredDirs.length + optionalDirs.length)];
696         int ctr = 0;
697 
698         try
699         {
700             // Validate requiredDirs exist first
701             for (int ir = 0; ir < requiredDirs.length; ir++)
702             {
703                 if (!requiredDirs[ir].exists())
704                 {
705                     reporter.logErrorMsg("validateDirs("
706                                          + requiredDirs[ir]
707                                          + ") requiredDir did not exist!");
708                     return null;
709                 }
710                 dirs[ctr] = requiredDirs[ir];
711                 ctr++;
712             }
713 
714             // Create any optionalDirs needed
715             for (int iopt = 0; iopt < optionalDirs.length; iopt++)
716             {
717                 if (!optionalDirs[iopt].exists())
718                 {
719                     if (!optionalDirs[iopt].mkdirs())
720                     {
721                         reporter.logWarningMsg("validateDirs("
722                                                + optionalDirs[iopt]
723                                                + ") optionalDir could not be created");
724                         dirs[ctr] = null;
725                     }
726                     else
727                     {
728                         reporter.logTraceMsg("validateDirs("
729                                              + optionalDirs[iopt]
730                                              + ") optionalDir was created");
731                         dirs[ctr] = optionalDirs[iopt];
732                     }
733                 }
734                 else
735                 {
736                     // It does previously exist, so copy it over
737                     dirs[ctr] = optionalDirs[iopt];
738                 }
739                 ctr++;
740             }
741         }
742         catch (Exception e)
743         {
744             reporter.logThrowable(Logger.ERRORMSG, e, "validateDirs threw: " + e.toString());
745             return null;
746         }
747 
748         return dirs;
749     }
750 
751 
752     /** Default FilenameFilter FQCN for directories.   */
753     protected String defaultDirFilter = "org.apache.qetest.xsl.ConformanceDirRules";
754 
755     /** Default FilenameFilter FQCN for files.   */
756     protected String defaultFileFilter = "org.apache.qetest.xsl.ConformanceFileRules";
757 
758     /** Default GoldFilenameFilter FQCN for files.   */
759     protected String defaultGoldFileFilter = "org.apache.qetest.xsl.GoldFileRules";
760 
761 
762     /** Default Testlet FQCN for executing stylesheet tests.   */
763     protected String defaultTestlet = "org.apache.qetest.xsl.StylesheetTestlet";
764 
765     /** Cached Testlet Class; used for life of this test.   */
766     protected Class cachedTestletClazz = null;
767 
768     /**
769      * Convenience method to get a Testlet to use.
770      * Attempts to return one as specified by our testlet parameter,
771      * otherwise returns a default StylesheetTestlet.
772      *
773      * @return Testlet for use in this test; null if error
774      */
getTestlet()775     public Testlet getTestlet()
776     {
777         // Find a Testlet class to use if we haven't already
778         if (null == cachedTestletClazz)
779         {
780             cachedTestletClazz = QetestUtils.testClassForName(testlet,
781                                                               QetestUtils.defaultPackages,
782                                                               defaultTestlet);
783         }
784         try
785         {
786             // Create it and set our reporter into it
787             Testlet t = (Testlet)cachedTestletClazz.newInstance();
788             t.setLogger((Logger)reporter);
789             return (Testlet)t;
790         }
791         catch (Exception e)
792         {
793             // Ooops, none found! This should be very rare, since
794             //  we know the defaultTestlet should be found
795             return null;
796         }
797     }
798 
799 
800     /**
801      * Convenience method to get a default filter for directories.
802      * Uses category member variable if set.
803      *
804      * @return FilenameFilter using ConformanceDirRules(category).
805      */
getDirFilter()806     public FilenameFilter getDirFilter()
807     {
808         // Find a FilenameFilter class to use
809         Class clazz = QetestUtils.testClassForName(dirFilter,
810                                                    QetestUtils.defaultPackages,
811                                                    defaultDirFilter);
812         try
813         {
814             // Create it, optionally with a category
815             String category = testProps.getProperty(OPT_CATEGORY);
816             if ((null != category) && (category.length() > 1))  // Arbitrary check for non-null, non-blank string
817             {
818                 Class[] parameterTypes = { java.lang.String.class };
819                 Constructor ctor = clazz.getConstructor(parameterTypes);
820 
821                 Object[] ctorArgs = { category };
822                 return (FilenameFilter)ctor.newInstance(ctorArgs);
823             }
824             else
825             {
826                 return (FilenameFilter)clazz.newInstance();
827             }
828         }
829         catch (Exception e)
830         {
831             // Ooops, none found!
832             return null;
833         }
834     }
835 
836 
837     /**
838      * Convenience method to get a default filter for files.
839      * Uses excludes member variable if set.
840      *
841      * @return FilenameFilter using ConformanceFileRules(excludes).
842      */
getFileFilter()843     public FilenameFilter getFileFilter()
844     {
845         // Find a FilenameFilter class to use
846         Class clazz = QetestUtils.testClassForName(fileFilter,
847                                                    QetestUtils.defaultPackages,
848                                                    defaultFileFilter);
849         try
850         {
851             // Create it, optionally with excludes
852             String excludes = testProps.getProperty(OPT_EXCLUDES);
853             if ((null != excludes) && (excludes.length() > 1))  // Arbitrary check for non-null, non-blank string
854             {
855                 Class[] parameterTypes = { java.lang.String.class };
856                 Constructor ctor = clazz.getConstructor(parameterTypes);
857 
858                 Object[] ctorArgs = { excludes };
859                 return (FilenameFilter) ctor.newInstance(ctorArgs);
860             }
861             else
862             {
863                 return (FilenameFilter)clazz.newInstance();
864             }
865         }
866         catch (Exception e)
867         {
868             // Ooops, none found!
869             return null;
870         }
871     }
872 
873     /**
874      * Convenience method to get a default filter for files.
875      * Uses excludes member variable if set.
876      *
877      * @return FilenameFilter using ConformanceFileRules(excludes).
878      */
getGoldFileFilter()879     public FilenameFilter getGoldFileFilter()
880     {
881         // Find a FilenameFilter class to use
882         Class clazz = QetestUtils.testClassForName(fileFilter,
883                                                    QetestUtils.defaultPackages,
884                                                    defaultGoldFileFilter);
885         try
886         {
887             // Create it, optionally with excludes
888             String excludes = testProps.getProperty(OPT_EXCLUDES);
889             if ((null != excludes) && (excludes.length() > 1))  // Arbitrary check for non-null, non-blank string
890             {
891                 Class[] parameterTypes = { java.lang.String.class };
892                 Constructor ctor = clazz.getConstructor(parameterTypes);
893 
894                 Object[] ctorArgs = { excludes };
895                 return (FilenameFilter) ctor.newInstance(ctorArgs);
896             }
897             else
898             {
899                 return (FilenameFilter)clazz.newInstance();
900             }
901         }
902         catch (Exception e)
903         {
904             // Ooops, none found!
905             return null;
906         }
907     }
908 
909 
910     /**
911      * Convenience method to get a default inputDir when none or
912      * a bad one was given.
913      * @return String pathname of default inputDir "tests\conf".
914      */
getDefaultInputDir()915     public String getDefaultInputDir()
916     {
917         return "tests" + File.separator + "conf";
918     }
919 
920 
921     /**
922      * Convenience method to print out usage information - update if needed.
923      * @return String denoting usage of this test class
924      */
usage()925     public String usage()
926     {
927         return ("Additional options supported by StylesheetTestletDriver:\n"
928                 + "    -" + OPT_FILELIST
929                 + "   <name of listfile of tests to run>\n"
930                 + "    -" + OPT_DIRFILTER
931                 + "  <classname of FilenameFilter for dirs>\n"
932                 + "    -" + OPT_FILEFILTER
933                 + " <classname of FilenameFilter for files>\n"
934                 + "    -" + OPT_TESTLET
935                 + "    <classname of Testlet to execute tests with>\n"
936                 + "    -" + OPT_EMBEDDED
937                 + "   <list;of;specific file.xml embedded tests to run>\n"
938                 + "    -" + OPT_FLAVOR
939                 + "     <trax.sax|trax.dom|etc> which TransformWrapper to use\n"
940                 + super.usage());   // Grab our parent classes usage as well
941     }
942 
943 
944     /**
945      * Main method to run test from the command line - can be left alone.
946      * @param args command line argument array
947      */
main(String[] args)948     public static void main(String[] args)
949     {
950         StylesheetTestletDriver app = new StylesheetTestletDriver();
951         app.doMain(args);
952     }
953 }
954