Home | History | Annotate | Download | only in testing
      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 }
    438