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