| /* |
| ******************************************************************************* |
| * Copyright (C) 2004-2007, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ******************************************************************************* |
| */ |
| 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: |
| * <br>a.equals(a) |
| * <br>!a.equals(null) |
| * <br>if a.equals(b) then |
| * <br>(1) a.hashCode() == b.hashCode // note: the reverse is not necessarily true. |
| * <br>(2) a functions in all aspects as equivalent to b |
| * <br>(3) b.equals(a) |
| * <br>if b = clone(a) |
| * <br>(1) b.equals(a), and the above checks |
| * <br>(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 |
| */ |
| 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. |
| */ |
| protected abstract boolean _addTestObject(List c); |
| /** |
| * Override if the tested objects are mutable. |
| * <br>Since Java doesn't tell us, we need a function to tell if so. |
| * The default is true, so must be overridden if not. |
| */ |
| protected boolean _isMutable(Object a) { |
| return true; |
| } |
| /** |
| * Override if the tested objects can be cloned. |
| */ |
| 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", (Class[])null); |
| return cloner.invoke(a,(Object[])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(); |
| } |
| |
| |
| } |