• 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  * FileBasedTest.java
25  *
26  */
27 package org.apache.qetest;
28 
29 import org.apache.test.android.AndroidFileUtils;
30 
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.util.Enumeration;
34 import java.util.Properties;
35 
36 //-------------------------------------------------------------------------
37 
38 /**
39  * Base class for file-based tests.
40  * Many tests will need to operate on files external to a product
41  * under test.  This class provides useful, generic functionality
42  * in these cases.
43  * <p>FileBasedTest defines a number of common fields that many
44  * tests that operate on data files may use.</p>
45  * <ul>These are each pre-initialized for you from the command line or property file.
46  * <li>inputDir (string representing dir where input files come from)</li>
47  * <li>outputDir (string representing dir where output, working, temp files go)</li>
48  * <li>goldDir  (string representing dir where known good reference files are)</li>
49  * <li>debug (generic boolean flag for debugging)</li>
50  * <li>(stored in testProps) loggers (FQCN;of;Loggers to add to our Reporter)</li>
51  * <li>(stored in testProps) loggingLevel (passed to Reporters)</li>
52  * <li>(stored in testProps) logFile (string filename for any file-based Reporter)</li>
53  * <li>fileChecker</li>
54  * <li>(stored in testProps) excludes</li>
55  * <li>(stored in testProps) category</li>
56  * </ul>
57  * @author Shane_Curcuru@lotus.com
58  * @version 3.0
59  */
60 public class FileBasedTest extends TestImpl
61 {
62 
63     /**
64      * Convenience method to print out usage information.
65      * @author Shane Curcuru
66      * <p>Should be overridden by subclasses, although they are free
67      * to call super.usage() to get the common options string.</p>
68      *
69      * @return String denoting usage of this class
70      */
usage()71     public String usage()
72     {
73 
74         return ("Common options supported by FileBasedTest:\n" + "    -"
75                 + OPT_LOAD
76                 + " <file.props>  (read in a .properties file,\n"
77                 + "                         that can set any/all of the other opts)\n"
78                 + "    -" + OPT_INPUTDIR
79                 + "    <path to input files>\n"
80                 + "    -" + OPT_OUTPUTDIR
81                 + "   <path to output area - where all output is sent>\n"
82                 + "    -" + OPT_GOLDDIR
83                 + "     <path to gold reference output>\n"
84                 + "    -" + OPT_CATEGORY
85                 + "    <names;of;categories of tests to run>\n"
86                 + "    -" + OPT_EXCLUDES
87                 + "    <list;of;specific file.ext tests to skip>\n"
88                 + "    -" + OPT_FILECHECKER
89                 + " <FQCN of a non-standard FileCheckService>\n"
90                 + "    -" + Reporter.OPT_LOGGERS
91                 + "     <FQCN;of;Loggers to use>\n"
92                 + "    -" + Logger.OPT_LOGFILE
93                 + "     <resultsFileName> (sends test results to XML file)\n"
94                 + "    -" + Reporter.OPT_LOGGINGLEVEL
95                 + " <int> (level of msgs to log out; 0=few, 99=lots)\n"
96                 + "    -" + Reporter.OPT_DEBUG
97                 + " (prints extra debugging info)\n");
98     }
99 
100     //-----------------------------------------------------
101     //-------- Constants for common input params --------
102     //-----------------------------------------------------
103 
104     /**
105      * Parameter: Load properties file for options
106      * <p>Will load named file as a Properties block, setting any
107      * applicable options. Command line takes precedence.
108      * Format: <code>-load FileName.prop</code></p>
109      */
110     public static final String OPT_LOAD = "load";
111 
112     /**
113      * Parameter: Where are test input files?
114      * <p>Default: .\inputs.
115      * Format: <code>-inputDir path\to\dir</code></p>
116      */
117     public static final String OPT_INPUTDIR = "inputDir";
118 
119     /** Field inputDir:holds String denoting local path for inputs.  */
120     protected String inputDir = "." + File.separator + "inputs";
121 
122     /**
123      * Parameter: Where should we place output files (or temp files, etc.)?
124      * <p>Default: .\outputs.
125      * Format: <code>-outputDir path\to\dir</code></p>
126      */
127     public static final String OPT_OUTPUTDIR = "outputDir";
128 
129     /** Field outputDir:holds String denoting local path for outputs.  */
130     // Android-changed: The original directory isn't writeable on Android.
131     // protected String outputDir = "." + File.separator + "outputs";
132     protected String outputDir = AndroidFileUtils.getOutputFile("." + File.separator + "outputs")
133             .getPath();
134 
135     /**
136      * Parameter: Where should get "gold" pre-validated XML files?
137      * <p>Default: .\golds.
138      * Format: <code>-goldDir path\to\dir</code></p>
139      */
140     public static final String OPT_GOLDDIR = "goldDir";
141 
142     /** Field goldDir:holds String denoting local path for golds.  */
143     protected String goldDir = "." + File.separator + "golds";
144 
145     /**
146      * Parameter: Only run a single subcategory of the tests.
147      * <p>Default: blank, runs all tests - supply the directory name
148      * of a subcategory to run just that set.  Set into testProps
149      * and used from there.</p>
150      */
151     public static final String OPT_CATEGORY = "category";
152 
153     /**
154      * Parameter: Should we exclude any specific test files?
155      * <p>Default: null (no excludes; otherwise specify
156      * semicolon delimited list of bare filenames something like
157      * 'axes01.xsl;bool99.xsl').  Set into testProps and used
158      * from there</p>
159      */
160     public static final String OPT_EXCLUDES = "excludes";
161 
162     /**
163      * Parameter: Which CheckService should we use for XML output Files?
164      * <p>Default: org.apache.qetest.XHTFileCheckService.</p>
165      */
166     public static final String OPT_FILECHECKER = "fileChecker";
167 
168     /**
169      * Parameter-Default value: org.apache.qetest.XHTFileCheckService.
170      */
171     public static final String OPT_FILECHECKER_DEFAULT = "org.apache.qetest.xsl.XHTFileCheckService";
172 
173     /** FileChecker instance for use by subclasses; created in preTestFileInit()  */
174     protected CheckService fileChecker = null;
175 
176     /**
177      * Parameter: if Reporters should log performance data, true/false.
178      */
179     protected boolean perfLogging = false;
180 
181     /**
182      * Parameter: general purpose debugging flag.
183      */
184     protected boolean debug = false;
185 
186     //-----------------------------------------------------
187     //-------- Class members and accessors --------
188     //-----------------------------------------------------
189 
190     /**
191      * Total Number of test case methods defined in this test.
192      * <p>Tests must either set this variable or override runTestCases().</p>
193      * <p>Unless you override runTestCases(), test cases must be named like so:.</p>
194      * <p>Tests must either set this variable or override runTestCases().</p>
195      * <p>&nbsp;&nbsp;testCase<I>N</I>, where <I>N</I> is a consecutively
196      * numbered whole integer (1, 2, 3,....</p>
197      * @see #runTestCases
198      */
199     public int numTestCases = 0;
200 
201     /**
202      * Generic Properties block for storing initialization info.
203      * All startup options get stored in here for later use, both by
204      * the test itself and by any Reporters we use.
205      */
206     protected Properties testProps = new Properties();
207 
208     /**
209      * Accessor method for our Properties block, for use by harnesses.
210      *
211      * @param p if (p != null) testProps = (Properties) p.clone();
212      */
setProperties(Properties p)213     public void setProperties(Properties p)
214     {
215 
216         // Don't allow setting to null!
217         if (p != null)
218         {
219             testProps = (Properties) p.clone();
220         }
221     }
222 
223     /**
224      * Accessor method for our Properties block, for use by harnesses.
225      *
226      * @return our Properties block itself
227      */
getProperties()228     public Properties getProperties()
229     {
230         return testProps;
231     }
232 
233     /**
234      * Default constructor - initialize testName, Comment.
235      */
FileBasedTest()236     public FileBasedTest()
237     {
238 
239         // Only set them if they're not set
240         if (testName == null)
241             testName = "FileBasedTest.defaultName";
242 
243         if (testComment == null)
244             testComment = "FileBasedTest.defaultComment";
245     }
246 
247     //-----------------------------------------------------
248     //-------- Implement Test/TestImpl methods --------
249     //-----------------------------------------------------
250 
251     /**
252      * Initialize this test - called once before running testcases.
253      * <p>Use the loggers field to create some loggers in a Reporter.</p>
254      * @author Shane_Curcuru@lotus.com
255      * @see TestImpl#testFileInit(java.util.Properties)
256      *
257      * @param p Properties to initialize from
258      *
259      * @return false if we should abort; true otherwise
260      */
preTestFileInit(Properties p)261     public boolean preTestFileInit(Properties p)
262     {
263 
264         // Pass our properties block directly to the reporter
265         //  so it can use the same values in initialization
266         // A Reporter will auto-initialize from the values
267         //  in the properties block
268         setReporter(QetestFactory.newReporter(p));
269         reporter.testFileInit(testName, testComment);
270 
271         // Create a file-based CheckService for later use
272         if (null == fileChecker)
273         {
274             String tmpName = testProps.getProperty(OPT_FILECHECKER);
275             if ((null != tmpName) && (tmpName.length() > 0))
276             {
277                 // Use the user's specified class; if not available
278                 //  will return null which gets covered below
279                 fileChecker = QetestFactory.newCheckService(reporter, tmpName);
280             }
281 
282             if (null == fileChecker)
283             {
284                 // If that didn't work, then ask for default one that does files
285                 fileChecker = QetestFactory.newCheckService(reporter, QetestFactory.TYPE_FILES);
286             }
287             // If we're creating a new one, also applyAttributes
288             // (Assume that if we already had one, it already had this done)
289             fileChecker.applyAttributes(p);
290         }
291 
292         return true;
293     }
294 
295     /**
296      * Initialize this test - called once before running testcases.
297      * <p>Subclasses <b>must</b> override this to do whatever specific
298      * processing they need to initialize their product under test.</p>
299      * <p>If for any reason the test should not continue, it <b>must</b>
300      * return false from this method.</p>
301      * @author Shane_Curcuru@lotus.com
302      * @see TestImpl#testFileInit(java.util.Properties)
303      *
304      * @param p Properties to initialize from
305      *
306      * @return false if we should abort; true otherwise
307      */
doTestFileInit(Properties p)308     public boolean doTestFileInit(Properties p)
309     {
310         /* no-op; feel free to override */
311         return true;
312     }
313 
314     /**
315      * Override mostly blank routine to dump environment info.
316      * <p>Log out information about our environment in a structured
317      * way: mainly by calling logTestProps() here.</p>
318      *
319      * @param p Properties to initialize from
320      *
321      * @return false if we should abort; true otherwise
322      */
postTestFileInit(Properties p)323     public boolean postTestFileInit(Properties p)
324     {
325         logTestProps();
326         return true;
327     }
328 
329     /**
330      * Run all of our testcases.
331      * <p>use nifty FileBasedTestReporter.executeTests().  May be overridden
332      * by subclasses to do their own processing.  If you do not override,
333      * you must set numTestCases properly!</p>
334      * @author Shane Curcuru
335      *
336      * @param p Properties to initialize from
337      *
338      * @return false if we should abort; true otherwise
339      */
runTestCases(Properties p)340     public boolean runTestCases(Properties p)
341     {
342 
343         // Properties may be currently unused
344         reporter.executeTests(this, numTestCases, p);
345 
346         return true;
347     }
348 
349     /**
350      * Cleanup this test - called once after running testcases.
351      * @author Shane Curcuru
352      * <p>Tests should override if they need to do any cleanup.</p>
353      *
354      * @param p Properties to initialize from
355      *
356      * @return false if we should abort; true otherwise
357      */
doTestFileClose(Properties p)358     public boolean doTestFileClose(Properties p)
359     {
360         /* no-op; feel free to override */
361         return true;
362     }
363 
364     // Use default implementations of preTestFileClose()
365 
366     /**
367      * Mark the test complete - called once after running testcases.
368      * <p>Currently logs a summary of our test status and then tells
369      * our reporter to log the testFileClose. This will calculate
370      * final results, and complete logging for any structured
371      * output logs (like XML files).</p>
372      *<p>We also call reporter.writeResultsStatus(true) to
373      * write out a pass/fail marker file.  (This last part is
374      * actually optional, but it's useful and quick, so I'll
375      * do it by default for now.)</p>
376      *
377      * @param p Unused; passed through to super
378      *
379      * @return true if OK, false otherwise
380      */
postTestFileClose(Properties p)381     protected boolean postTestFileClose(Properties p)
382     {
383         // Log out a special summary status, with marker file
384         reporter.writeResultsStatus(true);
385 
386         // Ask our superclass to handle this as well
387         return super.postTestFileClose(p);
388     }
389 
390     //-----------------------------------------------------
391     //-------- Initialize our common input params --------
392     //-----------------------------------------------------
393 
394     /**
395      * Set our instance variables from a Properties file.
396      * <p>Must <b>not</b> use reporter.</p>
397      * @author Shane Curcuru
398      * @param Properties block to set name=value pairs from
399      *
400      * NEEDSDOC @param props
401      * @return status - true if OK, false if error.
402      * @todo improve error checking, if needed
403      */
initializeFromProperties(Properties props)404     public boolean initializeFromProperties(Properties props)
405     {
406         // Copy over all properties into our local block
407         //  this is a little unusual, but it does allow users
408         //  to set any new sort of properties via the properties
409         //  file, and we'll pick it up - that way this class doesn't
410         //  have to get updated when we have new properties
411         // Note that this may result in duplicates since we
412         //  re-set many of the things from bleow
413         for (Enumeration names = props.propertyNames();
414                 names.hasMoreElements(); /* no increment portion */ )
415         {
416             Object key = names.nextElement();
417 
418             testProps.put(key, props.get(key));
419         }
420 
421 
422         // Parse out any values that match our internal convenience variables
423         // default all values to our current values
424         // String values are simply getProperty()'d
425         inputDir = props.getProperty(OPT_INPUTDIR, inputDir);
426         if (inputDir != null)
427             testProps.put(OPT_INPUTDIR, inputDir);
428 
429         outputDir = props.getProperty(OPT_OUTPUTDIR, outputDir);
430         if (outputDir != null)
431             testProps.put(OPT_OUTPUTDIR, outputDir);
432 
433         goldDir = props.getProperty(OPT_GOLDDIR, goldDir);
434         if (goldDir != null)
435             testProps.put(OPT_GOLDDIR, goldDir);
436 
437         // The actual fileChecker object is created in preTestFileInit()
438 
439         // Use a temp string for those properties we only set
440         //  in our testProps, but don't bother to save ourselves
441         String temp = null;
442 
443         temp = props.getProperty(OPT_FILECHECKER);
444         if (temp != null)
445             testProps.put(OPT_FILECHECKER, temp);
446 
447         temp = props.getProperty(OPT_CATEGORY);
448         if (temp != null)
449             testProps.put(OPT_CATEGORY, temp);
450 
451         temp = props.getProperty(OPT_EXCLUDES);
452         if (temp != null)
453             testProps.put(OPT_EXCLUDES, temp);
454 
455         temp = props.getProperty(Reporter.OPT_LOGGERS);
456         if (temp != null)
457             testProps.put(Reporter.OPT_LOGGERS, temp);
458 
459         temp = props.getProperty(Logger.OPT_LOGFILE);
460         if (temp != null)
461             testProps.put(Logger.OPT_LOGFILE, temp);
462 
463         // boolean values just check for the non-default value
464         String dbg = props.getProperty(Reporter.OPT_DEBUG);
465 
466         if ((dbg != null) && dbg.equalsIgnoreCase("true"))
467         {
468             debug = true;
469 
470             testProps.put(Reporter.OPT_DEBUG, "true");
471         }
472 
473         String pLog = props.getProperty(Reporter.OPT_PERFLOGGING);
474 
475         if ((pLog != null) && pLog.equalsIgnoreCase("true"))
476         {
477             perfLogging = true;
478 
479             testProps.put(Reporter.OPT_PERFLOGGING, "true");
480         }
481 
482         temp = props.getProperty(Reporter.OPT_LOGGINGLEVEL);
483 
484         if (temp != null)
485             testProps.put(Reporter.OPT_LOGGINGLEVEL, temp);
486 
487         return true;
488     }
489 
490     /**
491      * Sets the provided fields with data from an array, presumably
492      * from the command line.
493      * <p>May be overridden by subclasses, although you should probably
494      * read the code to see what default options this handles. Must
495      * not use reporter. Calls initializeFromProperties(). After that,
496      * sets any internal variables that match items in the array like:
497      * <code> -param1 value1 -paramNoValue -param2 value2 </code>
498      * Any params that do not match internal variables are simply set
499      * into our properties block for later use.  This allows subclasses
500      * to simply get their initialization data from the testProps
501      * without having to make code changes here.</p>
502      * <p>Assumes all params begin with "-" dash, and that all values
503      * do <b>not</b> start with a dash.</p>
504      * @author Shane Curcuru
505      * @param String[] array of arguments
506      *
507      * @param args array of command line arguments
508      * @param flag: are we being called from a subclass?
509      * @return status - true if OK, false if error.
510      */
initializeFromArray(String[] args, boolean flag)511     public boolean initializeFromArray(String[] args, boolean flag)
512     {
513 
514         // Read in command line args and setup internal variables
515         String optPrefix = "-";
516         int nArgs = args.length;
517 
518         // We don't require any arguments: but subclasses might
519         //  want to require certain ones
520         // Must read in properties file first, so cmdline can
521         //  override values from properties file
522         boolean propsOK = true;
523 
524         // IF we are being called the first time on this
525         //  array of arguments, go ahead and process unknown ones
526         //  otherwise, don't bother
527         if (flag)
528         {
529             for (int k = 0; k < nArgs; k++)
530             {
531                 if (args[k].equalsIgnoreCase(optPrefix + OPT_LOAD))
532                 {
533                     if (++k >= nArgs)
534                     {
535                         System.err.println(
536                             "ERROR: must supply properties filename for: "
537                             + optPrefix + OPT_LOAD);
538 
539                         return false;
540                     }
541 
542                     String loadPropsName = args[k];
543 
544                     try
545                     {
546 
547                         // Load named file into our properties block
548                         FileInputStream fIS = new FileInputStream(loadPropsName);
549                         Properties p = new Properties();
550 
551                         p.load(fIS);
552                         p.put(OPT_LOAD, loadPropsName); // Pass along with properties
553 
554                         propsOK &= initializeFromProperties(p);
555                     }
556                     catch (Exception e)
557                     {
558                         System.err.println(
559                             "ERROR: loading properties file failed: " + loadPropsName);
560                         e.printStackTrace();
561 
562                         return false;
563                     }
564 
565                     break;
566                 }
567             }  // end of for(...)
568         }  // end of if ((flag))
569 
570         // Now read in the rest of the command line
571         // @todo cleanup loop to be more table-driven
572         for (int i = 0; i < nArgs; i++)
573         {
574 
575             // Set any String args and place them in testProps
576             if (args[i].equalsIgnoreCase(optPrefix + OPT_INPUTDIR))
577             {
578                 if (++i >= nArgs)
579                 {
580                     System.err.println("ERROR: must supply arg for: "
581                                        + optPrefix + OPT_INPUTDIR);
582 
583                     return false;
584                 }
585 
586                 inputDir = args[i];
587 
588                 testProps.put(OPT_INPUTDIR, inputDir);
589 
590                 continue;
591             }
592 
593             if (args[i].equalsIgnoreCase(optPrefix + OPT_OUTPUTDIR))
594             {
595                 if (++i >= nArgs)
596                 {
597                     System.err.println("ERROR: must supply arg for: "
598                                        + optPrefix + OPT_OUTPUTDIR);
599 
600                     return false;
601                 }
602 
603                 outputDir = args[i];
604 
605                 testProps.put(OPT_OUTPUTDIR, outputDir);
606 
607                 continue;
608             }
609 
610             if (args[i].equalsIgnoreCase(optPrefix + OPT_GOLDDIR))
611             {
612                 if (++i >= nArgs)
613                 {
614                     System.err.println("ERROR: must supply arg for: "
615                                        + optPrefix + OPT_GOLDDIR);
616 
617                     return false;
618                 }
619 
620                 goldDir = args[i];
621 
622                 testProps.put(OPT_GOLDDIR, goldDir);
623 
624                 continue;
625             }
626 
627             if (args[i].equalsIgnoreCase(optPrefix + OPT_CATEGORY))
628             {
629                 if (++i >= nArgs)
630                 {
631                     System.err.println("ERROR: must supply arg for: "
632                                        + optPrefix + OPT_CATEGORY);
633 
634                     return false;
635                 }
636 
637                 testProps.put(OPT_CATEGORY, args[i]);
638 
639                 continue;
640             }
641 
642             if (args[i].equalsIgnoreCase(optPrefix + OPT_EXCLUDES))
643             {
644                 if (++i >= nArgs)
645                 {
646                     System.err.println("ERROR: must supply arg for: "
647                                        + optPrefix + OPT_EXCLUDES);
648 
649                     return false;
650                 }
651 
652                 testProps.put(OPT_EXCLUDES, args[i]);
653 
654                 continue;
655             }
656 
657             if (args[i].equalsIgnoreCase(optPrefix + Reporter.OPT_LOGGERS))
658             {
659                 if (++i >= nArgs)
660                 {
661                     System.err.println("ERROR: must supply arg for: "
662                                        + optPrefix + Reporter.OPT_LOGGERS);
663 
664                     return false;
665                 }
666 
667                 testProps.put(Reporter.OPT_LOGGERS, args[i]);
668 
669                 continue;
670             }
671 
672             if (args[i].equalsIgnoreCase(optPrefix + Logger.OPT_LOGFILE))
673             {
674                 if (++i >= nArgs)
675                 {
676                     System.err.println("ERROR: must supply arg for: "
677                                        + optPrefix + Logger.OPT_LOGFILE);
678 
679                     return false;
680                 }
681 
682                 testProps.put(Logger.OPT_LOGFILE, args[i]);
683 
684                 continue;
685             }
686 
687             if (args[i].equalsIgnoreCase(optPrefix + OPT_FILECHECKER))
688             {
689                 if (++i >= nArgs)
690                 {
691                     System.out.println("ERROR: must supply arg for: "
692                                        + optPrefix + OPT_FILECHECKER);
693 
694                     return false;
695                 }
696 
697                 testProps.put(OPT_FILECHECKER, args[i]);
698 
699                 continue;
700             }
701 
702             // Boolean values are simple flags to switch from defaults only
703             if (args[i].equalsIgnoreCase(optPrefix + Reporter.OPT_DEBUG))
704             {
705                 debug = true;
706 
707                 testProps.put(Reporter.OPT_DEBUG, "true");
708 
709                 continue;
710             }
711 
712             if (args[i].equalsIgnoreCase(optPrefix
713                                          + Reporter.OPT_PERFLOGGING))
714             {
715                 testProps.put(Reporter.OPT_PERFLOGGING, "true");
716 
717                 continue;
718             }
719 
720             // Parse out the integer value
721             //  This isn't strictly necessary since the catch-all
722             //  below should take care of it, but better safe than sorry
723             if (args[i].equalsIgnoreCase(optPrefix
724                                          + Reporter.OPT_LOGGINGLEVEL))
725             {
726                 if (++i >= nArgs)
727                 {
728                     System.err.println("ERROR: must supply arg for: "
729                                        + optPrefix
730                                        + Reporter.OPT_LOGGINGLEVEL);
731 
732                     return false;
733                 }
734 
735                 try
736                 {
737                     testProps.put(Reporter.OPT_LOGGINGLEVEL, args[i]);
738                 }
739                 catch (NumberFormatException numEx)
740                 { /* no-op */
741                 }
742 
743                 continue;
744             }
745 
746             // IF we are being called the first time on this
747             //  array of arguments, go ahead and process unknown ones
748             //  otherwise, don't bother
749             if (flag)
750             {
751 
752                 // Found an arg that we don't know how to process,
753                 //  so store it for any subclass' use as a catch-all
754                 // If it starts with - dash, and another non-dash arg follows,
755                 //  set as a name=value pair in the property block
756                 if ((args[i].startsWith(optPrefix)) && (i + 1 < nArgs)
757                         && (!args[i + 1].startsWith(optPrefix)))
758                 {
759 
760                     // Scrub off the "-" prefix before setting the name
761                     testProps.put(args[i].substring(1), args[i + 1]);
762 
763                     i++;  // Increment counter to skip next arg
764                 }
765 
766                 // Otherwise, just set as name="" in the property block
767                 else
768                 {
769 
770                     // Scrub off the "-" prefix before setting the name
771                     testProps.put(args[i].substring(1), "");
772                 }
773             }
774         }  // end of for() loop
775 
776         // If we got here, we set the array params OK, so simply return
777         //  the value the initializeFromProperties method returned
778         return propsOK;
779     }
780 
781     //-----------------------------------------------------
782     //-------- Other useful and utility methods --------
783     //-----------------------------------------------------
784 
785     /**
786      * Log out any System or common version info.
787      * <p>Logs System.getProperties(), and our the testProps block, etc..</p>
788      */
logTestProps()789     public void logTestProps()
790     {
791         reporter.logHashtable(reporter.CRITICALMSG, System.getProperties(),
792                               "System.getProperties");
793         reporter.logHashtable(reporter.CRITICALMSG, testProps, "testProps");
794         reporter.logHashtable(reporter.CRITICALMSG, QetestUtils.getEnvironmentHash(), "getEnvironmentHash");
795     }
796 
797 
798     /**
799      * Main worker method to run test from the command line.
800      * Test subclasses generally need not override.
801      * <p>This is primarily provided to make subclasses implementations
802      * of the main method as simple as possible: in general, they
803      * should simply do:
804      * <code>
805      *   public static void main (String[] args)
806      *   {
807      *       TestSubClass app = new TestSubClass();
808      *       app.doMain(args);
809      *   }
810      * </code>
811      *
812      * @param args command line arguments
813      */
doMain(String[] args)814     public void doMain(String[] args)
815     {
816         // Initialize any instance variables from the command line
817         //  OR specified properties block
818         if (!initializeFromArray(args, true))
819         {
820             System.err.println("ERROR in usage:");
821             System.err.println(usage());
822 
823             // Don't use System.exit, since that will blow away any containing harnesses
824             return;
825         }
826 
827         // Also pass along the command line, in case someone has
828         //  specific code that's counting on this
829         StringBuffer buf = new StringBuffer();
830         for (int i = 0; i < args.length; i++)
831         {
832             buf.append(args[i]);
833             buf.append(" ");
834         }
835         testProps.put(MAIN_CMDLINE, buf.toString());
836 
837         // Actually go and execute the test
838         runTest(testProps);
839     }
840 
841     /**
842      * Main method to run test from the command line.
843      * @author Shane Curcuru
844      * <p>Test subclasses <b>must</b> override, obviously.
845      * Only provided here for debugging.</p>
846      *
847      * @param args command line arguments
848      */
main(String[] args)849     public static void main(String[] args)
850     {
851         FileBasedTest app = new FileBasedTest();
852         app.doMain(args);
853     }
854 }  // end of class FileBasedTest
855 
856