001: /*
002: *******************************************************************************
003: * Copyright (C) 2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007:
008: package com.ibm.icu.tests;
009:
010: import java.io.ByteArrayInputStream;
011: import java.io.ByteArrayOutputStream;
012: import java.io.Externalizable;
013: import java.io.IOException;
014: import java.io.ObjectInputStream;
015: import java.io.ObjectOutputStream;
016: import java.io.Serializable;
017: import java.lang.reflect.Array;
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020: import java.util.Locale;
021:
022: import com.ibm.icu.util.TimeZone;
023: import com.ibm.icu.util.ULocale;
024:
025: import junit.framework.TestCase;
026:
027: /**
028: * Implement boilerplate tests.
029: * Currently there is only one method, testEHCS, which tests equals, hashCode,
030: * clone, and serialization.
031: */
032: public abstract class ICUTestCase extends TestCase {
033: private static final Object[] EMPTY_ARGS = {};
034: private static final Class[] EMPTY_CLASSES = {};
035:
036: private static final Locale oldLocale = Locale.getDefault();
037: private static final ULocale oldULocale = ULocale.getDefault();
038: private static final java.util.TimeZone oldJTimeZone = java.util.TimeZone
039: .getDefault();
040: private static final TimeZone oldITimeZone = TimeZone.getDefault();
041:
042: // TODO: what's the best way to check this?
043: public static final boolean testingWrapper = true;
044:
045: protected void setUp() throws Exception {
046: super .setUp();
047: Locale.setDefault(Locale.US);
048: ULocale.setDefault(ULocale.US);
049: java.util.TimeZone.setDefault(java.util.TimeZone
050: .getTimeZone("PST"));
051: TimeZone.setDefault(TimeZone.getTimeZone("PST"));
052: }
053:
054: protected void tearDown() throws Exception {
055: ULocale.setDefault(oldULocale);
056: Locale.setDefault(oldLocale);
057: TimeZone.setDefault(oldITimeZone);
058: java.util.TimeZone.setDefault(oldJTimeZone);
059: super .tearDown();
060: }
061:
062: private static final Object test = new Object();
063:
064: /**
065: * Assert that two objects are _not_ equal. Curiously missing from Assert.
066: * @param lhs an object to test, may be null
067: * @param rhs an object to test, may be null
068: */
069: public static void assertNotEqual(Object lhs, Object rhs) {
070: if (lhs == null) {
071: if (rhs == null)
072: fail("null equals null");
073: } else {
074: if (lhs.equals(rhs)) {
075: fail(lhs.toString() + " equals " + rhs);
076: }
077: }
078: }
079:
080: public static void assertNotEqual(long lhs, long rhs) {
081: if (lhs == rhs) {
082: fail("values are equal: " + lhs);
083: }
084: }
085:
086: /**
087: * Test whether equality, hashCode, clone, and serialization work as expected.
088: * Equals(Object) is assumed to return false (not throw an exception) if passed
089: * null or an object of an incompatible class.
090: * Hashcodes must be equal iff the two objects compare equal. No attempt is made to
091: * evaluate the quality of the hashcode distribution, so (in particular) degenerate
092: * hashcode implementations will pass this test.
093: * Clone will be tested if the method "clone" is public on the class of obj.
094: * It is assumed to return an object that compares equal to obj.
095: * Serialization will be tested if object implements Serializable or Externalizable.
096: * It is assumed the serialized/deserialized object compares equal to obj.
097: * @param obj the object to test
098: * @param eq an object that should compare equal to, but is not the same as, obj.
099: * it should be assignable to the class of obj.
100: * @param neq a non-null object that should not compare equal to obj.
101: * it should be assignable to the class of obj.
102: */
103: public static void testEHCS(Object obj, Object eq, Object neq) {
104: if (obj == null || eq == null || neq == null) {
105: throw new NullPointerException();
106: }
107: Class cls = obj.getClass();
108: if (!(cls.isAssignableFrom(eq.getClass()) && cls
109: .isAssignableFrom(neq.getClass()))) {
110: throw new IllegalArgumentException("unassignable classes");
111: }
112:
113: // reflexive
114: assertEquals(obj, obj);
115:
116: // should return false, not throw exception
117: assertNotEqual(obj, test);
118: assertNotEqual(obj, null);
119:
120: // commutative
121: assertEquals(obj, eq);
122: assertEquals(eq, obj);
123:
124: assertNotEqual(obj, neq);
125: assertNotEqual(neq, obj);
126:
127: // equal objects MUST have equal hashes, unequal objects MAY have equal hashes
128: assertEquals(obj.hashCode(), eq.hashCode());
129:
130: Object clone = null;
131: try {
132: // look for public clone method and call it if available
133: Method method_clone = cls.getMethod("clone", EMPTY_CLASSES);
134: clone = method_clone.invoke(obj, EMPTY_ARGS);
135: assertNotNull(clone);
136: } catch (NoSuchMethodException e) {
137: // ok
138: } catch (InvocationTargetException e) {
139: // ok
140: } catch (IllegalAccessException e) {
141: // ok
142: }
143:
144: if (clone != null) {
145: assertEquals(obj, clone);
146: assertEquals(clone, obj);
147: }
148:
149: if (obj instanceof Serializable
150: || 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
159: .toByteArray());
160: ObjectInputStream ois = new ObjectInputStream(bis);
161: ser = ois.readObject();
162: ois.close();
163: } catch (IOException e) {
164: System.err.println(e.getMessage());
165: throw new RuntimeException(e);
166: } catch (ClassNotFoundException e) {
167: System.err.println(e.getMessage());
168: throw new RuntimeException(e);
169: }
170:
171: if (ser != null) {
172: assertEquals(obj, ser);
173: assertEquals(ser, obj);
174: assertEquals(obj.hashCode(), ser.hashCode());
175: }
176: }
177: }
178:
179: /**
180: * Fail if the arrays are not equal. To be equal, the arrays must
181: * be the same length, and each element in the left array must compare
182: * equal to the corresponding element of the right array.
183: * Also fails if one of the objects is not an array.
184: * @param lhs the left array
185: * @param rhs the right array
186: */
187: public static void assertArraysEqual(Object lhs, Object rhs) {
188: Class lcls = lhs.getClass();
189: Class rcls = rhs.getClass();
190: if (!(lcls.isArray() && rcls.isArray())) {
191: fail("objects are not arrays");
192: }
193: String result = arraysAreEqual(lhs, rhs);
194: if (result != null) {
195: fail(result);
196: }
197: }
198:
199: /**
200: * Fail if the arrays are equal. Also fails if one or the other
201: * argument is not an array.
202: * @param lhs the left array
203: * @param rhs the right array
204: */
205: public static void assertArraysNotEqual(Object lhs, Object rhs) {
206: Class lcls = lhs.getClass();
207: Class rcls = rhs.getClass();
208: if (!(lcls.isArray() && rcls.isArray())) {
209: fail("objects are not arrays");
210: }
211: String result = arraysAreEqual(lhs, rhs);
212: if (result == null) {
213: fail("arrays are equal");
214: }
215: }
216:
217: // slow but general
218: private static String arraysAreEqual(Object lhsa, Object rhsa) {
219: int lhsl = Array.getLength(lhsa);
220: int rhsl = Array.getLength(rhsa);
221: if (lhsl != rhsl) {
222: return "length " + lhsl + " != " + rhsl;
223: }
224: boolean lhsaA = lhsa.getClass().getComponentType().isArray();
225: boolean rhsaA = rhsa.getClass().getComponentType().isArray();
226: if (lhsaA != rhsaA) {
227: return (lhsaA ? "" : "non-") + "array != "
228: + (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.
259: public static Object cloneComplex(Object obj) {
260: Object result = null;
261: if (obj != null) {
262: Class 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
271: : cloneComplex(elem));
272: }
273: } else {
274: result = obj; // default
275: try {
276: Method cloneM = cls.getMethod("clone", null);
277: result = cloneM.invoke(obj, null);
278: } catch (NoSuchMethodException e) {
279: } catch (IllegalAccessException e) {
280: } catch (InvocationTargetException e) {
281: }
282: }
283: }
284: return result;
285: }
286: }
|