001/* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect.testing; 018 019import com.google.common.annotations.GwtCompatible; 020import java.util.Collection; 021import java.util.Iterator; 022import java.util.List; 023import java.util.ListIterator; 024import java.util.Map; 025import java.util.Map.Entry; 026 027/** 028 * Base class for map testers. 029 * 030 * <p>TODO: see how much of this is actually needed once Map testers are written. (It was cloned 031 * from AbstractCollectionTester.) 032 * 033 * @param <K> the key type of the map to be tested. 034 * @param <V> the value type of the map to be tested. 035 * @author George van den Driessche 036 */ 037@GwtCompatible 038public abstract class AbstractMapTester<K, V> 039 extends AbstractContainerTester<Map<K, V>, Entry<K, V>> { 040 protected Map<K, V> getMap() { 041 return container; 042 } 043 044 @Override 045 protected Collection<Entry<K, V>> actualContents() { 046 return getMap().entrySet(); 047 } 048 049 /** @see AbstractContainerTester#resetContainer() */ 050 protected final void resetMap() { 051 resetContainer(); 052 } 053 054 protected void expectMissingKeys(K... elements) { 055 for (K element : elements) { 056 assertFalse("Should not contain key " + element, getMap().containsKey(element)); 057 } 058 } 059 060 protected void expectMissingValues(V... elements) { 061 for (V element : elements) { 062 assertFalse("Should not contain value " + element, getMap().containsValue(element)); 063 } 064 } 065 066 /** @return an array of the proper size with {@code null} as the key of the middle element. */ 067 protected Entry<K, V>[] createArrayWithNullKey() { 068 Entry<K, V>[] array = createSamplesArray(); 069 final int nullKeyLocation = getNullLocation(); 070 final Entry<K, V> oldEntry = array[nullKeyLocation]; 071 array[nullKeyLocation] = entry(null, oldEntry.getValue()); 072 return array; 073 } 074 075 protected V getValueForNullKey() { 076 return getEntryNullReplaces().getValue(); 077 } 078 079 protected K getKeyForNullValue() { 080 return getEntryNullReplaces().getKey(); 081 } 082 083 private Entry<K, V> getEntryNullReplaces() { 084 Iterator<Entry<K, V>> entries = getSampleElements().iterator(); 085 for (int i = 0; i < getNullLocation(); i++) { 086 entries.next(); 087 } 088 return entries.next(); 089 } 090 091 /** @return an array of the proper size with {@code null} as the value of the middle element. */ 092 protected Entry<K, V>[] createArrayWithNullValue() { 093 Entry<K, V>[] array = createSamplesArray(); 094 final int nullValueLocation = getNullLocation(); 095 final Entry<K, V> oldEntry = array[nullValueLocation]; 096 array[nullValueLocation] = entry(oldEntry.getKey(), null); 097 return array; 098 } 099 100 protected void initMapWithNullKey() { 101 resetMap(createArrayWithNullKey()); 102 } 103 104 protected void initMapWithNullValue() { 105 resetMap(createArrayWithNullValue()); 106 } 107 108 /** 109 * Equivalent to {@link #expectMissingKeys(Object[]) expectMissingKeys} {@code (null)} except that 110 * the call to {@code contains(null)} is permitted to throw a {@code NullPointerException}. 111 * 112 * @param message message to use upon assertion failure 113 */ 114 protected void expectNullKeyMissingWhenNullKeysUnsupported(String message) { 115 try { 116 assertFalse(message, getMap().containsKey(null)); 117 } catch (NullPointerException tolerated) { 118 // Tolerated 119 } 120 } 121 122 /** 123 * Equivalent to {@link #expectMissingValues(Object[]) expectMissingValues} {@code (null)} except 124 * that the call to {@code contains(null)} is permitted to throw a {@code NullPointerException}. 125 * 126 * @param message message to use upon assertion failure 127 */ 128 protected void expectNullValueMissingWhenNullValuesUnsupported(String message) { 129 try { 130 assertFalse(message, getMap().containsValue(null)); 131 } catch (NullPointerException tolerated) { 132 // Tolerated 133 } 134 } 135 136 @SuppressWarnings("unchecked") 137 @Override 138 protected MinimalCollection<Entry<K, V>> createDisjointCollection() { 139 return MinimalCollection.of(e3(), e4()); 140 } 141 142 protected int getNumEntries() { 143 return getNumElements(); 144 } 145 146 protected Collection<Entry<K, V>> getSampleEntries(int howMany) { 147 return getSampleElements(howMany); 148 } 149 150 protected Collection<Entry<K, V>> getSampleEntries() { 151 return getSampleElements(); 152 } 153 154 @Override 155 protected void expectMissing(Entry<K, V>... entries) { 156 for (Entry<K, V> entry : entries) { 157 assertFalse("Should not contain entry " + entry, actualContents().contains(entry)); 158 assertFalse( 159 "Should not contain key " + entry.getKey() + " mapped to value " + entry.getValue(), 160 equal(getMap().get(entry.getKey()), entry.getValue())); 161 } 162 } 163 164 private static boolean equal(Object a, Object b) { 165 return a == b || (a != null && a.equals(b)); 166 } 167 168 // This one-liner saves us from some ugly casts 169 protected Entry<K, V> entry(K key, V value) { 170 return Helpers.mapEntry(key, value); 171 } 172 173 @Override 174 protected void expectContents(Collection<Entry<K, V>> expected) { 175 // TODO: move this to invariant checks once the appropriate hook exists? 176 super.expectContents(expected); 177 for (Entry<K, V> entry : expected) { 178 assertEquals( 179 "Wrong value for key " + entry.getKey(), entry.getValue(), getMap().get(entry.getKey())); 180 } 181 } 182 183 protected final void expectReplacement(Entry<K, V> newEntry) { 184 List<Entry<K, V>> expected = Helpers.copyToList(getSampleElements()); 185 replaceValue(expected, newEntry); 186 expectContents(expected); 187 } 188 189 private void replaceValue(List<Entry<K, V>> expected, Entry<K, V> newEntry) { 190 for (ListIterator<Entry<K, V>> i = expected.listIterator(); i.hasNext(); ) { 191 if (Helpers.equal(i.next().getKey(), newEntry.getKey())) { 192 i.set(newEntry); 193 return; 194 } 195 } 196 197 throw new IllegalArgumentException( 198 Platform.format("key %s not found in entries %s", newEntry.getKey(), expected)); 199 } 200 201 /** 202 * Wrapper for {@link Map#get(Object)} that forces the caller to pass in a key of the same type as 203 * the map. Besides being slightly shorter than code that uses {@link #getMap()}, it also ensures 204 * that callers don't pass an {@link Entry} by mistake. 205 */ 206 protected V get(K key) { 207 return getMap().get(key); 208 } 209 210 protected void resetMap(Entry<K, V>[] entries) { 211 resetContainer(getSubjectGenerator().create((Object[]) entries)); 212 } 213 214 protected final K k0() { 215 return e0().getKey(); 216 } 217 218 protected final V v0() { 219 return e0().getValue(); 220 } 221 222 protected final K k1() { 223 return e1().getKey(); 224 } 225 226 protected final V v1() { 227 return e1().getValue(); 228 } 229 230 protected final K k2() { 231 return e2().getKey(); 232 } 233 234 protected final V v2() { 235 return e2().getValue(); 236 } 237 238 protected final K k3() { 239 return e3().getKey(); 240 } 241 242 protected final V v3() { 243 return e3().getValue(); 244 } 245 246 protected final K k4() { 247 return e4().getKey(); 248 } 249 250 protected final V v4() { 251 return e4().getValue(); 252 } 253}