View Javadoc
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
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     */
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     */
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     */
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     */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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 
205   public void testNullEqualityGroup() {
206     EqualsTester tester = new EqualsTester();
207     try {
208       tester.addEqualityGroup((Object[]) null);
209       fail();
210     } catch (NullPointerException e) {}
211   }
212 
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 
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 
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 
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 
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 
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 
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 
310     ValidTestObject(int aspect1, int aspect2) {
311       this.aspect1 = aspect1;
312       this.aspect2 = aspect2;
313     }
314 
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 
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 
342     InvalidHashCodeObject(int aspect1, int aspect2) {
343       this.aspect1 = aspect1;
344       this.aspect2 = aspect2;
345     }
346 
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 
365     @Override public boolean equals(Object o) {
366       return false;
367     }
368 
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 
377     @Override public boolean equals(Object o) {
378       return o == this || o == null;
379     }
380 
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 
391     @Override public boolean equals(Object o) {
392       if (o == null) {
393         return false;
394       }
395       return true;
396     }
397 
398     @Override public int hashCode() {
399       return 0;
400     }
401   }
402 
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 
412     NamedObject(String name) {
413       this.name = Preconditions.checkNotNull(name);
414     }
415 
416     NamedObject addPeers(String... names) {
417       peerNames.addAll(ImmutableList.copyOf(names));
418       return this;
419     }
420 
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 
429     @Override public int hashCode() {
430       return 0;
431     }
432 
433     @Override public String toString() {
434       return name;
435     }
436   }
437 }