1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2004-2016, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.dev.test; 10 11 import java.lang.reflect.Constructor; 12 import java.lang.reflect.Method; 13 import java.util.Collection; 14 import java.util.Iterator; 15 import java.util.LinkedList; 16 import java.util.List; 17 import java.util.Map; 18 import java.util.Random; 19 import java.util.Set; 20 import java.util.TreeSet; 21 22 import com.ibm.icu.text.UnicodeSet; 23 24 /** 25 * To use, override the abstract and the protected methods as necessary. 26 * Tests boilerplate invariants: 27 * <br>a.equals(a) 28 * <br>!a.equals(null) 29 * <br>if a.equals(b) then 30 * <br>(1) a.hashCode() == b.hashCode // note: the reverse is not necessarily true. 31 * <br>(2) a functions in all aspects as equivalent to b 32 * <br>(3) b.equals(a) 33 * <br>if b = clone(a) 34 * <br>(1) b.equals(a), and the above checks 35 * <br>(2) if mutable(a), then a.clone() != a // note: the reverse is not necessarily true. 36 * @author Davis 37 */ 38 public abstract class TestBoilerplate<T> extends TestFmwk { 39 40 protected static Random random = new Random(12345); 41 42 protected final void _test() throws Exception { 43 List<T> list = new LinkedList<T>(); 44 while (_addTestObject(list)) { 45 } 46 T[] testArray = (T[]) list.toArray(); 47 for (int i = 0; i < testArray.length; ++i) { 48 //logln("Testing " + i); 49 T a = testArray[i]; 50 int aHash = a.hashCode(); 51 if (a.equals(null)) { 52 errln("Equality/Null invariant fails: " + i); 53 } 54 if (!a.equals(a)) { 55 errln("Self-Equality invariant fails: " + i); 56 } 57 T b; 58 if (_canClone(a)) { 59 b = _clone(a); 60 if (b == a) { 61 if (_isMutable(a)) { 62 errln("Clone/Mutability invariant fails: " + i); 63 } 64 } else { 65 if (!a.equals(b)) { 66 errln("Clone/Equality invariant fails: " + i); 67 } 68 } 69 _checkEquals(i, -1, a, aHash, b); 70 } 71 for (int j = i; j < testArray.length; ++j) { 72 b = testArray[j]; 73 if (a.equals(b)) _checkEquals(i, j, a, aHash, b); 74 } 75 } 76 } 77 78 private void _checkEquals(int i, int j, T a, int aHash, T b) { 79 int bHash = b.hashCode(); 80 if (!b.equals(a)) errln("Equality/Symmetry",i, j); 81 if (aHash != bHash) errln("Equality/Hash",i, j); 82 if (a != b && !_hasSameBehavior(a,b)) { 83 errln("Equality/Equivalence",i, j); 84 } 85 } 86 87 private void errln(String title, int i, int j) { 88 if (j < 0) errln("Clone/" + title + "invariant fails: " + i); 89 else errln(title + "invariant fails: " + i + "," + j); 90 } 91 92 /** 93 * Must be overridden to check whether a and be behave the same 94 */ 95 protected abstract boolean _hasSameBehavior(T a, T b); 96 97 /** 98 * This method will be called multiple times until false is returned. 99 * The results should be a mixture of different objects of the same 100 * type: some equal and most not equal. 101 * The subclasser controls how many are produced (recommend about 102 * 100, based on the size of the objects and how costly they are 103 * to run this test on. The running time grows with the square of the 104 * count. 105 * NOTE: this method will only be called if the objects test as equal. 106 */ 107 protected abstract boolean _addTestObject(List<T> c); 108 /** 109 * Override if the tested objects are mutable. 110 * <br>Since Java doesn't tell us, we need a function to tell if so. 111 * The default is true, so must be overridden if not. 112 */ 113 protected boolean _isMutable(T a) { 114 return true; 115 } 116 /** 117 * Override if the tested objects can be cloned. 118 */ 119 protected boolean _canClone(T a) { 120 return true; 121 } 122 /** 123 * Produce a clone of the object. Tries two methods 124 * (a) clone 125 * (b) constructor 126 * Must be overridden if _canClone returns true and 127 * the above methods don't work. 128 * @param a 129 * @return clone 130 */ 131 protected T _clone(T a) throws Exception { 132 Class aClass = a.getClass(); 133 try { 134 Method cloner = aClass.getMethod("clone", (Class[])null); 135 return (T) cloner.invoke(a,(Object[])null); 136 } catch (NoSuchMethodException e) { 137 Constructor constructor = aClass.getConstructor(new Class[] {aClass}); 138 return (T) constructor.newInstance(new Object[]{a}); 139 } 140 } 141 142 /* Utilities */ 143 public static boolean verifySetsIdentical(AbstractTestLog here, UnicodeSet set1, UnicodeSet set2) { 144 if (set1.equals(set2)) return true; 145 TestFmwk.errln("Sets differ:"); 146 TestFmwk.errln("UnicodeMap - HashMap"); 147 TestFmwk.errln(new UnicodeSet(set1).removeAll(set2).toPattern(true)); 148 TestFmwk.errln("HashMap - UnicodeMap"); 149 TestFmwk.errln(new UnicodeSet(set2).removeAll(set1).toPattern(true)); 150 return false; 151 } 152 153 public static boolean verifySetsIdentical(AbstractTestLog here, Set values1, Set values2) { 154 if (values1.equals(values2)) return true; 155 Set temp; 156 TestFmwk.errln("Values differ:"); 157 TestFmwk.errln("UnicodeMap - HashMap"); 158 temp = new TreeSet(values1); 159 temp.removeAll(values2); 160 TestFmwk.errln(show(temp)); 161 TestFmwk.errln("HashMap - UnicodeMap"); 162 temp = new TreeSet(values2); 163 temp.removeAll(values1); 164 TestFmwk.errln(show(temp)); 165 return false; 166 } 167 168 public static String show(Map m) { 169 StringBuilder buffer = new StringBuilder(); 170 for (Iterator it = m.keySet().iterator(); it.hasNext();) { 171 Object key = it.next(); 172 buffer.append(key + "=>" + m.get(key) + "\r\n"); 173 } 174 return buffer.toString(); 175 } 176 177 public static <T> UnicodeSet getSet(Map<Integer, T> m, T value) { 178 UnicodeSet result = new UnicodeSet(); 179 for (Iterator<Integer> it = m.keySet().iterator(); it.hasNext();) { 180 Integer key = it.next(); 181 T val = m.get(key); 182 if (!val.equals(value)) continue; 183 result.add(key.intValue()); 184 } 185 return result; 186 } 187 188 public static String show(Collection c) { 189 StringBuilder buffer = new StringBuilder(); 190 for (Iterator it = c.iterator(); it.hasNext();) { 191 buffer.append(it.next() + "\r\n"); 192 } 193 return buffer.toString(); 194 } 195 } 196