1 package com.diasparsoftware.util.junit;
2
3 import java.util.*;
4
5 import junit.framework.*;
6
7 import org.apache.commons.collections.*;
8
9 import com.diasparsoftware.java.util.*;
10
11 public abstract class ValueObjectEqualsTest extends TestCase {
12 private Object control;
13 private Map differentObjects = new HashMap();
14
15 private Object equalToControl;
16 private Object equalToControl2;
17
18 private static final int NUM_ITERATIONS = 20;
19
20 /***
21 * Creates the "control" instance of the class under test —
22 * the object against which all the others are to be compared.
23 */
24 protected abstract Object createControlInstance() throws Exception;
25
26 /***
27 * Creates and returns an instance of the class under test that
28 * differs from the control instance by having a different value
29 * for the specified key property.
30 */
31 protected abstract Object createInstanceDiffersIn(String keyPropertyName)
32 throws Exception;
33
34 /***
35 * The names of the key properties used to distinguish
36 * unequal instances of this class.
37 *
38 * @return
39 */
40 protected abstract List keyPropertyNames();
41
42 protected void setUp() throws Exception {
43 super.setUp();
44
45 control = createControlInstance();
46 equalToControl = createControlInstance();
47 equalToControl2 = createControlInstance();
48
49 CollectionUtil
50 .forEachDo(keyPropertyNames(), new ExceptionalClosure() {
51 public Object execute(Object each) throws Exception {
52 String eachName = (String) each;
53 differentObjects.put(
54 each,
55 createInstanceDiffersIn(eachName));
56 return null;
57 }
58 });
59
60
61 try {
62 assertNotNull(
63 "createControlInstance() returned null",
64 control);
65 assertNotNull(
66 "2nd createControlInstance() returned null",
67 equalToControl);
68 assertNotNull(
69 "3rd createControlInstance() returned null",
70 equalToControl2);
71
72 eachDifferentObjectDo(new MapEntryClosure() {
73 public void eachMapEntry(Object key, Object value) {
74 assertNotNull(nameOf(key) + "returned null", value);
75 }
76 });
77
78 Assert.assertNotSame(control, equalToControl);
79 Assert.assertNotSame(control, equalToControl2);
80
81 eachDifferentObjectDo(new MapEntryClosure() {
82 public void eachMapEntry(Object key, Object value) {
83 Assert.assertNotSame(
84 nameOf(key) + " same as control",
85 control,
86 value);
87 Assert.assertNotSame(
88 nameOf(key) + " same as equalToControl",
89 equalToControl,
90 value);
91 Assert.assertNotSame(
92 nameOf(key) + " same as equalToControl2",
93 equalToControl2,
94 value);
95 }
96 });
97
98 Assert.assertNotSame(equalToControl, equalToControl2);
99
100 assertEquals(
101 "1st and 2nd equal instances of different classes",
102 control.getClass(),
103 equalToControl.getClass());
104 assertEquals(
105 "1st and 3rd equal instances of different classes",
106 control.getClass(),
107 equalToControl2.getClass());
108
109 eachDifferentObjectDo(new MapEntryClosure() {
110 public void eachMapEntry(Object key, Object value) {
111 assertEquals(
112 "control instance and "
113 + nameOf(key)
114 + " of different classes",
115 control.getClass(),
116 value.getClass());
117
118 }
119 });
120 }
121 catch (AssertionFailedError ex) {
122 throw new IllegalArgumentException(ex.getMessage());
123 }
124 }
125
126 /***
127 * Tests whether <code>equals</code> holds up against a new
128 * <code>Object</code> (should always be <code>false</code>).
129 */
130 public final void testEqualsAgainstNewObject() {
131 final Object o = new Object();
132
133 assertNotEquals(o, control);
134 assertNotEquals(o, equalToControl);
135 assertNotEquals(o, equalToControl2);
136
137 eachDifferentObjectDo(new MapEntryClosure() {
138 public void eachMapEntry(Object key, Object value) {
139 assertNotEquals(o, value);
140 }
141 });
142 }
143
144 /***
145 * Tests whether <code>equals</code> holds up against <code>null</code>.
146 */
147 public final void testEqualsAgainstNull() {
148 assertNotEquals("null vs. 1st", null, control);
149 assertNotEquals("null vs. 2nd", null, equalToControl);
150 assertNotEquals("null vs. 3rd", null, equalToControl2);
151
152 eachDifferentObjectDo(new MapEntryClosure() {
153 public void eachMapEntry(Object key, Object value) {
154 assertNotEquals("null vs. " + nameOf(key), null, value);
155 }
156 });
157 }
158
159 /***
160 * Tests whether <code>equals</code> holds up against objects that should
161 * not compare equal.
162 */
163 public final void testEqualsAgainstUnequalObjects() {
164 eachDifferentObjectDo(new MapEntryClosure() {
165 public void eachMapEntry(Object key, Object value) {
166 assertNotEquals(
167 "1st vs. " + nameOf(key),
168 control,
169 value);
170 assertNotEquals(
171 "2nd vs. " + nameOf(key),
172 equalToControl,
173 value);
174 assertNotEquals(
175 "3rd vs. " + nameOf(key),
176 equalToControl2,
177 value);
178
179 assertNotEquals(
180 nameOf(key) + " vs. 1st",
181 value,
182 control);
183 assertNotEquals(
184 nameOf(key) + " vs. 2nd",
185 value,
186 equalToControl);
187 assertNotEquals(
188 nameOf(key) + " vs. 3rd",
189 value,
190 equalToControl2);
191 }
192 });
193
194 }
195
196 /***
197 * Tests whether <code>equals</code> is <em>consistent</em>.
198 */
199 public final void testEqualsIsConsistentAcrossInvocations() {
200 for (int i = 0; i < NUM_ITERATIONS; ++i) {
201 testEqualsAgainstNewObject();
202 testEqualsAgainstNull();
203 testEqualsAgainstUnequalObjects();
204 testEqualsIsReflexive();
205 testEqualsIsSymmetricAndTransitive();
206 }
207 }
208
209 /***
210 * Tests whether <code>equals</code> is <em>reflexive</em>.
211 */
212 public final void testEqualsIsReflexive() {
213 assertEquals("1st equal instance", control, control);
214 assertEquals(
215 "2nd equal instance",
216 equalToControl,
217 equalToControl);
218 assertEquals(
219 "3rd equal instance",
220 equalToControl2,
221 equalToControl2);
222
223 eachDifferentObjectDo(new MapEntryClosure() {
224 public void eachMapEntry(Object key, Object value) {
225 assertEquals(nameOf(key) + " instance", value, value);
226 }
227 });
228 }
229
230 /***
231 * Tests whether <code>equals</code> is <em>symmetric</em> and
232 * <em>transitive</em>.
233 */
234 public final void testEqualsIsSymmetricAndTransitive() {
235 assertEquals("1st vs. 2nd", control, equalToControl);
236 assertEquals("2nd vs. 1st", equalToControl, control);
237
238 assertEquals("1st vs. 3rd", control, equalToControl2);
239 assertEquals("3rd vs. 1st", equalToControl2, control);
240
241 assertEquals("2nd vs. 3rd", equalToControl, equalToControl2);
242 assertEquals("3rd vs. 2nd", equalToControl2, equalToControl);
243 }
244
245 /***
246 * Tests the <code>hashCode</code> contract.
247 */
248 public final void testHashCodeContract() {
249 assertEquals(
250 "1st vs. 2nd",
251 control.hashCode(),
252 equalToControl.hashCode());
253 assertEquals(
254 "1st vs. 3rd",
255 control.hashCode(),
256 equalToControl2.hashCode());
257 assertEquals(
258 "2nd vs. 3rd",
259 equalToControl.hashCode(),
260 equalToControl2.hashCode());
261 }
262
263 /***
264 * Tests the consistency of <code>hashCode</code>.
265 */
266 public final void testHashCodeIsConsistentAcrossInvocations() {
267 int eq1Hash = control.hashCode();
268 int eq2Hash = equalToControl.hashCode();
269 int eq3Hash = equalToControl2.hashCode();
270
271 final Map differentObjectsHashes = new HashMap();
272
273 eachDifferentObjectDo(new MapEntryClosure() {
274 public void eachMapEntry(Object key, Object value) {
275 differentObjectsHashes.put(
276 key,
277 new Integer(value.hashCode()));
278 }
279 });
280
281 for (int i = 0; i < NUM_ITERATIONS; ++i) {
282 assertEquals(
283 "1st equal instance",
284 eq1Hash,
285 control.hashCode());
286 assertEquals(
287 "2nd equal instance",
288 eq2Hash,
289 equalToControl.hashCode());
290 assertEquals(
291 "3rd equal instance",
292 eq3Hash,
293 equalToControl2.hashCode());
294
295 eachDifferentObjectDo(new MapEntryClosure() {
296 public void eachMapEntry(Object key, Object value) {
297 assertEquals(
298 nameOf(key) + " instance",
299 ((Integer) differentObjectsHashes.get(key))
300 .intValue(),
301 value.hashCode());
302 }
303 });
304 }
305 }
306
307 protected static void assertNotEquals(Object lhs, Object rhs) {
308 assertNotEquals(null, lhs, rhs);
309 }
310
311 protected static void assertNotEquals(
312 String failureMessage,
313 Object lhs,
314 Object rhs) {
315 if (lhs != null)
316 assertFalse(failureMessage, lhs.equals(rhs));
317 }
318
319 private void eachDifferentObjectDo(Closure closure) {
320 CollectionUtils.forAllDo(differentObjects.entrySet(), closure);
321 }
322
323 private final String nameOf(Object key) {
324 return "objectDiffersBy('" + key + "')";
325 }
326 }