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 com.google.common.annotations.GwtCompatible;
     20 
     21 import java.util.Collection;
     22 import java.util.Iterator;
     23 import java.util.List;
     24 import java.util.ListIterator;
     25 import java.util.Map;
     26 import java.util.Map.Entry;
     27 
     28 /**
     29  * Base class for map testers.
     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 @GwtCompatible
     40 public abstract class AbstractMapTester<K, V> extends
     41     AbstractContainerTester<Map<K, V>, Map.Entry<K, V>> {
     42   protected Map<K, V> getMap() {
     43     return container;
     44   }
     45 
     46   @Override public void setUp() throws Exception {
     47     super.setUp();
     48     samples = this.getSubjectGenerator().samples();
     49     resetMap();
     50   }
     51 
     52   @Override protected Collection<Map.Entry<K, V>> actualContents() {
     53     return getMap().entrySet();
     54   }
     55 
     56   /** @see AbstractContainerTester#resetContainer() */
     57   protected void resetMap() {
     58     resetContainer();
     59   }
     60 
     61   protected void expectMissingKeys(K... elements) {
     62     for (K element : elements) {
     63       assertFalse("Should not contain key " + element,
     64           getMap().containsKey(element));
     65     }
     66   }
     67 
     68   protected void expectMissingValues(V... elements) {
     69     for (V element : elements) {
     70       assertFalse("Should not contain value " + element,
     71           getMap().containsValue(element));
     72     }
     73   }
     74 
     75   /**
     76    * @return an array of the proper size with {@code null} as the key of the
     77    * middle element.
     78    */
     79   protected Map.Entry<K, V>[] createArrayWithNullKey() {
     80     Map.Entry<K, V>[] array = createSamplesArray();
     81     final int nullKeyLocation = getNullLocation();
     82     final Map.Entry<K, V> oldEntry = array[nullKeyLocation];
     83     array[nullKeyLocation] = entry(null, oldEntry.getValue());
     84     return array;
     85   }
     86 
     87   protected V getValueForNullKey() {
     88     return getEntryNullReplaces().getValue();
     89   }
     90 
     91   protected K getKeyForNullValue() {
     92     return getEntryNullReplaces().getKey();
     93   }
     94 
     95   private Entry<K, V> getEntryNullReplaces() {
     96     Iterator<Entry<K, V>> entries = getSampleElements().iterator();
     97     for (int i = 0; i < getNullLocation(); i++) {
     98       entries.next();
     99     }
    100     return entries.next();
    101   }
    102 
    103   /**
    104    * @return an array of the proper size with {@code null} as the value of the
    105    * middle element.
    106    */
    107   protected Map.Entry<K, V>[] createArrayWithNullValue() {
    108     Map.Entry<K, V>[] array = createSamplesArray();
    109     final int nullValueLocation = getNullLocation();
    110     final Map.Entry<K, V> oldEntry = array[nullValueLocation];
    111     array[nullValueLocation] = entry(oldEntry.getKey(), null);
    112     return array;
    113   }
    114 
    115   protected void initMapWithNullKey() {
    116     resetMap(createArrayWithNullKey());
    117   }
    118 
    119   protected void initMapWithNullValue() {
    120     resetMap(createArrayWithNullValue());
    121   }
    122 
    123   /**
    124    * Equivalent to {@link #expectMissingKeys(Object[]) expectMissingKeys}
    125    * {@code (null)}
    126    * except that the call to {@code contains(null)} is permitted to throw a
    127    * {@code NullPointerException}.
    128    * @param message message to use upon assertion failure
    129    */
    130   protected void expectNullKeyMissingWhenNullKeysUnsupported(String message) {
    131     try {
    132       assertFalse(message, getMap().containsKey(null));
    133     } catch (NullPointerException tolerated) {
    134       // Tolerated
    135     }
    136   }
    137 
    138   /**
    139    * Equivalent to {@link #expectMissingValues(Object[]) expectMissingValues}
    140    * {@code (null)}
    141    * except that the call to {@code contains(null)} is permitted to throw a
    142    * {@code NullPointerException}.
    143    * @param message message to use upon assertion failure
    144    */
    145   protected void expectNullValueMissingWhenNullValuesUnsupported(
    146       String message) {
    147     try {
    148       assertFalse(message, getMap().containsValue(null));
    149     } catch (NullPointerException tolerated) {
    150       // Tolerated
    151     }
    152   }
    153 
    154   @SuppressWarnings("unchecked")
    155   @Override protected MinimalCollection<Map.Entry<K, V>>
    156       createDisjointCollection() {
    157     return MinimalCollection.of(samples.e3, samples.e4);
    158   }
    159 
    160   protected int getNumEntries() {
    161     return getNumElements();
    162   }
    163 
    164   protected Collection<Map.Entry<K, V>> getSampleEntries(int howMany) {
    165     return getSampleElements(howMany);
    166   }
    167 
    168   protected Collection<Map.Entry<K, V>> getSampleEntries() {
    169     return getSampleElements();
    170   }
    171 
    172   @Override protected void expectMissing(Entry<K, V>... entries) {
    173     for (Entry<K, V> entry : entries) {
    174       assertFalse("Should not contain entry " + entry,
    175           actualContents().contains(entry));
    176       assertFalse("Should not contain key " + entry.getKey() + " mapped to"
    177           + " value " + entry.getValue(),
    178           equal(getMap().get(entry.getKey()), entry.getValue()));
    179     }
    180   }
    181 
    182   private static boolean equal(Object a, Object b) {
    183     return a == b || (a != null && a.equals(b));
    184   }
    185 
    186   // This one-liner saves us from some ugly casts
    187   protected Entry<K, V> entry(K key, V value) {
    188     return Helpers.mapEntry(key, value);
    189   }
    190 
    191   @Override protected void expectContents(Collection<Entry<K, V>> expected) {
    192     // TODO: move this to invariant checks once the appropriate hook exists?
    193     super.expectContents(expected);
    194     for (Entry<K, V> entry : expected) {
    195       assertEquals("Wrong value for key " + entry.getKey(),
    196           entry.getValue(), getMap().get(entry.getKey()));
    197     }
    198   }
    199 
    200   protected final void expectReplacement(Entry<K, V> newEntry) {
    201     List<Entry<K, V>> expected = Helpers.copyToList(getSampleElements());
    202     replaceValue(expected, newEntry);
    203     expectContents(expected);
    204   }
    205 
    206   private void replaceValue(List<Entry<K, V>> expected, Entry<K, V> newEntry) {
    207     for (ListIterator<Entry<K, V>> i = expected.listIterator(); i.hasNext();) {
    208       if (Helpers.equal(i.next().getKey(), newEntry.getKey())) {
    209         i.set(newEntry);
    210         return;
    211       }
    212     }
    213 
    214     throw new IllegalArgumentException(Platform.format(
    215         "key %s not found in entries %s", newEntry.getKey(), expected));
    216   }
    217 
    218   /**
    219    * Wrapper for {@link Map#get(Object)} that forces the caller to pass in a key
    220    * of the same type as the map. Besides being slightly shorter than code that
    221    * uses {@link #getMap()}, it also ensures that callers don't pass an
    222    * {@link Entry} by mistake.
    223    */
    224   protected V get(K key) {
    225     return getMap().get(key);
    226   }
    227 
    228   protected void resetMap(Entry<K, V>[] entries) {
    229     resetContainer(getSubjectGenerator().create((Object[]) entries));
    230   }
    231 }
    232