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