From ab0b75881b8d2b411050affc10ac8c0299c2b82d Mon Sep 17 00:00:00 2001 From: Mark Davis Date: Tue, 24 Feb 2004 01:55:31 +0000 Subject: [PATCH] boilerplate testing X-SVN-Rev: 14567 --- .../com/ibm/icu/dev/test/TestBoilerplate.java | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 icu4j/src/com/ibm/icu/dev/test/TestBoilerplate.java diff --git a/icu4j/src/com/ibm/icu/dev/test/TestBoilerplate.java b/icu4j/src/com/ibm/icu/dev/test/TestBoilerplate.java new file mode 100644 index 0000000000..67783e9e33 --- /dev/null +++ b/icu4j/src/com/ibm/icu/dev/test/TestBoilerplate.java @@ -0,0 +1,191 @@ +package com.ibm.icu.dev.test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import com.ibm.icu.text.UnicodeSet; + +/** + * To use, override the abstract and the protected methods as necessary. + * Tests boilerplate invariants: + *
a.equals(a) + *
!a.equals(null) + *
if a.equals(b) then + *
(1) a.hashCode() == b.hashCode // note: the reverse is not necessarily true. + *
(2) a functions in all aspects as equivalent to b + *
(3) b.equals(a) + *
if b = clone(a) + *
(1) b.equals(a), and the above checks + *
(2) if mutable(a), then a.clone() != a // note: the reverse is not necessarily true. + * @author Davis + */ +public abstract class TestBoilerplate extends TestFmwk { + + public final void TestMain() throws Exception { + List list = new LinkedList(); + while (_addTestObject(list)); + Object[] testArray = list.toArray(); + for (int i = 0; i < testArray.length; ++i) { + //logln("Testing " + i); + Object a = testArray[i]; + int aHash = a.hashCode(); + if (a.equals(null)) { + errln("Equality/Null invariant fails: " + i); + } + if (!a.equals(a)) { + errln("Self-Equality invariant fails: " + i); + } + Object b; + if (_canClone(a)) { + b = _clone(a); + if (b == a) { + if (_isMutable(a)) { + errln("Clone/Mutability invariant fails: " + i); + } + } else { + if (!a.equals(b)) { + errln("Clone/Equality invariant fails: " + i); + } + } + _checkEquals(i, -1, a, aHash, b); + } + for (int j = i; j < testArray.length; ++j) { + b = testArray[j]; + if (a.equals(b)) _checkEquals(i, j, a, aHash, b); + } + } + } + + private void _checkEquals(int i, int j, Object a, int aHash, Object b) { + int bHash = b.hashCode(); + if (!b.equals(a)) errln("Equality/Symmetry",i, j); + if (aHash != bHash) errln("Equality/Hash",i, j); + if (a != b && !_hasSameBehavior(a,b)) { + errln("Equality/Equivalence",i, j); + } + } + + private void errln(String title, int i, int j) { + if (j < 0) errln("Clone/" + title + "invariant fails: " + i); + else errln(title + "invariant fails: " + i + "," + j); + } + + /** + * Must be overridden to check whether a and be behave the same + * @param a + * @param b + * @return + */ + protected abstract boolean _hasSameBehavior(Object a, Object b); + + /** + * This method will be called multiple times until false is returned. + * The results should be a mixture of different objects of the same + * type: some equal and most not equal. + * The subclasser controls how many are produced (recommend about + * 100, based on the size of the objects and how costly they are + * to run this test on. The running time grows with the square of the + * count. + * NOTE: this method will only be called if the objects test as equal. + * @return + */ + protected abstract boolean _addTestObject(List c); + /** + * Override if the tested objects are mutable. + *
Since Java doesn't tell us, we need a function to tell if so. + * The default is true, so must be overridden if not. + * @return + */ + protected boolean _isMutable(Object a) { + return true; + } + /** + * Override if the tested objects can be cloned. + * @return + */ + protected boolean _canClone(Object a) { + return true; + } + /** + * Produce a clone of the object. Tries two methods + * (a) clone + * (b) constructor + * Must be overridden if _canClone returns true and + * the above methods don't work. + * @param a + * @return clone + */ + protected Object _clone(Object a) throws Exception { + Class aClass = a.getClass(); + try { + Method cloner = aClass.getMethod("clone", null); + return cloner.invoke(a,null); + } catch (NoSuchMethodException e) { + Constructor constructor = aClass.getConstructor(new Class[] {aClass}); + return constructor.newInstance(new Object[]{a}); + } + } + + /* Utilities */ + public static boolean verifySetsIdentical(AbstractTestLog here, UnicodeSet set1, UnicodeSet set2) { + if (set1.equals(set2)) return true; + here.errln("Sets differ:"); + here.errln("UnicodeMap - HashMap"); + here.errln(new UnicodeSet(set1).removeAll(set2).toPattern(true)); + here.errln("HashMap - UnicodeMap"); + here.errln(new UnicodeSet(set2).removeAll(set1).toPattern(true)); + return false; + } + + public static boolean verifySetsIdentical(AbstractTestLog here, Set values1, Set values2) { + if (values1.equals(values2)) return true; + Set temp; + here.errln("Values differ:"); + here.errln("UnicodeMap - HashMap"); + temp = new TreeSet(values1); + temp.removeAll(values2); + here.errln(show(temp)); + here.errln("HashMap - UnicodeMap"); + temp = new TreeSet(values2); + temp.removeAll(values1); + here.errln(show(temp)); + return false; + } + + public static String show(Map m) { + StringBuffer buffer = new StringBuffer(); + for (Iterator it = m.keySet().iterator(); it.hasNext();) { + Object key = it.next(); + buffer.append(key + "=>" + m.get(key) + "\r\n"); + } + return buffer.toString(); + } + + public static UnicodeSet getSet(Map m, Object value) { + UnicodeSet result = new UnicodeSet(); + for (Iterator it = m.keySet().iterator(); it.hasNext();) { + Object key = it.next(); + Object val = m.get(key); + if (!val.equals(value)) continue; + result.add(((Integer)key).intValue()); + } + return result; + } + + public static String show(Collection c) { + StringBuffer buffer = new StringBuffer(); + for (Iterator it = c.iterator(); it.hasNext();) { + buffer.append(it.next() + "\r\n"); + } + return buffer.toString(); + } + + +}