Home | History | Annotate | Download | only in tests
      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