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