17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
3f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert * Copyright (C) 1996-2015, International Business Machines Corporation and    *
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved.                                                *
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.dev.test;
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ByteArrayOutputStream;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.CharArrayWriter;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.OutputStream;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.PrintStream;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.PrintWriter;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.Writer;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.lang.reflect.Field;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.lang.reflect.InvocationTargetException;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.lang.reflect.Method;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.DecimalFormat;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.NumberFormat;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Arrays;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Comparator;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map.Entry;
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException;
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Random;
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeMap;
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZone;
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * TestFmwk is a base class for tests that can be run conveniently from the
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * command line as well as under the Java test harness.
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sub-classes implement a set of methods named Test <something>. Each of these
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * methods performs some test. Test methods should indicate errors by calling
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * either err or errln. This will increment the errorCount field and may
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * optionally print a message to the log. Debugging information may also be
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * added to the log via the log and logln methods. These methods will add their
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * arguments to the log only if the test is being run in verbose mode.
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class TestFmwk extends AbstractTestLog {
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default time zone for all of our tests. Used in Target.run();
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final static TimeZone defaultTimeZone = TimeZone.getTimeZone("America/Los_Angeles");
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default locale used for all of our tests. Used in Target.run();
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final static Locale defaultLocale = Locale.US;
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final class TestFmwkException extends Exception {
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * For serialization
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = -3051148210247229194L;
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TestFmwkException(String msg) {
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(msg);
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final class ICUTestError extends RuntimeException {
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * For serialization
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 6170003850185143046L;
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ICUTestError(String msg) {
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(msg);
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Handling exception thrown during text execution (not including
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // RuntimeException thrown by errln).
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected void handleException(Throwable e){
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Throwable ex = e.getCause();
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(ex == null){
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ex = e;
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (ex instanceof OutOfMemoryError) {
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Once OOM happens, it does not make sense to run
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // the rest of test cases.
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new RuntimeException(ex);
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (ex instanceof ICUTestError) {
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // ICUTestError is one produced by errln.
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // We don't need to include useless stack trace information for
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // such case.
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return;
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (ex instanceof ExceptionInInitializerError){
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ex = ((ExceptionInInitializerError)ex).getException();
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //Stack trace
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        CharArrayWriter caw = new CharArrayWriter();
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PrintWriter pw = new PrintWriter(caw);
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ex.printStackTrace(pw);
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.close();
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String msg = caw.toString();
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //System.err.println("TF handleException msg: " + msg);
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (ex instanceof MissingResourceException || ex instanceof NoClassDefFoundError ||
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                msg.indexOf("java.util.MissingResourceException") >= 0) {
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (params.warnings || params.nodata) {
113f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                warnln(ex.toString() + '\n' + msg);
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
115f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                errln(ex.toString() + '\n' + msg);
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
117a53a69a88b9accc4d1b0e996601d199f1755dc87Yoshito Umaoka        } else {
118f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            errln(ex.toString() + '\n' + msg);
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // use this instead of new random so we get a consistent seed
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // for our tests
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected Random createRandom() {
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new Random(params.seed);
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A test that has no test methods itself, but instead runs other tests.
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This overrides methods are getTargets and getSubtest from TestFmwk.
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If you want the default behavior, pass an array of class names and an
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * optional description to the constructor. The named classes must extend
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TestFmwk. If a provided name doesn't include a ".", package name is
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * prefixed to it (the package of the current test is used if none was
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * provided in the constructor). The resulting full name is used to
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * instantiate an instance of the class using the default constructor.
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Class names are resolved to classes when getTargets or getSubtest is
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * called. This allows instances of TestGroup to be compiled and run without
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * all the targets they would normally invoke being available.
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static abstract class TestGroup extends TestFmwk {
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private String defaultPackage;
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private String[] names;
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private String description;
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private Class[] tests; // deferred init
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Constructor that takes a default package name and a list of class
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * names. Adopts and modifies the classname list
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected TestGroup(String defaultPackage, String[] classnames,
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String description) {
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (classnames == null) {
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalStateException("classnames must not be null");
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (defaultPackage == null) {
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                defaultPackage = getClass().getPackage().getName();
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            defaultPackage = defaultPackage + ".";
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.defaultPackage = defaultPackage;
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.names = classnames;
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.description = description;
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Constructor that takes a list of class names and a description, and
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * uses the package for this class as the default package.
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected TestGroup(String[] classnames, String description) {
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(null, classnames, description);
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Constructor that takes a list of class names, and uses the package
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * for this class as the default package.
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected TestGroup(String[] classnames) {
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(null, classnames, null);
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected String getDescription() {
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return description;
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected Target getTargets(String targetName) {
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Target target = null;
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (targetName != null) {
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                finishInit(); // hmmm, want to get subtest without initializing
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // all tests
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    TestFmwk test = getSubtest(targetName);
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (test != null) {
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        target = test.new ClassTarget();
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        target = this.new Target(targetName);
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (TestFmwkException e) {
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    target = this.new Target(targetName);
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (params.doRecurse()) {
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                finishInit();
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean groupOnly = params.doRecurseGroupsOnly();
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = names.length; --i >= 0;) {
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Target newTarget = null;
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Class cls = tests[i];
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (cls == null) { // hack no warning for missing tests
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (params.warnings) {
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            continue;
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        newTarget = this.new Target(names[i]);
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        TestFmwk test = getSubtest(i, groupOnly);
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (test != null) {
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            newTarget = test.new ClassTarget();
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (groupOnly) {
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                newTarget = this.new EmptyTarget(names[i]);
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else {
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                newTarget = this.new Target(names[i]);
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (newTarget != null) {
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        newTarget.setNext(target);
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        target = newTarget;
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return target;
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected TestFmwk getSubtest(String testName) throws TestFmwkException {
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finishInit();
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < names.length; ++i) {
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (names[i].equalsIgnoreCase(testName)) { // allow
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // case-insensitive
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // matching
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return getSubtest(i, false);
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new TestFmwkException(testName);
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private TestFmwk getSubtest(int i, boolean groupOnly) {
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Class cls = tests[i];
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (cls != null) {
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (groupOnly && !TestGroup.class.isAssignableFrom(cls)) {
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return null;
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    TestFmwk subtest = (TestFmwk) cls.newInstance();
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    subtest.params = params;
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return subtest;
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (InstantiationException e) {
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw new IllegalStateException(e.getMessage());
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (IllegalAccessException e) {
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw new IllegalStateException(e.getMessage());
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private void finishInit() {
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (tests == null) {
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tests = new Class[names.length];
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < names.length; ++i) {
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String name = names[i];
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (name.indexOf('.') == -1) {
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        name = defaultPackage + name;
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    try {
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        Class cls = Class.forName(name);
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (!TestFmwk.class.isAssignableFrom(cls)) {
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            throw new IllegalStateException("class " + name
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    + " does not extend TestFmwk");
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        tests[i] = cls;
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        names[i] = getClassTargetName(cls);
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (ClassNotFoundException e) {
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // leave tests[i] null and name as classname
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default target is invalid.
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public class Target {
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private Target next;
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final String name;
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Target(String name) {
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.name = name;
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Target setNext(Target next) {
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.next = next;
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Target getNext() {
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return next;
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Target append(Target targets) {
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Target t = this;
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            while(t.next != null) {
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                t = t.next;
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            t.next = targets;
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void run() throws Exception {
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int f = filter();
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (f == -1) {
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++params.invalidCount;
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Locale.setDefault(defaultLocale);
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZone.setDefault(defaultTimeZone);
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!validate()) {
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    params.writeTestInvalid(name, params.nodata);
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    params.push(name, getDescription(), f == 1);
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    execute();
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    params.pop();
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected int filter() {
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return params.filter(name);
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected boolean validate() {
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected String getDescription() {
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected void execute() throws Exception{
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public class EmptyTarget extends Target {
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public EmptyTarget(String name) {
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(name);
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected boolean validate() {
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public class MethodTarget extends Target {
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private Method testMethod;
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public MethodTarget(String name, Method method) {
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(name);
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            testMethod = method;
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected boolean validate() {
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return testMethod != null && validateMethod(name);
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected String getDescription() {
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return getMethodDescription(name);
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected void execute() throws Exception{
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (params.inDocMode()) {
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // nothing to execute
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (!params.stack.included) {
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++params.invalidCount;
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                final Object[] NO_ARGS = new Object[0];
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ++params.testCount;
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    init();
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    testMethod.invoke(TestFmwk.this, NO_ARGS);
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (IllegalAccessException e) {
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errln("Can't access test method " + testMethod.getName());
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (Exception e) {
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    handleException(e);
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // If non-exhaustive, check if the method target
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // takes excessive time.
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (params.inclusion <= 5) {
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                double deltaSec = (double)(System.currentTimeMillis() - params.stack.millis)/1000;
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (deltaSec > params.maxTargetSec) {
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (params.timeLog == null) {
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        params.timeLog = new StringBuffer();
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    params.stack.appendPath(params.timeLog);
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    params.timeLog.append(" (" + deltaSec + "s" + ")\n");
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected String getStackTrace(InvocationTargetException e) {
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ByteArrayOutputStream bs = new ByteArrayOutputStream();
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PrintStream ps = new PrintStream(bs);
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            e.getTargetException().printStackTrace(ps);
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return bs.toString();
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public class ClassTarget extends Target {
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String targetName;
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ClassTarget() {
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(null);
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ClassTarget(String targetName) {
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(getClassTargetName(TestFmwk.this.getClass()));
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.targetName = targetName;
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected boolean validate() {
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return TestFmwk.this.validate();
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected String getDescription() {
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return TestFmwk.this.getDescription();
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected void execute() throws Exception {
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            params.indentLevel++;
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Target target = randomize(getTargets(targetName));
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            while (target != null) {
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                target.run();
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                target = target.next;
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            params.indentLevel--;
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private Target randomize(Target t) {
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (t != null && t.getNext() != null) {
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ArrayList list = new ArrayList();
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while (t != null) {
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    list.add(t);
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    t = t.getNext();
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Target[] arr = (Target[]) list.toArray(new Target[list.size()]);
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (true) { // todo - add to params?
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // different jvms return class methods in different orders,
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // so we sort them (always, and then randomize them, so that
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // forcing a seed will also work across jvms).
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Arrays.sort(arr, new Comparator() {
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        public int compare(Object lhs, Object rhs) {
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // sort in reverse order, later we link up in
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // forward order
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            return ((Target) rhs).name
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    .compareTo(((Target) lhs).name);
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    });
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // t is null to start, ends up as first element
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // (arr[arr.length-1])
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (int i = 0; i < arr.length; ++i) {
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = arr[i].setNext(t); // relink in forward order
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (params.random != null) {
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    t = null; // reset t to null
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Random r = params.random;
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (int i = arr.length; --i >= 1;) {
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int x = r.nextInt(i + 1);
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = arr[x].setNext(t);
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        arr[x] = arr[i];
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    t = arr[0].setNext(t); // new first element
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return t;
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //------------------------------------------------------------------------
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Everything below here is boilerplate code that makes it possible
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // to add a new test by simply adding a function to an existing class
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //------------------------------------------------------------------------
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected TestFmwk() {
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected void init() throws Exception{
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Parse arguments into a TestParams object and a collection of target
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * paths. If there was an error parsing the TestParams, print usage and exit
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * with -1. Otherwise, call resolveTarget(TestParams, String) for each path,
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and run the returned target. After the last test returns, if prompt is
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * set, prompt and wait for input from stdin. Finally, exit with number of
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * errors.
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method never returns, since it always exits with System.exit();
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void run(String[] args) {
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        System.exit(run(args, new PrintWriter(System.out)));
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Like run(String[]) except this allows you to specify the error log.
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Unlike run(String[]) this returns the error code as a result instead of
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * calling System.exit().
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int run(String[] args, PrintWriter log) {
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean prompt = false;
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int wx = 0;
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < args.length; ++i) {
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String arg = args[i];
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (arg.equals("-p") || arg.equals("-prompt")) {
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prompt = true;
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (wx < i) {
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    args[wx] = arg;
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                wx++;
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while (wx < args.length) {
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            args[wx++] = null;
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TestParams localParams = TestParams.create(args, log);
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localParams == null) {
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return -1;
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int errorCount = runTests(localParams, args);
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localParams.seed != 0) {
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.println("-random:" + localParams.seed);
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.flush();
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localParams.timeLog != null && localParams.timeLog.length() > 0) {
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.println("\nTest cases taking excessive time (>" +
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    localParams.maxTargetSec + "s):");
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.println(localParams.timeLog.toString());
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localParams.knownIssues != null) {
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.println("\nKnown Issues:");
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Entry<String, List<String>> entry : localParams.knownIssues.entrySet()) {
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String ticketLink = entry.getKey();
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                localParams.log.println("[" + ticketLink + "]");
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (String line : entry.getValue()) {
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    localParams.log.println("  - " + line);
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (localParams.errorSummary != null && localParams.errorSummary.length() > 0) {
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.println("\nError summary:");
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.println(localParams.errorSummary.toString());
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (errorCount > 0) {
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.println("\n<< " + errorCount+ " TEST(S) FAILED >>");
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localParams.log.println("\n<< ALL TESTS PASSED >>");
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (prompt) {
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            System.out.println("Hit RETURN to exit...");
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            System.out.flush();
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                System.in.read();
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (IOException e) {
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                localParams.log.println("Exception: " + e.toString() + e.getMessage());
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        localParams.log.flush();
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return errorCount;
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int runTests(TestParams _params, String[] tests) {
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int ec = 0;
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer summary = null;
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (tests.length == 0 || tests[0] == null) { // no args
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _params.init();
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                resolveTarget(_params).run();
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ec = _params.errorCount;
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < tests.length ; ++i) {
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (tests[i] == null) continue;
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (i > 0) {
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        _params.log.println();
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    _params.init();
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    resolveTarget(_params, tests[i]).run();
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ec += _params.errorCount;
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (_params.errorSummary != null && _params.errorSummary.length() > 0) {
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (summary == null) {
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            summary = new StringBuffer();
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        summary.append("\nTest Root: " + tests[i] + "\n");
6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        summary.append(_params.errorSummary());
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _params.errorSummary = summary;
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (Exception e) {
638f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            // We should normally not get here because
639f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            // MethodTarget.execute() calls handleException().
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ec++;
641f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            _params.log.println("\nencountered a test failure, exiting\n" + e);
642f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            e.printStackTrace(_params.log);
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ec;
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return a ClassTarget for this test. Params is set on this test.
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Target resolveTarget(TestParams paramsArg) {
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.params = paramsArg;
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new ClassTarget();
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Resolve a path from this test to a target. If this test has subtests, and
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the path contains '/', the portion before the '/' is resolved to a
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * subtest, until the path is consumed or the test has no subtests. Returns
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a ClassTarget created using the resolved test and remaining path (which
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * ought to be null or a method name). Params is set on the target's test.
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Target resolveTarget(TestParams paramsArg, String targetPath) {
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TestFmwk test = this;
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        test.params = paramsArg;
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (targetPath != null) {
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (targetPath.length() == 0) {
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                targetPath = null;
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int p = 0;
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int e = targetPath.length();
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // trim all leading and trailing '/'
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while (targetPath.charAt(p) == '/') {
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ++p;
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while (e > p && targetPath.charAt(e - 1) == '/') {
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    --e;
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (p > 0 || e < targetPath.length()) {
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    targetPath = targetPath.substring(p, e - p);
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    p = 0;
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    e = targetPath.length();
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (;;) {
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int n = targetPath.indexOf('/');
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String prefix = n == -1 ? targetPath : targetPath
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                .substring(0, n);
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        TestFmwk subtest = test.getSubtest(prefix);
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (subtest == null) {
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        test = subtest;
6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (n == -1) {
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            targetPath = null;
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        targetPath = targetPath.substring(n + 1);
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (TestFmwkException ex) {
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return test.new Target(targetPath);
7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return test.new ClassTarget(targetPath);
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return true if we can run this test (allows test to inspect jvm,
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * environment, params before running)
7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean validate() {
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return the targets for this test. If targetName is null, return all
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * targets, otherwise return a target for just that name. The returned
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * target can be null.
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default implementation returns a MethodTarget for each public method
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * of the object's class whose name starts with "Test" or "test".
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected Target getTargets(String targetName) {
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getClassTargets(getClass(), targetName);
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected Target getClassTargets(Class cls, String targetName) {
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (cls == null) {
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Target target = null;
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (targetName != null) {
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Method method = cls.getMethod(targetName, (Class[])null);
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                target = new MethodTarget(targetName, method);
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (NoSuchMethodException e) {
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!inheritTargets()) {
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return new Target(targetName); // invalid target
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (SecurityException e) {
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (params.doMethods()) {
7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Method[] methods = cls.getDeclaredMethods();
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = methods.length; --i >= 0;) {
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String name = methods[i].getName();
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (name.startsWith("Test") || name.startsWith("test")) {
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        target = new MethodTarget(name, methods[i])
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        .setNext(target);
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (inheritTargets()) {
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Target parentTarget = getClassTargets(cls.getSuperclass(), targetName);
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (parentTarget == null) {
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return target;
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (target == null) {
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return parentTarget;
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return parentTarget.append(target);
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return target;
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean inheritTargets() {
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected String getDescription() {
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean validateMethod(String name) {
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected String getMethodDescription(String name) {
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // method tests have no subtests, group tests override
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected TestFmwk getSubtest(String prefix) throws TestFmwkException {
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isVerbose() {
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.verbose;
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean noData() {
8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.nodata;
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isTiming() {
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.timing < Long.MAX_VALUE;
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isMemTracking() {
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.memusage;
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 0 = fewest tests, 5 is normal build, 10 is most tests
8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getInclusion() {
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.inclusion;
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isModularBuild() {
8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.warnings;
8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isQuick() {
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.inclusion == 0;
8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void msg(String message, int level, boolean incCount, boolean newln) {
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        params.msg(message, level, incCount, newln);
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final String ICU_TRAC_URL = "http://bugs.icu-project.org/trac/ticket/";
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final String CLDR_TRAC_URL = "http://unicode.org/cldr/trac/ticket/";
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final String CLDR_TICKET_PREFIX = "cldrbug:";
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Log the known issue.
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method returns true unless -prop:logKnownIssue=no is specified
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in the argument list.
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ticket A ticket number string. For an ICU ticket, use numeric characters only,
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * such as "10245". For a CLDR ticket, use prefix "cldrbug:" followed by ticket number,
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * such as "cldrbug:5013".
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param comment Additional comment, or null
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return true unless -prop:logKnownIssue=no is specified in the test command line argument.
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean logKnownIssue(String ticket, String comment) {
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!getBooleanProperty("logKnownIssue", true)) {
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer descBuf = new StringBuffer();
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        params.stack.appendPath(descBuf);
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (comment != null && comment.length() > 0) {
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            descBuf.append(" (" + comment + ")");
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String description = descBuf.toString();
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String ticketLink = "Unknown Ticket";
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (ticket != null && ticket.length() > 0) {
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean isCldr = false;
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ticket = ticket.toLowerCase(Locale.ENGLISH);
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ticket.startsWith(CLDR_TICKET_PREFIX)) {
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                isCldr = true;
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ticket = ticket.substring(CLDR_TICKET_PREFIX.length());
8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ticketLink = (isCldr ? CLDR_TRAC_URL : ICU_TRAC_URL) + ticket;
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (params.knownIssues == null) {
8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            params.knownIssues = new TreeMap<String, List<String>>();
8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<String> lines = params.knownIssues.get(ticketLink);
8787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (lines == null) {
8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            lines = new ArrayList<String>();
8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            params.knownIssues.put(ticketLink, lines);
8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!lines.contains(description)) {
8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            lines.add(description);
8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected int getErrorCount() {
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.errorCount;
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getProperty(String key) {
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String val = null;
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (key != null && key.length() > 0 && params.props != null) {
8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            val = (String)params.props.get(key.toLowerCase());
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return val;
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean getBooleanProperty(String key, boolean defVal) {
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String s = getProperty(key);
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (s != null) {
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (s.equalsIgnoreCase("yes") || s.equals("true")) {
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return true;
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (s.equalsIgnoreCase("no") || s.equalsIgnoreCase("false")) {
9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return defVal;
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected TimeZone safeGetTimeZone(String id) {
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TimeZone tz = TimeZone.getTimeZone(id);
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (tz == null) {
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // should never happen
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("FAIL: TimeZone.getTimeZone(" + id + ") => null");
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!tz.getID().equals(id)) {
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            warnln("FAIL: TimeZone.getTimeZone(" + id + ") => " + tz.getID());
9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return tz;
9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Print a usage message for this test class.
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void usage() {
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        usage(new PrintWriter(System.out), getClass().getName());
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static void usage(PrintWriter pw, String className) {
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println("Usage: " + className + " option* target*");
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println();
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println("Options:");
9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -d[escribe] Print a short descriptive string for this test and all");
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println("       listed targets.");
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -e<n> Set exhaustiveness from 0..10.  Default is 0, fewest tests.\n"
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       To run all tests, specify -e10.  Giving -e with no <n> is\n"
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       the same as -e5.");
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -filter:<str> Only tests matching filter will be run or listed.\n"
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       <str> is of the form ['^']text[','['^']text].\n"
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       Each string delimited by ',' is a separate filter argument.\n"
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       If '^' is prepended to an argument, its matches are excluded.\n"
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       Filtering operates on test groups as well as tests, if a test\n"
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       group is included, all its subtests that are not excluded will\n"
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       be run.  Examples:\n"
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "    -filter:A -- only tests matching A are run.  If A matches a group,\n"
9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       all subtests of this group are run.\n"
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "    -filter:^A -- all tests except those matching A are run.  If A matches\n"
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "        a group, no subtest of that group will be run.\n"
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "    -filter:A,B,^C,^D -- tests matching A or B and not C and not D are run\n"
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       Note: Filters are case insensitive.");
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -h[elp] Print this help text and exit.");
956f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        pw.println(" -hex Display non-ASCII characters in hexadecimal format");
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -l[ist] List immediate targets of this test");
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println("   -la, -listAll List immediate targets of this test, and all subtests");
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println("   -le, -listExaustive List all subtests and targets");
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // don't know how to get useful numbers for memory usage using java API
9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // calls
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //      pw.println(" -m[emory] print memory usage and force gc for
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // each test");
964f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        pw.println(" -n[othrow] Message on test failure rather than exception.\n"
965f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                + "       This is the default behavior and has no effects on ICU 55+.");
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -p[rompt] Prompt before exiting");
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -prop:<key>=<value> Set optional property used by this test");
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -q[uiet] Do not show warnings");
9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -r[andom][:<n>] If present, randomize targets.  If n is present,\n"
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       use it as the seed.  If random is not set, targets will\n"
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + "       be in alphabetical order to ensure cross-platform consistency.");
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -s[ilent] No output except error summary or exceptions.");
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -tfilter:<str> Transliterator Test filter of ids.");
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -t[ime]:<n> Print elapsed time only for tests exceeding n milliseconds.");
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -v[erbose] Show log messages");
976f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        pw.println(" -u[nicode] Don't escape error or log messages (Default on ICU 55+)");
9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -w[arning] Continue in presence of warnings, and disable missing test warnings.");
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" -nodata | -nd Do not warn if resource data is not present.");
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println();
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" If a list or describe option is provided, no tests are run.");
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println();
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println("Targets:");
9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" If no target is specified, all targets for this test are run.");
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" If a target contains no '/' characters, and matches a target");
9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" of this test, the target is run.  Otherwise, the part before the");
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" '/' is used to match a subtest, which then evaluates the");
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" remainder of the target as above.  Target matching is case-insensitive.");
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println();
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.println(" If multiple targets are provided, each is executed in order.");
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pw.flush();
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String hex(char[] s){
9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer result = new StringBuffer();
9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < s.length; ++i) {
9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (i != 0) result.append(',');
9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(hex(s[i]));
9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.toString();
9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String hex(byte[] s){
10017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer result = new StringBuffer();
10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < s.length; ++i) {
10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (i != 0) result.append(',');
10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(hex(s[i]));
10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.toString();
10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String hex(char ch) {
10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer result = new StringBuffer();
10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String foo = Integer.toString(ch, 16).toUpperCase();
10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = foo.length(); i < 4; ++i) {
10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append('0');
10137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result + foo;
10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String hex(int ch) {
10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer result = new StringBuffer();
10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String foo = Integer.toString(ch, 16).toUpperCase();
10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = foo.length(); i < 4; ++i) {
10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append('0');
10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result + foo;
10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String hex(CharSequence s) {
10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder result = new StringBuilder();
10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < s.length(); ++i) {
10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (i != 0)
10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(',');
10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(hex(s.charAt(i)));
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.toString();
10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String prettify(CharSequence s) {
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder result = new StringBuilder();
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int ch;
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < s.length(); i += Character.charCount(ch)) {
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ch = Character.codePointAt(s, i);
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ch > 0xfffff) {
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append("\\U00");
10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(hex(ch));
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (ch > 0xffff) {
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append("\\U000");
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(hex(ch));
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (ch < 0x20 || 0x7e < ch) {
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append("\\u");
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(hex(ch));
10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append((char) ch);
10527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.toString();
10567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static java.util.GregorianCalendar cal;
10597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return a Date given a year, month, and day of month. This is similar to
10627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * new Date(y-1900, m, d). It uses the default time zone at the time this
10637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * method is first called.
10647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param year
10667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            use 2000 for 2000, unlike new Date()
10677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param month
10687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            use Calendar.JANUARY etc.
10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dom
10707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            day of month, 1-based
10717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a Date object for the given y/m/d
10727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected static synchronized java.util.Date getDate(int year, int month,
10747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int dom) {
10757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (cal == null) {
10767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            cal = new java.util.GregorianCalendar();
10777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.clear();
10797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.set(year, month, dom);
10807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return cal.getTime();
10817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class NullWriter extends PrintWriter {
10847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public NullWriter() {
10857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(System.out, false);
10867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void write(int c) {
10887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void write(char[] buf, int off, int len) {
10907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void write(String s, int off, int len) {
10927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void println() {
10947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class ASCIIWriter extends PrintWriter {
10987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private StringBuffer buffer = new StringBuffer();
10997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Characters that we think are printable but that escapeUnprintable
11017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // doesn't
11027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final String PRINTABLES = "\t\n\r";
11037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ASCIIWriter(Writer w, boolean autoFlush) {
11057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(w, autoFlush);
11067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ASCIIWriter(OutputStream os, boolean autoFlush) {
11097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(os, autoFlush);
11107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void write(int c) {
11137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            synchronized (lock) {
11147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buffer.setLength(0);
11157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (PRINTABLES.indexOf(c) < 0
11167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && TestUtil.escapeUnprintable(buffer, c)) {
11177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    super.write(buffer.toString());
11187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
11197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    super.write(c);
11207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
11217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void write(char[] buf, int off, int len) {
11257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            synchronized (lock) {
11267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buffer.setLength(0);
11277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int limit = off + len;
11287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while (off < limit) {
11297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int c = UTF16Util.charAt(buf, 0, buf.length, off);
11307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    off += UTF16Util.getCharCount(c);
11317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (PRINTABLES.indexOf(c) < 0
11327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            && TestUtil.escapeUnprintable(buffer, c)) {
11337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        super.write(buffer.toString());
11347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        buffer.setLength(0);
11357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
11367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        super.write(c);
11377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
11387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
11397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void write(String s, int off, int len) {
11437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            write(s.substring(off, off + len).toCharArray(), 0, len);
11447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // filters
11487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // match against the entire hierarchy
11497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // A;B;!C;!D --> (A ||B) && (!C && !D)
11507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // positive, negative, unknown matches
11517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // positive -- known to be included, negative- known to be excluded
11527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // positive only if no excludes, and matches at least one include, if any
11537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // negative only if matches at least one exclude
11547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // otherwise, we wait
11557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class TestParams {
11577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean prompt;
11587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean verbose;
11597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean quiet;
11607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int listlevel;
11617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean describe;
11627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean warnings;
11637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean nodata;
11647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public long timing = 0;
11657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean memusage;
11667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int inclusion;
11677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String filter;
11687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public long seed;
11697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String tfilter; // for transliterator tests
11707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public State stack;
11727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1173a53a69a88b9accc4d1b0e996601d199f1755dc87Yoshito Umaoka        public StringBuffer errorSummary = new StringBuffer();
11747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private StringBuffer timeLog;
11757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private Map<String, List<String>> knownIssues;
11767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public PrintWriter log;
11787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int indentLevel;
11797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private boolean needLineFeed;
11807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private boolean suppressIndent;
11817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int errorCount;
11827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int warnCount;
11837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int invalidCount;
11847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int testCount;
11857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private NumberFormat tformat;
11867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Random random;
11877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int maxTargetSec = 10;
11887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public HashMap props;
11897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private TestParams() {
11917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static TestParams create(String arglist, PrintWriter log) {
11947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String[] args = null;
11957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (arglist != null && arglist.length() > 0) {
11967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                args = arglist.split("\\s");
11977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return create(args, log);
11997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
12027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Create a TestParams from a list of arguments.  If successful, return the params object,
12037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * else return null.  Error messages will be reported on errlog if it is not null.
12047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Arguments and values understood by this method will be removed from the args array
12057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * and existing args will be shifted down, to be filled by nulls at the end.
12067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param args the list of arguments
12077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param log the error log, or null if no error log is desired
12087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return the new TestParams object, or null if error
12097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
12107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static TestParams create(String[] args, PrintWriter log) {
12117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TestParams params = new TestParams();
12127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1213f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            if (log == null) {
12147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                params.log = new NullWriter();
1215f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            } else {
1216f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                params.log = log;
12177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean usageError = false;
12207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String filter = null;
12217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String fmt = "#,##0.000s";
12227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int wx = 0; // write argets.
12237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (args != null) {
12247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < args.length; i++) {
12257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String arg = args[i];
12267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (arg == null || arg.length() == 0) {
12277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        continue;
12287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (arg.charAt(0) == '-') {
12307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        arg = arg.toLowerCase();
12317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (arg.equals("-verbose") || arg.equals("-v")) {
12327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.verbose = true;
12337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.quiet = false;
12347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-quiet") || arg.equals("-q")) {
12357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.quiet = true;
12367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.verbose = false;
1237f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        } else if (arg.equals("-hex")) {
1238f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            params.log =  new ASCIIWriter(log, true);
12397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-help") || arg.equals("-h")) {
12407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            usageError = true;
12417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-warning") || arg.equals("-w")) {
12427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.warnings = true;
12437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-nodata") || arg.equals("-nd")) {
12447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.nodata = true;
12457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-list") || arg.equals("-l")) {
12467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.listlevel = 1;
12477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-listall") || arg.equals("-la")) {
12487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.listlevel = 2;
12497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-listexaustive") || arg.equals("-le")) {
12507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.listlevel = 3;
12517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-memory") || arg.equals("-m")) {
12527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.memusage = true;
12537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-nothrow") || arg.equals("-n")) {
1254a53a69a88b9accc4d1b0e996601d199f1755dc87Yoshito Umaoka                            // Default since ICU 55. This option has no effects.
12557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.equals("-describe") || arg.equals("-d")) {
12567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.describe = true;
12577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-r")) {
12587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            String s = null;
12597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            int n = arg.indexOf(':');
12607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (n != -1) {
12617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                s = arg.substring(n + 1);
12627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                arg = arg.substring(0, n);
12637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
12647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (arg.equals("-r") || arg.equals("-random")) {
12667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (s == null) {
12677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    params.seed = System.currentTimeMillis();
12687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                } else {
12697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    params.seed = Long.parseLong(s);
12707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
12717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else {
12727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                log.println("*** Error: unrecognized argument: " + arg);
12737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                usageError = true;
12747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                break;
12757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
12767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-e")) {
12777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // see above
12787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.inclusion = (arg.length() == 2)
12797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    ? 5
12807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            : Integer.parseInt(arg.substring(2));
12817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (params.inclusion < 0 || params.inclusion > 10) {
12827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                usageError = true;
12837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                break;
12847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
12857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-tfilter:")) {
12867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.tfilter = arg.substring(8);
12877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-time") || arg.startsWith("-t")) {
12887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            long val = 0;
12897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            int inx = arg.indexOf(':');
12907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (inx > 0) {
12917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                String num = arg.substring(inx + 1);
12927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                try {
12937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    val = Long.parseLong(num);
12947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                } catch (Exception e) {
12957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    log.println("*** Error: could not parse time threshold '"
12967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            + num + "'");
12977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    usageError = true;
12987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    break;
12997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
13007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
13017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.timing = val;
13027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (val <= 10) {
13037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                fmt = "#,##0.000s";
13047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else if (val <= 100) {
13057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                fmt = "#,##0.00s";
13067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else if (val <= 1000) {
13077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                fmt = "#,##0.0s";
13087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
13097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-filter:")) {
13107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            String temp = arg.substring(8).toLowerCase();
13117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            filter = filter == null ? temp : filter + "," + temp;
13127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-f:")) {
13137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            String temp = arg.substring(3).toLowerCase();
13147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            filter = filter == null ? temp : filter + "," + temp;
13157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-s")) {
13167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.log = new NullWriter();
13177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-u")) {
13187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (params.log instanceof ASCIIWriter) {
13197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                params.log = log;
13207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
13217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (arg.startsWith("-prop:")) {
13227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            String temp = arg.substring(6);
13237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            int eql = temp.indexOf('=');
13247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (eql <= 0) {
13257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                log.println("*** Error: could not parse custom property '" + arg + "'");
13267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                usageError = true;
13277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                break;
13287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
13297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (params.props == null) {
13307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                params.props = new HashMap();
13317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
13327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            params.props.put(temp.substring(0, eql), temp.substring(eql+1));
13337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
13347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            log.println("*** Error: unrecognized argument: "
13357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    + args[i]);
13367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            usageError = true;
13377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
13387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
13397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
13407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        args[wx++] = arg; // shift down
13417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
13427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
13437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while (wx < args.length) {
13457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    args[wx++] = null;
13467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
13477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            params.tformat = new DecimalFormat(fmt);
13507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (usageError) {
13527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                usage(log, "TestAll");
13537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
13547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (filter != null) {
13577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                params.filter = filter.toLowerCase();
13587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            params.init();
13617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return params;
13637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String errorSummary() {
13667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return errorSummary == null ? "" : errorSummary.toString();
13677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void init() {
13707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            indentLevel = 0;
13717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            needLineFeed = false;
13727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            suppressIndent = false;
13737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errorCount = 0;
13747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            warnCount = 0;
13757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            invalidCount = 0;
13767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            testCount = 0;
13777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            random = seed == 0 ? null : new Random(seed);
13787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public class State {
13817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            State link;
13827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String name;
13837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuffer buffer;
13847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int level;
13857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int ec;
13867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int wc;
13877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int ic;
13887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int tc;
13897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean flushed;
13907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            public boolean included;
13917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long mem;
13927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long millis;
13937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            public State(State link, String name, boolean included) {
13957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                this.link = link;
13967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                this.name = name;
13977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (link == null) {
13987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.level = 0;
13997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.included = included;
14007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
14017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.level = link.level + 1;
14027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.included = included || link.included;
14037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                this.ec = errorCount;
14057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                this.wc = warnCount;
14067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                this.ic = invalidCount;
14077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                this.tc = testCount;
14087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (link == null || this.included) {
14107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    flush();
14117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                mem = getmem();
14147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                millis = System.currentTimeMillis();
14157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            void flush() {
14187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!flushed) {
14197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (link != null) {
14207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        link.flush();
14217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
14227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    indent(level);
14247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.print(name);
14257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.flush();
14267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    flushed = true;
14287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    needLineFeed = true;
14307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            void appendPath(StringBuffer buf) {
14347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (this.link != null) {
14357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.link.appendPath(buf);
14367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append('/');
14377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(name);
14397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void push(String name, String description, boolean included) {
14437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (inDocMode() && describe && description != null) {
14447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                name += ": " + description;
14457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            stack = new State(stack, name, included);
14477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void pop() {
14507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (stack != null) {
14517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                writeTestResult();
14527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                stack = stack.link;
14537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean inDocMode() {
14577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return describe || listlevel != 0;
14587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean doMethods() {
14617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return !inDocMode() || listlevel == 3
14627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || (indentLevel == 1 && listlevel > 0);
14637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean doRecurse() {
14667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return !inDocMode() || listlevel > 1
14677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || (indentLevel == 1 && listlevel > 0);
14687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean doRecurseGroupsOnly() {
14717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return inDocMode()
14727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    && (listlevel == 2 || (indentLevel == 1 && listlevel > 0));
14737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // return 0, -1, or 1
14767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 1: run this test
14777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 0: might run this test, no positive include or exclude on this group
14787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // -1: exclude this test
14797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int filter(String testName) {
14807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int result = 0;
14817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (filter == null) {
14827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result = 1;
14837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
14847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean noIncludes = true;
14857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean noExcludes = filter.indexOf('^') == -1;
14867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                testName = testName.toLowerCase();
14877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int ix = 0;
14887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while (ix < filter.length()) {
14897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int nix = filter.indexOf(',', ix);
14907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (nix == -1) {
14917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        nix = filter.length();
14927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
14937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (filter.charAt(ix) == '^') {
14947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (testName.indexOf(filter.substring(ix + 1, nix)) != -1) {
14957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            result = -1;
14967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
14977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
14987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
14997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        noIncludes = false;
15007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (testName.indexOf(filter.substring(ix, nix)) != -1) {
15017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            result = 1;
15027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (noExcludes) {
15037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                break;
15047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
15057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
15067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
15077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ix = nix + 1;
15097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (result == 0 && noIncludes) {
15117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result = 1;
15127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //              System.out.println("filter: " + testName + " returns: " +
15157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // result);
15167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
15177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
15207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Log access.
15217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param msg The string message to write
15227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
15237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void write(String msg) {
15247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            write(msg, false);
15257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void writeln(String msg) {
15287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            write(msg, true);
15297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private void write(String msg, boolean newln) {
15327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!suppressIndent) {
15337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (needLineFeed) {
15347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.println();
15357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    needLineFeed = false;
15367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.print(spaces.substring(0, indentLevel * 2));
15387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            log.print(msg);
15407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (newln) {
15417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.println();
15427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            log.flush();
15447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            suppressIndent = !newln;
15457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private void msg(String message, int level, boolean incCount,
15487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean newln) {
15497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int oldLevel = level;
15507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert//            if (level == WARN && (!warnings && !nodata)){
15517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert//                level = ERR;
15527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert//            }
15537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (incCount) {
15557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (level == WARN) {
15567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    warnCount++;
15577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert//                    invalidCount++;
15587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (level == ERR) {
15597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errorCount++;
15607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // should roll indentation stuff into log ???
15647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (verbose || level > (quiet ? WARN : LOG)) {
15657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!suppressIndent) {
15667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    indent(indentLevel + 1);
15677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    final String[] MSGNAMES = {"", "Warning: ", "Error: "};
15687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.print(MSGNAMES[oldLevel]);
15697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String testLocation = sourceLocation();
15727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                message = testLocation + message;
15737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.print(message);
15747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (newln) {
15757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.println();
15767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.flush();
15787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (level == ERR) {
15817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!suppressIndent && errorSummary != null && stack !=null
15827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && (errorCount == stack.ec + 1)) {
15837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    stack.appendPath(errorSummary);
15847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errorSummary.append("\n");
15857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            suppressIndent = !newln;
15897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private void writeTestInvalid(String name, boolean nodataArg) {
15927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //              msg("***" + name + "*** not found or not valid.", WARN, true,
15937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // true);
15947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (inDocMode()) {
15957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!warnings) {
15967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (stack != null) {
15977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        stack.flush();
15987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
15997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.println(" *** Target not found or not valid.");
16007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.flush();
16017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    needLineFeed = false;
16027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
16047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if(!nodataArg){
16057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    msg("Test " + name + " not found or not valid.", WARN, true,
16067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            true);
16077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long getmem() {
16127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long newmem = 0;
16137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (memusage) {
16147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Runtime rt = Runtime.getRuntime();
16157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long lastmem = Long.MAX_VALUE;
16167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                do {
16177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rt.gc();
16187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rt.gc();
16197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    try {
16207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        Thread.sleep(50);
16217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } catch (Exception e) {
16227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
16237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
16247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    lastmem = newmem;
16257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    newmem = rt.totalMemory() - rt.freeMemory();
16267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } while (newmem < lastmem);
16277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return newmem;
16297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private void writeTestResult() {
16327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (inDocMode()) {
16337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (needLineFeed) {
16347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.println();
16357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.flush();
16367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                needLineFeed = false;
16387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return;
16397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long dmem = getmem() - stack.mem;
16427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long dtime = System.currentTimeMillis() - stack.millis;
16437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int testDelta = testCount - stack.tc;
16457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (testDelta == 0) {
1646f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                if (stack.included) {
1647f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    stack.flush();
1648f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    indent(indentLevel);
1649f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    log.println("} (0s) Empty");
1650f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                }
16517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return;
16527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int errorDelta = errorCount - stack.ec;
16557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int warnDelta = warnCount - stack.wc;
16567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int invalidDelta = invalidCount - stack.ic;
16577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            stack.flush();
16597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!needLineFeed) {
16617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                indent(indentLevel);
16627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.print("}");
16637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            needLineFeed = false;
16657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (memusage || dtime >= timing) {
16677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.print(" (");
16687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (memusage) {
16697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.print("dmem: " + dmem);
16707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (dtime >= timing) {
16727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (memusage) {
16737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        log.print(", ");
16747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
16757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.print(tformat.format(dtime / 1000f));
16767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.print(")");
16787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (errorDelta != 0) {
16817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.println(" FAILED ("
16827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + errorDelta
16837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + " failure(s)"
16847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + ((warnDelta != 0) ? ", " + warnDelta
16857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                + " warning(s)" : "")
16867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + ((invalidDelta != 0) ? ", " + invalidDelta
16877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                + " test(s) skipped)" : ")"));
16887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (warnDelta != 0) {
16897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.println(" ALERT ("
16907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + warnDelta
16917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + " warning(s)"
16927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + ((invalidDelta != 0) ? ", " + invalidDelta
16937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                + " test(s) skipped)" : ")"));
16947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (invalidDelta != 0) {
16957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.println(" Qualified (" + invalidDelta + " test(s) skipped)");
16967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
16977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.println(" Passed");
16987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final void indent(int distance) {
17027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean idm = inDocMode();
17037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (needLineFeed) {
17047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (idm) {
17057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.println();
17067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
17077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    log.println(" {");
17087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                needLineFeed = false;
17107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            log.print(spaces.substring(0, distance * (idm ? 3 : 2)));
17137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (idm) {
17157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                log.print("-- ");
17167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getTranslitTestFilter() {
17217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return params.tfilter;
17227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return the target name for a test class. This is either the end of the
17267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * class name, or if the class declares a public static field
17277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * CLASS_TARGET_NAME, the value of that field.
17287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String getClassTargetName(Class testClass) {
17307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String name = testClass.getName();
17317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
17327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Field f = testClass.getField("CLASS_TARGET_NAME");
17337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            name = (String) f.get(null);
17347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (IllegalAccessException e) {
17357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalStateException(
17367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    "static field CLASS_TARGET_NAME must be accessible");
17377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (NoSuchFieldException e) {
17387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int n = Math.max(name.lastIndexOf('.'), name.lastIndexOf('$'));
17397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (n != -1) {
17407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                name = name.substring(n + 1);
17417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return name;
17447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Check the given array to see that all the strings in the expected array
17487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * are present.
17497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
17507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param msg
17517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            string message, for log output
17527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param array
17537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            array of strings to check
17547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param expected
17557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            array of strings we expect to see, or null
17567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the length of 'array', or -1 on error
17577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected int checkArray(String msg, String array[], String expected[]) {
17597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int explen = (expected != null) ? expected.length : 0;
17607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!(explen >= 0 && explen < 31)) { // [sic] 31 not 32
17617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("Internal error");
17627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return -1;
17637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int i = 0;
17657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer buf = new StringBuffer();
17667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int seenMask = 0;
17677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (; i < array.length; ++i) {
17687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String s = array[i];
17697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (i != 0)
17707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(", ");
17717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(s);
17727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // check expected list
17737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int j = 0, bit = 1; j < explen; ++j, bit <<= 1) {
17747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ((seenMask & bit) == 0) {
17757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (s.equals(expected[j])) {
17767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        seenMask |= bit;
17777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        logln("Ok: \"" + s + "\" seen");
17787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
17797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        logln(msg + " = [" + buf + "] (" + i + ")");
17837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // did we see all expected strings?
17847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (((1 << explen) - 1) != seenMask) {
17857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int j = 0, bit = 1; j < expected.length; ++j, bit <<= 1) {
17867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ((seenMask & bit) == 0) {
17877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errln("\"" + expected[j] + "\" not seen");
17887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return array.length;
17927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
17957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Check the given array to see that all the locales in the expected array
17967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * are present.
17977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
17987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param msg
17997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            string message, for log output
18007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param array
18017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            array of locales to check
18027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param expected
18037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            array of locales names we expect to see, or null
18047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the length of 'array'
18057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected int checkArray(String msg, Locale array[], String expected[]) {
18077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String strs[] = new String[array.length];
18087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < array.length; ++i)
18097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            strs[i] = array[i].toString();
18107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return checkArray(msg, strs, expected);
18117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Check the given array to see that all the locales in the expected array
18157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * are present.
18167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
18177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param msg
18187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            string message, for log output
18197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param array
18207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            array of locales to check
18217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param expected
18227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            array of locales names we expect to see, or null
18237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the length of 'array'
18247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected int checkArray(String msg, ULocale array[], String expected[]) {
18267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String strs[] = new String[array.length];
18277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < array.length; ++i)
18287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            strs[i] = array[i].toString();
18297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return checkArray(msg, strs, expected);
18307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // JUnit-like assertions.
18337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertTrue(String message, boolean condition) {
18357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(condition, message, "true", null);
18367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertFalse(String message, boolean condition) {
18397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(!condition, message, "false", null);
18407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertEquals(String message, boolean expected,
18437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean actual) {
18447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(expected == actual, message, String
18457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                .valueOf(expected), String.valueOf(actual));
18467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertEquals(String message, long expected, long actual) {
18497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(expected == actual, message, String
18507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                .valueOf(expected), String.valueOf(actual));
18517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // do NaN and range calculations to precision of float, don't rely on
18547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // promotion to double
18557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertEquals(String message, float expected,
18567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            float actual, double error) {
18577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean result = Float.isInfinite(expected)
18587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ? expected == actual
18597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                : !(Math.abs(expected - actual) > error); // handles NaN
18607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(result, message, String.valueOf(expected)
18617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + (error == 0 ? "" : " (within " + error + ")"), String
18627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                .valueOf(actual));
18637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertEquals(String message, double expected,
18667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            double actual, double error) {
18677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean result = Double.isInfinite(expected)
18687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ? expected == actual
18697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                : !(Math.abs(expected - actual) > error); // handles NaN
18707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(result, message, String.valueOf(expected)
18717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                + (error == 0 ? "" : " (within " + error + ")"), String
18727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                .valueOf(actual));
18737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected <T> boolean assertEquals(String message, T[] expected, T[] actual) {
18767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Use toString on a List to get useful, readable messages
18777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String expectedString = expected == null ? "null" : Arrays.asList(expected).toString();
18787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String actualString = actual == null ? "null" : Arrays.asList(actual).toString();
18797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return assertEquals(message, expectedString, actualString);
18807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertEquals(String message, Object expected,
18837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object actual) {
18847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean result = expected == null ? actual == null : expected
18857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                .equals(actual);
18867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(result, message, stringFor(expected),
18877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                stringFor(actual));
18887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertNotEquals(String message, Object expected,
18917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object actual) {
18927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean result = !(expected == null ? actual == null : expected
18937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                .equals(actual));
18947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(result, message, stringFor(expected),
18957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                stringFor(actual), "not equal to", true);
18967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertSame(String message, Object expected, Object actual) {
18997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(expected == actual, message, stringFor(expected),
19007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                stringFor(actual), "==", false);
19017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertNotSame(String message, Object expected,
19047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object actual) {
19057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(expected != actual, message, stringFor(expected),
19067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                stringFor(actual), "!=", true);
19077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertNull(String message, Object actual) {
19107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(actual == null, message, null, stringFor(actual));
19117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean assertNotNull(String message, Object actual) {
19147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(actual != null, message, null, stringFor(actual),
19157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "!=", true);
19167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected void fail() {
19197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fail("");
19207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected void fail(String message) {
19237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (message == null) {
19247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            message = "";
19257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!message.equals("")) {
19277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            message = ": " + message;
19287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        errln(sourceLocation() + message);
19307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean handleAssert(boolean result, String message,
19337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String expected, String actual) {
19347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return handleAssert(result, message, expected, actual, null, false);
19357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean handleAssert(boolean result, String message,
19387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object expected, Object actual, String relation, boolean flip) {
19397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!result || isVerbose()) {
19407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (message == null) {
19417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                message = "";
19427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
19437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!message.equals("")) {
19447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                message = ": " + message;
19457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
19467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            relation = relation == null ? ", got " : " " + relation + " ";
19477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (result) {
19487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                logln("OK " + message + ": "
19497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + (flip ? expected + relation + actual : expected));
19507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
19517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // assert must assume errors are true errors and not just warnings
19527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // so cannot warnln here
19537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln(  message
19547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + ": expected"
19557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        + (flip ? relation + expected : " " + expected
19567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                + (actual != null ? relation + actual : "")));
19577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
19587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
19607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final String stringFor(Object obj) {
19637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (obj == null) {
19647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return "null";
19657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (obj instanceof String) {
19677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return "\"" + obj + '"';
19687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return obj.getClass().getName() + "<" + obj + ">";
19707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Return the source code location of the caller located callDepth frames up the stack.
19737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String sourceLocation() {
19747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Walk up the stack to the first call site outside this file
19757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StackTraceElement[] st = new Throwable().getStackTrace();
19767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < st.length; ++i) {
19777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String source = st[i].getFileName();
19787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!source.equals("TestFmwk.java") && !source.equals("AbstractTestLog.java")) {
19797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return "(" + st[i].getFileName() + ":" + st[i].getLineNumber() + ") ";
19807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
19817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new InternalError();
19837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // End JUnit-like assertions
19877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // PrintWriter support
19897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PrintWriter getErrorLogPrintWriter() {
19917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new PrintWriter(new TestLogWriter(this, TestLog.ERR));
19927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PrintWriter getLogPrintWriter() {
19957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new PrintWriter(new TestLogWriter(this, TestLog.LOG));
19967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // end PrintWriter support
19997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected TestParams params = null;
20017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final static String spaces = "                                          ";
20037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
2005