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