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