1 /*
2  * Copyright (C) 2007 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.testing;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.base.Preconditions;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Sets;
23 
24 import junit.framework.AssertionFailedError;
25 import junit.framework.TestCase;
26 
27 import java.util.Set;
28 
29 /**
30  * Unit tests for {@link EqualsTester}.
31  *
32  * @author Jim McMaster
33  */
34 @GwtCompatible
35 public class EqualsTesterTest extends TestCase {
36   private ValidTestObject reference;
37   private EqualsTester equalsTester;
38   private ValidTestObject equalObject1;
39   private ValidTestObject equalObject2;
40   private ValidTestObject notEqualObject1;
41 
42   @Override
setUp()43   public void setUp() throws Exception {
44     super.setUp();
45     reference = new ValidTestObject(1, 2);
46     equalsTester = new EqualsTester();
47     equalObject1 = new ValidTestObject(1, 2);
48     equalObject2 = new ValidTestObject(1, 2);
49     notEqualObject1 = new ValidTestObject(0, 2);
50   }
51 
52   /**
53    * Test null reference yields error
54    */
testAddNullReference()55   public void testAddNullReference() {
56     try {
57       equalsTester.addEqualityGroup((Object) null);
58       fail("Should fail on null reference");
59     } catch (NullPointerException e) {}
60   }
61 
62   /**
63    * Test equalObjects after adding multiple instances at once with a null
64    */
testAddTwoEqualObjectsAtOnceWithNull()65   public void testAddTwoEqualObjectsAtOnceWithNull() {
66     try {
67       equalsTester.addEqualityGroup(reference, equalObject1, null);
68       fail("Should fail on null equal object");
69     } catch (NullPointerException e) {}
70   }
71 
72   /**
73    * Test adding null equal object yields error
74    */
testAddNullEqualObject()75   public void testAddNullEqualObject() {
76     try {
77       equalsTester.addEqualityGroup(reference, (Object[]) null);
78       fail("Should fail on null equal object");
79     } catch (NullPointerException e) {}
80   }
81 
82   /**
83    * Test adding objects only by addEqualityGroup, with no reference object
84    * specified in the constructor.
85    */
testAddEqualObjectWithOArgConstructor()86   public void testAddEqualObjectWithOArgConstructor() {
87     equalsTester.addEqualityGroup(equalObject1, notEqualObject1);
88     try {
89       equalsTester.testEquals();
90     } catch (AssertionFailedError e) {
91       assertErrorMessage(
92         e,
93         equalObject1 + " [group 1, item 1] must be Object#equals to "
94             + notEqualObject1 + " [group 1, item 2]");
95       return;
96     }
97     fail("Should get not equal to equal object error");
98   }
99 
100   /**
101    * Test EqualsTester with no equals or not equals objects.  This checks
102    * proper handling of null, incompatible class and reflexive tests
103    */
testTestEqualsEmptyLists()104   public void testTestEqualsEmptyLists() {
105     equalsTester.addEqualityGroup(reference);
106     equalsTester.testEquals();
107   }
108 
109   /**
110    * Test EqualsTester after populating equalObjects.  This checks proper
111    * handling of equality and verifies hashCode for valid objects
112    */
testTestEqualsEqualsObjects()113   public void testTestEqualsEqualsObjects() {
114     equalsTester.addEqualityGroup(reference, equalObject1, equalObject2);
115     equalsTester.testEquals();
116   }
117 
118   /**
119    * Test proper handling of case where an object is not equal to itself
120    */
testNonreflexiveEquals()121   public void testNonreflexiveEquals() {
122     Object obj = new NonReflexiveObject();
123     equalsTester.addEqualityGroup(obj);
124     try {
125       equalsTester.testEquals();
126     } catch (AssertionFailedError e) {
127       assertErrorMessage(
128           e, obj + " must be Object#equals to itself");
129       return;
130     }
131     fail("Should get non-reflexive error");
132   }
133 
134   /**
135    * Test proper handling where an object tests equal to null
136    */
testInvalidEqualsNull()137   public void testInvalidEqualsNull() {
138     Object obj = new InvalidEqualsNullObject();
139     equalsTester.addEqualityGroup(obj);
140     try {
141       equalsTester.testEquals();
142     } catch (AssertionFailedError e) {
143       assertErrorMessage(
144           e, obj + " must not be Object#equals to null");
145       return;
146     }
147     fail("Should get equal to null error");
148   }
149 
150   /**
151    * Test proper handling where an object incorrectly tests for an
152    * incompatible class
153    */
testInvalidEqualsIncompatibleClass()154   public void testInvalidEqualsIncompatibleClass() {
155     Object obj = new InvalidEqualsIncompatibleClassObject();
156     equalsTester.addEqualityGroup(obj);
157     try {
158       equalsTester.testEquals();
159     } catch (AssertionFailedError e) {
160       assertErrorMessage(
161           e,
162           obj
163           + " must not be Object#equals to an arbitrary object of another class");
164       return;
165     }
166     fail("Should get equal to incompatible class error");
167   }
168 
169   /**
170    * Test proper handling where an object is not equal to one the user has
171    * said should be equal
172    */
testInvalidNotEqualsEqualObject()173   public void testInvalidNotEqualsEqualObject() {
174     equalsTester.addEqualityGroup(reference, notEqualObject1);
175     try {
176       equalsTester.testEquals();
177     } catch (AssertionFailedError e) {
178       assertErrorMessage(e, reference + " [group 1, item 1]");
179       assertErrorMessage(e, notEqualObject1 + " [group 1, item 2]");
180       return;
181     }
182     fail("Should get not equal to equal object error");
183   }
184 
185   /**
186    * Test for an invalid hashCode method, i.e., one that returns different
187    * value for objects that are equal according to the equals method
188    */
testInvalidHashCode()189   public void testInvalidHashCode() {
190     Object a = new InvalidHashCodeObject(1, 2);
191     Object b = new InvalidHashCodeObject(1, 2);
192     equalsTester.addEqualityGroup(a, b);
193     try {
194       equalsTester.testEquals();
195     } catch (AssertionFailedError e) {
196       assertErrorMessage(
197           e, "the Object#hashCode (" + a.hashCode() + ") of " + a
198           + " [group 1, item 1] must be equal to the Object#hashCode ("
199           + b.hashCode() + ") of " + b);
200       return;
201     }
202     fail("Should get invalid hashCode error");
203   }
204 
testNullEqualityGroup()205   public void testNullEqualityGroup() {
206     EqualsTester tester = new EqualsTester();
207     try {
208       tester.addEqualityGroup((Object[]) null);
209       fail();
210     } catch (NullPointerException e) {}
211   }
212 
testNullObjectInEqualityGroup()213   public void testNullObjectInEqualityGroup() {
214     EqualsTester tester = new EqualsTester();
215     try {
216       tester.addEqualityGroup(1, null, 3);
217       fail();
218     } catch (NullPointerException e) {
219       assertErrorMessage(e, "at index 1");
220     }
221   }
222 
testSymmetryBroken()223   public void testSymmetryBroken() {
224     EqualsTester tester = new EqualsTester()
225         .addEqualityGroup(named("foo").addPeers("bar"), named("bar"));
226     try {
227       tester.testEquals();
228     } catch (AssertionFailedError e) {
229       assertErrorMessage(
230           e,
231           "bar [group 1, item 2] must be Object#equals to foo [group 1, item 1]");
232       return;
233     }
234     fail("should failed because symmetry is broken");
235   }
236 
testTransitivityBrokenInEqualityGroup()237   public void testTransitivityBrokenInEqualityGroup() {
238     EqualsTester tester = new EqualsTester()
239         .addEqualityGroup(
240             named("foo").addPeers("bar", "baz"),
241             named("bar").addPeers("foo"),
242             named("baz").addPeers("foo"));
243     try {
244       tester.testEquals();
245     } catch (AssertionFailedError e) {
246       assertErrorMessage(
247           e,
248           "bar [group 1, item 2] must be Object#equals to baz [group 1, item 3]");
249       return;
250     }
251     fail("should failed because transitivity is broken");
252   }
253 
testUnequalObjectsInEqualityGroup()254   public void testUnequalObjectsInEqualityGroup() {
255     EqualsTester tester = new EqualsTester()
256         .addEqualityGroup(named("foo"), named("bar"));
257     try {
258       tester.testEquals();
259     } catch (AssertionFailedError e) {
260       assertErrorMessage(
261           e,
262           "foo [group 1, item 1] must be Object#equals to bar [group 1, item 2]");
263       return;
264     }
265     fail("should failed because of unequal objects in the same equality group");
266   }
267 
testTransitivityBrokenAcrossEqualityGroups()268   public void testTransitivityBrokenAcrossEqualityGroups() {
269     EqualsTester tester = new EqualsTester()
270         .addEqualityGroup(
271             named("foo").addPeers("bar"),
272             named("bar").addPeers("foo", "x"))
273         .addEqualityGroup(
274             named("baz").addPeers("x"),
275             named("x").addPeers("baz", "bar"));
276     try {
277       tester.testEquals();
278     } catch (AssertionFailedError e) {
279       assertErrorMessage(
280           e,
281           "bar [group 1, item 2] must not be Object#equals to x [group 2, item 2]");
282       return;
283     }
284     fail("should failed because transitivity is broken");
285   }
286 
testEqualityGroups()287   public void testEqualityGroups() {
288     new EqualsTester()
289         .addEqualityGroup(
290             named("foo").addPeers("bar"), named("bar").addPeers("foo"))
291         .addEqualityGroup(named("baz"), named("baz"))
292         .testEquals();
293   }
294 
assertErrorMessage(Throwable e, String message)295   private static void assertErrorMessage(Throwable e, String message) {
296     // TODO(kevinb): use a Truth assertion here
297     if (!e.getMessage().contains(message)) {
298       fail("expected <" + e.getMessage() + "> to contain <" + message + ">");
299     }
300   }
301 
302   /**
303    * Test class with valid equals and hashCode methods.  Testers created
304    * with instances of this class should always pass.
305    */
306   private static class ValidTestObject {
307     private int aspect1;
308     private int aspect2;
309 
ValidTestObject(int aspect1, int aspect2)310     ValidTestObject(int aspect1, int aspect2) {
311       this.aspect1 = aspect1;
312       this.aspect2 = aspect2;
313     }
314 
equals(Object o)315     @Override public boolean equals(Object o) {
316       if (!(o instanceof ValidTestObject)) {
317         return false;
318       }
319       ValidTestObject other = (ValidTestObject) o;
320       if (aspect1 != other.aspect1) {
321         return false;
322       }
323       if (aspect2 != other.aspect2) {
324         return false;
325       }
326       return true;
327     }
328 
hashCode()329     @Override public int hashCode() {
330       int result = 17;
331       result = 37 * result + aspect1;
332       result = 37 * result + aspect2;
333       return result;
334     }
335   }
336 
337   /** Test class with invalid hashCode method. */
338   private static class InvalidHashCodeObject {
339     private int aspect1;
340     private int aspect2;
341 
InvalidHashCodeObject(int aspect1, int aspect2)342     InvalidHashCodeObject(int aspect1, int aspect2) {
343       this.aspect1 = aspect1;
344       this.aspect2 = aspect2;
345     }
346 
equals(Object o)347     @Override public boolean equals(Object o) {
348       if (!(o instanceof InvalidHashCodeObject)) {
349         return false;
350       }
351       InvalidHashCodeObject other = (InvalidHashCodeObject) o;
352       if (aspect1 != other.aspect1) {
353         return false;
354       }
355       if (aspect2 != other.aspect2) {
356         return false;
357       }
358       return true;
359     }
360   }
361 
362   /** Test class that violates reflexitivity.  It is not equal to itself */
363   private static class NonReflexiveObject {
364 
equals(Object o)365     @Override public boolean equals(Object o) {
366       return false;
367     }
368 
hashCode()369     @Override public int hashCode() {
370       return super.hashCode();
371     }
372   }
373 
374   /** Test class that returns true if the test object is null */
375   private static class InvalidEqualsNullObject {
376 
equals(Object o)377     @Override public boolean equals(Object o) {
378       return o == this || o == null;
379     }
380 
hashCode()381     @Override public int hashCode() {
382       return 0;
383     }
384   }
385 
386   /**
387    * Test class that returns true even if the test object is of the wrong class
388    */
389   private static class InvalidEqualsIncompatibleClassObject {
390 
equals(Object o)391     @Override public boolean equals(Object o) {
392       if (o == null) {
393         return false;
394       }
395       return true;
396     }
397 
hashCode()398     @Override public int hashCode() {
399       return 0;
400     }
401   }
402 
named(String name)403   private static NamedObject named(String name) {
404     return new NamedObject(name);
405   }
406 
407   private static class NamedObject {
408     private final Set<String> peerNames = Sets.newHashSet();
409 
410     private final String name;
411 
NamedObject(String name)412     NamedObject(String name) {
413       this.name = Preconditions.checkNotNull(name);
414     }
415 
addPeers(String... names)416     NamedObject addPeers(String... names) {
417       peerNames.addAll(ImmutableList.copyOf(names));
418       return this;
419     }
420 
equals(Object obj)421     @Override public boolean equals(Object obj) {
422       if (obj instanceof NamedObject) {
423         NamedObject that = (NamedObject) obj;
424         return name.equals(that.name) || peerNames.contains(that.name);
425       }
426       return false;
427     }
428 
hashCode()429     @Override public int hashCode() {
430       return 0;
431     }
432 
toString()433     @Override public String toString() {
434       return name;
435     }
436   }
437 }
438