/* ******************************************************************************* * 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: *
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 */ 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. *
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(); } }