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