Home | History | Annotate | Download | only in test
      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) 2004-2016, International Business Machines Corporation and         *
      6  * others. All Rights Reserved.                                                *
      7  *******************************************************************************
      8  */
      9 package com.ibm.icu.dev.test;
     10 
     11 import java.lang.reflect.Constructor;
     12 import java.lang.reflect.Method;
     13 import java.util.Collection;
     14 import java.util.Iterator;
     15 import java.util.LinkedList;
     16 import java.util.List;
     17 import java.util.Map;
     18 import java.util.Random;
     19 import java.util.Set;
     20 import java.util.TreeSet;
     21 
     22 import com.ibm.icu.text.UnicodeSet;
     23 
     24 /**
     25  * To use, override the abstract and the protected methods as necessary.
     26  * Tests boilerplate invariants:
     27  * <br>a.equals(a)
     28  * <br>!a.equals(null)
     29  * <br>if a.equals(b) then
     30  * <br>(1) a.hashCode() == b.hashCode  // note: the reverse is not necessarily true.
     31  * <br>(2) a functions in all aspects as equivalent to b
     32  * <br>(3) b.equals(a)
     33  * <br>if b = clone(a)
     34  * <br>(1) b.equals(a), and the above checks
     35  * <br>(2) if mutable(a), then a.clone() != a // note: the reverse is not necessarily true.
     36  * @author Davis
     37  */
     38 public abstract class TestBoilerplate<T> extends TestFmwk {
     39 
     40     protected static Random random = new Random(12345);
     41 
     42     protected final void _test() throws Exception {
     43         List<T> list = new LinkedList<T>();
     44         while (_addTestObject(list)) {
     45         }
     46         T[] testArray = (T[]) list.toArray();
     47         for (int i = 0; i < testArray.length; ++i) {
     48             //logln("Testing " + i);
     49             T a = testArray[i];
     50             int aHash = a.hashCode();
     51             if (a.equals(null)) {
     52                 errln("Equality/Null invariant fails: " + i);
     53             }
     54             if (!a.equals(a)) {
     55                 errln("Self-Equality invariant fails: " + i);
     56             }
     57             T b;
     58             if (_canClone(a)) {
     59                 b = _clone(a);
     60                 if (b == a) {
     61                     if (_isMutable(a)) {
     62                         errln("Clone/Mutability invariant fails: " + i);
     63                     }
     64                 } else {
     65                     if (!a.equals(b)) {
     66                         errln("Clone/Equality invariant fails: " + i);
     67                     }
     68                 }
     69                 _checkEquals(i, -1, a, aHash, b);
     70             }
     71             for (int j = i; j < testArray.length; ++j) {
     72                 b = testArray[j];
     73                 if (a.equals(b)) _checkEquals(i, j, a, aHash, b);
     74             }
     75         }
     76     }
     77 
     78     private void _checkEquals(int i, int j, T a, int aHash, T b) {
     79         int bHash = b.hashCode();
     80         if (!b.equals(a)) errln("Equality/Symmetry",i, j);
     81         if (aHash != bHash) errln("Equality/Hash",i, j);
     82         if (a != b && !_hasSameBehavior(a,b)) {
     83             errln("Equality/Equivalence",i, j);
     84         }
     85     }
     86 
     87     private void errln(String title, int i, int j) {
     88         if (j < 0) errln("Clone/" + title + "invariant fails: " + i);
     89         else errln(title + "invariant fails: " + i + "," + j);
     90     }
     91 
     92     /**
     93      * Must be overridden to check whether a and be behave the same
     94      */
     95     protected abstract boolean _hasSameBehavior(T a, T b);
     96 
     97     /**
     98      * This method will be called multiple times until false is returned.
     99      * The results should be a mixture of different objects of the same
    100      * type: some equal and most not equal.
    101      * The subclasser controls how many are produced (recommend about
    102      * 100, based on the size of the objects and how costly they are
    103      * to run this test on. The running time grows with the square of the
    104      * count.
    105      * NOTE: this method will only be called if the objects test as equal.
    106      */
    107     protected abstract boolean _addTestObject(List<T> c);
    108     /**
    109      * Override if the tested objects are mutable.
    110      * <br>Since Java doesn't tell us, we need a function to tell if so.
    111      * The default is true, so must be overridden if not.
    112      */
    113     protected boolean _isMutable(T a) {
    114         return true;
    115     }
    116     /**
    117      * Override if the tested objects can be cloned.
    118      */
    119     protected boolean _canClone(T a) {
    120         return true;
    121     }
    122     /**
    123      * Produce a clone of the object. Tries two methods
    124      * (a) clone
    125      * (b) constructor
    126      * Must be overridden if _canClone returns true and
    127      * the above methods don't work.
    128      * @param a
    129      * @return clone
    130      */
    131     protected T _clone(T a) throws Exception {
    132         Class aClass = a.getClass();
    133         try {
    134             Method cloner = aClass.getMethod("clone", (Class[])null);
    135             return (T) cloner.invoke(a,(Object[])null);
    136         } catch (NoSuchMethodException e) {
    137             Constructor constructor = aClass.getConstructor(new Class[] {aClass});
    138             return (T) constructor.newInstance(new Object[]{a});
    139         }
    140     }
    141 
    142     /* Utilities */
    143     public static boolean verifySetsIdentical(AbstractTestLog here, UnicodeSet set1, UnicodeSet set2) {
    144         if (set1.equals(set2)) return true;
    145         TestFmwk.errln("Sets differ:");
    146         TestFmwk.errln("UnicodeMap - HashMap");
    147         TestFmwk.errln(new UnicodeSet(set1).removeAll(set2).toPattern(true));
    148         TestFmwk.errln("HashMap - UnicodeMap");
    149         TestFmwk.errln(new UnicodeSet(set2).removeAll(set1).toPattern(true));
    150         return false;
    151     }
    152 
    153     public static boolean verifySetsIdentical(AbstractTestLog here, Set values1, Set values2) {
    154         if (values1.equals(values2)) return true;
    155         Set temp;
    156         TestFmwk.errln("Values differ:");
    157         TestFmwk.errln("UnicodeMap - HashMap");
    158         temp = new TreeSet(values1);
    159         temp.removeAll(values2);
    160         TestFmwk.errln(show(temp));
    161         TestFmwk.errln("HashMap - UnicodeMap");
    162         temp = new TreeSet(values2);
    163         temp.removeAll(values1);
    164         TestFmwk.errln(show(temp));
    165         return false;
    166     }
    167 
    168     public static String show(Map m) {
    169         StringBuilder buffer = new StringBuilder();
    170         for (Iterator it = m.keySet().iterator(); it.hasNext();) {
    171             Object key = it.next();
    172             buffer.append(key + "=>" + m.get(key) + "\r\n");
    173         }
    174         return buffer.toString();
    175     }
    176 
    177     public static <T> UnicodeSet getSet(Map<Integer, T> m, T value) {
    178         UnicodeSet result = new UnicodeSet();
    179         for (Iterator<Integer> it = m.keySet().iterator(); it.hasNext();) {
    180             Integer key = it.next();
    181             T val = m.get(key);
    182             if (!val.equals(value)) continue;
    183             result.add(key.intValue());
    184         }
    185         return result;
    186     }
    187 
    188     public static String show(Collection c) {
    189         StringBuilder buffer = new StringBuilder();
    190         for (Iterator it = c.iterator(); it.hasNext();) {
    191             buffer.append(it.next() + "\r\n");
    192         }
    193         return buffer.toString();
    194     }
    195 }
    196