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 static com.google.common.base.Preconditions.checkNotNull;
     20 import static junit.framework.Assert.assertEquals;
     21 import static junit.framework.Assert.assertTrue;
     22 
     23 import com.google.common.annotations.Beta;
     24 import com.google.common.annotations.GwtCompatible;
     25 import com.google.common.base.Objects;
     26 import com.google.common.collect.ImmutableList;
     27 import com.google.common.collect.Iterables;
     28 import com.google.common.collect.Lists;
     29 import com.google.common.testing.RelationshipTester.RelationshipAssertion;
     30 
     31 import java.util.List;
     32 
     33 /**
     34  * Tester for equals() and hashCode() methods of a class.
     35  *
     36  * <p>To use, create a new EqualsTester and add equality groups where each group
     37  * contains objects that are supposed to be equal to each other, and objects of
     38  * different groups are expected to be unequal. For example:
     39  * <pre>
     40  * new EqualsTester()
     41  *     .addEqualityGroup("hello", "h" + "ello")
     42  *     .addEqualityGroup("world", "wor" + "ld")
     43  *     .addEqualityGroup(2, 1 + 1)
     44  *     .testEquals();
     45  * </pre>
     46  * This tests:
     47  * <ul>
     48  * <li>comparing each object against itself returns true
     49  * <li>comparing each object against null returns false
     50  * <li>comparing each object an instance of an incompatible class returns false
     51  * <li>comparing each pair of objects within the same equality group returns
     52  *     true
     53  * <li>comparing each pair of objects from different equality groups returns
     54  *     false
     55  * <li>the hash code of any two equal objects are equal
     56  * </ul>
     57  *
     58  * <p>When a test fails, the error message labels the objects involved in
     59  * the failed comparison as follows:
     60  * <ul>
     61  *   <li>"{@code [group }<i>i</i>{@code , item }<i>j</i>{@code ]}" refers to the
     62  *       <i>j</i><sup>th</sup> item in the <i>i</i><sup>th</sup> equality group,
     63  *       where both equality groups and the items within equality groups are
     64  *       numbered starting from 1.  When either a constructor argument or an
     65  *       equal object is provided, that becomes group 1.
     66  * </ul>
     67  *
     68  * @author Jim McMaster
     69  * @author Jige Yu
     70  * @since 10.0
     71  */
     72 @Beta
     73 @GwtCompatible
     74 public final class EqualsTester {
     75   private static final int REPETITIONS = 3;
     76 
     77   private final List<List<Object>> equalityGroups = Lists.newArrayList();
     78 
     79   /**
     80    * Constructs an empty EqualsTester instance
     81    */
     82   public EqualsTester() {}
     83 
     84   /**
     85    * Adds {@code equalityGroup} with objects that are supposed to be equal to
     86    * each other and not equal to any other equality groups added to this tester.
     87    */
     88   public EqualsTester addEqualityGroup(Object... equalityGroup) {
     89     checkNotNull(equalityGroup);
     90     equalityGroups.add(ImmutableList.copyOf(equalityGroup));
     91     return this;
     92   }
     93 
     94   /**
     95    * Run tests on equals method, throwing a failure on an invalid test
     96    */
     97   public EqualsTester testEquals() {
     98     RelationshipTester<Object> delegate = new RelationshipTester<Object>(
     99         new RelationshipAssertion<Object>() {
    100           @Override public void assertRelated(Object item, Object related) {
    101             assertEquals("$ITEM must be equal to $RELATED", item, related);
    102             int itemHash = item.hashCode();
    103             int relatedHash = related.hashCode();
    104             assertEquals("the hash (" + itemHash + ") of $ITEM must be equal to the hash ("
    105                 + relatedHash +") of $RELATED", itemHash, relatedHash);
    106           }
    107 
    108           @Override public void assertUnrelated(Object item, Object unrelated) {
    109             // TODO(cpovirk): should this implementation (and
    110             // RelationshipAssertions in general) accept null inputs?
    111             assertTrue("$ITEM must be unequal to $UNRELATED", !Objects.equal(item, unrelated));
    112           }
    113         });
    114     for (List<Object> group : equalityGroups) {
    115       delegate.addRelatedGroup(group);
    116     }
    117     for (int run = 0; run < REPETITIONS; run++) {
    118       testItems();
    119       delegate.test();
    120     }
    121     return this;
    122   }
    123 
    124   private void testItems() {
    125     for (Object item : Iterables.concat(equalityGroups)) {
    126       assertTrue(item + " must be unequal to null", !item.equals(null));
    127       assertTrue(item + " must be unequal to an arbitrary object of another class",
    128           !item.equals(NotAnInstance.EQUAL_TO_NOTHING));
    129       assertEquals(item + " must be equal to itself", item, item);
    130       assertEquals("the hash of " + item + " must be consistent", item.hashCode(), item.hashCode());
    131     }
    132   }
    133 
    134   /**
    135    * Class used to test whether equals() correctly handles an instance
    136    * of an incompatible class.  Since it is a private inner class, the
    137    * invoker can never pass in an instance to the tester
    138    */
    139   private enum NotAnInstance {
    140     EQUAL_TO_NOTHING;
    141   }
    142 }
    143