• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 
21 import com.google.common.annotations.GwtCompatible;
22 import com.google.common.base.Equivalence;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.Lists;
25 import java.util.List;
26 import junit.framework.AssertionFailedError;
27 
28 /**
29  * Implementation helper for {@link EqualsTester} and {@link EquivalenceTester} that tests for
30  * equivalence classes.
31  *
32  * @author Gregory Kick
33  */
34 @GwtCompatible
35 final class RelationshipTester<T> {
36 
37   static class ItemReporter {
reportItem(Item<?> item)38     String reportItem(Item<?> item) {
39       return item.toString();
40     }
41   }
42 
43   /**
44    * A word about using {@link Equivalence}, which automatically checks for {@code null} and
45    * identical inputs: This sounds like it ought to be a problem here, since the goals of this class
46    * include testing that {@code equals()} is reflexive and is tolerant of {@code null}. However,
47    * there's no problem. The reason: {@link EqualsTester} tests {@code null} and identical inputs
48    * directly against {@code equals()} rather than through the {@code Equivalence}.
49    */
50   private final Equivalence<? super T> equivalence;
51 
52   private final String relationshipName;
53   private final String hashName;
54   private final ItemReporter itemReporter;
55   private final List<ImmutableList<T>> groups = Lists.newArrayList();
56 
RelationshipTester( Equivalence<? super T> equivalence, String relationshipName, String hashName, ItemReporter itemReporter)57   RelationshipTester(
58       Equivalence<? super T> equivalence,
59       String relationshipName,
60       String hashName,
61       ItemReporter itemReporter) {
62     this.equivalence = checkNotNull(equivalence);
63     this.relationshipName = checkNotNull(relationshipName);
64     this.hashName = checkNotNull(hashName);
65     this.itemReporter = checkNotNull(itemReporter);
66   }
67 
68   // TODO(cpovirk): should we reject null items, since the tests already check null automatically?
addRelatedGroup(Iterable<? extends T> group)69   public RelationshipTester<T> addRelatedGroup(Iterable<? extends T> group) {
70     groups.add(ImmutableList.copyOf(group));
71     return this;
72   }
73 
test()74   public void test() {
75     for (int groupNumber = 0; groupNumber < groups.size(); groupNumber++) {
76       ImmutableList<T> group = groups.get(groupNumber);
77       for (int itemNumber = 0; itemNumber < group.size(); itemNumber++) {
78         // check related items in same group
79         for (int relatedItemNumber = 0; relatedItemNumber < group.size(); relatedItemNumber++) {
80           if (itemNumber != relatedItemNumber) {
81             assertRelated(groupNumber, itemNumber, relatedItemNumber);
82           }
83         }
84         // check unrelated items in all other groups
85         for (int unrelatedGroupNumber = 0;
86             unrelatedGroupNumber < groups.size();
87             unrelatedGroupNumber++) {
88           if (groupNumber != unrelatedGroupNumber) {
89             ImmutableList<T> unrelatedGroup = groups.get(unrelatedGroupNumber);
90             for (int unrelatedItemNumber = 0;
91                 unrelatedItemNumber < unrelatedGroup.size();
92                 unrelatedItemNumber++) {
93               assertUnrelated(groupNumber, itemNumber, unrelatedGroupNumber, unrelatedItemNumber);
94             }
95           }
96         }
97       }
98     }
99   }
100 
assertRelated(int groupNumber, int itemNumber, int relatedItemNumber)101   private void assertRelated(int groupNumber, int itemNumber, int relatedItemNumber) {
102     Item<T> itemInfo = getItem(groupNumber, itemNumber);
103     Item<T> relatedInfo = getItem(groupNumber, relatedItemNumber);
104 
105     T item = itemInfo.value;
106     T related = relatedInfo.value;
107     assertWithTemplate(
108         "$ITEM must be $RELATIONSHIP to $OTHER",
109         itemInfo,
110         relatedInfo,
111         equivalence.equivalent(item, related));
112 
113     int itemHash = equivalence.hash(item);
114     int relatedHash = equivalence.hash(related);
115     assertWithTemplate(
116         "the $HASH ("
117             + itemHash
118             + ") of $ITEM must be equal to the $HASH ("
119             + relatedHash
120             + ") of $OTHER",
121         itemInfo,
122         relatedInfo,
123         itemHash == relatedHash);
124   }
125 
assertUnrelated( int groupNumber, int itemNumber, int unrelatedGroupNumber, int unrelatedItemNumber)126   private void assertUnrelated(
127       int groupNumber, int itemNumber, int unrelatedGroupNumber, int unrelatedItemNumber) {
128     Item<T> itemInfo = getItem(groupNumber, itemNumber);
129     Item<T> unrelatedInfo = getItem(unrelatedGroupNumber, unrelatedItemNumber);
130 
131     assertWithTemplate(
132         "$ITEM must not be $RELATIONSHIP to $OTHER",
133         itemInfo,
134         unrelatedInfo,
135         !equivalence.equivalent(itemInfo.value, unrelatedInfo.value));
136   }
137 
assertWithTemplate(String template, Item<T> item, Item<T> other, boolean condition)138   private void assertWithTemplate(String template, Item<T> item, Item<T> other, boolean condition) {
139     if (!condition) {
140       throw new AssertionFailedError(
141           template
142               .replace("$RELATIONSHIP", relationshipName)
143               .replace("$HASH", hashName)
144               .replace("$ITEM", itemReporter.reportItem(item))
145               .replace("$OTHER", itemReporter.reportItem(other)));
146     }
147   }
148 
getItem(int groupNumber, int itemNumber)149   private Item<T> getItem(int groupNumber, int itemNumber) {
150     return new Item<T>(groups.get(groupNumber).get(itemNumber), groupNumber, itemNumber);
151   }
152 
153   static final class Item<T> {
154     final T value;
155     final int groupNumber;
156     final int itemNumber;
157 
Item(T value, int groupNumber, int itemNumber)158     Item(T value, int groupNumber, int itemNumber) {
159       this.value = value;
160       this.groupNumber = groupNumber;
161       this.itemNumber = itemNumber;
162     }
163 
164     @Override
toString()165     public String toString() {
166       return value + " [group " + (groupNumber + 1) + ", item " + (itemNumber + 1) + ']';
167     }
168   }
169 }
170