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 17package com.google.common.testing; 18 19import static com.google.common.base.Preconditions.checkNotNull; 20 21import com.google.common.annotations.GwtCompatible; 22import com.google.common.collect.ImmutableList; 23import com.google.common.collect.Lists; 24 25import junit.framework.AssertionFailedError; 26 27import java.util.List; 28 29/** 30 * Tests a collection of objects according to the rules specified in a 31 * {@link RelationshipAssertion}. 32 * 33 * @author Gregory Kick 34 */ 35@GwtCompatible 36final class RelationshipTester<T> { 37 private final List<ImmutableList<T>> groups = Lists.newArrayList(); 38 private final RelationshipAssertion<T> assertion; 39 40 RelationshipTester(RelationshipAssertion<T> assertion) { 41 this.assertion = checkNotNull(assertion); 42 } 43 44 public RelationshipTester<T> addRelatedGroup(Iterable<? extends T> group) { 45 groups.add(ImmutableList.copyOf(group)); 46 return this; 47 } 48 49 public void test() { 50 for (int groupNumber = 0; groupNumber < groups.size(); groupNumber++) { 51 ImmutableList<T> group = groups.get(groupNumber); 52 for (int itemNumber = 0; itemNumber < group.size(); itemNumber++) { 53 // check related items in same group 54 for (int relatedItemNumber = 0; relatedItemNumber < group.size(); relatedItemNumber++) { 55 if (itemNumber != relatedItemNumber) { 56 assertRelated(groupNumber, itemNumber, relatedItemNumber); 57 } 58 } 59 // check unrelated items in all other groups 60 for (int unrelatedGroupNumber = 0; unrelatedGroupNumber < groups.size(); 61 unrelatedGroupNumber++) { 62 if (groupNumber != unrelatedGroupNumber) { 63 ImmutableList<T> unrelatedGroup = groups.get(unrelatedGroupNumber); 64 for (int unrelatedItemNumber = 0; unrelatedItemNumber < unrelatedGroup.size(); 65 unrelatedItemNumber++) { 66 assertUnrelated(groupNumber, itemNumber, unrelatedGroupNumber, unrelatedItemNumber); 67 } 68 } 69 } 70 } 71 } 72 } 73 74 private void assertRelated(int groupNumber, int itemNumber, int relatedItemNumber) { 75 ImmutableList<T> group = groups.get(groupNumber); 76 T item = group.get(itemNumber); 77 T related = group.get(relatedItemNumber); 78 try { 79 assertion.assertRelated(item, related); 80 } catch (AssertionFailedError e) { 81 // TODO(gak): special handling for ComparisonFailure? 82 throw new AssertionFailedError(e.getMessage() 83 .replace("$ITEM", itemString(item, groupNumber, itemNumber)) 84 .replace("$RELATED", itemString(related, groupNumber, relatedItemNumber))); 85 } 86 } 87 88 private void assertUnrelated(int groupNumber, int itemNumber, int unrelatedGroupNumber, 89 int unrelatedItemNumber) { 90 T item = groups.get(groupNumber).get(itemNumber); 91 T unrelated = groups.get(unrelatedGroupNumber).get(unrelatedItemNumber); 92 try { 93 assertion.assertUnrelated(item, unrelated); 94 } catch (AssertionFailedError e) { 95 // TODO(gak): special handling for ComparisonFailure? 96 throw new AssertionFailedError(e.getMessage() 97 .replace("$ITEM", itemString(item, groupNumber, itemNumber)) 98 .replace("$UNRELATED", itemString(unrelated, unrelatedGroupNumber, unrelatedItemNumber))); 99 } 100 } 101 102 private static String itemString(Object item, int groupNumber, int itemNumber) { 103 return new StringBuilder() 104 .append(item) 105 .append(" [group ") 106 .append(groupNumber + 1) 107 .append(", item ") 108 .append(itemNumber + 1) 109 .append(']') 110 .toString(); 111 } 112 113 /** 114 * A strategy for testing the relationship between objects. Methods are expected to throw 115 * {@link AssertionFailedError} whenever the relationship is violated. 116 * 117 * <p>As a convenience, any occurrence of {@code $ITEM}, {@code $RELATED} or {@code $UNRELATED} in 118 * the error message will be replaced with a string that combines the {@link Object#toString()}, 119 * item number and group number of the respective item. 120 * 121 */ 122 interface RelationshipAssertion<T> { 123 void assertRelated(T item, T related); 124 125 void assertUnrelated(T item, T unrelated); 126 } 127} 128