1 /* 2 ******************************************************************************* 3 * Copyright (C) 2006-2011, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 package com.ibm.icu.tests; 9 10 import java.io.ByteArrayInputStream; 11 import java.io.ByteArrayOutputStream; 12 import java.io.Externalizable; 13 import java.io.IOException; 14 import java.io.ObjectInputStream; 15 import java.io.ObjectOutputStream; 16 import java.io.Serializable; 17 import java.lang.reflect.Array; 18 import java.lang.reflect.InvocationTargetException; 19 import java.lang.reflect.Method; 20 import java.util.Locale; 21 22 import com.ibm.icu.util.TimeZone; 23 import com.ibm.icu.util.ULocale; 24 25 import junit.framework.TestCase; 26 27 /** 28 * Implement boilerplate tests. 29 * Currently there is only one method, testEHCS, which tests equals, hashCode, 30 * clone, and serialization. 31 */ 32 public abstract class ICUTestCase extends TestCase { 33 private static final Object[] EMPTY_ARGS = {}; 34 private static final Class<?>[] EMPTY_CLASSES = {}; 35 36 private static final Locale oldLocale = Locale.getDefault(); 37 private static final ULocale oldULocale = ULocale.getDefault(); 38 private static final java.util.TimeZone oldJTimeZone = java.util.TimeZone.getDefault(); 39 private static final TimeZone oldITimeZone = TimeZone.getDefault(); 40 41 // TODO: what's the best way to check this? 42 public static final boolean testingWrapper = true; 43 44 protected void setUp() throws Exception { 45 super.setUp(); 46 Locale.setDefault(Locale.US); 47 ULocale.setDefault(ULocale.US); 48 java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("PST")); 49 TimeZone.setDefault(TimeZone.getTimeZone("PST")); 50 } 51 52 protected void tearDown() throws Exception { 53 ULocale.setDefault(oldULocale); 54 Locale.setDefault(oldLocale); 55 TimeZone.setDefault(oldITimeZone); 56 java.util.TimeZone.setDefault(oldJTimeZone); 57 super.tearDown(); 58 } 59 60 private static final Object test = new Object(); 61 62 /** 63 * Assert that two objects are _not_ equal. Curiously missing from Assert. 64 * @param lhs an object to test, may be null 65 * @param rhs an object to test, may be null 66 */ 67 public static void assertNotEqual(Object lhs, Object rhs) { 68 if (lhs == null) { 69 if (rhs == null) fail("null equals null"); 70 } else { 71 if (lhs.equals(rhs)) { 72 fail(lhs.toString() + " equals " + rhs); 73 } 74 } 75 } 76 77 public static void assertNotEqual(long lhs, long rhs) { 78 if (lhs == rhs) { 79 fail("values are equal: " + lhs); 80 } 81 } 82 83 /** 84 * Test whether equality, hashCode, clone, and serialization work as expected. 85 * Equals(Object) is assumed to return false (not throw an exception) if passed 86 * null or an object of an incompatible class. 87 * Hashcodes must be equal iff the two objects compare equal. No attempt is made to 88 * evaluate the quality of the hashcode distribution, so (in particular) degenerate 89 * hashcode implementations will pass this test. 90 * Clone will be tested if the method "clone" is public on the class of obj. 91 * It is assumed to return an object that compares equal to obj. 92 * Serialization will be tested if object implements Serializable or Externalizable. 93 * It is assumed the serialized/deserialized object compares equal to obj. 94 * @param obj the object to test 95 * @param eq an object that should compare equal to, but is not the same as, obj. 96 * it should be assignable to the class of obj. 97 * @param neq a non-null object that should not compare equal to obj. 98 * it should be assignable to the class of obj. 99 */ 100 public static void testEHCS(Object obj, Object eq, Object neq) { 101 if (obj == null || eq == null || neq == null) { 102 throw new NullPointerException(); 103 } 104 Class<? extends Object> cls = obj.getClass(); 105 if (!(cls.isAssignableFrom(eq.getClass()) && cls.isAssignableFrom(neq.getClass()))) { 106 throw new IllegalArgumentException("unassignable classes"); 107 } 108 109 // reflexive 110 assertEquals(obj, obj); 111 112 // should return false, not throw exception 113 assertNotEqual(obj, test); 114 assertNotEqual(obj, null); 115 116 // commutative 117 assertEquals(obj, eq); 118 assertEquals(eq, obj); 119 120 assertNotEqual(obj, neq); 121 assertNotEqual(neq, obj); 122 123 // equal objects MUST have equal hashes, unequal objects MAY have equal hashes 124 assertEquals(obj.hashCode(), eq.hashCode()); 125 126 Object clone = null; 127 try { 128 // look for public clone method and call it if available 129 Method method_clone = cls.getMethod("clone", EMPTY_CLASSES); 130 clone = method_clone.invoke(obj, EMPTY_ARGS); 131 assertNotNull(clone); 132 } 133 catch(NoSuchMethodException e) { 134 // ok 135 } 136 catch(InvocationTargetException e) { 137 // ok 138 } 139 catch(IllegalAccessException e) { 140 // ok 141 } 142 143 if (clone != null) { 144 assertEquals(obj, clone); 145 assertEquals(clone, obj); 146 } 147 148 if (obj instanceof Serializable || obj instanceof Externalizable) { 149 Object ser = null; 150 try { 151 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 152 ObjectOutputStream oos = new ObjectOutputStream(bos); 153 oos.writeObject(clone); 154 oos.close(); 155 156 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 157 ObjectInputStream ois = new ObjectInputStream(bis); 158 ser = ois.readObject(); 159 ois.close(); 160 } 161 catch(IOException e) { 162 System.err.println(e.getMessage()); 163 throw new RuntimeException(e); 164 } 165 catch(ClassNotFoundException e) { 166 System.err.println(e.getMessage()); 167 throw new RuntimeException(e); 168 } 169 170 if (ser != null) { 171 assertEquals(obj, ser); 172 assertEquals(ser, obj); 173 assertEquals(obj.hashCode(), ser.hashCode()); 174 } 175 } 176 } 177 178 /** 179 * Fail if the arrays are not equal. To be equal, the arrays must 180 * be the same length, and each element in the left array must compare 181 * equal to the corresponding element of the right array. 182 * Also fails if one of the objects is not an array. 183 * @param lhs the left array 184 * @param rhs the right array 185 */ 186 public static void assertArraysEqual(Object lhs, Object rhs) { 187 Class<? extends Object> lcls = lhs.getClass(); 188 Class<? extends Object> rcls = rhs.getClass(); 189 if (!(lcls.isArray() && rcls.isArray())) { 190 fail("objects are not arrays"); 191 } 192 String result = arraysAreEqual(lhs, rhs); 193 if (result != null) { 194 fail(result); 195 } 196 } 197 198 /** 199 * Fail if the arrays are equal. Also fails if one or the other 200 * argument is not an array. 201 * @param lhs the left array 202 * @param rhs the right array 203 */ 204 public static void assertArraysNotEqual(Object lhs, Object rhs) { 205 Class<? extends Object> lcls = lhs.getClass(); 206 Class<? extends Object> rcls = rhs.getClass(); 207 if (!(lcls.isArray() && rcls.isArray())) { 208 fail("objects are not arrays"); 209 } 210 String result = arraysAreEqual(lhs, rhs); 211 if (result == null) { 212 fail("arrays are equal"); 213 } 214 } 215 216 // slow but general 217 private static String arraysAreEqual(Object lhsa, Object rhsa) { 218 int lhsl = Array.getLength(lhsa); 219 int rhsl = Array.getLength(rhsa); 220 if (lhsl != rhsl) { 221 return "length " + lhsl + " != " + rhsl; 222 } 223 boolean lhsaA = lhsa.getClass().getComponentType().isArray(); 224 boolean rhsaA = rhsa.getClass().getComponentType().isArray(); 225 if (lhsaA != rhsaA) { 226 return (lhsaA ? "" : "non-") + "array != " + (rhsaA ? "" : "non-") + "array"; 227 } 228 for (int i = 0; i < lhsl; ++i) { 229 Object lhse = Array.get(lhsa, i); 230 Object rhse = Array.get(rhsa, i); 231 if (lhse == null) { 232 if (rhse != null) { 233 return "null != " + rhse; 234 } 235 } else { 236 if (lhsaA) { 237 String result = arraysAreEqual(lhse, rhse); 238 if (result != null) { 239 if (result.charAt(0) != '[') { 240 result = " " + result; 241 } 242 return "[" + i + "]" + result; 243 } 244 } else { 245 if (!lhse.equals(rhse)) { 246 return lhse.toString() + " != " + rhse; 247 } 248 } 249 } 250 } 251 return null; 252 } 253 254 // much more painful and slow than it should be... partly because of the 255 // oddness of clone, partly because arrays don't provide a Method for 256 // 'clone' despite the fact that they implement it and make it public. 257 public static Object cloneComplex(Object obj) { 258 Object result = null; 259 if (obj != null) { 260 Class<? extends Object> cls = obj.getClass(); 261 if (cls.isArray()) { 262 int len = Array.getLength(obj); 263 Class<?> typ = cls.getComponentType(); 264 result = Array.newInstance(typ, len); 265 boolean prim = typ.isPrimitive(); 266 for (int i = 0; i < len; ++i) { 267 Object elem = Array.get(obj, i); 268 Array.set(result, i, prim ? elem : cloneComplex(elem)); 269 } 270 } else { 271 result = obj; // default 272 try { 273 Method cloneM = cls.getMethod("clone", EMPTY_CLASSES); 274 result = cloneM.invoke(obj, EMPTY_ARGS); 275 } 276 catch (NoSuchMethodException e) { 277 } 278 catch (IllegalAccessException e) { 279 } 280 catch (InvocationTargetException e) { 281 } 282 } 283 } 284 return result; 285 } 286 } 287