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 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.Equivalence;
26  import com.google.common.collect.ImmutableList;
27  import com.google.common.collect.Iterables;
28  import com.google.common.collect.Lists;
29  
30  import java.util.List;
31  
32  /**
33   * Tester for equals() and hashCode() methods of a class.
34   *
35   * <p>To use, create a new EqualsTester and add equality groups where each group
36   * contains objects that are supposed to be equal to each other, and objects of
37   * different groups are expected to be unequal. For example:
38   * <pre>
39   * new EqualsTester()
40   *     .addEqualityGroup("hello", "h" + "ello")
41   *     .addEqualityGroup("world", "wor" + "ld")
42   *     .addEqualityGroup(2, 1 + 1)
43   *     .testEquals();
44   * </pre>
45   * <p>This tests:
46   * <ul>
47   * <li>comparing each object against itself returns true
48   * <li>comparing each object against null returns false
49   * <li>comparing each object against an instance of an incompatible class
50   *     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 codes 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    private final RelationshipTester.ItemReporter itemReporter;
79  
80    /**
81     * Constructs an empty EqualsTester instance
82     */
83    public EqualsTester() {
84      this(new RelationshipTester.ItemReporter());
85    }
86  
87    EqualsTester(RelationshipTester.ItemReporter itemReporter) {
88      this.itemReporter = checkNotNull(itemReporter);
89    }
90  
91    /**
92     * Adds {@code equalityGroup} with objects that are supposed to be equal to
93     * each other and not equal to any other equality groups added to this tester.
94     */
95    public EqualsTester addEqualityGroup(Object... equalityGroup) {
96      checkNotNull(equalityGroup);
97      equalityGroups.add(ImmutableList.copyOf(equalityGroup));
98      return this;
99    }
100 
101   /**
102    * Run tests on equals method, throwing a failure on an invalid test
103    */
104   public EqualsTester testEquals() {
105     RelationshipTester<Object> delegate = new RelationshipTester<Object>(
106         Equivalence.equals(), "Object#equals", "Object#hashCode", itemReporter);
107     for (List<Object> group : equalityGroups) {
108       delegate.addRelatedGroup(group);
109     }
110     for (int run = 0; run < REPETITIONS; run++) {
111       testItems();
112       delegate.test();
113     }
114     return this;
115   }
116 
117   private void testItems() {
118     for (Object item : Iterables.concat(equalityGroups)) {
119       assertTrue(item + " must not be Object#equals to null", !item.equals(null));
120       assertTrue(item + " must not be Object#equals to an arbitrary object of another class",
121           !item.equals(NotAnInstance.EQUAL_TO_NOTHING));
122       assertEquals(item + " must be Object#equals to itself", item, item);
123       assertEquals("the Object#hashCode of " + item + " must be consistent",
124           item.hashCode(), item.hashCode());
125     }
126   }
127 
128   /**
129    * Class used to test whether equals() correctly handles an instance
130    * of an incompatible class.  Since it is a private inner class, the
131    * invoker can never pass in an instance to the tester
132    */
133   private enum NotAnInstance {
134     EQUAL_TO_NOTHING;
135   }
136 }