1 /* 2 * Copyright 2011 Google Inc. 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.testing.util; 18 19 import static java.util.Arrays.asList; 20 21 import com.google.common.base.Objects; 22 import com.google.common.collect.HashMultiset; 23 import com.google.common.collect.ImmutableList; 24 import com.google.common.collect.Lists; 25 import java.util.Comparator; 26 27 import junit.framework.Assert; 28 29 import java.util.Iterator; 30 import java.util.List; 31 32 public final class MoreAsserts { 33 MoreAsserts()34 private MoreAsserts() { } 35 36 /** 37 * Asserts that {@code actual} contains precisely the elements 38 * {@code expected}, in any order. Both collections may contain 39 * duplicates, and this method will only pass if the quantities are 40 * exactly the same. 41 */ assertContentsAnyOrder( String message, Iterable<?> actual, Object... expected)42 public static void assertContentsAnyOrder( 43 String message, Iterable<?> actual, Object... expected) { 44 assertEqualsImpl(message, 45 HashMultiset.create(asList(expected)), HashMultiset.create(actual)); 46 } 47 48 /** 49 * Variant of {@link #assertContentsAnyOrder(String,Iterable,Object...)} 50 * using a generic message. 51 */ assertContentsAnyOrder( Iterable<?> actual, Object... expected)52 public static void assertContentsAnyOrder( 53 Iterable<?> actual, Object... expected) { 54 assertContentsAnyOrder((String) null, actual, expected); 55 } 56 57 /** 58 * Asserts that {@code actual} contains precisely the elements 59 * {@code expected}, in any order. Both collections may contain 60 * duplicates, and this method will only pass if the quantities are 61 * exactly the same. This method uses the user-provided Comparator 62 * object for doing the object comparison, instead of relying on the 63 * contents' implementation of {@link Object#equals(Object)}. It also takes 64 * in the expected set of objects as an Iterable. 65 * <p> 66 * Note the different order of expected and actual from the other 67 * {@link #assertContentsAnyOrder(String,Iterable,Object...)} 68 */ assertContentsAnyOrder(String message, Iterable<? extends T> expected, Iterable<? extends T> actual, Comparator<? super T> comparator)69 public static <T> void assertContentsAnyOrder(String message, 70 Iterable<? extends T> expected, Iterable<? extends T> actual, 71 Comparator<? super T> comparator) { 72 // We should not iterate over an Iterable more than once. There's 73 // no guarentees that Iterable.iterator() returns an iterator over the 74 // entire collection every time. 75 // 76 // Why don't we use TreeMultiset? Unfortunately, TreeMultiset.toString() 77 // produces really odd output for duplicates. In addition, our contract 78 // states that we use the comparator to compare equality, not to order 79 // items. 80 ImmutableList<T> actualList = ImmutableList.copyOf(actual); 81 ImmutableList<T> expectedList = ImmutableList.copyOf(expected); 82 83 // First compare sizes to save ourselves on N X M operation. 84 // This also handles the case where "expected" is a subset of "actual". 85 if (actualList.size() != expectedList.size()) { 86 failNotEqual(message, expectedList, actualList); 87 } 88 89 // Now for each expected value, iterate through actuals and delete entry 90 // if found. We need to make another copy of the "actual" items because 91 // we will be removing items from this list, and we need to keep the original 92 // for the failure message. 93 List<T> unfoundItems = Lists.newLinkedList(actualList); 94 for (T ex : expectedList) { 95 boolean found = false; 96 Iterator<T> iter = unfoundItems.iterator(); 97 while (iter.hasNext()) { 98 T ac = iter.next(); 99 if (comparator.compare(ex, ac) == 0) { 100 iter.remove(); 101 found = true; 102 break; 103 } 104 } 105 if (!found) { 106 failNotEqual(message, expectedList, actualList); 107 } 108 } 109 } 110 111 /** 112 * Variant of {@link #assertContentsAnyOrder(String,Iterable,Object...)} 113 * using a generic message. 114 */ assertContentsAnyOrder( Iterable<? extends T> expected, Iterable<? extends T> actual, Comparator<? super T> comparator)115 public static <T> void assertContentsAnyOrder( 116 Iterable<? extends T> expected, Iterable<? extends T> actual, 117 Comparator<? super T> comparator) { 118 assertContentsAnyOrder((String) null, expected, actual, comparator); 119 } 120 failNotEqual(String message, Object expected, Object actual)121 private static void failNotEqual(String message, Object expected, 122 Object actual) { 123 if ((expected != null) && (actual != null) 124 && expected.toString().equals(actual.toString())) { 125 failWithMessage(message, "expected:<(" 126 + expected.getClass().getName() + ") " + expected + "> but was:<(" 127 + actual.getClass().getName() + ") " + actual + ">"); 128 } else { 129 failWithMessage(message, "expected:<" + expected + "> but was:<" + actual 130 + ">"); 131 } 132 } 133 134 /** 135 * Replacement of {@link Assert#assertEquals} which provides the same error 136 * message in GWT and java. 137 */ assertEqualsImpl( String message, Object expected, Object actual)138 private static void assertEqualsImpl( 139 String message, Object expected, Object actual) { 140 if (!Objects.equal(expected, actual)) { 141 failWithMessage( 142 message, "expected:<" + expected + "> but was:<" + actual + ">"); 143 } 144 } 145 failWithMessage(String userMessage, String ourMessage)146 private static void failWithMessage(String userMessage, String ourMessage) { 147 Assert.fail((userMessage == null) 148 ? ourMessage 149 : userMessage + ' ' + ourMessage); 150 } 151 } 152