0001: //##header
0002: /*
0003: *******************************************************************************
0004: * Copyright (C) 1996-2006, International Business Machines Corporation and *
0005: * others. All Rights Reserved. *
0006: *******************************************************************************
0007: */
0008: package com.ibm.icu.dev.test;
0009:
0010: import com.ibm.icu.text.UTF16;
0011: import com.ibm.icu.text.DecimalFormat;
0012: import com.ibm.icu.text.NumberFormat;
0013: import com.ibm.icu.util.Calendar;
0014: import com.ibm.icu.util.TimeZone;
0015: import com.ibm.icu.util.ULocale;
0016: import java.io.ByteArrayOutputStream;
0017: import java.io.IOException;
0018: import java.io.OutputStream;
0019: import java.io.PrintStream;
0020: import java.io.PrintWriter;
0021: import java.io.Writer;
0022: import java.lang.reflect.Field;
0023: import java.lang.reflect.InvocationTargetException;
0024: import java.lang.reflect.Method;
0025: import java.util.ArrayList;
0026: import java.util.Arrays;
0027: import java.util.Comparator;
0028: import java.util.Date;
0029: import java.util.Locale;
0030: import java.util.MissingResourceException;
0031: import java.util.Random;
0032:
0033: //#ifdef FOUNDATION
0034: //## import com.ibm.icu.impl.Utility;
0035: //#endif
0036: /**
0037: * TestFmwk is a base class for tests that can be run conveniently from the
0038: * command line as well as under the Java test harness.
0039: * <p>
0040: * Sub-classes implement a set of methods named Test <something>. Each of these
0041: * methods performs some test. Test methods should indicate errors by calling
0042: * either err or errln. This will increment the errorCount field and may
0043: * optionally print a message to the log. Debugging information may also be
0044: * added to the log via the log and logln methods. These methods will add their
0045: * arguments to the log only if the test is being run in verbose mode.
0046: */
0047: public class TestFmwk extends AbstractTestLog {
0048: /**
0049: * Puts a copyright in the .class file
0050: */
0051: private static final String copyrightNotice = "Copyright \u00a91997-2003 IBM Corp. All rights reserved.";
0052:
0053: /**
0054: * The default time zone for all of our tests. Used in Target.run();
0055: */
0056: private final static TimeZone defaultTimeZone = TimeZone
0057: .getTimeZone("PST");
0058:
0059: /**
0060: * The default locale used for all of our tests. Used in Target.run();
0061: */
0062: private final static Locale defaultLocale = Locale.US;
0063:
0064: public static final class TestFmwkException extends Exception {
0065: TestFmwkException(String msg) {
0066: super (msg);
0067: }
0068: }
0069:
0070: protected void handleException(Throwable e) {
0071: //#ifndef FOUNDATION
0072: Throwable ex = e.getCause();
0073: //#else
0074: //## Throwable ex = null;
0075: //#endif
0076: if (ex == null) {
0077: ex = e;
0078: }
0079: if (ex instanceof ExceptionInInitializerError) {
0080: ex = ((ExceptionInInitializerError) ex).getException();
0081: }
0082: String msg = ex.getMessage();
0083: //System.err.println("TF handleException msg: " + msg);
0084: if (ex instanceof MissingResourceException
0085: || ex instanceof NoClassDefFoundError
0086: || msg.indexOf("java.util.MissingResourceException") >= 0) {
0087: if (params.warnings || params.nodata) {
0088: warnln(msg);
0089: } else if (params.nothrow) {
0090: errln(msg);
0091: ex.printStackTrace();
0092: } else {
0093: ex.printStackTrace();
0094: throw new RuntimeException(msg);
0095: }
0096: } else {
0097: if (params.nothrow) {
0098: errln(msg);
0099: ex.printStackTrace();
0100: } else {
0101: errln(msg);
0102: ex.printStackTrace();
0103: throw new RuntimeException(msg);
0104: }
0105: }
0106: }
0107:
0108: // use this instead of new random so we get a consistent seed
0109: // for our tests
0110: protected Random createRandom() {
0111: return new Random(params.seed);
0112: }
0113:
0114: /**
0115: * A test that has no test methods itself, but instead runs other tests.
0116: *
0117: * This overrides methods are getTargets and getSubtest from TestFmwk.
0118: *
0119: * If you want the default behavior, pass an array of class names and an
0120: * optional description to the constructor. The named classes must extend
0121: * TestFmwk. If a provided name doesn't include a ".", package name is
0122: * prefixed to it (the package of the current test is used if none was
0123: * provided in the constructor). The resulting full name is used to
0124: * instantiate an instance of the class using the default constructor.
0125: *
0126: * Class names are resolved to classes when getTargets or getSubtest is
0127: * called. This allows instances of TestGroup to be compiled and run without
0128: * all the targets they would normally invoke being available.
0129: */
0130: public static abstract class TestGroup extends TestFmwk {
0131: private String defaultPackage;
0132: private String[] names;
0133: private String description;
0134:
0135: private Class[] tests; // deferred init
0136:
0137: /**
0138: * Constructor that takes a default package name and a list of class
0139: * names. Adopts and modifies the classname list
0140: */
0141: protected TestGroup(String defaultPackage, String[] classnames,
0142: String description) {
0143: if (classnames == null) {
0144: throw new IllegalStateException(
0145: "classnames must not be null");
0146: }
0147:
0148: if (defaultPackage == null) {
0149: defaultPackage = getClass().getPackage().getName();
0150: }
0151: defaultPackage = defaultPackage + ".";
0152:
0153: this .defaultPackage = defaultPackage;
0154: this .names = classnames;
0155: this .description = description;
0156: }
0157:
0158: /**
0159: * Constructor that takes a list of class names and a description, and
0160: * uses the package for this class as the default package.
0161: */
0162: protected TestGroup(String[] classnames, String description) {
0163: this (null, classnames, description);
0164: }
0165:
0166: /**
0167: * Constructor that takes a list of class names, and uses the package
0168: * for this class as the default package.
0169: */
0170: protected TestGroup(String[] classnames) {
0171: this (null, classnames, null);
0172: }
0173:
0174: protected String getDescription() {
0175: return description;
0176: }
0177:
0178: protected Target getTargets(String targetName) {
0179: Target target = null;
0180: if (targetName != null) {
0181: finishInit(); // hmmm, want to get subtest without initializing
0182: // all tests
0183:
0184: try {
0185: TestFmwk test = getSubtest(targetName);
0186: if (test != null) {
0187: target = test.new ClassTarget();
0188: } else {
0189: target = this .new Target(targetName);
0190: }
0191: } catch (TestFmwkException e) {
0192: target = this .new Target(targetName);
0193: }
0194: } else if (params.doRecurse()) {
0195: finishInit();
0196: boolean groupOnly = params.doRecurseGroupsOnly();
0197: for (int i = names.length; --i >= 0;) {
0198: Target newTarget = null;
0199: Class cls = tests[i];
0200: if (cls == null) { // hack no warning for missing tests
0201: if (params.warnings) {
0202: continue;
0203: }
0204: newTarget = this .new Target(names[i]);
0205: } else {
0206: TestFmwk test = getSubtest(i, groupOnly);
0207: if (test != null) {
0208: newTarget = test.new ClassTarget();
0209: } else {
0210: if (groupOnly) {
0211: newTarget = this .new EmptyTarget(
0212: names[i]);
0213: } else {
0214: newTarget = this .new Target(names[i]);
0215: }
0216: }
0217: }
0218: if (newTarget != null) {
0219: newTarget.setNext(target);
0220: target = newTarget;
0221: }
0222: }
0223: }
0224:
0225: return target;
0226: }
0227:
0228: protected TestFmwk getSubtest(String testName)
0229: throws TestFmwkException {
0230: finishInit();
0231:
0232: for (int i = 0; i < names.length; ++i) {
0233: if (names[i].equalsIgnoreCase(testName)) { // allow
0234: // case-insensitive
0235: // matching
0236: return getSubtest(i, false);
0237: }
0238: }
0239: throw new TestFmwkException(testName);
0240: }
0241:
0242: private TestFmwk getSubtest(int i, boolean groupOnly) {
0243: Class cls = tests[i];
0244: if (cls != null) {
0245: if (groupOnly && !TestGroup.class.isAssignableFrom(cls)) {
0246: return null;
0247: }
0248:
0249: try {
0250: TestFmwk subtest = (TestFmwk) cls.newInstance();
0251: subtest.params = params;
0252: return subtest;
0253: } catch (InstantiationException e) {
0254: throw new IllegalStateException(e.getMessage());
0255: } catch (IllegalAccessException e) {
0256: throw new IllegalStateException(e.getMessage());
0257: }
0258: }
0259: return null;
0260: }
0261:
0262: private void finishInit() {
0263: if (tests == null) {
0264: tests = new Class[names.length];
0265:
0266: for (int i = 0; i < names.length; ++i) {
0267: String name = names[i];
0268: if (name.indexOf('.') == -1) {
0269: name = defaultPackage + name;
0270: }
0271: try {
0272: Class cls = Class.forName(name);
0273: if (!TestFmwk.class.isAssignableFrom(cls)) {
0274: throw new IllegalStateException("class "
0275: + name
0276: + " does not extend TestFmwk");
0277: }
0278:
0279: tests[i] = cls;
0280: names[i] = getClassTargetName(cls);
0281: } catch (ClassNotFoundException e) {
0282: // leave tests[i] null and name as classname
0283: }
0284: }
0285: }
0286: }
0287: }
0288:
0289: /**
0290: * The default target is invalid.
0291: */
0292: public class Target {
0293: private Target next;
0294: public final String name;
0295:
0296: public Target(String name) {
0297: this .name = name;
0298: }
0299:
0300: public Target setNext(Target next) {
0301: this .next = next;
0302: return this ;
0303: }
0304:
0305: public Target getNext() {
0306: return next;
0307: }
0308:
0309: public void run() throws Exception {
0310: int f = filter();
0311: if (f == -1) {
0312: ++params.invalidCount;
0313: } else {
0314: Locale.setDefault(defaultLocale);
0315: TimeZone.setDefault(defaultTimeZone);
0316:
0317: if (!validate()) {
0318: params.writeTestInvalid(name, params.nodata);
0319: } else {
0320: params.push(name, getDescription(), f == 1);
0321: execute();
0322: params.pop();
0323: }
0324: }
0325: }
0326:
0327: protected int filter() {
0328: return params.filter(name);
0329: }
0330:
0331: protected boolean validate() {
0332: return false;
0333: }
0334:
0335: protected String getDescription() {
0336: return null;
0337: }
0338:
0339: protected void execute() throws Exception {
0340: }
0341: }
0342:
0343: public class EmptyTarget extends Target {
0344: public EmptyTarget(String name) {
0345: super (name);
0346: }
0347:
0348: protected boolean validate() {
0349: return true;
0350: }
0351: }
0352:
0353: public class MethodTarget extends Target {
0354: private Method testMethod;
0355:
0356: public MethodTarget(String name, Method method) {
0357: super (name);
0358: testMethod = method;
0359: }
0360:
0361: protected boolean validate() {
0362: return testMethod != null && validateMethod(name);
0363: }
0364:
0365: protected String getDescription() {
0366: return getMethodDescription(name);
0367: }
0368:
0369: protected void execute() throws Exception {
0370: if (params.inDocMode()) {
0371: // nothing to execute
0372: } else if (!params.stack.included) {
0373: ++params.invalidCount;
0374: } else {
0375: final Object[] NO_ARGS = new Object[0];
0376: try {
0377: ++params.testCount;
0378: init();
0379: testMethod.invoke(TestFmwk.this , NO_ARGS);
0380: } catch (IllegalAccessException e) {
0381: errln("Can't access test method "
0382: + testMethod.getName());
0383: } catch (ExceptionInInitializerError e) {
0384: handleException(e);
0385: } catch (InvocationTargetException e) {
0386: handleException(e);
0387: } catch (MissingResourceException e) {
0388: handleException(e);
0389: } catch (NoClassDefFoundError e) {
0390: handleException(e);
0391: } catch (Exception o) {
0392: System.out.println(o);
0393: }
0394: }
0395: }
0396:
0397: protected String getStackTrace(InvocationTargetException e) {
0398: ByteArrayOutputStream bs = new ByteArrayOutputStream();
0399: PrintStream ps = new PrintStream(bs);
0400: e.getTargetException().printStackTrace(ps);
0401: return bs.toString();
0402: }
0403: }
0404:
0405: public class ClassTarget extends Target {
0406: String targetName;
0407:
0408: public ClassTarget() {
0409: this (null);
0410: }
0411:
0412: public ClassTarget(String targetName) {
0413: super (getClassTargetName(TestFmwk.this .getClass()));
0414: this .targetName = targetName;
0415: }
0416:
0417: protected boolean validate() {
0418: return TestFmwk.this .validate();
0419: }
0420:
0421: protected String getDescription() {
0422: return TestFmwk.this .getDescription();
0423: }
0424:
0425: protected void execute() throws Exception {
0426: params.indentLevel++;
0427: Target target = randomize(getTargets(targetName));
0428: while (target != null) {
0429: target.run();
0430: target = target.next;
0431: }
0432: params.indentLevel--;
0433: }
0434:
0435: private Target randomize(Target t) {
0436: if (t != null && t.getNext() != null) {
0437: ArrayList list = new ArrayList();
0438: while (t != null) {
0439: list.add(t);
0440: t = t.getNext();
0441: }
0442:
0443: Target[] arr = (Target[]) list.toArray(new Target[list
0444: .size()]);
0445:
0446: if (true) { // todo - add to params?
0447: // different jvms return class methods in different orders,
0448: // so we sort them (always, and then randomize them, so that
0449: // forcing a seed will also work across jvms).
0450: Arrays.sort(arr, new Comparator() {
0451: public int compare(Object lhs, Object rhs) {
0452: // sort in reverse order, later we link up in
0453: // forward order
0454: return ((Target) rhs).name
0455: .compareTo(((Target) lhs).name);
0456: }
0457: });
0458:
0459: // t is null to start, ends up as first element
0460: // (arr[arr.length-1])
0461: for (int i = 0; i < arr.length; ++i) {
0462: t = arr[i].setNext(t); // relink in forward order
0463: }
0464: }
0465:
0466: if (params.random != null) {
0467: t = null; // reset t to null
0468: Random r = params.random;
0469: for (int i = arr.length; --i >= 1;) {
0470: int x = r.nextInt(i + 1);
0471: t = arr[x].setNext(t);
0472: arr[x] = arr[i];
0473: }
0474:
0475: t = arr[0].setNext(t); // new first element
0476: }
0477: }
0478:
0479: return t;
0480: }
0481: }
0482:
0483: //------------------------------------------------------------------------
0484: // Everything below here is boilerplate code that makes it possible
0485: // to add a new test by simply adding a function to an existing class
0486: //------------------------------------------------------------------------
0487:
0488: protected TestFmwk() {
0489: }
0490:
0491: protected void init() throws Exception {
0492: }
0493:
0494: /**
0495: * Parse arguments into a TestParams object and a collection of target
0496: * paths. If there was an error parsing the TestParams, print usage and exit
0497: * with -1. Otherwise, call resolveTarget(TestParams, String) for each path,
0498: * and run the returned target. After the last test returns, if prompt is
0499: * set, prompt and wait for input from stdin. Finally, exit with number of
0500: * errors.
0501: *
0502: * This method never returns, since it always exits with System.exit();
0503: */
0504: public void run(String[] args) {
0505: System.exit(run(args, new PrintWriter(System.out)));
0506: }
0507:
0508: /**
0509: * Like run(String[]) except this allows you to specify the error log.
0510: * Unlike run(String[]) this returns the error code as a result instead of
0511: * calling System.exit().
0512: */
0513: public int run(String[] args, PrintWriter log) {
0514: boolean prompt = false;
0515: int wx = 0;
0516: for (int i = 0; i < args.length; ++i) {
0517: String arg = args[i];
0518: if (arg.equals("-p") || arg.equals("-prompt")) {
0519: prompt = true;
0520: } else {
0521: if (wx < i) {
0522: args[wx] = arg;
0523: }
0524: wx++;
0525: }
0526: }
0527: while (wx < args.length) {
0528: args[wx++] = null;
0529: }
0530:
0531: TestParams params = TestParams.create(args, log);
0532: if (params == null) {
0533: return -1;
0534: }
0535:
0536: int errorCount = runTests(params, args);
0537:
0538: if (params.seed != 0) {
0539: params.log.println("-random:" + params.seed);
0540: params.log.flush();
0541: }
0542:
0543: if (params.errorSummary != null
0544: && params.errorSummary.length() > 0) {
0545: params.log.println("\nError summary:");
0546: params.log.println(params.errorSummary.toString());
0547: }
0548:
0549: if (prompt) {
0550: System.out.println("Hit RETURN to exit...");
0551: System.out.flush();
0552: try {
0553: System.in.read();
0554: } catch (IOException e) {
0555: params.log.println("Exception: " + e.toString()
0556: + e.getMessage());
0557: }
0558: }
0559:
0560: return errorCount;
0561: }
0562:
0563: public int runTests(TestParams params, String[] tests) {
0564: int ec = 0;
0565:
0566: StringBuffer summary = null;
0567: try {
0568: if (tests.length == 0 || tests[0] == null) { // no args
0569: params.init();
0570: resolveTarget(params).run();
0571: ec = params.errorCount;
0572: } else {
0573: for (int i = 0; i < tests.length; ++i) {
0574: if (tests[i] == null)
0575: continue;
0576:
0577: if (i > 0) {
0578: params.log.println();
0579: }
0580:
0581: params.init();
0582: resolveTarget(params, tests[i]).run();
0583: ec += params.errorCount;
0584:
0585: if (params.errorSummary != null
0586: && params.errorSummary.length() > 0) {
0587: if (summary == null) {
0588: summary = new StringBuffer();
0589: }
0590: summary.append("\nTest Root: " + tests[i]
0591: + "\n");
0592: summary.append(params.errorSummary());
0593: }
0594: }
0595: params.errorSummary = summary;
0596: }
0597: } catch (Exception e) {
0598: e.printStackTrace(params.log);
0599: params.log.println(e.getMessage());
0600: params.log.println("encountered exception, exiting");
0601: }
0602:
0603: return ec;
0604: }
0605:
0606: /**
0607: * Return a ClassTarget for this test. Params is set on this test.
0608: */
0609: public Target resolveTarget(TestParams params) {
0610: this .params = params;
0611: return new ClassTarget();
0612: }
0613:
0614: /**
0615: * Resolve a path from this test to a target. If this test has subtests, and
0616: * the path contains '/', the portion before the '/' is resolved to a
0617: * subtest, until the path is consumed or the test has no subtests. Returns
0618: * a ClassTarget created using the resolved test and remaining path (which
0619: * ought to be null or a method name). Params is set on the target's test.
0620: */
0621: public Target resolveTarget(TestParams params, String targetPath) {
0622: TestFmwk test = this ;
0623: test.params = params;
0624:
0625: if (targetPath != null) {
0626: if (targetPath.length() == 0) {
0627: targetPath = null;
0628: } else {
0629: int p = 0;
0630: int e = targetPath.length();
0631:
0632: // trim all leading and trailing '/'
0633: while (targetPath.charAt(p) == '/') {
0634: ++p;
0635: }
0636: while (e > p && targetPath.charAt(e - 1) == '/') {
0637: --e;
0638: }
0639: if (p > 0 || e < targetPath.length()) {
0640: targetPath = targetPath.substring(p, e - p);
0641: p = 0;
0642: e = targetPath.length();
0643: }
0644:
0645: try {
0646: for (;;) {
0647: int n = targetPath.indexOf('/');
0648: String prefix = n == -1 ? targetPath
0649: : targetPath.substring(0, n);
0650: TestFmwk subtest = test.getSubtest(prefix);
0651:
0652: if (subtest == null) {
0653: break;
0654: }
0655:
0656: test = subtest;
0657:
0658: if (n == -1) {
0659: targetPath = null;
0660: break;
0661: }
0662:
0663: targetPath = targetPath.substring(n + 1);
0664: }
0665: } catch (TestFmwkException ex) {
0666: return test.new Target(targetPath);
0667: }
0668: }
0669: }
0670:
0671: return test.new ClassTarget(targetPath);
0672: }
0673:
0674: /**
0675: * Return true if we can run this test (allows test to inspect jvm,
0676: * environment, params before running)
0677: */
0678: protected boolean validate() {
0679: return true;
0680: }
0681:
0682: /**
0683: * Return the targets for this test. If targetName is null, return all
0684: * targets, otherwise return a target for just that name. The returned
0685: * target can be null.
0686: *
0687: * The default implementation returns a MethodTarget for each public method
0688: * of the object's class whose name starts with "Test" or "test".
0689: */
0690: protected Target getTargets(String targetName) {
0691: Class cls = getClass();
0692: if (targetName != null) {
0693: try {
0694: Method method = cls.getMethod(targetName, null);
0695: return new MethodTarget(targetName, method);
0696: } catch (NoSuchMethodException e) {
0697: return new Target(targetName); // invalid target
0698: } catch (SecurityException e) {
0699: return null;
0700: }
0701: } else {
0702: Target target = null;
0703: if (params.doMethods()) {
0704: Method[] methods = cls.getDeclaredMethods();
0705: for (int i = methods.length; --i >= 0;) {
0706: String name = methods[i].getName();
0707: if (name.startsWith("Test")
0708: || name.startsWith("test")) {
0709: target = new MethodTarget(name, methods[i])
0710: .setNext(target);
0711: }
0712: }
0713: }
0714: return target;
0715: }
0716: }
0717:
0718: protected String getDescription() {
0719: return null;
0720: }
0721:
0722: protected boolean validateMethod(String name) {
0723: return true;
0724: }
0725:
0726: protected String getMethodDescription(String name) {
0727: return null;
0728: }
0729:
0730: // method tests have no subtests, group tests override
0731: protected TestFmwk getSubtest(String prefix)
0732: throws TestFmwkException {
0733: return null;
0734: }
0735:
0736: public boolean isVerbose() {
0737: return params.verbose;
0738: }
0739:
0740: public boolean noData() {
0741: return params.nodata;
0742: }
0743:
0744: public boolean isTiming() {
0745: return params.timing < Long.MAX_VALUE;
0746: }
0747:
0748: public boolean isMemTracking() {
0749: return params.memusage;
0750: }
0751:
0752: /**
0753: * 0 = fewest tests, 5 is normal build, 10 is most tests
0754: */
0755: public int getInclusion() {
0756: return params.inclusion;
0757: }
0758:
0759: public boolean isModularBuild() {
0760: return params.warnings;
0761: }
0762:
0763: public boolean isQuick() {
0764: return params.inclusion == 0;
0765: }
0766:
0767: public void msg(String message, int level, boolean incCount,
0768: boolean newln) {
0769: params.msg(message, level, incCount, newln);
0770: }
0771:
0772: protected int getErrorCount() {
0773: return params.errorCount;
0774: }
0775:
0776: protected TimeZone safeGetTimeZone(String id) {
0777: TimeZone tz = TimeZone.getTimeZone(id);
0778: if (tz == null) {
0779: // should never happen
0780: errln("FAIL: TimeZone.getTimeZone(" + id + ") => null");
0781: }
0782: if (!tz.getID().equals(id)) {
0783: warnln("FAIL: TimeZone.getTimeZone(" + id + ") => "
0784: + tz.getID());
0785: }
0786: return tz;
0787: }
0788:
0789: /**
0790: * Print a usage message for this test class.
0791: */
0792: public void usage() {
0793: usage(new PrintWriter(System.out), getClass().getName());
0794: }
0795:
0796: public static void usage(PrintWriter pw, String className) {
0797: pw.println("Usage: " + className + " option* target*");
0798: pw.println();
0799: pw.println("Options:");
0800: pw
0801: .println(" -describe Print a short descriptive string for this test and all");
0802: pw.println(" listed targets.");
0803: pw
0804: .println(" -e<n> Set exhaustiveness from 0..10. Default is 0, fewest tests.\n"
0805: + " To run all tests, specify -e10. Giving -e with no <n> is\n"
0806: + " the same as -e5.");
0807: pw
0808: .println(" -filter:<str> Only tests matching filter will be run or listed.\n"
0809: + " <str> is of the form ['^']text[','['^']text].\n"
0810: + " Each string delimited by ',' is a separate filter argument.\n"
0811: + " If '^' is prepended to an argument, its matches are excluded.\n"
0812: + " Filtering operates on test groups as well as tests, if a test\n"
0813: + " group is included, all its subtests that are not excluded will\n"
0814: + " be run. Examples:\n"
0815: + " -filter:A -- only tests matching A are run. If A matches a group,\n"
0816: + " all subtests of this group are run.\n"
0817: + " -filter:^A -- all tests except those matching A are run. If A matches\n"
0818: + " a group, no subtest of that group will be run.\n"
0819: + " -filter:A,B,^C,^D -- tests matching A or B and not C and not D are run\n"
0820: + " Note: Filters are case insensitive.");
0821: pw.println(" -h[elp] Print this help text and exit.");
0822: pw.println(" -l[ist] List immediate targets of this test");
0823: pw
0824: .println(" -la, -listAll List immediate targets of this test, and all subtests");
0825: pw
0826: .println(" -le, -listExaustive List all subtests and targets");
0827: // don't know how to get useful numbers for memory usage using java API
0828: // calls
0829: // pw.println(" -m[emory] print memory usage and force gc for
0830: // each test");
0831: pw
0832: .println(" -n[othrow] Message on test failure rather than exception");
0833: pw.println(" -prompt Prompt before exiting");
0834: pw.println(" -q[uiet] Do not show warnings");
0835: pw
0836: .println(" -r[andom][:<n>] If present, randomize targets. If n is present,\n"
0837: + " use it as the seed. If random is not set, targets will\n"
0838: + " be in alphabetical order to ensure cross-platform consistency.");
0839: pw
0840: .println(" -s[ilent] No output except error summary or exceptions.");
0841: pw
0842: .println(" -tfilter:<str> Transliterator Test filter of ids.");
0843: pw
0844: .println(" -t[ime][:<n>] Print elapsed time for each test. if n is present\n"
0845: + " only print times >= n milliseconds.");
0846: pw.println(" -v[erbose] Show log messages");
0847: pw
0848: .println(" -w[arning] Continue in presence of warnings, and disable missing test warnings.");
0849: pw
0850: .println(" -nodata | -nd Do not warn if resource data is not present.");
0851: pw.println();
0852: pw
0853: .println(" If a list or describe option is provided, no tests are run.");
0854: pw.println();
0855: pw.println("Targets:");
0856: pw
0857: .println(" If no target is specified, all targets for this test are run.");
0858: pw
0859: .println(" If a target contains no '/' characters, and matches a target");
0860: pw
0861: .println(" of this test, the target is run. Otherwise, the part before the");
0862: pw
0863: .println(" '/' is used to match a subtest, which then evaluates the");
0864: pw
0865: .println(" remainder of the target as above. Target matching is case-insensitive.");
0866: pw.println();
0867: pw
0868: .println(" If multiple targets are provided, each is executed in order.");
0869: pw.flush();
0870: }
0871:
0872: public static String hex(char[] s) {
0873: StringBuffer result = new StringBuffer();
0874: for (int i = 0; i < s.length; ++i) {
0875: if (i != 0)
0876: result.append(',');
0877: result.append(hex(s[i]));
0878: }
0879: return result.toString();
0880: }
0881:
0882: public static String hex(byte[] s) {
0883: StringBuffer result = new StringBuffer();
0884: for (int i = 0; i < s.length; ++i) {
0885: if (i != 0)
0886: result.append(',');
0887: result.append(hex(s[i]));
0888: }
0889: return result.toString();
0890: }
0891:
0892: public static String hex(char ch) {
0893: StringBuffer result = new StringBuffer();
0894: String foo = Integer.toString(ch, 16).toUpperCase();
0895: for (int i = foo.length(); i < 4; ++i) {
0896: result.append('0');
0897: }
0898: return result + foo;
0899: }
0900:
0901: public static String hex(int ch) {
0902: StringBuffer result = new StringBuffer();
0903: String foo = Integer.toString(ch, 16).toUpperCase();
0904: for (int i = foo.length(); i < 4; ++i) {
0905: result.append('0');
0906: }
0907: return result + foo;
0908: }
0909:
0910: public static String hex(String s) {
0911: StringBuffer result = new StringBuffer();
0912: for (int i = 0; i < s.length(); ++i) {
0913: if (i != 0)
0914: result.append(',');
0915: result.append(hex(s.charAt(i)));
0916: }
0917: return result.toString();
0918: }
0919:
0920: public static String hex(StringBuffer s) {
0921: return hex(s.toString());
0922: }
0923:
0924: public static String prettify(String s) {
0925: StringBuffer result = new StringBuffer();
0926: int ch;
0927: for (int i = 0; i < s.length(); i += UTF16.getCharCount(ch)) {
0928: ch = UTF16.charAt(s, i);
0929: if (ch > 0xfffff) {
0930: result.append("\\U00");
0931: result.append(hex(ch));
0932: } else if (ch > 0xffff) {
0933: result.append("\\U000");
0934: result.append(hex(ch));
0935: } else if (ch > 0x7f) {
0936: result.append("\\u");
0937: result.append(hex(ch));
0938: } else {
0939: result.append((char) ch);
0940: }
0941:
0942: }
0943: return result.toString();
0944: }
0945:
0946: public static String prettify(StringBuffer s) {
0947: return prettify(s.toString());
0948: }
0949:
0950: private static java.util.GregorianCalendar cal;
0951:
0952: /**
0953: * Return a Date given a year, month, and day of month. This is similar to
0954: * new Date(y-1900, m, d). It uses the default time zone at the time this
0955: * method is first called.
0956: *
0957: * @param year
0958: * use 2000 for 2000, unlike new Date()
0959: * @param month
0960: * use Calendar.JANUARY etc.
0961: * @param dom
0962: * day of month, 1-based
0963: * @return a Date object for the given y/m/d
0964: */
0965: protected static synchronized java.util.Date getDate(int year,
0966: int month, int dom) {
0967: if (cal == null) {
0968: cal = new java.util.GregorianCalendar();
0969: }
0970: cal.clear();
0971: cal.set(year, month, dom);
0972: return cal.getTime();
0973: }
0974:
0975: public static class NullWriter extends PrintWriter {
0976: public NullWriter() {
0977: super (System.out, false);
0978: }
0979:
0980: public void write(int c) {
0981: }
0982:
0983: public void write(char[] buf, int off, int len) {
0984: }
0985:
0986: public void write(String s, int off, int len) {
0987: }
0988:
0989: public void println() {
0990: }
0991: }
0992:
0993: public static class ASCIIWriter extends PrintWriter {
0994: private StringBuffer buffer = new StringBuffer();
0995:
0996: // Characters that we think are printable but that escapeUnprintable
0997: // doesn't
0998: private static final String PRINTABLES = "\t\n\r";
0999:
1000: public ASCIIWriter(Writer w, boolean autoFlush) {
1001: super (w, autoFlush);
1002: }
1003:
1004: public ASCIIWriter(OutputStream os, boolean autoFlush) {
1005: super (os, autoFlush);
1006: }
1007:
1008: public void write(int c) {
1009: synchronized (lock) {
1010: buffer.setLength(0);
1011: if (PRINTABLES.indexOf(c) < 0
1012: && TestUtil.escapeUnprintable(buffer, c)) {
1013: super .write(buffer.toString());
1014: } else {
1015: super .write(c);
1016: }
1017: }
1018: }
1019:
1020: public void write(char[] buf, int off, int len) {
1021: synchronized (lock) {
1022: buffer.setLength(0);
1023: int limit = off + len;
1024: while (off < limit) {
1025: int c = UTF16Util.charAt(buf, 0, buf.length, off);
1026: off += UTF16Util.getCharCount(c);
1027: if (PRINTABLES.indexOf(c) < 0
1028: && TestUtil.escapeUnprintable(buffer, c)) {
1029: super .write(buffer.toString());
1030: buffer.setLength(0);
1031: } else {
1032: super .write(c);
1033: }
1034: }
1035: }
1036: }
1037:
1038: public void write(String s, int off, int len) {
1039: write(s.substring(off, off + len).toCharArray(), 0, len);
1040: }
1041: }
1042:
1043: // filters
1044: // match against the entire hierarchy
1045: // A;B;!C;!D --> (A ||B) && (!C && !D)
1046: // positive, negative, unknown matches
1047: // positive -- known to be included, negative- known to be excluded
1048: // positive only if no excludes, and matches at least one include, if any
1049: // negative only if matches at least one exclude
1050: // otherwise, we wait
1051:
1052: public static class TestParams {
1053: public boolean prompt;
1054: public boolean nothrow;
1055: public boolean verbose;
1056: public boolean quiet;
1057: public int listlevel;
1058: public boolean describe;
1059: public boolean warnings;
1060: public boolean nodata;
1061: public long timing = Long.MAX_VALUE;
1062: public boolean memusage;
1063: public int inclusion;
1064: public String filter;
1065: public long seed;
1066: public String tfilter; // for transliterator tests
1067:
1068: public State stack;
1069:
1070: public StringBuffer errorSummary;
1071:
1072: public PrintWriter log;
1073: public int indentLevel;
1074: private boolean needLineFeed;
1075: private boolean suppressIndent;
1076: public int errorCount;
1077: public int warnCount;
1078: public int invalidCount;
1079: public int testCount;
1080: private NumberFormat tformat;
1081: public Random random;
1082:
1083: private TestParams() {
1084: }
1085:
1086: public static TestParams create(String arglist, PrintWriter log) {
1087: String[] args = null;
1088: if (arglist != null && arglist.length() > 0) {
1089: //#ifndef FOUNDATION
1090: args = arglist.split("\\s");
1091: //#else
1092: //## args = Utility.split(arglist, '\u0020');
1093: //#endif
1094: }
1095: return create(args, log);
1096: }
1097:
1098: /**
1099: * Create a TestParams from a list of arguments. If successful, return the params object,
1100: * else return null. Error messages will be reported on errlog if it is not null.
1101: * Arguments and values understood by this method will be removed from the args array
1102: * and existing args will be shifted down, to be filled by nulls at the end.
1103: * @param args the list of arguments
1104: * @param log the error log, or null if no error log is desired
1105: * @return the new TestParams object, or null if error
1106: */
1107: public static TestParams create(String[] args, PrintWriter log) {
1108: TestParams params = new TestParams();
1109:
1110: if (log == null) {
1111: params.log = new NullWriter();
1112: } else {
1113: params.log = new ASCIIWriter(log, true);
1114: }
1115:
1116: boolean usageError = false;
1117: String filter = null;
1118: int wx = 0; // write argets.
1119: if (args != null) {
1120: for (int i = 0; i < args.length; i++) {
1121: String arg = args[i];
1122: if (arg == null || arg.length() == 0) {
1123: continue;
1124: }
1125: if (arg.charAt(0) == '-') {
1126: arg = arg.toLowerCase();
1127: if (arg.equals("-verbose") || arg.equals("-v")) {
1128: params.verbose = true;
1129: params.quiet = false;
1130: } else if (arg.equals("-quiet")
1131: || arg.equals("-q")) {
1132: params.quiet = true;
1133: params.verbose = false;
1134: } else if (arg.equals("-help")
1135: || arg.equals("-h")) {
1136: usageError = true;
1137: } else if (arg.equals("-warning")
1138: || arg.equals("-w")) {
1139: params.warnings = true;
1140: } else if (arg.equals("-nodata")
1141: || arg.equals("-nd")) {
1142: params.nodata = true;
1143: } else if (arg.equals("-list")
1144: || arg.equals("-l")) {
1145: params.listlevel = 1;
1146: } else if (arg.equals("-listall")
1147: || arg.equals("-la")) {
1148: params.listlevel = 2;
1149: } else if (arg.equals("-listexaustive")
1150: || arg.equals("-le")) {
1151: params.listlevel = 3;
1152: } else if (arg.equals("-memory")
1153: || arg.equals("-m")) {
1154: params.memusage = true;
1155: } else if (arg.equals("-nothrow")
1156: || arg.equals("-n")) {
1157: params.nothrow = true;
1158: params.errorSummary = new StringBuffer();
1159: } else if (arg.equals("-describe")
1160: || arg.equals("-d")) {
1161: params.describe = true;
1162: } else if (arg.startsWith("-r")) {
1163: String s = null;
1164: int n = arg.indexOf(':');
1165: if (n != -1) {
1166: s = arg.substring(n + 1);
1167: arg = arg.substring(0, n);
1168: }
1169:
1170: if (arg.equals("-r")
1171: || arg.equals("-random")) {
1172: if (s == null) {
1173: params.seed = System
1174: .currentTimeMillis();
1175: } else {
1176: params.seed = Long.parseLong(s);
1177: }
1178: } else {
1179: log
1180: .println("*** Error: unrecognized argument: "
1181: + arg);
1182: usageError = true;
1183: break;
1184: }
1185: } else if (arg.startsWith("-e")) {
1186: // see above
1187: params.inclusion = (arg.length() == 2) ? 5
1188: : Integer
1189: .parseInt(arg.substring(2));
1190: if (params.inclusion < 0
1191: || params.inclusion > 10) {
1192: usageError = true;
1193: break;
1194: }
1195: } else if (arg.startsWith("-tfilter:")) {
1196: params.tfilter = arg.substring(8);
1197: } else if (arg.startsWith("-time")
1198: || arg.startsWith("-t")) {
1199: long val = 0;
1200: int inx = arg.indexOf(':');
1201: if (inx > 0) {
1202: String num = arg.substring(inx + 1);
1203: try {
1204: val = Long.parseLong(num);
1205: } catch (Exception e) {
1206: log
1207: .println("*** Error: could not parse time threshold '"
1208: + num + "'");
1209: usageError = true;
1210: break;
1211: }
1212: }
1213: params.timing = val;
1214: String fmt = "#,00s";
1215: if (val <= 10) {
1216: fmt = "#,##0.000s";
1217: } else if (val <= 100) {
1218: fmt = "#,##0.00s";
1219: } else if (val <= 1000) {
1220: fmt = "#,##0.0s";
1221: }
1222: params.tformat = new DecimalFormat(fmt);
1223: } else if (arg.startsWith("-filter:")) {
1224: String temp = arg.substring(8)
1225: .toLowerCase();
1226: filter = filter == null ? temp : filter
1227: + "," + temp;
1228: } else if (arg.startsWith("-f:")) {
1229: String temp = arg.substring(3)
1230: .toLowerCase();
1231: filter = filter == null ? temp : filter
1232: + "," + temp;
1233: } else if (arg.startsWith("-s")) {
1234: params.log = new NullWriter();
1235: } else {
1236: log
1237: .println("*** Error: unrecognized argument: "
1238: + args[i]);
1239: usageError = true;
1240: break;
1241: }
1242: } else {
1243: args[wx++] = arg; // shift down
1244: }
1245: }
1246:
1247: while (wx < args.length) {
1248: args[wx++] = null;
1249: }
1250: }
1251:
1252: if (usageError) {
1253: usage(log, "TestAll");
1254: return null;
1255: }
1256:
1257: if (filter != null) {
1258: params.filter = filter.toLowerCase();
1259: }
1260:
1261: params.init();
1262:
1263: return params;
1264: }
1265:
1266: public String errorSummary() {
1267: return errorSummary == null ? "" : errorSummary.toString();
1268: }
1269:
1270: public void init() {
1271: indentLevel = 0;
1272: needLineFeed = false;
1273: suppressIndent = false;
1274: errorCount = 0;
1275: warnCount = 0;
1276: invalidCount = 0;
1277: testCount = 0;
1278: random = seed == 0 ? null : new Random(seed);
1279: }
1280:
1281: public class State {
1282: State link;
1283: String name;
1284: StringBuffer buffer;
1285: int level;
1286: int ec;
1287: int wc;
1288: int ic;
1289: int tc;
1290: boolean flushed;
1291: public boolean included;
1292: long mem;
1293: long millis;
1294:
1295: public State(State link, String name, boolean included) {
1296: this .link = link;
1297: this .name = name;
1298: if (link == null) {
1299: this .level = 0;
1300: this .included = included;
1301: } else {
1302: this .level = link.level + 1;
1303: this .included = included || link.included;
1304: }
1305: this .ec = errorCount;
1306: this .wc = warnCount;
1307: this .ic = invalidCount;
1308: this .tc = testCount;
1309:
1310: if (link == null || this .included) {
1311: flush();
1312: }
1313:
1314: mem = getmem();
1315: millis = System.currentTimeMillis();
1316: }
1317:
1318: void flush() {
1319: if (!flushed) {
1320: if (link != null) {
1321: link.flush();
1322: }
1323:
1324: indent(level);
1325: log.print(name);
1326: log.flush();
1327:
1328: flushed = true;
1329:
1330: needLineFeed = true;
1331: }
1332: }
1333:
1334: void appendPath(StringBuffer buf) {
1335: if (this .link != null) {
1336: this .link.appendPath(buf);
1337: buf.append('/');
1338: }
1339: buf.append(name);
1340: }
1341: }
1342:
1343: public void push(String name, String description,
1344: boolean included) {
1345: if (inDocMode() && describe && description != null) {
1346: name += ": " + description;
1347: }
1348: stack = new State(stack, name, included);
1349: }
1350:
1351: public void pop() {
1352: if (stack != null) {
1353: writeTestResult();
1354: stack = stack.link;
1355: }
1356: }
1357:
1358: public boolean inDocMode() {
1359: return describe || listlevel != 0;
1360: }
1361:
1362: public boolean doMethods() {
1363: return !inDocMode() || listlevel == 3
1364: || (indentLevel == 1 && listlevel > 0);
1365: }
1366:
1367: public boolean doRecurse() {
1368: return !inDocMode() || listlevel > 1
1369: || (indentLevel == 1 && listlevel > 0);
1370: }
1371:
1372: public boolean doRecurseGroupsOnly() {
1373: return inDocMode()
1374: && (listlevel == 2 || (indentLevel == 1 && listlevel > 0));
1375: }
1376:
1377: // return 0, -1, or 1
1378: // 1: run this test
1379: // 0: might run this test, no positive include or exclude on this group
1380: // -1: exclude this test
1381: public int filter(String testName) {
1382: int result = 0;
1383: if (filter == null) {
1384: result = 1;
1385: } else {
1386: boolean noIncludes = true;
1387: boolean noExcludes = filter.indexOf('^') == -1;
1388: testName = testName.toLowerCase();
1389: int ix = 0;
1390: while (ix < filter.length()) {
1391: int nix = filter.indexOf(',', ix);
1392: if (nix == -1) {
1393: nix = filter.length();
1394: }
1395: if (filter.charAt(ix) == '^') {
1396: if (testName.indexOf(filter.substring(ix + 1,
1397: nix)) != -1) {
1398: result = -1;
1399: break;
1400: }
1401: } else {
1402: noIncludes = false;
1403: if (testName.indexOf(filter.substring(ix, nix)) != -1) {
1404: result = 1;
1405: if (noExcludes) {
1406: break;
1407: }
1408: }
1409: }
1410:
1411: ix = nix + 1;
1412: }
1413: if (result == 0 && noIncludes) {
1414: result = 1;
1415: }
1416: }
1417: // System.out.println("filter: " + testName + " returns: " +
1418: // result);
1419: return result;
1420: }
1421:
1422: /**
1423: * Log access.
1424: * @param msg The string message to write
1425: */
1426: public void write(String msg) {
1427: write(msg, false);
1428: }
1429:
1430: public void writeln(String msg) {
1431: write(msg, true);
1432: }
1433:
1434: private void write(String msg, boolean newln) {
1435: if (!suppressIndent) {
1436: if (needLineFeed) {
1437: log.println();
1438: needLineFeed = false;
1439: }
1440: log.print(spaces.substring(0, indentLevel * 2));
1441: }
1442: log.print(msg);
1443: if (newln) {
1444: log.println();
1445: }
1446: log.flush();
1447: suppressIndent = !newln;
1448: }
1449:
1450: private void msg(String message, int level, boolean incCount,
1451: boolean newln) {
1452: if (level == WARN && (!warnings && !nodata)) {
1453: level = ERR;
1454: }
1455:
1456: if (incCount) {
1457: if (level == WARN) {
1458: warnCount++;
1459: invalidCount++;
1460: } else if (level == ERR) {
1461: errorCount++;
1462: }
1463: }
1464:
1465: // should roll indentation stuff into log ???
1466: if (verbose || level > (quiet ? WARN : LOG)) {
1467: if (!suppressIndent) {
1468: indent(indentLevel + 1);
1469: final String[] MSGNAMES = { "", "Warning: ",
1470: "Error: " };
1471: log.print(MSGNAMES[level]);
1472: }
1473:
1474: log.print(message);
1475: if (newln) {
1476: log.println();
1477: }
1478: log.flush();
1479: }
1480:
1481: if (level == ERR) {
1482: if (!nothrow) {
1483: throw new RuntimeException(message);
1484: }
1485: if (!suppressIndent && errorSummary != null
1486: && stack != null
1487: && (errorCount == stack.ec + 1)) {
1488: stack.appendPath(errorSummary);
1489: errorSummary.append("\n");
1490: }
1491: }
1492:
1493: suppressIndent = !newln;
1494: }
1495:
1496: private void writeTestInvalid(String name, boolean nodata) {
1497: // msg("***" + name + "*** not found or not valid.", WARN, true,
1498: // true);
1499: if (inDocMode()) {
1500: if (!warnings) {
1501: if (stack != null) {
1502: stack.flush();
1503: }
1504: log.println(" *** Target not found or not valid.");
1505: log.flush();
1506: needLineFeed = false;
1507: }
1508: } else {
1509: if (!nodata) {
1510: msg("Test " + name + " not found or not valid.",
1511: WARN, true, true);
1512: }
1513: }
1514: }
1515:
1516: long getmem() {
1517: long newmem = 0;
1518: if (memusage) {
1519: Runtime rt = Runtime.getRuntime();
1520: long lastmem = Long.MAX_VALUE;
1521: do {
1522: rt.gc();
1523: rt.gc();
1524: try {
1525: Thread.sleep(50);
1526: } catch (Exception e) {
1527: break;
1528: }
1529: lastmem = newmem;
1530: newmem = rt.totalMemory() - rt.freeMemory();
1531: } while (newmem < lastmem);
1532: }
1533: return newmem;
1534: }
1535:
1536: private void writeTestResult() {
1537: if (inDocMode()) {
1538: if (needLineFeed) {
1539: log.println();
1540: log.flush();
1541: }
1542: needLineFeed = false;
1543: return;
1544: }
1545:
1546: long dmem = getmem() - stack.mem;
1547: long dtime = System.currentTimeMillis() - stack.millis;
1548:
1549: int testDelta = testCount - stack.tc;
1550: if (testDelta == 0) {
1551: return;
1552: }
1553:
1554: int errorDelta = errorCount - stack.ec;
1555: int invalidDelta = invalidCount - stack.ic;
1556:
1557: stack.flush();
1558:
1559: if (!needLineFeed) {
1560: indent(indentLevel);
1561: log.print("}");
1562: }
1563: needLineFeed = false;
1564:
1565: if (memusage || dtime >= timing) {
1566: log.print(" (");
1567: if (memusage) {
1568: log.print("dmem: " + dmem);
1569: }
1570: if (dtime >= timing) {
1571: if (memusage) {
1572: log.print(", ");
1573: }
1574: log.print(tformat.format(dtime / 1000f));
1575: }
1576: log.print(")");
1577: }
1578: if (errorDelta != 0) {
1579: log.println(" FAILED ("
1580: + errorDelta
1581: + " failures"
1582: + ((invalidDelta != 0) ? ", " + invalidDelta
1583: + " tests skipped)" : ")"));
1584: } else if (invalidDelta != 0) {
1585: log.println(" Qualified (" + invalidDelta
1586: + " tests skipped)");
1587: } else {
1588: log.println(" Passed");
1589: }
1590: }
1591:
1592: private final void indent(int distance) {
1593: boolean idm = inDocMode();
1594: if (needLineFeed) {
1595: if (idm) {
1596: log.println();
1597: } else {
1598: log.println(" {");
1599: }
1600: needLineFeed = false;
1601: }
1602:
1603: log.print(spaces.substring(0, distance * (idm ? 3 : 2)));
1604:
1605: if (idm) {
1606: log.print("-- ");
1607: }
1608: }
1609: }
1610:
1611: public String getTranslitTestFilter() {
1612: return params.tfilter;
1613: }
1614:
1615: /**
1616: * Return the target name for a test class. This is either the end of the
1617: * class name, or if the class declares a public static field
1618: * CLASS_TARGET_NAME, the value of that field.
1619: */
1620: private static String getClassTargetName(Class testClass) {
1621: String name = testClass.getName();
1622: try {
1623: Field f = testClass.getField("CLASS_TARGET_NAME");
1624: name = (String) f.get(null);
1625: } catch (IllegalAccessException e) {
1626: throw new IllegalStateException(
1627: "static field CLASS_TARGET_NAME must be accessible");
1628: } catch (NoSuchFieldException e) {
1629: int n = Math.max(name.lastIndexOf('.'), name
1630: .lastIndexOf('$'));
1631: if (n != -1) {
1632: name = name.substring(n + 1);
1633: }
1634: }
1635: return name;
1636: }
1637:
1638: /**
1639: * Check the given array to see that all the strings in the expected array
1640: * are present.
1641: *
1642: * @param msg
1643: * string message, for log output
1644: * @param array
1645: * array of strings to check
1646: * @param expected
1647: * array of strings we expect to see, or null
1648: * @return the length of 'array', or -1 on error
1649: */
1650: protected int checkArray(String msg, String array[],
1651: String expected[]) {
1652: int explen = (expected != null) ? expected.length : 0;
1653: if (!(explen >= 0 && explen < 31)) { // [sic] 31 not 32
1654: errln("Internal error");
1655: return -1;
1656: }
1657: int i = 0;
1658: StringBuffer buf = new StringBuffer();
1659: int seenMask = 0;
1660: for (; i < array.length; ++i) {
1661: String s = array[i];
1662: if (i != 0)
1663: buf.append(", ");
1664: buf.append(s);
1665: // check expected list
1666: for (int j = 0, bit = 1; j < explen; ++j, bit <<= 1) {
1667: if ((seenMask & bit) == 0) {
1668: if (s.equals(expected[j])) {
1669: seenMask |= bit;
1670: logln("Ok: \"" + s + "\" seen");
1671: }
1672: }
1673: }
1674: }
1675: logln(msg + " = [" + buf + "] (" + i + ")");
1676: // did we see all expected strings?
1677: if (((1 << explen) - 1) != seenMask) {
1678: for (int j = 0, bit = 1; j < expected.length; ++j, bit <<= 1) {
1679: if ((seenMask & bit) == 0) {
1680: errln("\"" + expected[j] + "\" not seen");
1681: }
1682: }
1683: }
1684: return array.length;
1685: }
1686:
1687: /**
1688: * Check the given array to see that all the locales in the expected array
1689: * are present.
1690: *
1691: * @param msg
1692: * string message, for log output
1693: * @param array
1694: * array of locales to check
1695: * @param expected
1696: * array of locales names we expect to see, or null
1697: * @return the length of 'array'
1698: */
1699: protected int checkArray(String msg, Locale array[],
1700: String expected[]) {
1701: String strs[] = new String[array.length];
1702: for (int i = 0; i < array.length; ++i)
1703: strs[i] = array[i].toString();
1704: return checkArray(msg, strs, expected);
1705: }
1706:
1707: /**
1708: * Check the given array to see that all the locales in the expected array
1709: * are present.
1710: *
1711: * @param msg
1712: * string message, for log output
1713: * @param array
1714: * array of locales to check
1715: * @param expected
1716: * array of locales names we expect to see, or null
1717: * @return the length of 'array'
1718: */
1719: protected int checkArray(String msg, ULocale array[],
1720: String expected[]) {
1721: String strs[] = new String[array.length];
1722: for (int i = 0; i < array.length; ++i)
1723: strs[i] = array[i].toString();
1724: return checkArray(msg, strs, expected);
1725: }
1726:
1727: // JUnit-like assertions.
1728:
1729: protected boolean assertTrue(String message, boolean condition) {
1730: return handleAssert(condition, message, "true", null);
1731: }
1732:
1733: protected boolean assertFalse(String message, boolean condition) {
1734: return handleAssert(!condition, message, "false", null);
1735: }
1736:
1737: protected boolean assertEquals(String message, boolean expected,
1738: boolean actual) {
1739: return handleAssert(expected == actual, message, String
1740: .valueOf(expected), String.valueOf(actual));
1741: }
1742:
1743: protected boolean assertEquals(String message, long expected,
1744: long actual) {
1745: return handleAssert(expected == actual, message, String
1746: .valueOf(expected), String.valueOf(actual));
1747: }
1748:
1749: // do NaN and range calculations to precision of float, don't rely on
1750: // promotion to double
1751: protected boolean assertEquals(String message, float expected,
1752: float actual, double error) {
1753: boolean result = Float.isInfinite(expected) ? expected == actual
1754: : !(Math.abs(expected - actual) > error); // handles NaN
1755: return handleAssert(result, message, String.valueOf(expected)
1756: + (error == 0 ? "" : " (within " + error + ")"), String
1757: .valueOf(actual));
1758: }
1759:
1760: protected boolean assertEquals(String message, double expected,
1761: double actual, double error) {
1762: boolean result = Double.isInfinite(expected) ? expected == actual
1763: : !(Math.abs(expected - actual) > error); // handles NaN
1764: return handleAssert(result, message, String.valueOf(expected)
1765: + (error == 0 ? "" : " (within " + error + ")"), String
1766: .valueOf(actual));
1767: }
1768:
1769: protected boolean assertEquals(String message, Object expected,
1770: Object actual) {
1771: boolean result = expected == null ? actual == null : expected
1772: .equals(actual);
1773: return handleAssert(result, message, stringFor(expected),
1774: stringFor(actual));
1775: }
1776:
1777: protected boolean assertNotEquals(String message, Object expected,
1778: Object actual) {
1779: boolean result = !(expected == null ? actual == null : expected
1780: .equals(actual));
1781: return handleAssert(result, message, stringFor(expected),
1782: stringFor(actual), "not equal to", true);
1783: }
1784:
1785: protected boolean assertSame(String message, Object expected,
1786: Object actual) {
1787: return handleAssert(expected == actual, message,
1788: stringFor(expected), stringFor(actual), "==", false);
1789: }
1790:
1791: protected boolean assertNotSame(String message, Object expected,
1792: Object actual) {
1793: return handleAssert(expected != actual, message,
1794: stringFor(expected), stringFor(actual), "!=", true);
1795: }
1796:
1797: protected boolean assertNull(String message, Object actual) {
1798: return handleAssert(actual == null, message, null,
1799: stringFor(actual));
1800: }
1801:
1802: protected boolean assertNotNull(String message, Object actual) {
1803: return handleAssert(actual != null, message, null,
1804: stringFor(actual), "!=", true);
1805: }
1806:
1807: protected void fail(String message) {
1808: errln(message);
1809: }
1810:
1811: private boolean handleAssert(boolean result, String message,
1812: String expected, String actual) {
1813: return handleAssert(result, message, expected, actual, null,
1814: false);
1815: }
1816:
1817: private boolean handleAssert(boolean result, String message,
1818: String expected, String actual, String relation,
1819: boolean flip) {
1820: if (!result || isVerbose()) {
1821: message = message == null ? "" : " " + message;
1822: relation = relation == null ? ", got " : " " + relation
1823: + " ";
1824: if (result) {
1825: logln("OK"
1826: + message
1827: + ": "
1828: + (flip ? expected + relation + actual
1829: : expected));
1830: } else {
1831: // assert must assume errors are true errors and not just warnings
1832: // so cannot warnln here
1833: errln(message
1834: + ": expected"
1835: + (flip ? relation + expected : " "
1836: + expected
1837: + (actual != null ? relation + actual
1838: : "")));
1839: }
1840: }
1841: return result;
1842: }
1843:
1844: private final String stringFor(Object obj) {
1845: if (obj == null)
1846: return "null";
1847: if (obj instanceof String)
1848: return "\"" + obj + '"';
1849: return obj.getClass().getName() + "<" + obj + ">";
1850: }
1851:
1852: // End JUnit-like assertions
1853:
1854: // PrintWriter support
1855:
1856: public PrintWriter getErrorLogPrintWriter() {
1857: return new PrintWriter(new TestLogWriter(this , TestLog.ERR));
1858: }
1859:
1860: public PrintWriter getLogPrintWriter() {
1861: return new PrintWriter(new TestLogWriter(this , TestLog.LOG));
1862: }
1863:
1864: // end PrintWriter support
1865:
1866: protected TestParams params = null;
1867:
1868: private final static String spaces = " ";
1869:
1870: public boolean isDateAtLeast(int year, int month, int day) {
1871: Calendar c = Calendar.getInstance();
1872: Date dt = new Date(year, month, day);
1873: if (c.getTime().compareTo(dt) >= 0) {
1874: return true;
1875: }
1876: return false;
1877: }
1878: }
|