• 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 /*
23  *
24  * BugzillaTestletDriver.java
25  *
26  */
27 package org.apache.qetest.xsl;
28 
29 import java.io.File;
30 import java.io.FilenameFilter;
31 import java.io.IOException;
32 import java.lang.reflect.Constructor;
33 import java.util.Enumeration;
34 import java.util.Properties;
35 import java.util.Vector;
36 
37 import org.apache.qetest.Datalet;
38 import org.apache.qetest.Logger;
39 import org.apache.qetest.QetestUtils;
40 import org.apache.qetest.Testlet;
41 
42 //-------------------------------------------------------------------------
43 
44 /**
45  * Test driver for Bugzilla tests with .java/.xsl files..
46  *
47  * This driver does not iterate over a directory tree; only
48  * over a single directory.  It supports either 'classic' tests
49  * with matching .xsl/.xml/.out files like the conformance test,
50  * or tests that also include a .java file that is the specific
51  * testlet to execute for that test.
52  *
53  *
54  *
55  * @author shane_curcuru@lotus.com
56  * @version $Id$
57  */
58 public class BugzillaTestletDriver extends StylesheetTestletDriver
59 {
60 
61     /** Convenience constant: .java extension for Java Testlet source.  */
62     public static final String JAVA_EXTENSION = ".java";
63 
64     /** Convenience constant: Property key for java filenames.  */
65     public static final String JAVA_SOURCE_NAME = "java.source.name";
66 
67     /** Convenience constant: Default .xml file to use.  */
68     public static final String DEFAULT_XML_FILE = "identity.xml";
69 
70     /**
71      * Default FilenameFilter FQCN for files - overridden.
72      * By default, use a custom FilenameFilter that picks up
73      * both .java and .xsl files, with slightly different
74      * naming conventions than normal.
75      */
76     protected String defaultFileFilter = "org.apache.qetest.xsl.BugzillaFileRules";
77 
78 
79     /** Just initialize test name, comment; numTestCases is not used. */
BugzillaTestletDriver()80     public BugzillaTestletDriver()
81     {
82         testName = "BugzillaTestletDriver";
83         testComment = "Test driver for Bugzilla tests with .java/.xsl files.";
84     }
85 
86 
87     /**
88      * Special: test all Bugzilla* files in just the bugzilla directory.
89      * This does not iterate down directories.
90      * This is a specific test driver for testlets that may have
91      * matching foo*.java and foo*.xml/xsl/out
92      * Parameters: none, uses our internal members inputDir,
93      * outputDir, testlet, etc.
94      */
processInputDir()95     public void processInputDir()
96     {
97         // Ensure the inputDir is there - we must have a valid location for input files
98         File testDirectory = new File(inputDir);
99 
100         if (!testDirectory.exists())
101         {
102             // Try a default inputDir
103             String oldInputDir = inputDir; // cache for potential error message
104             testDirectory = new File((inputDir = getDefaultInputDir()));
105             if (!testDirectory.exists())
106             {
107                 // No inputDir, can't do any tests!
108                 // @todo check if this is the best way to express this
109                 reporter.checkErr("inputDir(" + oldInputDir
110                                   + ", or " + inputDir + ") does not exist, aborting!");
111                 return;
112             }
113         }
114 
115         // Validate that each of the specified dirs exists
116         // Returns directory references like so:
117         //  testDirectory = 0, outDirectory = 1, goldDirectory = 2
118         File[] dirs = validateDirs(new File[] { testDirectory },
119                                    new File[] { new File(outputDir), new File(goldDir) });
120 
121         if (null == dirs)  // this should never happen...
122         {
123             // No inputDir, can't do any tests!
124             // @todo check if this is the best way to express this
125             reporter.checkErr("inputDir(" + dirs[0] + ") does not exist, aborting!");
126             return;
127         }
128 
129         // Call worker method to process the individual directory
130         //  and get a list of .java or .xsl files to test
131         Vector files = getFilesFromDir(dirs[0], getFileFilter(), embedded);
132 
133         // 'Transform' the list of individual test files into a
134         //  list of Datalets with all fields filled in
135         //@todo should getFilesFromDir and buildDatalets be combined?
136         Vector datalets = buildDatalets(files, dirs[0], dirs[1], dirs[2]);
137 
138         if ((null == datalets) || (0 == datalets.size()))
139         {
140             // No tests, log error and return
141             //  other directories to test
142             reporter.checkErr("inputDir(" + dirs[0] + ") did not contain any tests, aborting!");
143             return;
144         }
145 
146         // Now process the list of files found in this dir
147         processFileList(datalets, "Bugzilla tests of: " + dirs[0]);
148     }
149 
150 
151     /**
152      * Run a list of bugzilla-specific tests.
153      * Bugzilla tests may either be encoded as a .java file that
154      * defines a Testlet, or as a normal .xsl/.xml file pair that
155      * should simply be transformed simply, by a StylesheetTestlet.
156      *
157      * @param vector of Datalet objects to pass in
158      * @param desc String to use as testCase description
159      */
processFileList(Vector datalets, String desc)160     public void processFileList(Vector datalets, String desc)
161     {
162         // Validate arguments
163         if ((null == datalets) || (0 == datalets.size()))
164         {
165             // Bad arguments, report it as an error
166             // Note: normally, this should never happen, since
167             //  this class normally validates these arguments
168             //  before calling us
169             reporter.checkErr("Testlet or datalets are null/blank, nothing to test!");
170             return;
171         }
172 
173         // Now just go through the list and process each set
174         int numDatalets = datalets.size();
175         reporter.logInfoMsg("processFileList() with " + numDatalets
176                             + " potential Bugzillas");
177         // Iterate over every datalet and test it
178         for (int ctr = 0; ctr < numDatalets; ctr++)
179         {
180             try
181             {
182                 // Depending on the Datalet class, run a different algorithim
183                 Datalet d = (Datalet)datalets.elementAt(ctr);
184                 if (d instanceof TraxDatalet)
185                 {
186                     // Assume we the datalet holds the name of a
187                     //  .java file that's a testlet, and just
188                     //  execute that itself
189                     // Note: Since they're packageless and have
190                     //  hardcoded paths to the current dir, must
191                     //  change user.dir each time in worker method
192                     Testlet t = getTestlet((TraxDatalet)d);
193                     // Each Bugzilla is it's own testcase
194                     reporter.testCaseInit(t.getDescription());
195                     executeTestletInDir(t, d, inputDir);
196                 }
197                 else if (d instanceof StylesheetDatalet)
198                 {
199                     // Create plain Testlet to execute a test with this
200                     //  next datalet - the Testlet will log all info
201                     //  about the test, including calling check*()
202                     // Each Bugzilla is it's own testcase
203                     reporter.testCaseInit(d.getDescription());
204                     getTestlet().execute(d);
205                 }
206                 else
207                 {
208                     reporter.checkErr("Unknown Datalet type: " + d);
209                 }
210             }
211             catch (Throwable t)
212             {
213                 // Log any exceptions as fails and keep going
214                 //@todo improve the below to output more useful info
215                 reporter.checkFail("Datalet num " + ctr + " threw: " + t.toString());
216                 reporter.logThrowable(Logger.ERRORMSG, t, "Datalet threw");
217             }
218             reporter.testCaseClose();
219         }  // of while...
220     }
221 
222 
223     /**
224      * Transform a vector of individual test names into a Vector
225      * of filled-in datalets to be tested - Bugzilla-specific.
226      *
227      * This does special processing since we may either have .java
228      * files that should be compiled, or we may have plain .xsl/.xml
229      * file pairs that we should simpy execute through a default
230      * StylesheetTestlet as-is.
231      * This basically just calculates local path\filenames across
232      * the three presumably-parallel directory trees of testLocation
233      * (inputDir), outLocation (outputDir) and goldLocation
234      * (forced to be same as inputDir).  It then stuffs each of
235      * these values plus some generic info like our testProps
236      * into each datalet it creates.
237      *
238      * @param files Vector of local path\filenames to be tested
239      * @param testLocation File denoting directory where all
240      * .xml/.xsl tests are found
241      * @param outLocation File denoting directory where all
242      * output files should be put
243      * @param goldLocation File denoting directory where all
244      * gold files are found - IGNORED; forces testLocation instead
245      * @return Vector of StylesheetDatalets that are fully filled in,
246      * i.e. outputName, goldName, etc are filled in respectively
247      * to inputName
248      */
buildDatalets(Vector files, File testLocation, File outLocation, File goldLocation)249     public Vector buildDatalets(Vector files, File testLocation,
250                                 File outLocation, File goldLocation)
251     {
252         // Validate arguments
253         if ((null == files) || (files.size() < 1))
254         {
255             // Bad arguments, report it as an error
256             // Note: normally, this should never happen, since
257             //  this class normally validates these arguments
258             //  before calling us
259             reporter.logWarningMsg("buildDatalets null or empty file vector");
260             return null;
261         }
262         Vector v = new Vector(files.size());
263         int xslCtr = 0;
264         int javaCtr = 0;
265 
266         // For every file in the vector, construct the matching
267         //  out, gold, and xml/xsl files; plus see if we have
268         //  a .java file as well
269         for (Enumeration elements = files.elements();
270                 elements.hasMoreElements(); /* no increment portion */ )
271         {
272             String file = null;
273             try
274             {
275                 file = (String)elements.nextElement();
276             }
277             catch (ClassCastException cce)
278             {
279                 // Just skip this entry
280                 reporter.logWarningMsg("Bad file element found, skipping: " + cce.toString());
281                 continue;
282             }
283 
284             Datalet d = null;
285             // If it's a .java file: just set java.source.name/java.class.name
286             if (file.endsWith(JAVA_EXTENSION))
287             {
288                 // Use TraxDatalets if we have .java
289                 d = new TraxDatalet();
290                 ((TraxDatalet)d).options = new Properties(testProps);
291                 ((TraxDatalet)d).options.put("java.source.dir", testLocation);
292                 ((TraxDatalet)d).options.put(JAVA_SOURCE_NAME, file);
293                 ((TraxDatalet)d).options.put("fileCheckerImpl", fileChecker);
294                 // That's it - when we execute tests later on, if
295                 //  there's a JAVA_SOURCE_NAME we simply use that to
296                 //  find the testlet to execute
297                 javaCtr++;
298             }
299             // If it's a .xsl file, just set the filenames as usual
300             else if (file.endsWith(XSL_EXTENSION))
301             {
302                 // Use plain StylesheetDatalets if we just have .xsl
303                 d = new StylesheetDatalet();
304                 ((StylesheetDatalet)d).inputName = testLocation.getPath() + File.separator + file;
305 
306                 String fileNameRoot = file.substring(0, file.indexOf(XSL_EXTENSION));
307                 // Check for existence of xml - if not there, then set to some default
308                 //@todo this would be a perfect use of TraxDatalet.setXMLString()
309                 String xmlFileName = testLocation.getPath() + File.separator + fileNameRoot + XML_EXTENSION;
310                 if ((new File(xmlFileName)).exists())
311                 {
312                     ((StylesheetDatalet)d).xmlName = xmlFileName;
313                 }
314                 else
315                 {
316                     ((StylesheetDatalet)d).xmlName = testLocation.getPath() + File.separator + DEFAULT_XML_FILE;
317                 }
318                 ((StylesheetDatalet)d).outputName = outLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
319                 ((StylesheetDatalet)d).goldName = testLocation.getPath() + File.separator + fileNameRoot + OUT_EXTENSION;
320                 ((StylesheetDatalet)d).flavor = flavor;
321                 ((StylesheetDatalet)d).options = new Properties(testProps);
322                 ((StylesheetDatalet)d).options.put("fileCheckerImpl", fileChecker);
323                 // These tests will be run by a plain StylesheetTestlet
324                 xslCtr++;
325             }
326             else
327             {
328                 // Hmmm - I'm not sure what we should do here
329                 reporter.logWarningMsg("Unexpected test file found, skipping: " + file);
330                 continue;
331             }
332             d.setDescription(file);
333             v.addElement(d);
334         }
335         reporter.logTraceMsg("Bugzilla buildDatalets with " + javaCtr
336                 + " .java Testlets, and " + xslCtr + " .xsl files to test");
337         return v;
338     }
339 
340 
341     /**
342      * Execute a Testlet with a specific user.dir.
343      * Bugzilla testlets hardcode their input file names, assuming
344      * they're in the current directory.  But this automation is
345      * frequently run in another directory, and uses the inputDir
346      * setting to point where the files are.  Hence this worker
347      * method to change user.dir, execute the Testlet, and then
348      * switch back.
349      * Note: will not work in Applet context, obviously.
350      *
351      * @param t Testlet to execute
352      * @param dir to change user.dir to first
353      * @throws propagates any non-user.dir exceptions
354      */
executeTestletInDir(Testlet t, Datalet d, String dir)355     public void executeTestletInDir(Testlet t, Datalet d, String dir)
356         throws Exception
357     {
358         final String USER_DIR = "user.dir";
359         try
360         {
361             // Note: we must actually keep a cloned copy of the
362             //  whole system properties block to replace later
363             //  in case a Bugzilla testlet changes any other
364             //  properties during it's execution
365             Properties p = System.getProperties();
366             Properties cacheProps = (Properties)p.clone();
367             // This should, I hope, properly get the correct path
368             //  for what the inputDir would be, whether it's a
369             //  relative or absolute path from where we are now
370             File f = new File(inputDir);
371             try
372             {
373                 // Note the canonical form seems to be the most reliable for our purpose
374                 p.put(USER_DIR, f.getCanonicalPath());
375             }
376             catch (IOException ioe)
377             {
378                 p.put(USER_DIR, f.getAbsolutePath());
379             }
380             System.setProperties(p);
381 
382             // Now just execute the Testlet from here
383             t.execute(d);
384 
385             // Replace the system properties to be polite!
386             System.setProperties(cacheProps);
387         }
388         catch (SecurityException se)
389         {
390             reporter.logThrowable(Logger.ERRORMSG, se, "executeTestletInDir threw");
391             reporter.checkErr("executeTestletInDir threw :" + se
392                     + " cannot execute Testlet in correct dir " + dir);
393         }
394     }
395 
396 
397     /**
398      * Convenience method to get a Bugzilla Testlet to use.
399      * Take the TraxDatalet given and find the java classname
400      * from it.  Then just load an instance of that Testlet class.
401      *
402      * @return Testlet for use in this test; null if error
403      */
getTestlet(TraxDatalet d)404     public Testlet getTestlet(TraxDatalet d)
405     {
406         try
407         {
408             // Calculate the java classname
409             String testletSourceName = (String)d.options.get(JAVA_SOURCE_NAME);
410             // Potential problem: what if the SourceName doesn't have .java at end?
411             String testletClassName = testletSourceName.substring(0, testletSourceName.indexOf(JAVA_EXTENSION));
412             //@todo should we attempt to compile to a .class file
413             //  if we can't find the class here?  This adds a bunch
414             //  of complexity here; so I'm thinking it's better to
415             //  simply require the user to 'build all' first
416             Class testletClazz = Class.forName(testletClassName);
417             // Create it and set our reporter into it
418             Testlet t = (Testlet)testletClazz.newInstance();
419             t.setLogger((Logger)reporter);
420             return (Testlet)t;
421         }
422         catch (Exception e)
423         {
424             // Ooops, none found, log an error
425             reporter.logThrowable(Logger.ERRORMSG, e, "getTestlet(d) threw");
426             reporter.checkErr("getTestlet(d) threw: " + e.toString());
427             return null;
428         }
429     }
430 
431 
432     /**
433      * Convenience method to get a default filter for files.
434      * Returns special file filter for our use.
435      *
436      * @return FilenameFilter using BugzillaFileRules(excludes).
437      */
getFileFilter()438     public FilenameFilter getFileFilter()
439     {
440         // Find a Testlet class to use
441         Class clazz = QetestUtils.testClassForName("org.apache.qetest.xsl.BugzillaFileRules",
442                                                    QetestUtils.defaultPackages,
443                                                    defaultFileFilter);
444         try
445         {
446             // Create it, optionally with a category
447             String excludes = testProps.getProperty(OPT_EXCLUDES);
448             if ((null != excludes) && (excludes.length() > 1))  // Arbitrary check for non-null, non-blank string
449             {
450                 Class[] parameterTypes = { java.lang.String.class };
451                 Constructor ctor = clazz.getConstructor(parameterTypes);
452 
453                 Object[] ctorArgs = { excludes };
454                 return (FilenameFilter) ctor.newInstance(ctorArgs);
455             }
456             else
457             {
458                 return (FilenameFilter)clazz.newInstance();
459             }
460         }
461         catch (Exception e)
462         {
463             // Ooops, none found!
464             return null;
465         }
466     }
467 
468 
469     /**
470      * Convenience method to get a default inputDir when none or
471      * a bad one was given.
472      * @return String pathname of default inputDir "tests\bugzilla".
473      */
getDefaultInputDir()474     public String getDefaultInputDir()
475     {
476         return "tests" + File.separator + "bugzilla";
477     }
478 
479 
480     /**
481      * Convenience method to print out usage information - update if needed.
482       * @return String denoting usage of this test class
483      */
usage()484     public String usage()
485     {
486         return ("Additional options supported by BugzillaTestletDriver:\n"
487                 + "    (Note: assumes inputDir=test/tests/bugzilla)"
488                 + "    (Note: we do *not* support -embedded)"
489                 + super.usage());   // Grab our parent classes usage as well
490     }
491 
492 
493     /**
494      * Main method to run test from the command line - can be left alone.
495      * @param args command line argument array
496      */
main(String[] args)497     public static void main(String[] args)
498     {
499         BugzillaTestletDriver app = new BugzillaTestletDriver();
500         app.doMain(args);
501     }
502 }
503