17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Copyright (C) 2004-2007, 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.lang.reflect.Constructor;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.lang.reflect.Method;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collection;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Iterator;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.LinkedList;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeSet;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.UnicodeSet;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * To use, override the abstract and the protected methods as necessary.
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Tests boilerplate invariants:
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>a.equals(a)
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>!a.equals(null)
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>if a.equals(b) then
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>(1) a.hashCode() == b.hashCode  // note: the reverse is not necessarily true.
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>(2) a functions in all aspects as equivalent to b
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>(3) b.equals(a)
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>if b = clone(a)
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>(1) b.equals(a), and the above checks
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <br>(2) if mutable(a), then a.clone() != a // note: the reverse is not necessarily true.
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author Davis
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic abstract class TestBoilerplate extends TestFmwk {
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final void TestMain() throws Exception {
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List list = new LinkedList();
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while (_addTestObject(list)) {
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Object[] testArray = list.toArray();
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < testArray.length; ++i) {
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //logln("Testing " + i);
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object a = testArray[i];
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int aHash = a.hashCode();
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (a.equals(null)) {
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("Equality/Null invariant fails: " + i);
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!a.equals(a)) {
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("Self-Equality invariant fails: " + i);
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object b;
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (_canClone(a)) {
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                b = _clone(a);
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (b == a) {
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (_isMutable(a)) {
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        errln("Clone/Mutability invariant fails: " + i);
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (!a.equals(b)) {
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        errln("Clone/Equality invariant fails: " + i);
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                _checkEquals(i, -1, a, aHash, b);
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int j = i; j < testArray.length; ++j) {
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                b = testArray[j];
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (a.equals(b)) _checkEquals(i, j, a, aHash, b);
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void _checkEquals(int i, int j, Object a, int aHash, Object b) {
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int bHash = b.hashCode();
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!b.equals(a)) errln("Equality/Symmetry",i, j);
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (aHash != bHash) errln("Equality/Hash",i, j);
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (a != b && !_hasSameBehavior(a,b)) {
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("Equality/Equivalence",i, j);
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void errln(String title, int i, int j) {
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (j < 0) errln("Clone/" + title + "invariant fails: " + i);
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else errln(title + "invariant fails: " + i + "," + j);
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Must be overridden to check whether a and be behave the same
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected abstract boolean _hasSameBehavior(Object a, Object b);
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method will be called multiple times until false is returned.
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The results should be a mixture of different objects of the same
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * type: some equal and most not equal.
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The subclasser controls how many are produced (recommend about
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 100, based on the size of the objects and how costly they are
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to run this test on. The running time grows with the square of the
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * count.
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * NOTE: this method will only be called if the objects test as equal.
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected abstract boolean _addTestObject(List c);
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Override if the tested objects are mutable.
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <br>Since Java doesn't tell us, we need a function to tell if so.
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default is true, so must be overridden if not.
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean _isMutable(Object a) {
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Override if the tested objects can be cloned.
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected boolean _canClone(Object a) {
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Produce a clone of the object. Tries two methods
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (a) clone
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * (b) constructor
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Must be overridden if _canClone returns true and
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the above methods don't work.
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param a
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return clone
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    protected Object _clone(Object a) throws Exception {
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Class aClass = a.getClass();
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Method cloner = aClass.getMethod("clone", (Class[])null);
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return cloner.invoke(a,(Object[])null);
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (NoSuchMethodException e) {
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Constructor constructor = aClass.getConstructor(new Class[] {aClass});
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return constructor.newInstance(new Object[]{a});
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* Utilities */
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static boolean verifySetsIdentical(AbstractTestLog here, UnicodeSet set1, UnicodeSet set2) {
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (set1.equals(set2)) return true;
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln("Sets differ:");
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln("UnicodeMap - HashMap");
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln(new UnicodeSet(set1).removeAll(set2).toPattern(true));
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln("HashMap - UnicodeMap");
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln(new UnicodeSet(set2).removeAll(set1).toPattern(true));
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static boolean verifySetsIdentical(AbstractTestLog here, Set values1, Set values2) {
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (values1.equals(values2)) return true;
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Set temp;
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln("Values differ:");
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln("UnicodeMap - HashMap");
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        temp = new TreeSet(values1);
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        temp.removeAll(values2);
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln(show(temp));
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln("HashMap - UnicodeMap");
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        temp = new TreeSet(values2);
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        temp.removeAll(values1);
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        here.errln(show(temp));
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String show(Map m) {
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer buffer = new StringBuffer();
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Iterator it = m.keySet().iterator(); it.hasNext();) {
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object key = it.next();
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buffer.append(key + "=>" + m.get(key) + "\r\n");
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return buffer.toString();
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static UnicodeSet getSet(Map m, Object value) {
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UnicodeSet result = new UnicodeSet();
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Iterator it = m.keySet().iterator(); it.hasNext();) {
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object key = it.next();
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Object val = m.get(key);
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!val.equals(value)) continue;
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.add(((Integer)key).intValue());
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static String show(Collection c) {
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer buffer = new StringBuffer();
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Iterator it = c.iterator(); it.hasNext();) {
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buffer.append(it.next() + "\r\n");
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return buffer.toString();
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
193