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