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.collect.testing; 18 19 import com.google.common.annotations.GwtCompatible; 20 21 import java.util.Collection; 22 import java.util.Iterator; 23 import java.util.List; 24 import java.util.ListIterator; 25 import java.util.Map; 26 import java.util.Map.Entry; 27 28 /** 29 * Base class for map testers. 30 * 31 * TODO: see how much of this is actually needed once Map testers are written. 32 * (It was cloned from AbstractCollectionTester.) 33 * 34 * @param <K> the key type of the map to be tested. 35 * @param <V> the value type of the map to be tested. 36 * 37 * @author George van den Driessche 38 */ 39 @GwtCompatible 40 public abstract class AbstractMapTester<K, V> extends 41 AbstractContainerTester<Map<K, V>, Map.Entry<K, V>> { 42 protected Map<K, V> getMap() { 43 return container; 44 } 45 46 @Override public void setUp() throws Exception { 47 super.setUp(); 48 samples = this.getSubjectGenerator().samples(); 49 resetMap(); 50 } 51 52 @Override protected Collection<Map.Entry<K, V>> actualContents() { 53 return getMap().entrySet(); 54 } 55 56 /** @see AbstractContainerTester#resetContainer() */ 57 protected void resetMap() { 58 resetContainer(); 59 } 60 61 protected void expectMissingKeys(K... elements) { 62 for (K element : elements) { 63 assertFalse("Should not contain key " + element, 64 getMap().containsKey(element)); 65 } 66 } 67 68 protected void expectMissingValues(V... elements) { 69 for (V element : elements) { 70 assertFalse("Should not contain value " + element, 71 getMap().containsValue(element)); 72 } 73 } 74 75 /** 76 * @return an array of the proper size with {@code null} as the key of the 77 * middle element. 78 */ 79 protected Map.Entry<K, V>[] createArrayWithNullKey() { 80 Map.Entry<K, V>[] array = createSamplesArray(); 81 final int nullKeyLocation = getNullLocation(); 82 final Map.Entry<K, V> oldEntry = array[nullKeyLocation]; 83 array[nullKeyLocation] = entry(null, oldEntry.getValue()); 84 return array; 85 } 86 87 protected V getValueForNullKey() { 88 return getEntryNullReplaces().getValue(); 89 } 90 91 protected K getKeyForNullValue() { 92 return getEntryNullReplaces().getKey(); 93 } 94 95 private Entry<K, V> getEntryNullReplaces() { 96 Iterator<Entry<K, V>> entries = getSampleElements().iterator(); 97 for (int i = 0; i < getNullLocation(); i++) { 98 entries.next(); 99 } 100 return entries.next(); 101 } 102 103 /** 104 * @return an array of the proper size with {@code null} as the value of the 105 * middle element. 106 */ 107 protected Map.Entry<K, V>[] createArrayWithNullValue() { 108 Map.Entry<K, V>[] array = createSamplesArray(); 109 final int nullValueLocation = getNullLocation(); 110 final Map.Entry<K, V> oldEntry = array[nullValueLocation]; 111 array[nullValueLocation] = entry(oldEntry.getKey(), null); 112 return array; 113 } 114 115 protected void initMapWithNullKey() { 116 resetMap(createArrayWithNullKey()); 117 } 118 119 protected void initMapWithNullValue() { 120 resetMap(createArrayWithNullValue()); 121 } 122 123 /** 124 * Equivalent to {@link #expectMissingKeys(Object[]) expectMissingKeys} 125 * {@code (null)} 126 * except that the call to {@code contains(null)} is permitted to throw a 127 * {@code NullPointerException}. 128 * @param message message to use upon assertion failure 129 */ 130 protected void expectNullKeyMissingWhenNullKeysUnsupported(String message) { 131 try { 132 assertFalse(message, getMap().containsKey(null)); 133 } catch (NullPointerException tolerated) { 134 // Tolerated 135 } 136 } 137 138 /** 139 * Equivalent to {@link #expectMissingValues(Object[]) expectMissingValues} 140 * {@code (null)} 141 * except that the call to {@code contains(null)} is permitted to throw a 142 * {@code NullPointerException}. 143 * @param message message to use upon assertion failure 144 */ 145 protected void expectNullValueMissingWhenNullValuesUnsupported( 146 String message) { 147 try { 148 assertFalse(message, getMap().containsValue(null)); 149 } catch (NullPointerException tolerated) { 150 // Tolerated 151 } 152 } 153 154 @SuppressWarnings("unchecked") 155 @Override protected MinimalCollection<Map.Entry<K, V>> 156 createDisjointCollection() { 157 return MinimalCollection.of(samples.e3, samples.e4); 158 } 159 160 protected int getNumEntries() { 161 return getNumElements(); 162 } 163 164 protected Collection<Map.Entry<K, V>> getSampleEntries(int howMany) { 165 return getSampleElements(howMany); 166 } 167 168 protected Collection<Map.Entry<K, V>> getSampleEntries() { 169 return getSampleElements(); 170 } 171 172 @Override protected void expectMissing(Entry<K, V>... entries) { 173 for (Entry<K, V> entry : entries) { 174 assertFalse("Should not contain entry " + entry, 175 actualContents().contains(entry)); 176 assertFalse("Should not contain key " + entry.getKey() + " mapped to" 177 + " value " + entry.getValue(), 178 equal(getMap().get(entry.getKey()), entry.getValue())); 179 } 180 } 181 182 private static boolean equal(Object a, Object b) { 183 return a == b || (a != null && a.equals(b)); 184 } 185 186 // This one-liner saves us from some ugly casts 187 protected Entry<K, V> entry(K key, V value) { 188 return Helpers.mapEntry(key, value); 189 } 190 191 @Override protected void expectContents(Collection<Entry<K, V>> expected) { 192 // TODO: move this to invariant checks once the appropriate hook exists? 193 super.expectContents(expected); 194 for (Entry<K, V> entry : expected) { 195 assertEquals("Wrong value for key " + entry.getKey(), 196 entry.getValue(), getMap().get(entry.getKey())); 197 } 198 } 199 200 protected final void expectReplacement(Entry<K, V> newEntry) { 201 List<Entry<K, V>> expected = Helpers.copyToList(getSampleElements()); 202 replaceValue(expected, newEntry); 203 expectContents(expected); 204 } 205 206 private void replaceValue(List<Entry<K, V>> expected, Entry<K, V> newEntry) { 207 for (ListIterator<Entry<K, V>> i = expected.listIterator(); i.hasNext();) { 208 if (Helpers.equal(i.next().getKey(), newEntry.getKey())) { 209 i.set(newEntry); 210 return; 211 } 212 } 213 214 throw new IllegalArgumentException(Platform.format( 215 "key %s not found in entries %s", newEntry.getKey(), expected)); 216 } 217 218 /** 219 * Wrapper for {@link Map#get(Object)} that forces the caller to pass in a key 220 * of the same type as the map. Besides being slightly shorter than code that 221 * uses {@link #getMap()}, it also ensures that callers don't pass an 222 * {@link Entry} by mistake. 223 */ 224 protected V get(K key) { 225 return getMap().get(key); 226 } 227 228 protected void resetMap(Entry<K, V>[] entries) { 229 resetContainer(getSubjectGenerator().create((Object[]) entries)); 230 } 231 } 232