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