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