| /* |
| * Copyright (C) 2011 The Guava Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.common.testing; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import com.google.common.annotations.GwtCompatible; |
| import com.google.common.base.Equivalence; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| import java.util.List; |
| import junit.framework.AssertionFailedError; |
| |
| /** |
| * Implementation helper for {@link EqualsTester} and {@link EquivalenceTester} that tests for |
| * equivalence classes. |
| * |
| * @author Gregory Kick |
| */ |
| @GwtCompatible |
| final class RelationshipTester<T> { |
| |
| static class ItemReporter { |
| String reportItem(Item<?> item) { |
| return item.toString(); |
| } |
| } |
| |
| /** |
| * A word about using {@link Equivalence}, which automatically checks for {@code null} and |
| * identical inputs: This sounds like it ought to be a problem here, since the goals of this class |
| * include testing that {@code equals()} is reflexive and is tolerant of {@code null}. However, |
| * there's no problem. The reason: {@link EqualsTester} tests {@code null} and identical inputs |
| * directly against {@code equals()} rather than through the {@code Equivalence}. |
| */ |
| private final Equivalence<? super T> equivalence; |
| |
| private final String relationshipName; |
| private final String hashName; |
| private final ItemReporter itemReporter; |
| private final List<ImmutableList<T>> groups = Lists.newArrayList(); |
| |
| RelationshipTester( |
| Equivalence<? super T> equivalence, |
| String relationshipName, |
| String hashName, |
| ItemReporter itemReporter) { |
| this.equivalence = checkNotNull(equivalence); |
| this.relationshipName = checkNotNull(relationshipName); |
| this.hashName = checkNotNull(hashName); |
| this.itemReporter = checkNotNull(itemReporter); |
| } |
| |
| // TODO(cpovirk): should we reject null items, since the tests already check null automatically? |
| public RelationshipTester<T> addRelatedGroup(Iterable<? extends T> group) { |
| groups.add(ImmutableList.copyOf(group)); |
| return this; |
| } |
| |
| public void test() { |
| for (int groupNumber = 0; groupNumber < groups.size(); groupNumber++) { |
| ImmutableList<T> group = groups.get(groupNumber); |
| for (int itemNumber = 0; itemNumber < group.size(); itemNumber++) { |
| // check related items in same group |
| for (int relatedItemNumber = 0; relatedItemNumber < group.size(); relatedItemNumber++) { |
| if (itemNumber != relatedItemNumber) { |
| assertRelated(groupNumber, itemNumber, relatedItemNumber); |
| } |
| } |
| // check unrelated items in all other groups |
| for (int unrelatedGroupNumber = 0; |
| unrelatedGroupNumber < groups.size(); |
| unrelatedGroupNumber++) { |
| if (groupNumber != unrelatedGroupNumber) { |
| ImmutableList<T> unrelatedGroup = groups.get(unrelatedGroupNumber); |
| for (int unrelatedItemNumber = 0; |
| unrelatedItemNumber < unrelatedGroup.size(); |
| unrelatedItemNumber++) { |
| assertUnrelated(groupNumber, itemNumber, unrelatedGroupNumber, unrelatedItemNumber); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private void assertRelated(int groupNumber, int itemNumber, int relatedItemNumber) { |
| Item<T> itemInfo = getItem(groupNumber, itemNumber); |
| Item<T> relatedInfo = getItem(groupNumber, relatedItemNumber); |
| |
| T item = itemInfo.value; |
| T related = relatedInfo.value; |
| assertWithTemplate( |
| "$ITEM must be $RELATIONSHIP to $OTHER", |
| itemInfo, |
| relatedInfo, |
| equivalence.equivalent(item, related)); |
| |
| int itemHash = equivalence.hash(item); |
| int relatedHash = equivalence.hash(related); |
| assertWithTemplate( |
| "the $HASH (" |
| + itemHash |
| + ") of $ITEM must be equal to the $HASH (" |
| + relatedHash |
| + ") of $OTHER", |
| itemInfo, |
| relatedInfo, |
| itemHash == relatedHash); |
| } |
| |
| private void assertUnrelated( |
| int groupNumber, int itemNumber, int unrelatedGroupNumber, int unrelatedItemNumber) { |
| Item<T> itemInfo = getItem(groupNumber, itemNumber); |
| Item<T> unrelatedInfo = getItem(unrelatedGroupNumber, unrelatedItemNumber); |
| |
| assertWithTemplate( |
| "$ITEM must not be $RELATIONSHIP to $OTHER", |
| itemInfo, |
| unrelatedInfo, |
| !equivalence.equivalent(itemInfo.value, unrelatedInfo.value)); |
| } |
| |
| private void assertWithTemplate(String template, Item<T> item, Item<T> other, boolean condition) { |
| if (!condition) { |
| throw new AssertionFailedError( |
| template |
| .replace("$RELATIONSHIP", relationshipName) |
| .replace("$HASH", hashName) |
| .replace("$ITEM", itemReporter.reportItem(item)) |
| .replace("$OTHER", itemReporter.reportItem(other))); |
| } |
| } |
| |
| private Item<T> getItem(int groupNumber, int itemNumber) { |
| return new Item<T>(groups.get(groupNumber).get(itemNumber), groupNumber, itemNumber); |
| } |
| |
| static final class Item<T> { |
| final T value; |
| final int groupNumber; |
| final int itemNumber; |
| |
| Item(T value, int groupNumber, int itemNumber) { |
| this.value = value; |
| this.groupNumber = groupNumber; |
| this.itemNumber = itemNumber; |
| } |
| |
| @Override |
| public String toString() { |
| return value + " [group " + (groupNumber + 1) + ", item " + (itemNumber + 1) + ']'; |
| } |
| } |
| } |