Home | History | Annotate | Download | only in testing
      1 /*
      2  * Copyright (C) 2007 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect.testing;
     18 
     19 import java.util.Collection;
     20 import java.util.Iterator;
     21 import java.util.List;
     22 import java.util.ListIterator;
     23 import java.util.Map;
     24 import java.util.Map.Entry;
     25 
     26 /**
     27  * Base class for map testers.
     28  *
     29  * <p>This class is GWT compatible.
     30  *
     31  * TODO: see how much of this is actually needed once Map testers are written.
     32  * (It was cloned from AbstractCollectionTester.)
     33  *
     34  * @param <K> the key type of the map to be tested.
     35  * @param <V> the value type of the map to be tested.
     36  *
     37  * @author George van den Driessche
     38  */
     39 public abstract class AbstractMapTester<K, V> extends
     40     AbstractContainerTester<Map<K, V>, Map.Entry<K, V>> {
     41   protected Map<K, V> getMap() {
     42     return container;
     43   }
     44 
     45   @Override public void setUp() throws Exception {
     46     super.setUp();
     47     samples = this.getSubjectGenerator().samples();
     48     resetMap();
     49   }
     50 
     51   @Override protected Collection<Map.Entry<K, V>> actualContents() {
     52     return getMap().entrySet();
     53   }
     54 
     55   /** @see AbstractContainerTester#resetContainer() */
     56   protected void resetMap() {
     57     resetContainer();
     58   }
     59 
     60   protected void expectMissingKeys(K... elements) {
     61     for (K element : elements) {
     62       assertFalse("Should not contain key " + element,
     63           getMap().containsKey(element));
     64     }
     65   }
     66 
     67   protected void expectMissingValues(V... elements) {
     68     for (V element : elements) {
     69       assertFalse("Should not contain value " + element,
     70           getMap().containsValue(element));
     71     }
     72   }
     73 
     74   /**
     75    * @return an array of the proper size with {@code null} as the key of the
     76    * middle element.
     77    */
     78   protected Map.Entry<K, V>[] createArrayWithNullKey() {
     79     Map.Entry<K, V>[] array = createSamplesArray();
     80     final int nullKeyLocation = getNullLocation();
     81     final Map.Entry<K, V> oldEntry = array[nullKeyLocation];
     82     array[nullKeyLocation] = entry(null, oldEntry.getValue());
     83     return array;
     84   }
     85 
     86   protected V getValueForNullKey() {
     87     return getEntryNullReplaces().getValue();
     88   }
     89 
     90   protected K getKeyForNullValue() {
     91     return getEntryNullReplaces().getKey();
     92   }
     93 
     94   private Entry<K, V> getEntryNullReplaces() {
     95     Iterator<Entry<K, V>> entries = getSampleElements().iterator();
     96     for (int i = 0; i < getNullLocation(); i++) {
     97       entries.next();
     98     }
     99     return entries.next();
    100   }
    101 
    102   /**
    103    * @return an array of the proper size with {@code null} as the value of the
    104    * middle element.
    105    */
    106   protected Map.Entry<K, V>[] createArrayWithNullValue() {
    107     Map.Entry<K, V>[] array = createSamplesArray();
    108     final int nullValueLocation = getNullLocation();
    109     final Map.Entry<K, V> oldEntry = array[nullValueLocation];
    110     array[nullValueLocation] = entry(oldEntry.getKey(), null);
    111     return array;
    112   }
    113 
    114   protected void initMapWithNullKey() {
    115     resetMap(createArrayWithNullKey());
    116   }
    117 
    118   protected void initMapWithNullValue() {
    119     resetMap(createArrayWithNullValue());
    120   }
    121 
    122   /**
    123    * Equivalent to {@link #expectMissingKeys(Object[]) expectMissingKeys}
    124    * {@code (null)}
    125    * except that the call to {@code contains(null)} is permitted to throw a
    126    * {@code NullPointerException}.
    127    * @param message message to use upon assertion failure
    128    */
    129   protected void expectNullKeyMissingWhenNullKeysUnsupported(String message) {
    130     try {
    131       assertFalse(message, getMap().containsKey(null));
    132     } catch (NullPointerException tolerated) {
    133       // Tolerated
    134     }
    135   }
    136 
    137   /**
    138    * Equivalent to {@link #expectMissingValues(Object[]) expectMissingValues}
    139    * {@code (null)}
    140    * except that the call to {@code contains(null)} is permitted to throw a
    141    * {@code NullPointerException}.
    142    * @param message message to use upon assertion failure
    143    */
    144   protected void expectNullValueMissingWhenNullValuesUnsupported(
    145       String message) {
    146     try {
    147       assertFalse(message, getMap().containsValue(null));
    148     } catch (NullPointerException tolerated) {
    149       // Tolerated
    150     }
    151   }
    152 
    153   @SuppressWarnings("unchecked")
    154   @Override protected MinimalCollection<Map.Entry<K, V>>
    155       createDisjointCollection() {
    156     return MinimalCollection.of(samples.e3, samples.e4);
    157   }
    158 
    159   protected int getNumEntries() {
    160     return getNumElements();
    161   }
    162 
    163   protected Collection<Map.Entry<K, V>> getSampleEntries(int howMany) {
    164     return getSampleElements(howMany);
    165   }
    166 
    167   protected Collection<Map.Entry<K, V>> getSampleEntries() {
    168     return getSampleElements();
    169   }
    170 
    171   @Override protected void expectMissing(Entry<K, V>... entries) {
    172     for (Entry<K, V> entry : entries) {
    173       assertFalse("Should not contain entry " + entry,
    174           actualContents().contains(entry));
    175       assertFalse("Should not contain key " + entry.getKey(),
    176           getMap().containsKey(entry.getKey()));
    177       assertFalse("Should not contain value " + entry.getValue(),
    178           getMap().containsValue(entry.getValue()));
    179       assertNull("Should not return a mapping for key " + entry.getKey(),
    180           getMap().get(entry.getKey()));
    181     }
    182   }
    183 
    184   // This one-liner saves us from some ugly casts
    185   protected Entry<K, V> entry(K key, V value) {
    186     return Helpers.mapEntry(key, value);
    187   }
    188 
    189   @Override protected void expectContents(Collection<Entry<K, V>> expected) {
    190     // TODO: move this to invariant checks once the appropriate hook exists?
    191     super.expectContents(expected);
    192     for (Entry<K, V> entry : expected) {
    193       assertEquals("Wrong value for key " + entry.getKey(),
    194           entry.getValue(), getMap().get(entry.getKey()));
    195     }
    196   }
    197 
    198   protected final void expectReplacement(Entry<K, V> newEntry) {
    199     List<Entry<K, V>> expected = Helpers.copyToList(getSampleElements());
    200     replaceValue(expected, newEntry);
    201     expectContents(expected);
    202   }
    203 
    204   private void replaceValue(List<Entry<K, V>> expected, Entry<K, V> newEntry) {
    205     for (ListIterator<Entry<K, V>> i = expected.listIterator(); i.hasNext();) {
    206       if (Helpers.equal(i.next().getKey(), newEntry.getKey())) {
    207         i.set(newEntry);
    208         return;
    209       }
    210     }
    211 
    212     throw new IllegalArgumentException(Platform.format(
    213         "key %s not found in entries %s", newEntry.getKey(), expected));
    214   }
    215 
    216   /**
    217    * Wrapper for {@link Map#get(Object)} that forces the caller to pass in a key
    218    * of the same type as the map. Besides being slightly shorter than code that
    219    * uses {@link #getMap()}, it also ensures that callers don't pass an
    220    * {@link Entry} by mistake.
    221    */
    222   protected V get(K key) {
    223     return getMap().get(key);
    224   }
    225 
    226   protected void resetMap(Entry<K, V>[] entries) {
    227     resetContainer(getSubjectGenerator().create((Object[]) entries));
    228   }
    229 }
    230