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