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