]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/test/TestFmwk.java
deddc9fe62ad0901c1ba740a7cd3abd280b3f8a3
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / test / TestFmwk.java
1 //##header\r
2 /*\r
3  *******************************************************************************\r
4  * Copyright (C) 1996-2009, International Business Machines Corporation and    *\r
5  * others. All Rights Reserved.                                                *\r
6  *******************************************************************************\r
7  */\r
8 package com.ibm.icu.dev.test;\r
9 \r
10 import com.ibm.icu.text.UTF16;\r
11 import com.ibm.icu.text.DecimalFormat;\r
12 import com.ibm.icu.text.NumberFormat;\r
13 import com.ibm.icu.util.TimeZone;\r
14 import com.ibm.icu.util.ULocale;\r
15 import java.io.ByteArrayOutputStream;\r
16 import java.io.IOException;\r
17 import java.io.OutputStream;\r
18 import java.io.PrintStream;\r
19 import java.io.PrintWriter;\r
20 import java.io.Writer;\r
21 import java.lang.reflect.Field;\r
22 import java.lang.reflect.InvocationTargetException;\r
23 import java.lang.reflect.Method;\r
24 import java.util.ArrayList;\r
25 import java.util.Arrays;\r
26 import java.util.Comparator;\r
27 import java.util.HashMap;\r
28 import java.util.Locale;\r
29 import java.util.MissingResourceException;\r
30 import java.util.Random;\r
31 //#if defined(FOUNDATION10) || defined(J2SE13)\r
32 //## import com.ibm.icu.impl.Utility;\r
33 //#endif\r
34 /**\r
35  * TestFmwk is a base class for tests that can be run conveniently from the\r
36  * command line as well as under the Java test harness.\r
37  * <p>\r
38  * Sub-classes implement a set of methods named Test <something>. Each of these\r
39  * methods performs some test. Test methods should indicate errors by calling\r
40  * either err or errln. This will increment the errorCount field and may\r
41  * optionally print a message to the log. Debugging information may also be\r
42  * added to the log via the log and logln methods. These methods will add their\r
43  * arguments to the log only if the test is being run in verbose mode.\r
44  */\r
45 public class TestFmwk extends AbstractTestLog {\r
46     /**\r
47      * The default time zone for all of our tests. Used in Target.run();\r
48      */\r
49     private final static TimeZone defaultTimeZone = TimeZone.getTimeZone("PST");\r
50 \r
51     /**\r
52      * The default locale used for all of our tests. Used in Target.run();\r
53      */\r
54     private final static Locale defaultLocale = Locale.US;\r
55 \r
56     public static final class TestFmwkException extends Exception {\r
57         /**\r
58          * For serialization\r
59          */\r
60         private static final long serialVersionUID = -3051148210247229194L;\r
61 \r
62         TestFmwkException(String msg) {\r
63             super(msg);\r
64         }\r
65     }\r
66     protected void handleException(Throwable e){\r
67 //#if defined(FOUNDATION10) || defined(J2SE13)\r
68 //##    Throwable ex = null;\r
69 //#else\r
70         Throwable ex = e.getCause();\r
71 //#endif\r
72         if(ex==null){\r
73             ex = e;\r
74         }\r
75         if(ex instanceof ExceptionInInitializerError){\r
76             ex = ((ExceptionInInitializerError)ex).getException();\r
77         }\r
78         String msg = ex.getMessage();\r
79         if(msg==null){\r
80             msg = "";\r
81         }\r
82         //System.err.println("TF handleException msg: " + msg);\r
83         if (ex instanceof MissingResourceException || ex instanceof NoClassDefFoundError || msg.indexOf("java.util.MissingResourceException")>=0) {\r
84             if (params.warnings || params.nodata) {\r
85                 warnln(msg);\r
86             } else if (params.nothrow) {\r
87                 errln(msg);\r
88                 ex.printStackTrace();\r
89             } else {\r
90                 ex.printStackTrace();\r
91                 throw new RuntimeException(msg);\r
92             }\r
93         } else {\r
94             if (params.nothrow) {\r
95                 errln(msg);\r
96                 ex.printStackTrace();\r
97             } else {\r
98                 errln(msg);\r
99                 ex.printStackTrace();\r
100                 throw new RuntimeException(msg);\r
101             }\r
102         }\r
103     }\r
104     // use this instead of new random so we get a consistent seed\r
105     // for our tests\r
106     protected Random createRandom() {\r
107         return new Random(params.seed);\r
108     }\r
109 \r
110     /**\r
111      * A test that has no test methods itself, but instead runs other tests.\r
112      * \r
113      * This overrides methods are getTargets and getSubtest from TestFmwk.\r
114      * \r
115      * If you want the default behavior, pass an array of class names and an\r
116      * optional description to the constructor. The named classes must extend\r
117      * TestFmwk. If a provided name doesn't include a ".", package name is\r
118      * prefixed to it (the package of the current test is used if none was\r
119      * provided in the constructor). The resulting full name is used to\r
120      * instantiate an instance of the class using the default constructor.\r
121      * \r
122      * Class names are resolved to classes when getTargets or getSubtest is\r
123      * called. This allows instances of TestGroup to be compiled and run without\r
124      * all the targets they would normally invoke being available.\r
125      */\r
126     public static abstract class TestGroup extends TestFmwk {\r
127         private String defaultPackage;\r
128         private String[] names;\r
129         private String description;\r
130 \r
131         private Class[] tests; // deferred init\r
132 \r
133         /**\r
134          * Constructor that takes a default package name and a list of class\r
135          * names. Adopts and modifies the classname list\r
136          */\r
137         protected TestGroup(String defaultPackage, String[] classnames,\r
138                 String description) {\r
139             if (classnames == null) {\r
140                 throw new IllegalStateException("classnames must not be null");\r
141             }\r
142 \r
143             if (defaultPackage == null) {\r
144                 defaultPackage = getClass().getPackage().getName();\r
145             }\r
146             defaultPackage = defaultPackage + ".";\r
147 \r
148             this.defaultPackage = defaultPackage;\r
149             this.names = classnames;\r
150             this.description = description;\r
151         }\r
152 \r
153         /**\r
154          * Constructor that takes a list of class names and a description, and\r
155          * uses the package for this class as the default package.\r
156          */\r
157         protected TestGroup(String[] classnames, String description) {\r
158             this(null, classnames, description);\r
159         }\r
160 \r
161         /**\r
162          * Constructor that takes a list of class names, and uses the package\r
163          * for this class as the default package.\r
164          */\r
165         protected TestGroup(String[] classnames) {\r
166             this(null, classnames, null);\r
167         }\r
168 \r
169         protected String getDescription() {\r
170             return description;\r
171         }\r
172 \r
173         protected Target getTargets(String targetName) {\r
174             Target target = null;\r
175             if (targetName != null) {\r
176                 finishInit(); // hmmm, want to get subtest without initializing\r
177                               // all tests\r
178 \r
179                 try {\r
180                     TestFmwk test = getSubtest(targetName);\r
181                     if (test != null) {\r
182                         target = test.new ClassTarget();\r
183                     } else {\r
184                         target = this.new Target(targetName);\r
185                     }\r
186                 } catch (TestFmwkException e) {\r
187                     target = this.new Target(targetName);\r
188                 }\r
189             } else if (params.doRecurse()) {\r
190                 finishInit();\r
191                 boolean groupOnly = params.doRecurseGroupsOnly();\r
192                 for (int i = names.length; --i >= 0;) {\r
193                     Target newTarget = null;\r
194                     Class cls = tests[i];\r
195                     if (cls == null) { // hack no warning for missing tests\r
196                         if (params.warnings) {\r
197                             continue;\r
198                         }\r
199                         newTarget = this.new Target(names[i]);\r
200                     } else {\r
201                         TestFmwk test = getSubtest(i, groupOnly);\r
202                         if (test != null) {\r
203                             newTarget = test.new ClassTarget();\r
204                         } else {\r
205                             if (groupOnly) {\r
206                                 newTarget = this.new EmptyTarget(names[i]);\r
207                             } else {\r
208                                 newTarget = this.new Target(names[i]);\r
209                             }\r
210                         }\r
211                     }\r
212                     if (newTarget != null) {\r
213                         newTarget.setNext(target);\r
214                         target = newTarget;\r
215                     }\r
216                 }\r
217             }\r
218 \r
219             return target;\r
220         }\r
221         protected TestFmwk getSubtest(String testName) throws TestFmwkException {\r
222             finishInit();\r
223 \r
224             for (int i = 0; i < names.length; ++i) {\r
225                 if (names[i].equalsIgnoreCase(testName)) { // allow\r
226                                                            // case-insensitive\r
227                                                            // matching\r
228                     return getSubtest(i, false);\r
229                 }\r
230             }\r
231             throw new TestFmwkException(testName);\r
232         }\r
233 \r
234         private TestFmwk getSubtest(int i, boolean groupOnly) {\r
235             Class cls = tests[i];\r
236             if (cls != null) {\r
237                 if (groupOnly && !TestGroup.class.isAssignableFrom(cls)) {\r
238                     return null;\r
239                 }\r
240 \r
241                 try {\r
242                     TestFmwk subtest = (TestFmwk) cls.newInstance();\r
243                     subtest.params = params;\r
244                     return subtest;\r
245                 } catch (InstantiationException e) {\r
246                     throw new IllegalStateException(e.getMessage());\r
247                 } catch (IllegalAccessException e) {\r
248                     throw new IllegalStateException(e.getMessage());\r
249                 }\r
250             }\r
251             return null;\r
252         }\r
253 \r
254         private void finishInit() {\r
255             if (tests == null) {\r
256                 tests = new Class[names.length];\r
257 \r
258                 for (int i = 0; i < names.length; ++i) {\r
259                     String name = names[i];\r
260                     if (name.indexOf('.') == -1) {\r
261                         name = defaultPackage + name;\r
262                     }\r
263                     try {\r
264                         Class cls = Class.forName(name);\r
265                         if (!TestFmwk.class.isAssignableFrom(cls)) {\r
266                             throw new IllegalStateException("class " + name\r
267                                     + " does not extend TestFmwk");\r
268                         }\r
269 \r
270                         tests[i] = cls;\r
271                         names[i] = getClassTargetName(cls);\r
272                     } catch (ClassNotFoundException e) {\r
273                         // leave tests[i] null and name as classname\r
274                     }\r
275                 }\r
276             }\r
277         }\r
278     }\r
279 \r
280     /**\r
281      * The default target is invalid.\r
282      */\r
283     public class Target {\r
284         private Target next;\r
285         public final String name;\r
286 \r
287         public Target(String name) {\r
288             this.name = name;\r
289         }\r
290 \r
291         public Target setNext(Target next) {\r
292             this.next = next;\r
293             return this;\r
294         }\r
295 \r
296         public Target getNext() {\r
297             return next;\r
298         }\r
299 \r
300         public Target append(Target targets) {\r
301             Target t = this;\r
302             while(t.next != null) {\r
303                 t = t.next;\r
304             }\r
305             t.next = targets;\r
306             return this;\r
307         }\r
308 \r
309         public void run() throws Exception {\r
310             int f = filter();\r
311             if (f == -1) {\r
312                 ++params.invalidCount;\r
313             } else {\r
314                 Locale.setDefault(defaultLocale);\r
315                 TimeZone.setDefault(defaultTimeZone);\r
316 \r
317                 if (!validate()) {\r
318                     params.writeTestInvalid(name, params.nodata);\r
319                 } else {\r
320                     params.push(name, getDescription(), f == 1);\r
321                     execute();\r
322                     params.pop();\r
323                 }\r
324             }\r
325         }\r
326 \r
327         protected int filter() {\r
328             return params.filter(name);\r
329         }\r
330 \r
331         protected boolean validate() {\r
332             return false;\r
333         }\r
334 \r
335         protected String getDescription() {\r
336             return null;\r
337         }\r
338 \r
339         protected void execute() throws Exception{\r
340         }\r
341     }\r
342 \r
343     public class EmptyTarget extends Target {\r
344         public EmptyTarget(String name) {\r
345             super(name);\r
346         }\r
347 \r
348         protected boolean validate() {\r
349             return true;\r
350         }\r
351     }\r
352 \r
353     public class MethodTarget extends Target {\r
354         private Method testMethod;\r
355 \r
356         public MethodTarget(String name, Method method) {\r
357             super(name);\r
358             testMethod = method;\r
359         }\r
360 \r
361         protected boolean validate() {\r
362             return testMethod != null && validateMethod(name);\r
363         }\r
364 \r
365         protected String getDescription() {\r
366             return getMethodDescription(name);\r
367         }\r
368 \r
369         protected void execute() throws Exception{\r
370             if (params.inDocMode()) {\r
371                 // nothing to execute\r
372             } else if (!params.stack.included) {\r
373                 ++params.invalidCount;\r
374             } else {\r
375                 final Object[] NO_ARGS = new Object[0];\r
376                 try {\r
377                     ++params.testCount;\r
378                     init();\r
379                     testMethod.invoke(TestFmwk.this, NO_ARGS);\r
380                 } catch (IllegalAccessException e) {\r
381                     errln("Can't access test method " + testMethod.getName());\r
382                 }catch (ExceptionInInitializerError e){\r
383                     handleException(e);\r
384                 } catch (InvocationTargetException e) {\r
385                     //e.printStackTrace();\r
386                     handleException(e);\r
387                 }catch (MissingResourceException e) {\r
388                     handleException(e);\r
389                 }catch (NoClassDefFoundError e) {\r
390                     handleException(e);\r
391                 }catch (Exception e){\r
392                     /*errln("Encountered: "+ e.toString());\r
393                     e.printStackTrace(System.err);\r
394                     */\r
395                     handleException(e);\r
396                 }\r
397             }\r
398             // If non-exhaustive, check if the method target\r
399             // takes excessive time.\r
400             if (params.inclusion <= 5) {\r
401                 double deltaSec = (double)(System.currentTimeMillis() - params.stack.millis)/1000;\r
402                 if (deltaSec > params.maxTargetSec) {\r
403                     if (params.timeLog == null) {\r
404                         params.timeLog = new StringBuffer();\r
405                     }\r
406                     params.stack.appendPath(params.timeLog);\r
407                     params.timeLog.append(" (" + deltaSec + "s" + ")\n");\r
408                 }\r
409             }\r
410         }\r
411 \r
412         protected String getStackTrace(InvocationTargetException e) {\r
413             ByteArrayOutputStream bs = new ByteArrayOutputStream();\r
414             PrintStream ps = new PrintStream(bs);\r
415             e.getTargetException().printStackTrace(ps);\r
416             return bs.toString();\r
417         }\r
418     }\r
419 \r
420     public class ClassTarget extends Target {\r
421         String targetName;\r
422 \r
423         public ClassTarget() {\r
424             this(null);\r
425         }\r
426 \r
427         public ClassTarget(String targetName) {\r
428             super(getClassTargetName(TestFmwk.this.getClass()));\r
429             this.targetName = targetName;\r
430         }\r
431 \r
432         protected boolean validate() {\r
433             return TestFmwk.this.validate();\r
434         }\r
435 \r
436         protected String getDescription() {\r
437             return TestFmwk.this.getDescription();\r
438         }\r
439 \r
440         protected void execute() throws Exception {\r
441             params.indentLevel++;\r
442             Target target = randomize(getTargets(targetName));\r
443             while (target != null) {\r
444                 target.run();\r
445                 target = target.next;\r
446             }\r
447             params.indentLevel--;\r
448         }\r
449 \r
450         private Target randomize(Target t) {\r
451             if (t != null && t.getNext() != null) {\r
452                 ArrayList list = new ArrayList();\r
453                 while (t != null) {\r
454                     list.add(t);\r
455                     t = t.getNext();\r
456                 }\r
457 \r
458                 Target[] arr = (Target[]) list.toArray(new Target[list.size()]);\r
459 \r
460                 if (true) { // todo - add to params?\r
461                     // different jvms return class methods in different orders,\r
462                     // so we sort them (always, and then randomize them, so that\r
463                     // forcing a seed will also work across jvms).\r
464                     Arrays.sort(arr, new Comparator() {\r
465                         public int compare(Object lhs, Object rhs) {\r
466                             // sort in reverse order, later we link up in\r
467                             // forward order\r
468                             return ((Target) rhs).name\r
469                                     .compareTo(((Target) lhs).name);\r
470                         }\r
471                     });\r
472 \r
473                     // t is null to start, ends up as first element\r
474                     // (arr[arr.length-1])\r
475                     for (int i = 0; i < arr.length; ++i) {\r
476                         t = arr[i].setNext(t); // relink in forward order\r
477                     }\r
478                 }\r
479 \r
480                 if (params.random != null) {\r
481                     t = null; // reset t to null\r
482                     Random r = params.random;\r
483                     for (int i = arr.length; --i >= 1;) {\r
484                         int x = r.nextInt(i + 1);\r
485                         t = arr[x].setNext(t);\r
486                         arr[x] = arr[i];\r
487                     }\r
488 \r
489                     t = arr[0].setNext(t); // new first element\r
490                 }\r
491             }\r
492 \r
493             return t;\r
494         }\r
495     }\r
496 \r
497     //------------------------------------------------------------------------\r
498     // Everything below here is boilerplate code that makes it possible\r
499     // to add a new test by simply adding a function to an existing class\r
500     //------------------------------------------------------------------------\r
501 \r
502     protected TestFmwk() {\r
503     }\r
504     \r
505     protected void init() throws Exception{\r
506     }\r
507     \r
508     /**\r
509      * Parse arguments into a TestParams object and a collection of target\r
510      * paths. If there was an error parsing the TestParams, print usage and exit\r
511      * with -1. Otherwise, call resolveTarget(TestParams, String) for each path,\r
512      * and run the returned target. After the last test returns, if prompt is\r
513      * set, prompt and wait for input from stdin. Finally, exit with number of\r
514      * errors.\r
515      * \r
516      * This method never returns, since it always exits with System.exit();\r
517      */\r
518     public void run(String[] args) {\r
519         System.exit(run(args, new PrintWriter(System.out)));\r
520      }\r
521     \r
522     /**\r
523      * Like run(String[]) except this allows you to specify the error log.\r
524      * Unlike run(String[]) this returns the error code as a result instead of\r
525      * calling System.exit().\r
526      */\r
527     public int run(String[] args, PrintWriter log) {\r
528         boolean prompt = false;\r
529         int wx = 0;\r
530         for (int i = 0; i < args.length; ++i) {\r
531             String arg = args[i];\r
532             if (arg.equals("-p") || arg.equals("-prompt")) {\r
533                 prompt = true;\r
534             } else {\r
535                 if (wx < i) {\r
536                     args[wx] = arg;\r
537                 }\r
538                 wx++;\r
539             }\r
540         }\r
541         while (wx < args.length) {\r
542             args[wx++] = null;\r
543         }\r
544         \r
545         TestParams localParams = TestParams.create(args, log);\r
546         if (localParams == null) {\r
547             return -1;\r
548         }\r
549         \r
550         int errorCount = runTests(localParams, args);\r
551         \r
552         if (localParams.seed != 0) {\r
553             localParams.log.println("-random:" + localParams.seed);\r
554             localParams.log.flush();\r
555         }\r
556 \r
557         if (localParams.errorSummary != null && localParams.errorSummary.length() > 0) {\r
558             localParams.log.println("\nError summary:");\r
559             localParams.log.println(localParams.errorSummary.toString());\r
560         }\r
561 \r
562         if (localParams.timeLog != null && localParams.timeLog.length() > 0) {\r
563             localParams.log.println("\nTest cases taking excessive time (>" +\r
564                     localParams.maxTargetSec + "s):");\r
565             localParams.log.println(localParams.timeLog.toString());\r
566         }\r
567 \r
568         if (prompt) {\r
569             System.out.println("Hit RETURN to exit...");\r
570             System.out.flush();\r
571             try {\r
572                 System.in.read();\r
573             } catch (IOException e) {\r
574                 localParams.log.println("Exception: " + e.toString() + e.getMessage());\r
575             }\r
576         }\r
577 \r
578         return errorCount;\r
579     }\r
580 \r
581     public int runTests(TestParams _params, String[] tests) {\r
582         int ec = 0;\r
583         \r
584         StringBuffer summary = null;\r
585         try {\r
586             if (tests.length == 0 || tests[0] == null) { // no args\r
587                 _params.init();\r
588                 resolveTarget(_params).run();\r
589                 ec = _params.errorCount;\r
590             } else {\r
591                 for (int i = 0; i < tests.length ; ++i) {\r
592                     if (tests[i] == null) continue;\r
593                     \r
594                     if (i > 0) {\r
595                         _params.log.println();\r
596                     }\r
597 \r
598                     _params.init();\r
599                     resolveTarget(_params, tests[i]).run();\r
600                     ec += _params.errorCount;\r
601                     \r
602                     if (_params.errorSummary != null && _params.errorSummary.length() > 0) {\r
603                         if (summary == null) {\r
604                             summary = new StringBuffer();\r
605                         }\r
606                         summary.append("\nTest Root: " + tests[i] + "\n");\r
607                         summary.append(_params.errorSummary());\r
608                     }\r
609                 }\r
610                 _params.errorSummary = summary;\r
611             }\r
612         } catch (Exception e) {\r
613             e.printStackTrace(_params.log);\r
614             _params.log.println(e.getMessage());\r
615             _params.log.println("encountered exception, exiting");\r
616         }\r
617         \r
618         return ec;\r
619     }\r
620     \r
621     /**\r
622      * Return a ClassTarget for this test. Params is set on this test.\r
623      */\r
624     public Target resolveTarget(TestParams paramsArg) {\r
625         this.params = paramsArg;\r
626         return new ClassTarget();\r
627     }\r
628 \r
629     /**\r
630      * Resolve a path from this test to a target. If this test has subtests, and\r
631      * the path contains '/', the portion before the '/' is resolved to a\r
632      * subtest, until the path is consumed or the test has no subtests. Returns\r
633      * a ClassTarget created using the resolved test and remaining path (which\r
634      * ought to be null or a method name). Params is set on the target's test.\r
635      */\r
636     public Target resolveTarget(TestParams paramsArg, String targetPath) {\r
637         TestFmwk test = this;\r
638         test.params = paramsArg;\r
639 \r
640         if (targetPath != null) {\r
641             if (targetPath.length() == 0) {\r
642                 targetPath = null;\r
643             } else {\r
644                 int p = 0;\r
645                 int e = targetPath.length();\r
646 \r
647                 // trim all leading and trailing '/'\r
648                 while (targetPath.charAt(p) == '/') {\r
649                     ++p;\r
650                 }\r
651                 while (e > p && targetPath.charAt(e - 1) == '/') {\r
652                     --e;\r
653                 }\r
654                 if (p > 0 || e < targetPath.length()) {\r
655                     targetPath = targetPath.substring(p, e - p);\r
656                     p = 0;\r
657                     e = targetPath.length();\r
658                 }\r
659 \r
660                 try {\r
661                     for (;;) {\r
662                         int n = targetPath.indexOf('/');\r
663                         String prefix = n == -1 ? targetPath : targetPath\r
664                                 .substring(0, n);\r
665                         TestFmwk subtest = test.getSubtest(prefix);\r
666 \r
667                         if (subtest == null) {\r
668                             break;\r
669                         }\r
670 \r
671                         test = subtest;\r
672 \r
673                         if (n == -1) {\r
674                             targetPath = null;\r
675                             break;\r
676                         }\r
677 \r
678                         targetPath = targetPath.substring(n + 1);\r
679                     }\r
680                 } catch (TestFmwkException ex) {\r
681                     return test.new Target(targetPath);\r
682                 }\r
683             }\r
684         }\r
685 \r
686         return test.new ClassTarget(targetPath);\r
687     }\r
688 \r
689     /**\r
690      * Return true if we can run this test (allows test to inspect jvm,\r
691      * environment, params before running)\r
692      */\r
693     protected boolean validate() {\r
694         return true;\r
695     }\r
696 \r
697     /**\r
698      * Return the targets for this test. If targetName is null, return all\r
699      * targets, otherwise return a target for just that name. The returned\r
700      * target can be null.\r
701      * \r
702      * The default implementation returns a MethodTarget for each public method\r
703      * of the object's class whose name starts with "Test" or "test".\r
704      */\r
705     protected Target getTargets(String targetName) {\r
706         return getClassTargets(getClass(), targetName);\r
707     }\r
708 \r
709     protected Target getClassTargets(Class cls, String targetName) {\r
710         if (cls == null) {\r
711             return null;\r
712         }\r
713 \r
714         Target target = null;\r
715         if (targetName != null) {\r
716             try {\r
717                 Method method = cls.getMethod(targetName, (Class[])null);\r
718                 target = new MethodTarget(targetName, method);\r
719             } catch (NoSuchMethodException e) {\r
720         if (!inheritTargets()) {\r
721             return new Target(targetName); // invalid target\r
722         }\r
723             } catch (SecurityException e) {\r
724                 return null;\r
725             }\r
726         } else {\r
727             if (params.doMethods()) {\r
728                 Method[] methods = cls.getDeclaredMethods();\r
729                 for (int i = methods.length; --i >= 0;) {\r
730                     String name = methods[i].getName();\r
731                     if (name.startsWith("Test") || name.startsWith("test")) {\r
732                         target = new MethodTarget(name, methods[i])\r
733                                 .setNext(target);\r
734                     }\r
735                 }\r
736             }\r
737         }\r
738 \r
739         if (inheritTargets()) {\r
740           Target parentTarget = getClassTargets(cls.getSuperclass(), targetName);\r
741           if (parentTarget == null) {\r
742             return target;\r
743           }\r
744           if (target == null) {\r
745             return parentTarget;\r
746           }\r
747           return parentTarget.append(target);\r
748         }\r
749 \r
750         return target;\r
751     }\r
752 \r
753     protected boolean inheritTargets() {\r
754         return false;\r
755     }\r
756 \r
757     protected String getDescription() {\r
758         return null;\r
759     }\r
760 \r
761     protected boolean validateMethod(String name) {\r
762         return true;\r
763     }\r
764 \r
765     protected String getMethodDescription(String name) {\r
766         return null;\r
767     }\r
768 \r
769     // method tests have no subtests, group tests override\r
770     protected TestFmwk getSubtest(String prefix) throws TestFmwkException {\r
771         return null;\r
772     }\r
773 \r
774     public boolean isVerbose() {\r
775         return params.verbose;\r
776     }\r
777 \r
778     public boolean noData() {\r
779         return params.nodata;\r
780     }\r
781 \r
782     public boolean isTiming() {\r
783         return params.timing < Long.MAX_VALUE;\r
784     }\r
785 \r
786     public boolean isMemTracking() {\r
787         return params.memusage;\r
788     }\r
789 \r
790     /**\r
791      * 0 = fewest tests, 5 is normal build, 10 is most tests\r
792      */\r
793     public int getInclusion() {\r
794         return params.inclusion;\r
795     }\r
796 \r
797     public boolean isModularBuild() {\r
798         return params.warnings;\r
799     }\r
800 \r
801     public boolean isQuick() {\r
802         return params.inclusion == 0;\r
803     }\r
804 \r
805     public void msg(String message, int level, boolean incCount, boolean newln) {\r
806         params.msg(message, level, incCount, newln);\r
807     }\r
808 \r
809     protected int getErrorCount() {\r
810         return params.errorCount;\r
811     }\r
812 \r
813     public String getProperty(String key) {\r
814         String val = null;\r
815         if (key != null && key.length() > 0 && params.props != null) {\r
816             val = (String)params.props.get(key.toLowerCase());\r
817         }\r
818         return val;\r
819     }\r
820 \r
821     protected TimeZone safeGetTimeZone(String id) {\r
822         TimeZone tz = TimeZone.getTimeZone(id);\r
823         if (tz == null) {\r
824             // should never happen\r
825             errln("FAIL: TimeZone.getTimeZone(" + id + ") => null");\r
826         }\r
827         if (!tz.getID().equals(id)) {\r
828             warnln("FAIL: TimeZone.getTimeZone(" + id + ") => " + tz.getID());\r
829         }\r
830         return tz;\r
831     }\r
832 \r
833     /**\r
834      * Print a usage message for this test class.\r
835      */\r
836     public void usage() {\r
837         usage(new PrintWriter(System.out), getClass().getName());\r
838     }\r
839     \r
840     public static void usage(PrintWriter pw, String className) {\r
841         pw.println("Usage: " + className + " option* target*");\r
842         pw.println();\r
843         pw.println("Options:");\r
844         pw.println(" -d[escribe] Print a short descriptive string for this test and all");\r
845         pw.println("       listed targets.");\r
846         pw.println(" -e<n> Set exhaustiveness from 0..10.  Default is 0, fewest tests.\n"\r
847                  + "       To run all tests, specify -e10.  Giving -e with no <n> is\n"\r
848                  + "       the same as -e5.");\r
849         pw.println(" -filter:<str> Only tests matching filter will be run or listed.\n"\r
850                  + "       <str> is of the form ['^']text[','['^']text].\n"\r
851                  + "       Each string delimited by ',' is a separate filter argument.\n"\r
852                  + "       If '^' is prepended to an argument, its matches are excluded.\n"\r
853                  + "       Filtering operates on test groups as well as tests, if a test\n"\r
854                  + "       group is included, all its subtests that are not excluded will\n"\r
855                  + "       be run.  Examples:\n"\r
856                  + "    -filter:A -- only tests matching A are run.  If A matches a group,\n"\r
857                  + "       all subtests of this group are run.\n"\r
858                  + "    -filter:^A -- all tests except those matching A are run.  If A matches\n"\r
859                  + "        a group, no subtest of that group will be run.\n"\r
860                  + "    -filter:A,B,^C,^D -- tests matching A or B and not C and not D are run\n"\r
861                  + "       Note: Filters are case insensitive.");\r
862         pw.println(" -h[elp] Print this help text and exit.");\r
863         pw.println(" -l[ist] List immediate targets of this test");\r
864         pw.println("   -la, -listAll List immediate targets of this test, and all subtests");\r
865         pw.println("   -le, -listExaustive List all subtests and targets");\r
866         // don't know how to get useful numbers for memory usage using java API\r
867         // calls\r
868         //      pw.println(" -m[emory] print memory usage and force gc for\r
869         // each test");\r
870         pw.println(" -n[othrow] Message on test failure rather than exception");\r
871         pw.println(" -p[rompt] Prompt before exiting");\r
872         pw.println(" -prop:<key>=<value> Set optional property used by this test");\r
873         pw.println(" -q[uiet] Do not show warnings");\r
874         pw.println(" -r[andom][:<n>] If present, randomize targets.  If n is present,\n"\r
875                         + "       use it as the seed.  If random is not set, targets will\n"\r
876                         + "       be in alphabetical order to ensure cross-platform consistency.");\r
877         pw.println(" -s[ilent] No output except error summary or exceptions.");\r
878         pw.println(" -tfilter:<str> Transliterator Test filter of ids.");\r
879         pw.println(" -t[ime][:<n>] Print elapsed time for each test.  if n is present\n"\r
880                         + "       only print times >= n milliseconds.");\r
881         pw.println(" -v[erbose] Show log messages");\r
882         pw.println(" -u[nicode] Don't escape error or log messages");\r
883         pw.println(" -w[arning] Continue in presence of warnings, and disable missing test warnings.");\r
884         pw.println(" -nodata | -nd Do not warn if resource data is not present.");\r
885         pw.println();\r
886         pw.println(" If a list or describe option is provided, no tests are run.");\r
887         pw.println();\r
888         pw.println("Targets:");\r
889         pw.println(" If no target is specified, all targets for this test are run.");\r
890         pw.println(" If a target contains no '/' characters, and matches a target");\r
891         pw.println(" of this test, the target is run.  Otherwise, the part before the");\r
892         pw.println(" '/' is used to match a subtest, which then evaluates the");\r
893         pw.println(" remainder of the target as above.  Target matching is case-insensitive.");\r
894         pw.println();\r
895         pw.println(" If multiple targets are provided, each is executed in order.");\r
896         pw.flush();\r
897     }\r
898     public static String hex(char[] s){\r
899         StringBuffer result = new StringBuffer();\r
900         for (int i = 0; i < s.length; ++i) {\r
901             if (i != 0) result.append(',');\r
902             result.append(hex(s[i]));\r
903         }\r
904         return result.toString();\r
905     }\r
906     public static String hex(byte[] s){\r
907         StringBuffer result = new StringBuffer();\r
908         for (int i = 0; i < s.length; ++i) {\r
909             if (i != 0) result.append(',');\r
910             result.append(hex(s[i]));\r
911         }\r
912         return result.toString();\r
913     }\r
914     public static String hex(char ch) {\r
915         StringBuffer result = new StringBuffer();\r
916         String foo = Integer.toString(ch, 16).toUpperCase();\r
917         for (int i = foo.length(); i < 4; ++i) {\r
918             result.append('0');\r
919         }\r
920         return result + foo;\r
921     }\r
922 \r
923     public static String hex(int ch) {\r
924         StringBuffer result = new StringBuffer();\r
925         String foo = Integer.toString(ch, 16).toUpperCase();\r
926         for (int i = foo.length(); i < 4; ++i) {\r
927             result.append('0');\r
928         }\r
929         return result + foo;\r
930     }\r
931 \r
932     public static String hex(String s) {\r
933         StringBuffer result = new StringBuffer();\r
934         for (int i = 0; i < s.length(); ++i) {\r
935             if (i != 0)\r
936                 result.append(',');\r
937             result.append(hex(s.charAt(i)));\r
938         }\r
939         return result.toString();\r
940     }\r
941 \r
942     public static String hex(StringBuffer s) {\r
943         return hex(s.toString());\r
944     }\r
945     public static String prettify(String s) {\r
946         StringBuffer result = new StringBuffer();\r
947         int ch;\r
948         for (int i = 0; i < s.length(); i += UTF16.getCharCount(ch)) {\r
949             ch = UTF16.charAt(s, i);\r
950             if (ch > 0xfffff) {\r
951                 result.append("\\U00");\r
952                 result.append(hex(ch));\r
953             } else if (ch > 0xffff) {\r
954                 result.append("\\U000");\r
955                 result.append(hex(ch));\r
956             } else if (ch > 0x7f) {\r
957                 result.append("\\u");\r
958                 result.append(hex(ch));\r
959             } else {\r
960                 result.append((char) ch);\r
961             }\r
962 \r
963         }\r
964         return result.toString();\r
965     }\r
966     public static String prettify(StringBuffer s) {\r
967         return prettify(s.toString());\r
968     }\r
969 \r
970     private static java.util.GregorianCalendar cal;\r
971 \r
972     /**\r
973      * Return a Date given a year, month, and day of month. This is similar to\r
974      * new Date(y-1900, m, d). It uses the default time zone at the time this\r
975      * method is first called.\r
976      * \r
977      * @param year\r
978      *            use 2000 for 2000, unlike new Date()\r
979      * @param month\r
980      *            use Calendar.JANUARY etc.\r
981      * @param dom\r
982      *            day of month, 1-based\r
983      * @return a Date object for the given y/m/d\r
984      */\r
985     protected static synchronized java.util.Date getDate(int year, int month,\r
986             int dom) {\r
987         if (cal == null) {\r
988             cal = new java.util.GregorianCalendar();\r
989         }\r
990         cal.clear();\r
991         cal.set(year, month, dom);\r
992         return cal.getTime();\r
993     }\r
994 \r
995     public static class NullWriter extends PrintWriter {\r
996         public NullWriter() {\r
997             super(System.out, false);\r
998         }\r
999         public void write(int c) {\r
1000         }\r
1001         public void write(char[] buf, int off, int len) {\r
1002         }\r
1003         public void write(String s, int off, int len) {\r
1004         }\r
1005         public void println() {\r
1006         }\r
1007     }\r
1008 \r
1009     public static class ASCIIWriter extends PrintWriter {\r
1010         private StringBuffer buffer = new StringBuffer();\r
1011 \r
1012         // Characters that we think are printable but that escapeUnprintable\r
1013         // doesn't\r
1014         private static final String PRINTABLES = "\t\n\r";\r
1015 \r
1016         public ASCIIWriter(Writer w, boolean autoFlush) {\r
1017             super(w, autoFlush);\r
1018         }\r
1019 \r
1020         public ASCIIWriter(OutputStream os, boolean autoFlush) {\r
1021             super(os, autoFlush);\r
1022         }\r
1023 \r
1024         public void write(int c) {\r
1025             synchronized (lock) {\r
1026                 buffer.setLength(0);\r
1027                 if (PRINTABLES.indexOf(c) < 0\r
1028                         && TestUtil.escapeUnprintable(buffer, c)) {\r
1029                     super.write(buffer.toString());\r
1030                 } else {\r
1031                     super.write(c);\r
1032                 }\r
1033             }\r
1034         }\r
1035 \r
1036         public void write(char[] buf, int off, int len) {\r
1037             synchronized (lock) {\r
1038                 buffer.setLength(0);\r
1039                 int limit = off + len;\r
1040                 while (off < limit) {\r
1041                     int c = UTF16Util.charAt(buf, 0, buf.length, off);\r
1042                     off += UTF16Util.getCharCount(c);\r
1043                     if (PRINTABLES.indexOf(c) < 0\r
1044                             && TestUtil.escapeUnprintable(buffer, c)) {\r
1045                         super.write(buffer.toString());\r
1046                         buffer.setLength(0);\r
1047                     } else {\r
1048                         super.write(c);\r
1049                     }\r
1050                 }\r
1051             }\r
1052         }\r
1053 \r
1054         public void write(String s, int off, int len) {\r
1055             write(s.substring(off, off + len).toCharArray(), 0, len);\r
1056         }\r
1057     }\r
1058 \r
1059     // filters\r
1060     // match against the entire hierarchy\r
1061     // A;B;!C;!D --> (A ||B) && (!C && !D)\r
1062     // positive, negative, unknown matches\r
1063     // positive -- known to be included, negative- known to be excluded\r
1064     // positive only if no excludes, and matches at least one include, if any\r
1065     // negative only if matches at least one exclude\r
1066     // otherwise, we wait\r
1067 \r
1068     public static class TestParams {\r
1069         public boolean prompt;\r
1070         public boolean nothrow;\r
1071         public boolean verbose;\r
1072         public boolean quiet;\r
1073         public int listlevel;\r
1074         public boolean describe;\r
1075         public boolean warnings;\r
1076         public boolean nodata;\r
1077         public long timing = Long.MAX_VALUE;\r
1078         public boolean memusage;\r
1079         public int inclusion;\r
1080         public String filter;\r
1081         public long seed;\r
1082         public String tfilter; // for transliterator tests\r
1083 \r
1084         public State stack;\r
1085 \r
1086         public StringBuffer errorSummary;\r
1087         private StringBuffer timeLog;\r
1088 \r
1089         public PrintWriter log;\r
1090         public int indentLevel;\r
1091         private boolean needLineFeed;\r
1092         private boolean suppressIndent;\r
1093         public int errorCount;\r
1094         public int warnCount;\r
1095         public int invalidCount;\r
1096         public int testCount;\r
1097         private NumberFormat tformat;\r
1098         public Random random;\r
1099         public int maxTargetSec = 10;\r
1100         public HashMap props;\r
1101 \r
1102         private TestParams() {\r
1103         }\r
1104         \r
1105         public static TestParams create(String arglist, PrintWriter log) {\r
1106             String[] args = null;\r
1107             if (arglist != null && arglist.length() > 0) {\r
1108 //#if defined(FOUNDATION10) || defined(J2SE13)\r
1109 //##            args = Utility.split(arglist, '\u0020');\r
1110 //#else\r
1111                 args = arglist.split("\\s");\r
1112 //#endif\r
1113             }\r
1114             return create(args, log);\r
1115         }\r
1116         \r
1117         /**\r
1118          * Create a TestParams from a list of arguments.  If successful, return the params object,\r
1119          * else return null.  Error messages will be reported on errlog if it is not null.\r
1120          * Arguments and values understood by this method will be removed from the args array\r
1121          * and existing args will be shifted down, to be filled by nulls at the end.\r
1122          * @param args the list of arguments\r
1123          * @param log the error log, or null if no error log is desired\r
1124          * @return the new TestParams object, or null if error\r
1125          */\r
1126         public static TestParams create(String[] args, PrintWriter log) {\r
1127             TestParams params = new TestParams();\r
1128             \r
1129             if(log == null){\r
1130                 params.log = new NullWriter();\r
1131             }else{\r
1132                 params.log =  new ASCIIWriter(log, true);\r
1133             }\r
1134             \r
1135             boolean usageError = false;\r
1136             String filter = null;\r
1137             int wx = 0; // write argets.\r
1138             if (args != null) {\r
1139                 for (int i = 0; i < args.length; i++) {\r
1140                     String arg = args[i];\r
1141                     if (arg == null || arg.length() == 0) {\r
1142                         continue;\r
1143                     }\r
1144                     if (arg.charAt(0) == '-') {\r
1145                         arg = arg.toLowerCase();\r
1146                         if (arg.equals("-verbose") || arg.equals("-v")) {\r
1147                             params.verbose = true;\r
1148                             params.quiet = false;\r
1149                         } else if (arg.equals("-quiet") || arg.equals("-q")) {\r
1150                             params.quiet = true;\r
1151                             params.verbose = false;\r
1152                         } else if (arg.equals("-help") || arg.equals("-h")) {\r
1153                             usageError = true;\r
1154                         } else if (arg.equals("-warning") || arg.equals("-w")) {\r
1155                             params.warnings = true;\r
1156                         } else if (arg.equals("-nodata") || arg.equals("-nd")) {\r
1157                             params.nodata = true;\r
1158                         } else if (arg.equals("-list") || arg.equals("-l")) {\r
1159                             params.listlevel = 1;\r
1160                         } else if (arg.equals("-listall") || arg.equals("-la")) {\r
1161                             params.listlevel = 2;\r
1162                         } else if (arg.equals("-listexaustive") || arg.equals("-le")) {\r
1163                             params.listlevel = 3;\r
1164                         } else if (arg.equals("-memory") || arg.equals("-m")) {\r
1165                             params.memusage = true;\r
1166                         } else if (arg.equals("-nothrow") || arg.equals("-n")) {\r
1167                             params.nothrow = true;\r
1168                             params.errorSummary = new StringBuffer();\r
1169                         } else if (arg.equals("-describe") || arg.equals("-d")) {\r
1170                             params.describe = true;\r
1171                         } else if (arg.startsWith("-r")) {\r
1172                             String s = null;\r
1173                             int n = arg.indexOf(':');\r
1174                             if (n != -1) {\r
1175                                 s = arg.substring(n + 1);\r
1176                                 arg = arg.substring(0, n);\r
1177                             }\r
1178 \r
1179                             if (arg.equals("-r") || arg.equals("-random")) {\r
1180                                 if (s == null) {\r
1181                                     params.seed = System.currentTimeMillis();\r
1182                                 } else {\r
1183                                     params.seed = Long.parseLong(s);\r
1184                                 }\r
1185                             } else {\r
1186                                 log.println("*** Error: unrecognized argument: " + arg);\r
1187                                 usageError = true;\r
1188                                 break;\r
1189                             }\r
1190                         } else if (arg.startsWith("-e")) {\r
1191                             // see above\r
1192                             params.inclusion = (arg.length() == 2) \r
1193                                 ? 5 \r
1194                                 : Integer.parseInt(arg.substring(2));\r
1195                             if (params.inclusion < 0 || params.inclusion > 10) {\r
1196                                 usageError = true;\r
1197                                 break;\r
1198                             }\r
1199                         } else if (arg.startsWith("-tfilter:")) {\r
1200                             params.tfilter = arg.substring(8);\r
1201                         } else if (arg.startsWith("-time") || arg.startsWith("-t")) {\r
1202                             long val = 0;\r
1203                             int inx = arg.indexOf(':');\r
1204                             if (inx > 0) {\r
1205                                 String num = arg.substring(inx + 1);\r
1206                                 try {\r
1207                                     val = Long.parseLong(num);\r
1208                                 } catch (Exception e) {\r
1209                                     log.println("*** Error: could not parse time threshold '"\r
1210                                                 + num + "'");\r
1211                                     usageError = true;\r
1212                                     break;\r
1213                                 }\r
1214                             }\r
1215                             params.timing = val;\r
1216                             String fmt = "#,00s";\r
1217                             if (val <= 10) {\r
1218                                 fmt = "#,##0.000s";\r
1219                             } else if (val <= 100) {\r
1220                                 fmt = "#,##0.00s";\r
1221                             } else if (val <= 1000) {\r
1222                                 fmt = "#,##0.0s";\r
1223                             }\r
1224                             params.tformat = new DecimalFormat(fmt);\r
1225                         } else if (arg.startsWith("-filter:")) {\r
1226                             String temp = arg.substring(8).toLowerCase();\r
1227                             filter = filter == null ? temp : filter + "," + temp;\r
1228                         } else if (arg.startsWith("-f:")) {\r
1229                             String temp = arg.substring(3).toLowerCase();\r
1230                             filter = filter == null ? temp : filter + "," + temp;\r
1231                         } else if (arg.startsWith("-s")) {\r
1232                             params.log = new NullWriter();\r
1233                         } else if (arg.startsWith("-u")) {\r
1234                             if (params.log instanceof ASCIIWriter) {\r
1235                                 params.log = log;\r
1236                             }\r
1237                         } else if (arg.startsWith("-prop:")) {\r
1238                             String temp = arg.substring(6);\r
1239                             int eql = temp.indexOf('=');\r
1240                             if (eql <= 0) {\r
1241                                 log.println("*** Error: could not parse custom property '" + arg + "'");\r
1242                                 usageError = true;\r
1243                                 break;\r
1244                             }\r
1245                             if (params.props == null) {\r
1246                                 params.props = new HashMap();\r
1247                             }\r
1248                             params.props.put(temp.substring(0, eql), temp.substring(eql+1));\r
1249                         } else {\r
1250                             log.println("*** Error: unrecognized argument: "\r
1251                                         + args[i]);\r
1252                             usageError = true;\r
1253                             break;\r
1254                         }\r
1255                     } else {\r
1256                         args[wx++] = arg; // shift down\r
1257                     }\r
1258                 }\r
1259 \r
1260                 while (wx < args.length) {\r
1261                     args[wx++] = null;\r
1262                 }\r
1263             }\r
1264             \r
1265             if (usageError) {\r
1266                 usage(log, "TestAll");\r
1267                 return null;\r
1268             }\r
1269 \r
1270             if (filter != null) {\r
1271                 params.filter = filter.toLowerCase();\r
1272             }\r
1273 \r
1274             params.init();\r
1275             \r
1276             return params;\r
1277         }\r
1278         \r
1279         public String errorSummary() {\r
1280             return errorSummary == null ? "" : errorSummary.toString();\r
1281         }\r
1282 \r
1283         public void init() {\r
1284             indentLevel = 0;\r
1285             needLineFeed = false;\r
1286             suppressIndent = false;\r
1287             errorCount = 0;\r
1288             warnCount = 0;\r
1289             invalidCount = 0;\r
1290             testCount = 0;\r
1291             random = seed == 0 ? null : new Random(seed);\r
1292         }\r
1293 \r
1294         public class State {\r
1295             State link;\r
1296             String name;\r
1297             StringBuffer buffer;\r
1298             int level;\r
1299             int ec;\r
1300             int wc;\r
1301             int ic;\r
1302             int tc;\r
1303             boolean flushed;\r
1304             public boolean included;\r
1305             long mem;\r
1306             long millis;\r
1307 \r
1308             public State(State link, String name, boolean included) {\r
1309                 this.link = link;\r
1310                 this.name = name;\r
1311                 if (link == null) {\r
1312                     this.level = 0;\r
1313                     this.included = included;\r
1314                 } else {\r
1315                     this.level = link.level + 1;\r
1316                     this.included = included || link.included;\r
1317                 }\r
1318                 this.ec = errorCount;\r
1319                 this.wc = warnCount;\r
1320                 this.ic = invalidCount;\r
1321                 this.tc = testCount;\r
1322 \r
1323                 if (link == null || this.included) {\r
1324                     flush();\r
1325                 }\r
1326 \r
1327                 mem = getmem();\r
1328                 millis = System.currentTimeMillis();\r
1329             }\r
1330 \r
1331             void flush() {\r
1332                 if (!flushed) {\r
1333                     if (link != null) {\r
1334                         link.flush();\r
1335                     }\r
1336 \r
1337                     indent(level);\r
1338                     log.print(name);\r
1339                     log.flush();\r
1340 \r
1341                     flushed = true;\r
1342 \r
1343                     needLineFeed = true;\r
1344                 }\r
1345             }\r
1346 \r
1347             void appendPath(StringBuffer buf) {\r
1348                 if (this.link != null) {\r
1349                     this.link.appendPath(buf);\r
1350                     buf.append('/');\r
1351                 }\r
1352                 buf.append(name);\r
1353             }\r
1354         }\r
1355 \r
1356         public void push(String name, String description, boolean included) {\r
1357             if (inDocMode() && describe && description != null) {\r
1358                 name += ": " + description;\r
1359             }\r
1360             stack = new State(stack, name, included);\r
1361         }\r
1362 \r
1363         public void pop() {\r
1364             if (stack != null) {\r
1365                 writeTestResult();\r
1366                 stack = stack.link;\r
1367             }\r
1368         }\r
1369 \r
1370         public boolean inDocMode() {\r
1371             return describe || listlevel != 0;\r
1372         }\r
1373 \r
1374         public boolean doMethods() {\r
1375             return !inDocMode() || listlevel == 3\r
1376                     || (indentLevel == 1 && listlevel > 0);\r
1377         }\r
1378 \r
1379         public boolean doRecurse() {\r
1380             return !inDocMode() || listlevel > 1\r
1381                     || (indentLevel == 1 && listlevel > 0);\r
1382         }\r
1383 \r
1384         public boolean doRecurseGroupsOnly() {\r
1385             return inDocMode()\r
1386                     && (listlevel == 2 || (indentLevel == 1 && listlevel > 0));\r
1387         }\r
1388 \r
1389         // return 0, -1, or 1\r
1390         // 1: run this test\r
1391         // 0: might run this test, no positive include or exclude on this group\r
1392         // -1: exclude this test\r
1393         public int filter(String testName) {\r
1394             int result = 0;\r
1395             if (filter == null) {\r
1396                 result = 1;\r
1397             } else {\r
1398                 boolean noIncludes = true;\r
1399                 boolean noExcludes = filter.indexOf('^') == -1;\r
1400                 testName = testName.toLowerCase();\r
1401                 int ix = 0;\r
1402                 while (ix < filter.length()) {\r
1403                     int nix = filter.indexOf(',', ix);\r
1404                     if (nix == -1) {\r
1405                         nix = filter.length();\r
1406                     }\r
1407                     if (filter.charAt(ix) == '^') {\r
1408                         if (testName.indexOf(filter.substring(ix + 1, nix)) != -1) {\r
1409                             result = -1;\r
1410                             break;\r
1411                         }\r
1412                     } else {\r
1413                         noIncludes = false;\r
1414                         if (testName.indexOf(filter.substring(ix, nix)) != -1) {\r
1415                             result = 1;\r
1416                             if (noExcludes) {\r
1417                                 break;\r
1418                             }\r
1419                         }\r
1420                     }\r
1421 \r
1422                     ix = nix + 1;\r
1423                 }\r
1424                 if (result == 0 && noIncludes) {\r
1425                     result = 1;\r
1426                 }\r
1427             }\r
1428             //              System.out.println("filter: " + testName + " returns: " +\r
1429             // result);\r
1430             return result;\r
1431         }\r
1432 \r
1433         /**\r
1434          * Log access.\r
1435          * @param msg The string message to write\r
1436          */\r
1437         public void write(String msg) {\r
1438             write(msg, false);\r
1439         }\r
1440         \r
1441         public void writeln(String msg) {\r
1442             write(msg, true);\r
1443         }\r
1444         \r
1445         private void write(String msg, boolean newln) {\r
1446             if (!suppressIndent) {\r
1447                 if (needLineFeed) {\r
1448                     log.println();\r
1449                     needLineFeed = false;\r
1450                 }\r
1451                 log.print(spaces.substring(0, indentLevel * 2));\r
1452             }\r
1453             log.print(msg);\r
1454             if (newln) {\r
1455                 log.println(); \r
1456             }\r
1457             log.flush();\r
1458             suppressIndent = !newln;\r
1459         }\r
1460         \r
1461         private void msg(String message, int level, boolean incCount,\r
1462                 boolean newln) {\r
1463             if (level == WARN && (!warnings && !nodata)){\r
1464                 level = ERR;\r
1465             }\r
1466 \r
1467             if (incCount) {\r
1468                 if (level == WARN) {\r
1469                     warnCount++;\r
1470                     invalidCount++;\r
1471                 } else if (level == ERR) {\r
1472                     errorCount++;\r
1473                 }\r
1474             }\r
1475 \r
1476             // should roll indentation stuff into log ???\r
1477             if (verbose || level > (quiet ? WARN : LOG)) {\r
1478                 if (!suppressIndent) {\r
1479                     indent(indentLevel + 1);\r
1480                     final String[] MSGNAMES = {"", "Warning: ", "Error: "};\r
1481                     log.print(MSGNAMES[level]);\r
1482                 }\r
1483 \r
1484                 log.print(message);\r
1485                 if (newln) {\r
1486                     log.println();\r
1487                 }\r
1488                 log.flush();\r
1489             }\r
1490 \r
1491             if (level == ERR) {\r
1492                 if (!nothrow) {\r
1493                     throw new RuntimeException(message);\r
1494                 }\r
1495                 if (!suppressIndent && errorSummary != null && stack !=null \r
1496                         && (errorCount == stack.ec + 1)) {\r
1497                     stack.appendPath(errorSummary);\r
1498                     errorSummary.append("\n");\r
1499                 }\r
1500             }\r
1501 \r
1502             suppressIndent = !newln;\r
1503         }\r
1504 \r
1505         private void writeTestInvalid(String name, boolean nodataArg) {\r
1506             //              msg("***" + name + "*** not found or not valid.", WARN, true,\r
1507             // true);\r
1508             if (inDocMode()) {\r
1509                 if (!warnings) {\r
1510                     if (stack != null) {\r
1511                         stack.flush();\r
1512                     }\r
1513                     log.println(" *** Target not found or not valid.");\r
1514                     log.flush();\r
1515                     needLineFeed = false;\r
1516                 }\r
1517             } else {\r
1518                 if(!nodataArg){\r
1519                     msg("Test " + name + " not found or not valid.", WARN, true,\r
1520                         true);\r
1521                 }\r
1522             }\r
1523         }\r
1524 \r
1525         long getmem() {\r
1526             long newmem = 0;\r
1527             if (memusage) {\r
1528                 Runtime rt = Runtime.getRuntime();\r
1529                 long lastmem = Long.MAX_VALUE;\r
1530                 do {\r
1531                     rt.gc();\r
1532                     rt.gc();\r
1533                     try {\r
1534                         Thread.sleep(50);\r
1535                     } catch (Exception e) {\r
1536                         break;\r
1537                     }\r
1538                     lastmem = newmem;\r
1539                     newmem = rt.totalMemory() - rt.freeMemory();\r
1540                 } while (newmem < lastmem);\r
1541             }\r
1542             return newmem;\r
1543         }\r
1544 \r
1545         private void writeTestResult() {\r
1546             if (inDocMode()) {\r
1547                 if (needLineFeed) {\r
1548                     log.println();\r
1549                     log.flush();\r
1550                 }\r
1551                 needLineFeed = false;\r
1552                 return;\r
1553             }\r
1554 \r
1555             long dmem = getmem() - stack.mem;\r
1556             long dtime = System.currentTimeMillis() - stack.millis;\r
1557 \r
1558             int testDelta = testCount - stack.tc;\r
1559             if (testDelta == 0) {\r
1560                 return;\r
1561             }\r
1562 \r
1563             int errorDelta = errorCount - stack.ec;\r
1564             int invalidDelta = invalidCount - stack.ic;\r
1565 \r
1566             stack.flush();\r
1567 \r
1568             if (!needLineFeed) {\r
1569                 indent(indentLevel);\r
1570                 log.print("}");\r
1571             }\r
1572             needLineFeed = false;\r
1573 \r
1574             if (memusage || dtime >= timing) {\r
1575                 log.print(" (");\r
1576                 if (memusage) {\r
1577                     log.print("dmem: " + dmem);\r
1578                 }\r
1579                 if (dtime >= timing) {\r
1580                     if (memusage) {\r
1581                         log.print(", ");\r
1582                     }\r
1583                     log.print(tformat.format(dtime / 1000f));\r
1584                 }\r
1585                 log.print(")");\r
1586             }\r
1587 \r
1588             if (errorDelta != 0) {\r
1589                 log.println(" FAILED ("\r
1590                         + errorDelta\r
1591                         + " failures"\r
1592                         + ((invalidDelta != 0) ? ", " + invalidDelta\r
1593                                 + " tests skipped)" : ")"));\r
1594             } else if (invalidDelta != 0) {\r
1595                 log.println(" Qualified (" + invalidDelta + " tests skipped)");\r
1596             } else {\r
1597                 log.println(" Passed");\r
1598             }\r
1599         }\r
1600 \r
1601         private final void indent(int distance) {\r
1602             boolean idm = inDocMode();\r
1603             if (needLineFeed) {\r
1604                 if (idm) {\r
1605                     log.println();\r
1606                 } else {\r
1607                     log.println(" {");\r
1608                 }\r
1609                 needLineFeed = false;\r
1610             }\r
1611 \r
1612             log.print(spaces.substring(0, distance * (idm ? 3 : 2)));\r
1613 \r
1614             if (idm) {\r
1615                 log.print("-- ");\r
1616             }\r
1617         }\r
1618     }\r
1619 \r
1620     public String getTranslitTestFilter() {\r
1621         return params.tfilter;\r
1622     }\r
1623 \r
1624     /**\r
1625      * Return the target name for a test class. This is either the end of the\r
1626      * class name, or if the class declares a public static field\r
1627      * CLASS_TARGET_NAME, the value of that field.\r
1628      */\r
1629     private static String getClassTargetName(Class testClass) {\r
1630         String name = testClass.getName();\r
1631         try {\r
1632             Field f = testClass.getField("CLASS_TARGET_NAME");\r
1633             name = (String) f.get(null);\r
1634         } catch (IllegalAccessException e) {\r
1635             throw new IllegalStateException(\r
1636                     "static field CLASS_TARGET_NAME must be accessible");\r
1637         } catch (NoSuchFieldException e) {\r
1638             int n = Math.max(name.lastIndexOf('.'), name.lastIndexOf('$'));\r
1639             if (n != -1) {\r
1640                 name = name.substring(n + 1);\r
1641             }\r
1642         }\r
1643         return name;\r
1644     }\r
1645 \r
1646     /**\r
1647      * Check the given array to see that all the strings in the expected array\r
1648      * are present.\r
1649      * \r
1650      * @param msg\r
1651      *            string message, for log output\r
1652      * @param array\r
1653      *            array of strings to check\r
1654      * @param expected\r
1655      *            array of strings we expect to see, or null\r
1656      * @return the length of 'array', or -1 on error\r
1657      */\r
1658     protected int checkArray(String msg, String array[], String expected[]) {\r
1659         int explen = (expected != null) ? expected.length : 0;\r
1660         if (!(explen >= 0 && explen < 31)) { // [sic] 31 not 32\r
1661             errln("Internal error");\r
1662             return -1;\r
1663         }\r
1664         int i = 0;\r
1665         StringBuffer buf = new StringBuffer();\r
1666         int seenMask = 0;\r
1667         for (; i < array.length; ++i) {\r
1668             String s = array[i];\r
1669             if (i != 0)\r
1670                 buf.append(", ");\r
1671             buf.append(s);\r
1672             // check expected list\r
1673             for (int j = 0, bit = 1; j < explen; ++j, bit <<= 1) {\r
1674                 if ((seenMask & bit) == 0) {\r
1675                     if (s.equals(expected[j])) {\r
1676                         seenMask |= bit;\r
1677                         logln("Ok: \"" + s + "\" seen");\r
1678                     }\r
1679                 }\r
1680             }\r
1681         }\r
1682         logln(msg + " = [" + buf + "] (" + i + ")");\r
1683         // did we see all expected strings?\r
1684         if (((1 << explen) - 1) != seenMask) {\r
1685             for (int j = 0, bit = 1; j < expected.length; ++j, bit <<= 1) {\r
1686                 if ((seenMask & bit) == 0) {\r
1687                     errln("\"" + expected[j] + "\" not seen");\r
1688                 }\r
1689             }\r
1690         }\r
1691         return array.length;\r
1692     }\r
1693 \r
1694     /**\r
1695      * Check the given array to see that all the locales in the expected array\r
1696      * are present.\r
1697      * \r
1698      * @param msg\r
1699      *            string message, for log output\r
1700      * @param array\r
1701      *            array of locales to check\r
1702      * @param expected\r
1703      *            array of locales names we expect to see, or null\r
1704      * @return the length of 'array'\r
1705      */\r
1706     protected int checkArray(String msg, Locale array[], String expected[]) {\r
1707         String strs[] = new String[array.length];\r
1708         for (int i = 0; i < array.length; ++i)\r
1709             strs[i] = array[i].toString();\r
1710         return checkArray(msg, strs, expected);\r
1711     }\r
1712 \r
1713     /**\r
1714      * Check the given array to see that all the locales in the expected array\r
1715      * are present.\r
1716      * \r
1717      * @param msg\r
1718      *            string message, for log output\r
1719      * @param array\r
1720      *            array of locales to check\r
1721      * @param expected\r
1722      *            array of locales names we expect to see, or null\r
1723      * @return the length of 'array'\r
1724      */\r
1725     protected int checkArray(String msg, ULocale array[], String expected[]) {\r
1726         String strs[] = new String[array.length];\r
1727         for (int i = 0; i < array.length; ++i)\r
1728             strs[i] = array[i].toString();\r
1729         return checkArray(msg, strs, expected);\r
1730     }\r
1731 \r
1732     // JUnit-like assertions.\r
1733 \r
1734     protected boolean assertTrue(String message, boolean condition) {\r
1735         return handleAssert(condition, message, "true", null);\r
1736     }\r
1737 \r
1738     protected boolean assertFalse(String message, boolean condition) {\r
1739         return handleAssert(!condition, message, "false", null);\r
1740     }\r
1741 \r
1742     protected boolean assertEquals(String message, boolean expected,\r
1743             boolean actual) {\r
1744         return handleAssert(expected == actual, message, String\r
1745                 .valueOf(expected), String.valueOf(actual));\r
1746     }\r
1747 \r
1748     protected boolean assertEquals(String message, long expected, long actual) {\r
1749         return handleAssert(expected == actual, message, String\r
1750                 .valueOf(expected), String.valueOf(actual));\r
1751     }\r
1752 \r
1753     // do NaN and range calculations to precision of float, don't rely on\r
1754     // promotion to double\r
1755     protected boolean assertEquals(String message, float expected,\r
1756             float actual, double error) {\r
1757         boolean result = Float.isInfinite(expected)\r
1758                 ? expected == actual\r
1759                 : !(Math.abs(expected - actual) > error); // handles NaN\r
1760         return handleAssert(result, message, String.valueOf(expected)\r
1761                 + (error == 0 ? "" : " (within " + error + ")"), String\r
1762                 .valueOf(actual));\r
1763     }\r
1764 \r
1765     protected boolean assertEquals(String message, double expected,\r
1766             double actual, double error) {\r
1767         boolean result = Double.isInfinite(expected)\r
1768                 ? expected == actual\r
1769                 : !(Math.abs(expected - actual) > error); // handles NaN\r
1770         return handleAssert(result, message, String.valueOf(expected)\r
1771                 + (error == 0 ? "" : " (within " + error + ")"), String\r
1772                 .valueOf(actual));\r
1773     }\r
1774 \r
1775     protected boolean assertEquals(String message, Object expected,\r
1776             Object actual) {\r
1777         boolean result = expected == null ? actual == null : expected\r
1778                 .equals(actual);\r
1779         return handleAssert(result, message, stringFor(expected),\r
1780                 stringFor(actual));\r
1781     }\r
1782 \r
1783     protected boolean assertNotEquals(String message, Object expected,\r
1784             Object actual) {\r
1785         boolean result = !(expected == null ? actual == null : expected\r
1786                 .equals(actual));\r
1787         return handleAssert(result, message, stringFor(expected),\r
1788                 stringFor(actual), "not equal to", true);\r
1789     }\r
1790 \r
1791     protected boolean assertSame(String message, Object expected, Object actual) {\r
1792         return handleAssert(expected == actual, message, stringFor(expected),\r
1793                 stringFor(actual), "==", false);\r
1794     }\r
1795 \r
1796     protected boolean assertNotSame(String message, Object expected,\r
1797             Object actual) {\r
1798         return handleAssert(expected != actual, message, stringFor(expected),\r
1799                 stringFor(actual), "!=", true);\r
1800     }\r
1801 \r
1802     protected boolean assertNull(String message, Object actual) {\r
1803         return handleAssert(actual == null, message, null, stringFor(actual));\r
1804     }\r
1805 \r
1806     protected boolean assertNotNull(String message, Object actual) {\r
1807         return handleAssert(actual != null, message, null, stringFor(actual),\r
1808                 "!=", true);\r
1809     }\r
1810 \r
1811     protected void fail(String message) {\r
1812         errln(message);\r
1813     }\r
1814 \r
1815     private boolean handleAssert(boolean result, String message,\r
1816             String expected, String actual) {\r
1817         return handleAssert(result, message, expected, actual, null, false);\r
1818     }\r
1819 \r
1820     public boolean handleAssert(boolean result, String message,\r
1821             Object expected, Object actual, String relation, boolean flip) {\r
1822         if (!result || isVerbose()) {\r
1823             message = message == null ? "" : " " + message;\r
1824             relation = relation == null ? ", got " : " " + relation + " ";\r
1825             if (result) {\r
1826                 logln("OK" + message + ": "\r
1827                         + (flip ? expected + relation + actual : expected));\r
1828             } else {\r
1829                 // assert must assume errors are true errors and not just warnings\r
1830                 // so cannot warnln here\r
1831                 errln(message\r
1832                         + ": expected"\r
1833                         + (flip ? relation + expected : " " + expected\r
1834                                 + (actual != null ? relation + actual : "")));\r
1835             }\r
1836         }\r
1837         return result;\r
1838     }\r
1839 \r
1840     private final String stringFor(Object obj) {\r
1841         if (obj == null)\r
1842             return "null";\r
1843         if (obj instanceof String)\r
1844             return "\"" + obj + '"';\r
1845         return obj.getClass().getName() + "<" + obj + ">";\r
1846     }\r
1847 \r
1848     // End JUnit-like assertions\r
1849 \r
1850     // PrintWriter support\r
1851 \r
1852     public PrintWriter getErrorLogPrintWriter() {\r
1853         return new PrintWriter(new TestLogWriter(this, TestLog.ERR));\r
1854     }\r
1855 \r
1856     public PrintWriter getLogPrintWriter() {\r
1857         return new PrintWriter(new TestLogWriter(this, TestLog.LOG));\r
1858     }\r
1859 \r
1860     // end PrintWriter support\r
1861 \r
1862     protected TestParams params = null;\r
1863 \r
1864     private final static String spaces = "                                          ";\r
1865     \r
1866 }\r