2 *******************************************************************************
\r
3 * Copyright (C) 1996-2009, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
7 package com.ibm.icu.dev.test;
\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
28 import com.ibm.icu.text.UTF16;
\r
29 import com.ibm.icu.util.TimeZone;
\r
30 import com.ibm.icu.util.ULocale;
\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
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
43 public class TestFmwk extends AbstractTestLog {
\r
45 * The default time zone for all of our tests. Used in Target.run();
\r
47 private final static TimeZone defaultTimeZone = TimeZone.getTimeZone("PST");
\r
50 * The default locale used for all of our tests. Used in Target.run();
\r
52 private final static Locale defaultLocale = Locale.US;
\r
54 public static final class TestFmwkException extends Exception {
\r
58 private static final long serialVersionUID = -3051148210247229194L;
\r
60 TestFmwkException(String msg) {
\r
64 protected void handleException(Throwable e){
\r
65 Throwable ex = e.getCause();
\r
69 if(ex instanceof ExceptionInInitializerError){
\r
70 ex = ((ExceptionInInitializerError)ex).getException();
\r
72 String msg = ex.getMessage();
\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
81 } else if (params.nothrow) {
\r
83 ex.printStackTrace();
\r
85 ex.printStackTrace();
\r
86 throw new RuntimeException(msg);
\r
89 if (params.nothrow) {
\r
91 ex.printStackTrace();
\r
94 ex.printStackTrace();
\r
95 throw new RuntimeException(msg);
\r
99 // use this instead of new random so we get a consistent seed
\r
101 protected Random createRandom() {
\r
102 return new Random(params.seed);
\r
106 * A test that has no test methods itself, but instead runs other tests.
\r
108 * This overrides methods are getTargets and getSubtest from TestFmwk.
\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
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
121 public static abstract class TestGroup extends TestFmwk {
\r
122 private String defaultPackage;
\r
123 private String[] names;
\r
124 private String description;
\r
126 private Class[] tests; // deferred init
\r
129 * Constructor that takes a default package name and a list of class
\r
130 * names. Adopts and modifies the classname list
\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
138 if (defaultPackage == null) {
\r
139 defaultPackage = getClass().getPackage().getName();
\r
141 defaultPackage = defaultPackage + ".";
\r
143 this.defaultPackage = defaultPackage;
\r
144 this.names = classnames;
\r
145 this.description = description;
\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
152 protected TestGroup(String[] classnames, String description) {
\r
153 this(null, classnames, description);
\r
157 * Constructor that takes a list of class names, and uses the package
\r
158 * for this class as the default package.
\r
160 protected TestGroup(String[] classnames) {
\r
161 this(null, classnames, null);
\r
164 protected String getDescription() {
\r
165 return description;
\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
175 TestFmwk test = getSubtest(targetName);
\r
176 if (test != null) {
\r
177 target = test.new ClassTarget();
\r
179 target = this.new Target(targetName);
\r
181 } catch (TestFmwkException e) {
\r
182 target = this.new Target(targetName);
\r
184 } else if (params.doRecurse()) {
\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
194 newTarget = this.new Target(names[i]);
\r
196 TestFmwk test = getSubtest(i, groupOnly);
\r
197 if (test != null) {
\r
198 newTarget = test.new ClassTarget();
\r
201 newTarget = this.new EmptyTarget(names[i]);
\r
203 newTarget = this.new Target(names[i]);
\r
207 if (newTarget != null) {
\r
208 newTarget.setNext(target);
\r
209 target = newTarget;
\r
216 protected TestFmwk getSubtest(String testName) throws TestFmwkException {
\r
219 for (int i = 0; i < names.length; ++i) {
\r
220 if (names[i].equalsIgnoreCase(testName)) { // allow
\r
221 // case-insensitive
\r
223 return getSubtest(i, false);
\r
226 throw new TestFmwkException(testName);
\r
229 private TestFmwk getSubtest(int i, boolean groupOnly) {
\r
230 Class cls = tests[i];
\r
232 if (groupOnly && !TestGroup.class.isAssignableFrom(cls)) {
\r
237 TestFmwk subtest = (TestFmwk) cls.newInstance();
\r
238 subtest.params = params;
\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
249 private void finishInit() {
\r
250 if (tests == null) {
\r
251 tests = new Class[names.length];
\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
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
266 names[i] = getClassTargetName(cls);
\r
267 } catch (ClassNotFoundException e) {
\r
268 // leave tests[i] null and name as classname
\r
276 * The default target is invalid.
\r
278 public class Target {
\r
279 private Target next;
\r
280 public final String name;
\r
282 public Target(String name) {
\r
286 public Target setNext(Target next) {
\r
291 public Target getNext() {
\r
295 public Target append(Target targets) {
\r
297 while(t.next != null) {
\r
304 public void run() throws Exception {
\r
307 ++params.invalidCount;
\r
309 Locale.setDefault(defaultLocale);
\r
310 TimeZone.setDefault(defaultTimeZone);
\r
313 params.writeTestInvalid(name, params.nodata);
\r
315 params.push(name, getDescription(), f == 1);
\r
322 protected int filter() {
\r
323 return params.filter(name);
\r
326 protected boolean validate() {
\r
330 protected String getDescription() {
\r
334 protected void execute() throws Exception{
\r
338 public class EmptyTarget extends Target {
\r
339 public EmptyTarget(String name) {
\r
343 protected boolean validate() {
\r
348 public class MethodTarget extends Target {
\r
349 private Method testMethod;
\r
351 public MethodTarget(String name, Method method) {
\r
353 testMethod = method;
\r
356 protected boolean validate() {
\r
357 return testMethod != null && validateMethod(name);
\r
360 protected String getDescription() {
\r
361 return getMethodDescription(name);
\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
370 final Object[] NO_ARGS = new Object[0];
\r
372 ++params.testCount;
\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
390 handleException(e);
\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
401 params.stack.appendPath(params.timeLog);
\r
402 params.timeLog.append(" (" + deltaSec + "s" + ")\n");
\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
415 public class ClassTarget extends Target {
\r
418 public ClassTarget() {
\r
422 public ClassTarget(String targetName) {
\r
423 super(getClassTargetName(TestFmwk.this.getClass()));
\r
424 this.targetName = targetName;
\r
427 protected boolean validate() {
\r
428 return TestFmwk.this.validate();
\r
431 protected String getDescription() {
\r
432 return TestFmwk.this.getDescription();
\r
435 protected void execute() throws Exception {
\r
436 params.indentLevel++;
\r
437 Target target = randomize(getTargets(targetName));
\r
438 while (target != null) {
\r
440 target = target.next;
\r
442 params.indentLevel--;
\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
453 Target[] arr = (Target[]) list.toArray(new Target[list.size()]);
\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
463 return ((Target) rhs).name
\r
464 .compareTo(((Target) lhs).name);
\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
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
484 t = arr[0].setNext(t); // new first element
\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
497 protected TestFmwk() {
\r
500 protected void init() throws Exception{
\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
511 * This method never returns, since it always exits with System.exit();
\r
513 public void run(String[] args) {
\r
514 System.exit(run(args, new PrintWriter(System.out)));
\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
522 public int run(String[] args, PrintWriter log) {
\r
523 boolean prompt = false;
\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
536 while (wx < args.length) {
\r
540 TestParams localParams = TestParams.create(args, log);
\r
541 if (localParams == null) {
\r
545 int errorCount = runTests(localParams, args);
\r
547 if (localParams.seed != 0) {
\r
548 localParams.log.println("-random:" + localParams.seed);
\r
549 localParams.log.flush();
\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
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
564 System.out.println("Hit RETURN to exit...");
\r
565 System.out.flush();
\r
568 } catch (IOException e) {
\r
569 localParams.log.println("Exception: " + e.toString() + e.getMessage());
\r
576 public int runTests(TestParams _params, String[] tests) {
\r
579 StringBuffer summary = null;
\r
581 if (tests.length == 0 || tests[0] == null) { // no args
\r
583 resolveTarget(_params).run();
\r
584 ec = _params.errorCount;
\r
586 for (int i = 0; i < tests.length ; ++i) {
\r
587 if (tests[i] == null) continue;
\r
590 _params.log.println();
\r
594 resolveTarget(_params, tests[i]).run();
\r
595 ec += _params.errorCount;
\r
597 if (_params.errorSummary != null && _params.errorSummary.length() > 0) {
\r
598 if (summary == null) {
\r
599 summary = new StringBuffer();
\r
601 summary.append("\nTest Root: " + tests[i] + "\n");
\r
602 summary.append(_params.errorSummary());
\r
605 _params.errorSummary = summary;
\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
617 * Return a ClassTarget for this test. Params is set on this test.
\r
619 public Target resolveTarget(TestParams paramsArg) {
\r
620 this.params = paramsArg;
\r
621 return new ClassTarget();
\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
631 public Target resolveTarget(TestParams paramsArg, String targetPath) {
\r
632 TestFmwk test = this;
\r
633 test.params = paramsArg;
\r
635 if (targetPath != null) {
\r
636 if (targetPath.length() == 0) {
\r
640 int e = targetPath.length();
\r
642 // trim all leading and trailing '/'
\r
643 while (targetPath.charAt(p) == '/') {
\r
646 while (e > p && targetPath.charAt(e - 1) == '/') {
\r
649 if (p > 0 || e < targetPath.length()) {
\r
650 targetPath = targetPath.substring(p, e - p);
\r
652 e = targetPath.length();
\r
657 int n = targetPath.indexOf('/');
\r
658 String prefix = n == -1 ? targetPath : targetPath
\r
660 TestFmwk subtest = test.getSubtest(prefix);
\r
662 if (subtest == null) {
\r
673 targetPath = targetPath.substring(n + 1);
\r
675 } catch (TestFmwkException ex) {
\r
676 return test.new Target(targetPath);
\r
681 return test.new ClassTarget(targetPath);
\r
685 * Return true if we can run this test (allows test to inspect jvm,
\r
686 * environment, params before running)
\r
688 protected boolean validate() {
\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
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
700 protected Target getTargets(String targetName) {
\r
701 return getClassTargets(getClass(), targetName);
\r
704 protected Target getClassTargets(Class cls, String targetName) {
\r
709 Target target = null;
\r
710 if (targetName != null) {
\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
718 } catch (SecurityException e) {
\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
734 if (inheritTargets()) {
\r
735 Target parentTarget = getClassTargets(cls.getSuperclass(), targetName);
\r
736 if (parentTarget == null) {
\r
739 if (target == null) {
\r
740 return parentTarget;
\r
742 return parentTarget.append(target);
\r
748 protected boolean inheritTargets() {
\r
752 protected String getDescription() {
\r
756 protected boolean validateMethod(String name) {
\r
760 protected String getMethodDescription(String name) {
\r
764 // method tests have no subtests, group tests override
\r
765 protected TestFmwk getSubtest(String prefix) throws TestFmwkException {
\r
769 public boolean isVerbose() {
\r
770 return params.verbose;
\r
773 public boolean noData() {
\r
774 return params.nodata;
\r
777 public boolean isTiming() {
\r
778 return params.timing < Long.MAX_VALUE;
\r
781 public boolean isMemTracking() {
\r
782 return params.memusage;
\r
786 * 0 = fewest tests, 5 is normal build, 10 is most tests
\r
788 public int getInclusion() {
\r
789 return params.inclusion;
\r
792 public boolean isModularBuild() {
\r
793 return params.warnings;
\r
796 public boolean isQuick() {
\r
797 return params.inclusion == 0;
\r
800 public void msg(String message, int level, boolean incCount, boolean newln) {
\r
801 params.msg(message, level, incCount, newln);
\r
804 protected int getErrorCount() {
\r
805 return params.errorCount;
\r
808 public String getProperty(String key) {
\r
810 if (key != null && key.length() > 0 && params.props != null) {
\r
811 val = (String)params.props.get(key.toLowerCase());
\r
816 protected TimeZone safeGetTimeZone(String id) {
\r
817 TimeZone tz = TimeZone.getTimeZone(id);
\r
819 // should never happen
\r
820 errln("FAIL: TimeZone.getTimeZone(" + id + ") => null");
\r
822 if (!tz.getID().equals(id)) {
\r
823 warnln("FAIL: TimeZone.getTimeZone(" + id + ") => " + tz.getID());
\r
829 * Print a usage message for this test class.
\r
831 public void usage() {
\r
832 usage(new PrintWriter(System.out), getClass().getName());
\r
835 public static void usage(PrintWriter pw, String className) {
\r
836 pw.println("Usage: " + className + " option* target*");
\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
863 // pw.println(" -m[emory] print memory usage and force gc for
\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
880 pw.println(" If a list or describe option is provided, no tests are run.");
\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
889 pw.println(" If multiple targets are provided, each is executed in order.");
\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
898 return result.toString();
\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
906 return result.toString();
\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
914 return result + foo;
\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
923 return result + foo;
\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
930 result.append(',');
\r
931 result.append(hex(s.charAt(i)));
\r
933 return result.toString();
\r
936 public static String hex(StringBuffer s) {
\r
937 return hex(s.toString());
\r
939 public static String prettify(String s) {
\r
940 StringBuffer result = new StringBuffer();
\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
954 result.append((char) ch);
\r
958 return result.toString();
\r
960 public static String prettify(StringBuffer s) {
\r
961 return prettify(s.toString());
\r
964 private static java.util.GregorianCalendar cal;
\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
972 * use 2000 for 2000, unlike new Date()
\r
974 * use Calendar.JANUARY etc.
\r
976 * day of month, 1-based
\r
977 * @return a Date object for the given y/m/d
\r
979 protected static synchronized java.util.Date getDate(int year, int month,
\r
982 cal = new java.util.GregorianCalendar();
\r
985 cal.set(year, month, dom);
\r
986 return cal.getTime();
\r
989 public static class NullWriter extends PrintWriter {
\r
990 public NullWriter() {
\r
991 super(System.out, false);
\r
993 public void write(int c) {
\r
995 public void write(char[] buf, int off, int len) {
\r
997 public void write(String s, int off, int len) {
\r
999 public void println() {
\r
1003 public static class ASCIIWriter extends PrintWriter {
\r
1004 private StringBuffer buffer = new StringBuffer();
\r
1006 // Characters that we think are printable but that escapeUnprintable
\r
1008 private static final String PRINTABLES = "\t\n\r";
\r
1010 public ASCIIWriter(Writer w, boolean autoFlush) {
\r
1011 super(w, autoFlush);
\r
1014 public ASCIIWriter(OutputStream os, boolean autoFlush) {
\r
1015 super(os, autoFlush);
\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
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
1048 public void write(String s, int off, int len) {
\r
1049 write(s.substring(off, off + len).toCharArray(), 0, len);
\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
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
1076 public String tfilter; // for transliterator tests
\r
1078 public State stack;
\r
1080 public StringBuffer errorSummary;
\r
1081 private StringBuffer timeLog;
\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
1096 private TestParams() {
\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
1104 return create(args, log);
\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
1116 public static TestParams create(String[] args, PrintWriter log) {
\r
1117 TestParams params = new TestParams();
\r
1120 params.log = new NullWriter();
\r
1122 params.log = new ASCIIWriter(log, true);
\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
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
1164 int n = arg.indexOf(':');
\r
1166 s = arg.substring(n + 1);
\r
1167 arg = arg.substring(0, n);
\r
1170 if (arg.equals("-r") || arg.equals("-random")) {
\r
1172 params.seed = System.currentTimeMillis();
\r
1174 params.seed = Long.parseLong(s);
\r
1177 log.println("*** Error: unrecognized argument: " + arg);
\r
1178 usageError = true;
\r
1181 } else if (arg.startsWith("-e")) {
\r
1183 params.inclusion = (arg.length() == 2)
\r
1185 : Integer.parseInt(arg.substring(2));
\r
1186 if (params.inclusion < 0 || params.inclusion > 10) {
\r
1187 usageError = true;
\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
1194 int inx = arg.indexOf(':');
\r
1196 String num = arg.substring(inx + 1);
\r
1198 val = Long.parseLong(num);
\r
1199 } catch (Exception e) {
\r
1200 log.println("*** Error: could not parse time threshold '"
\r
1202 usageError = true;
\r
1206 params.timing = val;
\r
1208 fmt = "#,##0.000s";
\r
1209 } else if (val <= 100) {
\r
1210 fmt = "#,##0.00s";
\r
1211 } else if (val <= 1000) {
\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
1226 } else if (arg.startsWith("-prop:")) {
\r
1227 String temp = arg.substring(6);
\r
1228 int eql = temp.indexOf('=');
\r
1230 log.println("*** Error: could not parse custom property '" + arg + "'");
\r
1231 usageError = true;
\r
1234 if (params.props == null) {
\r
1235 params.props = new HashMap();
\r
1237 params.props.put(temp.substring(0, eql), temp.substring(eql+1));
\r
1239 log.println("*** Error: unrecognized argument: "
\r
1241 usageError = true;
\r
1245 args[wx++] = arg; // shift down
\r
1249 while (wx < args.length) {
\r
1250 args[wx++] = null;
\r
1254 params.tformat = new DecimalFormat(fmt);
\r
1257 usage(log, "TestAll");
\r
1261 if (filter != null) {
\r
1262 params.filter = filter.toLowerCase();
\r
1270 public String errorSummary() {
\r
1271 return errorSummary == null ? "" : errorSummary.toString();
\r
1274 public void init() {
\r
1276 needLineFeed = false;
\r
1277 suppressIndent = false;
\r
1282 random = seed == 0 ? null : new Random(seed);
\r
1285 public class State {
\r
1288 StringBuffer buffer;
\r
1295 public boolean included;
\r
1299 public State(State link, String name, boolean included) {
\r
1302 if (link == null) {
\r
1304 this.included = included;
\r
1306 this.level = link.level + 1;
\r
1307 this.included = included || link.included;
\r
1309 this.ec = errorCount;
\r
1310 this.wc = warnCount;
\r
1311 this.ic = invalidCount;
\r
1312 this.tc = testCount;
\r
1314 if (link == null || this.included) {
\r
1319 millis = System.currentTimeMillis();
\r
1324 if (link != null) {
\r
1334 needLineFeed = true;
\r
1338 void appendPath(StringBuffer buf) {
\r
1339 if (this.link != null) {
\r
1340 this.link.appendPath(buf);
\r
1347 public void push(String name, String description, boolean included) {
\r
1348 if (inDocMode() && describe && description != null) {
\r
1349 name += ": " + description;
\r
1351 stack = new State(stack, name, included);
\r
1354 public void pop() {
\r
1355 if (stack != null) {
\r
1356 writeTestResult();
\r
1357 stack = stack.link;
\r
1361 public boolean inDocMode() {
\r
1362 return describe || listlevel != 0;
\r
1365 public boolean doMethods() {
\r
1366 return !inDocMode() || listlevel == 3
\r
1367 || (indentLevel == 1 && listlevel > 0);
\r
1370 public boolean doRecurse() {
\r
1371 return !inDocMode() || listlevel > 1
\r
1372 || (indentLevel == 1 && listlevel > 0);
\r
1375 public boolean doRecurseGroupsOnly() {
\r
1376 return inDocMode()
\r
1377 && (listlevel == 2 || (indentLevel == 1 && listlevel > 0));
\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
1386 if (filter == null) {
\r
1389 boolean noIncludes = true;
\r
1390 boolean noExcludes = filter.indexOf('^') == -1;
\r
1391 testName = testName.toLowerCase();
\r
1393 while (ix < filter.length()) {
\r
1394 int nix = filter.indexOf(',', ix);
\r
1396 nix = filter.length();
\r
1398 if (filter.charAt(ix) == '^') {
\r
1399 if (testName.indexOf(filter.substring(ix + 1, nix)) != -1) {
\r
1404 noIncludes = false;
\r
1405 if (testName.indexOf(filter.substring(ix, nix)) != -1) {
\r
1415 if (result == 0 && noIncludes) {
\r
1419 // System.out.println("filter: " + testName + " returns: " +
\r
1426 * @param msg The string message to write
\r
1428 public void write(String msg) {
\r
1429 write(msg, false);
\r
1432 public void writeln(String msg) {
\r
1436 private void write(String msg, boolean newln) {
\r
1437 if (!suppressIndent) {
\r
1438 if (needLineFeed) {
\r
1440 needLineFeed = false;
\r
1442 log.print(spaces.substring(0, indentLevel * 2));
\r
1449 suppressIndent = !newln;
\r
1452 private void msg(String message, int level, boolean incCount,
\r
1454 if (level == WARN && (!warnings && !nodata)){
\r
1459 if (level == WARN) {
\r
1462 } else if (level == ERR) {
\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
1475 log.print(message);
\r
1482 if (level == ERR) {
\r
1484 throw new RuntimeException(message);
\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
1493 suppressIndent = !newln;
\r
1496 private void writeTestInvalid(String name, boolean nodataArg) {
\r
1497 // msg("***" + name + "*** not found or not valid.", WARN, true,
\r
1499 if (inDocMode()) {
\r
1501 if (stack != null) {
\r
1504 log.println(" *** Target not found or not valid.");
\r
1506 needLineFeed = false;
\r
1510 msg("Test " + name + " not found or not valid.", WARN, true,
\r
1519 Runtime rt = Runtime.getRuntime();
\r
1520 long lastmem = Long.MAX_VALUE;
\r
1526 } catch (Exception e) {
\r
1530 newmem = rt.totalMemory() - rt.freeMemory();
\r
1531 } while (newmem < lastmem);
\r
1536 private void writeTestResult() {
\r
1537 if (inDocMode()) {
\r
1538 if (needLineFeed) {
\r
1542 needLineFeed = false;
\r
1546 long dmem = getmem() - stack.mem;
\r
1547 long dtime = System.currentTimeMillis() - stack.millis;
\r
1549 int testDelta = testCount - stack.tc;
\r
1550 if (testDelta == 0) {
\r
1554 int errorDelta = errorCount - stack.ec;
\r
1555 int invalidDelta = invalidCount - stack.ic;
\r
1559 if (!needLineFeed) {
\r
1560 indent(indentLevel);
\r
1563 needLineFeed = false;
\r
1565 if (memusage || dtime >= timing) {
\r
1568 log.print("dmem: " + dmem);
\r
1570 if (dtime >= timing) {
\r
1574 log.print(tformat.format(dtime / 1000f));
\r
1579 if (errorDelta != 0) {
\r
1580 log.println(" FAILED ("
\r
1583 + ((invalidDelta != 0) ? ", " + invalidDelta
\r
1584 + " tests skipped)" : ")"));
\r
1585 } else if (invalidDelta != 0) {
\r
1586 log.println(" Qualified (" + invalidDelta + " tests skipped)");
\r
1588 log.println(" Passed");
\r
1592 private final void indent(int distance) {
\r
1593 boolean idm = inDocMode();
\r
1594 if (needLineFeed) {
\r
1598 log.println(" {");
\r
1600 needLineFeed = false;
\r
1603 log.print(spaces.substring(0, distance * (idm ? 3 : 2)));
\r
1611 public String getTranslitTestFilter() {
\r
1612 return params.tfilter;
\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
1620 private static String getClassTargetName(Class testClass) {
\r
1621 String name = testClass.getName();
\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
1631 name = name.substring(n + 1);
\r
1638 * Check the given array to see that all the strings in the expected array
\r
1642 * string message, for log output
\r
1644 * array of strings to check
\r
1646 * array of strings we expect to see, or null
\r
1647 * @return the length of 'array', or -1 on error
\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
1656 StringBuffer buf = new StringBuffer();
\r
1658 for (; i < array.length; ++i) {
\r
1659 String s = array[i];
\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
1668 logln("Ok: \"" + s + "\" seen");
\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
1682 return array.length;
\r
1686 * Check the given array to see that all the locales in the expected array
\r
1690 * string message, for log output
\r
1692 * array of locales to check
\r
1694 * array of locales names we expect to see, or null
\r
1695 * @return the length of 'array'
\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
1705 * Check the given array to see that all the locales in the expected array
\r
1709 * string message, for log output
\r
1711 * array of locales to check
\r
1713 * array of locales names we expect to see, or null
\r
1714 * @return the length of 'array'
\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
1723 // JUnit-like assertions.
\r
1725 protected boolean assertTrue(String message, boolean condition) {
\r
1726 return handleAssert(condition, message, "true", null);
\r
1729 protected boolean assertFalse(String message, boolean condition) {
\r
1730 return handleAssert(!condition, message, "false", null);
\r
1733 protected boolean assertEquals(String message, boolean expected,
\r
1735 return handleAssert(expected == actual, message, String
\r
1736 .valueOf(expected), String.valueOf(actual));
\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
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
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
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
1773 protected boolean assertEquals(String message, Object expected,
\r
1775 boolean result = expected == null ? actual == null : expected
\r
1777 return handleAssert(result, message, stringFor(expected),
\r
1778 stringFor(actual));
\r
1781 protected boolean assertNotEquals(String message, Object expected,
\r
1783 boolean result = !(expected == null ? actual == null : expected
\r
1785 return handleAssert(result, message, stringFor(expected),
\r
1786 stringFor(actual), "not equal to", true);
\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
1794 protected boolean assertNotSame(String message, Object expected,
\r
1796 return handleAssert(expected != actual, message, stringFor(expected),
\r
1797 stringFor(actual), "!=", true);
\r
1800 protected boolean assertNull(String message, Object actual) {
\r
1801 return handleAssert(actual == null, message, null, stringFor(actual));
\r
1804 protected boolean assertNotNull(String message, Object actual) {
\r
1805 return handleAssert(actual != null, message, null, stringFor(actual),
\r
1809 protected void fail(String message) {
\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
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
1825 if (!message.equals("")) {
\r
1826 message = ": " + message;
\r
1828 relation = relation == null ? ", got " : " " + relation + " ";
\r
1830 logln("OK " + testLocation + message + ": "
\r
1831 + (flip ? expected + relation + actual : expected));
\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
1837 + (flip ? relation + expected : " " + expected
\r
1838 + (actual != null ? relation + actual : "")));
\r
1844 private final String stringFor(Object obj) {
\r
1845 if (obj == null) {
\r
1848 if (obj instanceof String) {
\r
1849 return "\"" + obj + '"';
\r
1851 return obj.getClass().getName() + "<" + obj + ">";
\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
1863 throw new InternalError();
\r
1867 // End JUnit-like assertions
\r
1869 // PrintWriter support
\r
1871 public PrintWriter getErrorLogPrintWriter() {
\r
1872 return new PrintWriter(new TestLogWriter(this, TestLog.ERR));
\r
1875 public PrintWriter getLogPrintWriter() {
\r
1876 return new PrintWriter(new TestLogWriter(this, TestLog.LOG));
\r
1879 // end PrintWriter support
\r
1881 protected TestParams params = null;
\r
1883 private final static String spaces = " ";
\r