blob: 617fc14709cb2c5237c11a4f91992e2b522461d2 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2006-2011, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.tests;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.ULocale;
import junit.framework.TestCase;
/**
* Implement boilerplate tests.
* Currently there is only one method, testEHCS, which tests equals, hashCode,
* clone, and serialization.
*/
public abstract class ICUTestCase extends TestCase {
private static final Object[] EMPTY_ARGS = {};
private static final Class<?>[] EMPTY_CLASSES = {};
private static final Locale oldLocale = Locale.getDefault();
private static final ULocale oldULocale = ULocale.getDefault();
private static final java.util.TimeZone oldJTimeZone = java.util.TimeZone.getDefault();
private static final TimeZone oldITimeZone = TimeZone.getDefault();
// TODO: what's the best way to check this?
public static final boolean testingWrapper = true;
protected void setUp() throws Exception {
super.setUp();
Locale.setDefault(Locale.US);
ULocale.setDefault(ULocale.US);
java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("PST"));
TimeZone.setDefault(TimeZone.getTimeZone("PST"));
}
protected void tearDown() throws Exception {
ULocale.setDefault(oldULocale);
Locale.setDefault(oldLocale);
TimeZone.setDefault(oldITimeZone);
java.util.TimeZone.setDefault(oldJTimeZone);
super.tearDown();
}
private static final Object test = new Object();
/**
* Assert that two objects are _not_ equal. Curiously missing from Assert.
* @param lhs an object to test, may be null
* @param rhs an object to test, may be null
*/
public static void assertNotEqual(Object lhs, Object rhs) {
if (lhs == null) {
if (rhs == null) fail("null equals null");
} else {
if (lhs.equals(rhs)) {
fail(lhs.toString() + " equals " + rhs);
}
}
}
public static void assertNotEqual(long lhs, long rhs) {
if (lhs == rhs) {
fail("values are equal: " + lhs);
}
}
/**
* Test whether equality, hashCode, clone, and serialization work as expected.
* Equals(Object) is assumed to return false (not throw an exception) if passed
* null or an object of an incompatible class.
* Hashcodes must be equal iff the two objects compare equal. No attempt is made to
* evaluate the quality of the hashcode distribution, so (in particular) degenerate
* hashcode implementations will pass this test.
* Clone will be tested if the method "clone" is public on the class of obj.
* It is assumed to return an object that compares equal to obj.
* Serialization will be tested if object implements Serializable or Externalizable.
* It is assumed the serialized/deserialized object compares equal to obj.
* @param obj the object to test
* @param eq an object that should compare equal to, but is not the same as, obj.
* it should be assignable to the class of obj.
* @param neq a non-null object that should not compare equal to obj.
* it should be assignable to the class of obj.
*/
public static void testEHCS(Object obj, Object eq, Object neq) {
if (obj == null || eq == null || neq == null) {
throw new NullPointerException();
}
Class<? extends Object> cls = obj.getClass();
if (!(cls.isAssignableFrom(eq.getClass()) && cls.isAssignableFrom(neq.getClass()))) {
throw new IllegalArgumentException("unassignable classes");
}
// reflexive
assertEquals(obj, obj);
// should return false, not throw exception
assertNotEqual(obj, test);
assertNotEqual(obj, null);
// commutative
assertEquals(obj, eq);
assertEquals(eq, obj);
assertNotEqual(obj, neq);
assertNotEqual(neq, obj);
// equal objects MUST have equal hashes, unequal objects MAY have equal hashes
assertEquals(obj.hashCode(), eq.hashCode());
Object clone = null;
try {
// look for public clone method and call it if available
Method method_clone = cls.getMethod("clone", EMPTY_CLASSES);
clone = method_clone.invoke(obj, EMPTY_ARGS);
assertNotNull(clone);
}
catch(NoSuchMethodException e) {
// ok
}
catch(InvocationTargetException e) {
// ok
}
catch(IllegalAccessException e) {
// ok
}
if (clone != null) {
assertEquals(obj, clone);
assertEquals(clone, obj);
}
if (obj instanceof Serializable || obj instanceof Externalizable) {
Object ser = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(clone);
oos.close();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ser = ois.readObject();
ois.close();
}
catch(IOException e) {
System.err.println(e.getMessage());
throw new RuntimeException(e);
}
catch(ClassNotFoundException e) {
System.err.println(e.getMessage());
throw new RuntimeException(e);
}
if (ser != null) {
assertEquals(obj, ser);
assertEquals(ser, obj);
assertEquals(obj.hashCode(), ser.hashCode());
}
}
}
/**
* Fail if the arrays are not equal. To be equal, the arrays must
* be the same length, and each element in the left array must compare
* equal to the corresponding element of the right array.
* Also fails if one of the objects is not an array.
* @param lhs the left array
* @param rhs the right array
*/
public static void assertArraysEqual(Object lhs, Object rhs) {
Class<? extends Object> lcls = lhs.getClass();
Class<? extends Object> rcls = rhs.getClass();
if (!(lcls.isArray() && rcls.isArray())) {
fail("objects are not arrays");
}
String result = arraysAreEqual(lhs, rhs);
if (result != null) {
fail(result);
}
}
/**
* Fail if the arrays are equal. Also fails if one or the other
* argument is not an array.
* @param lhs the left array
* @param rhs the right array
*/
public static void assertArraysNotEqual(Object lhs, Object rhs) {
Class<? extends Object> lcls = lhs.getClass();
Class<? extends Object> rcls = rhs.getClass();
if (!(lcls.isArray() && rcls.isArray())) {
fail("objects are not arrays");
}
String result = arraysAreEqual(lhs, rhs);
if (result == null) {
fail("arrays are equal");
}
}
// slow but general
private static String arraysAreEqual(Object lhsa, Object rhsa) {
int lhsl = Array.getLength(lhsa);
int rhsl = Array.getLength(rhsa);
if (lhsl != rhsl) {
return "length " + lhsl + " != " + rhsl;
}
boolean lhsaA = lhsa.getClass().getComponentType().isArray();
boolean rhsaA = rhsa.getClass().getComponentType().isArray();
if (lhsaA != rhsaA) {
return (lhsaA ? "" : "non-") + "array != " + (rhsaA ? "" : "non-") + "array";
}
for (int i = 0; i < lhsl; ++i) {
Object lhse = Array.get(lhsa, i);
Object rhse = Array.get(rhsa, i);
if (lhse == null) {
if (rhse != null) {
return "null != " + rhse;
}
} else {
if (lhsaA) {
String result = arraysAreEqual(lhse, rhse);
if (result != null) {
if (result.charAt(0) != '[') {
result = " " + result;
}
return "[" + i + "]" + result;
}
} else {
if (!lhse.equals(rhse)) {
return lhse.toString() + " != " + rhse;
}
}
}
}
return null;
}
// much more painful and slow than it should be... partly because of the
// oddness of clone, partly because arrays don't provide a Method for
// 'clone' despite the fact that they implement it and make it public.
public static Object cloneComplex(Object obj) {
Object result = null;
if (obj != null) {
Class<? extends Object> cls = obj.getClass();
if (cls.isArray()) {
int len = Array.getLength(obj);
Class<?> typ = cls.getComponentType();
result = Array.newInstance(typ, len);
boolean prim = typ.isPrimitive();
for (int i = 0; i < len; ++i) {
Object elem = Array.get(obj, i);
Array.set(result, i, prim ? elem : cloneComplex(elem));
}
} else {
result = obj; // default
try {
Method cloneM = cls.getMethod("clone", EMPTY_CLASSES);
result = cloneM.invoke(obj, EMPTY_ARGS);
}
catch (NoSuchMethodException e) {
}
catch (IllegalAccessException e) {
}
catch (InvocationTargetException e) {
}
}
}
return result;
}
}