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