Home | History | Annotate | Download | only in testing
      1 /*
      2  * Copyright (C) 2008 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.collect.testing.features.CollectionFeature;
     20 import com.google.common.collect.testing.features.CollectionSize;
     21 import com.google.common.collect.testing.features.Feature;
     22 import com.google.common.collect.testing.features.MapFeature;
     23 import com.google.common.collect.testing.testers.MapClearTester;
     24 import com.google.common.collect.testing.testers.MapContainsKeyTester;
     25 import com.google.common.collect.testing.testers.MapContainsValueTester;
     26 import com.google.common.collect.testing.testers.MapCreationTester;
     27 import com.google.common.collect.testing.testers.MapEqualsTester;
     28 import com.google.common.collect.testing.testers.MapGetTester;
     29 import com.google.common.collect.testing.testers.MapHashCodeTester;
     30 import com.google.common.collect.testing.testers.MapIsEmptyTester;
     31 import com.google.common.collect.testing.testers.MapPutAllTester;
     32 import com.google.common.collect.testing.testers.MapPutTester;
     33 import com.google.common.collect.testing.testers.MapRemoveTester;
     34 import com.google.common.collect.testing.testers.MapSizeTester;
     35 
     36 import junit.framework.TestSuite;
     37 
     38 import java.util.ArrayList;
     39 import java.util.Arrays;
     40 import java.util.Collection;
     41 import java.util.HashSet;
     42 import java.util.List;
     43 import java.util.Map;
     44 import java.util.Map.Entry;
     45 import java.util.Set;
     46 
     47 /**
     48  * Creates, based on your criteria, a JUnit test suite that exhaustively tests
     49  * a Map implementation.
     50  *
     51  * @author George van den Driessche
     52  */
     53 public class MapTestSuiteBuilder<K, V>
     54     extends PerCollectionSizeTestSuiteBuilder<
     55         MapTestSuiteBuilder<K, V>,
     56         TestMapGenerator<K, V>, Map<K, V>, Map.Entry<K, V>> {
     57   public static <K, V> MapTestSuiteBuilder<K, V> using(
     58       TestMapGenerator<K, V> generator) {
     59     return new MapTestSuiteBuilder<K, V>().usingGenerator(generator);
     60   }
     61 
     62   @SuppressWarnings("unchecked") // Class parameters must be raw.
     63   @Override protected List<Class<? extends AbstractTester>> getTesters() {
     64     return Arrays.<Class<? extends AbstractTester>>asList(
     65         MapClearTester.class,
     66         MapContainsKeyTester.class,
     67         MapContainsValueTester.class,
     68         MapCreationTester.class,
     69         MapEqualsTester.class,
     70         MapGetTester.class,
     71         MapHashCodeTester.class,
     72         MapIsEmptyTester.class,
     73         MapPutTester.class,
     74         MapPutAllTester.class,
     75         MapRemoveTester.class,
     76         MapSizeTester.class
     77     );
     78   }
     79 
     80   @Override List<TestSuite> createDerivedSuites(
     81       FeatureSpecificTestSuiteBuilder<
     82           ?,
     83           ? extends OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>>
     84       parentBuilder) {
     85     // TODO: Once invariant support is added, supply invariants to each of the
     86     // derived suites, to check that mutations to the derived collections are
     87     // reflected in the underlying map.
     88 
     89     List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder);
     90 
     91     derivedSuites.add(SetTestSuiteBuilder.using(
     92             new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
     93         .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures()))
     94         .named(parentBuilder.getName() + " entrySet")
     95         .suppressing(parentBuilder.getSuppressedTests())
     96         .createTestSuite());
     97 
     98     derivedSuites.add(createDerivedKeySetSuite(
     99             new MapKeySetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
    100         .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures()))
    101         .named(parentBuilder.getName() + " keys")
    102         .suppressing(parentBuilder.getSuppressedTests())
    103         .createTestSuite());
    104 
    105     derivedSuites.add(CollectionTestSuiteBuilder.using(
    106             new MapValueCollectionGenerator<K, V>(
    107                 parentBuilder.getSubjectGenerator()))
    108         .named(parentBuilder.getName() + " values")
    109         .withFeatures(computeValuesCollectionFeatures(
    110             parentBuilder.getFeatures()))
    111         .suppressing(parentBuilder.getSuppressedTests())
    112         .createTestSuite());
    113 
    114     return derivedSuites;
    115   }
    116 
    117   protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) {
    118     return SetTestSuiteBuilder.using(keySetGenerator);
    119   }
    120 
    121   private static Set<Feature<?>> computeEntrySetFeatures(
    122       Set<Feature<?>> mapFeatures) {
    123     Set<Feature<?>> entrySetFeatures =
    124         computeCommonDerivedCollectionFeatures(mapFeatures);
    125     entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
    126     return entrySetFeatures;
    127   }
    128 
    129   private static Set<Feature<?>> computeKeySetFeatures(
    130       Set<Feature<?>> mapFeatures) {
    131     Set<Feature<?>> keySetFeatures =
    132         computeCommonDerivedCollectionFeatures(mapFeatures);
    133 
    134     if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) {
    135       keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
    136     } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_QUERIES)) {
    137       keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
    138     }
    139 
    140     return keySetFeatures;
    141   }
    142 
    143   private static Set<Feature<?>> computeValuesCollectionFeatures(
    144       Set<Feature<?>> mapFeatures) {
    145     Set<Feature<?>> valuesCollectionFeatures =
    146         computeCommonDerivedCollectionFeatures(mapFeatures);
    147     valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
    148 
    149     if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
    150       valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
    151     }
    152 
    153     return valuesCollectionFeatures;
    154   }
    155 
    156   private static Set<Feature<?>> computeCommonDerivedCollectionFeatures(
    157       Set<Feature<?>> mapFeatures) {
    158     Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>();
    159     if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) {
    160       derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE);
    161       derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE_ALL);
    162       derivedFeatures.add(CollectionFeature.SUPPORTS_RETAIN_ALL);
    163     }
    164     if (mapFeatures.contains(MapFeature.SUPPORTS_CLEAR)) {
    165       derivedFeatures.add(CollectionFeature.SUPPORTS_CLEAR);
    166     }
    167     if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) {
    168       derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
    169     }
    170     // add the intersection of CollectionSize.values() and mapFeatures
    171     for (CollectionSize size : CollectionSize.values()) {
    172       if (mapFeatures.contains(size)) {
    173         derivedFeatures.add(size);
    174       }
    175     }
    176     return derivedFeatures;
    177   }
    178 
    179   private static class MapEntrySetGenerator<K, V>
    180       implements TestSetGenerator<Map.Entry<K, V>> {
    181     private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
    182         mapGenerator;
    183 
    184     public MapEntrySetGenerator(
    185         OneSizeTestContainerGenerator<
    186             Map<K, V>, Map.Entry<K, V>> mapGenerator) {
    187       this.mapGenerator = mapGenerator;
    188     }
    189 
    190     @Override
    191     public SampleElements<Map.Entry<K, V>> samples() {
    192       return mapGenerator.samples();
    193     }
    194 
    195     @Override
    196     public Set<Map.Entry<K, V>> create(Object... elements) {
    197       return mapGenerator.create(elements).entrySet();
    198     }
    199 
    200     @Override
    201     public Map.Entry<K, V>[] createArray(int length) {
    202       return mapGenerator.createArray(length);
    203     }
    204 
    205     @Override
    206     public Iterable<Map.Entry<K, V>> order(
    207         List<Map.Entry<K, V>> insertionOrder) {
    208       return mapGenerator.order(insertionOrder);
    209     }
    210   }
    211 
    212   // TODO: investigate some API changes to SampleElements that would tidy up
    213   // parts of the following classes.
    214 
    215   private static class MapKeySetGenerator<K, V> implements TestSetGenerator<K> {
    216     private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
    217         mapGenerator;
    218     private final SampleElements<K> samples;
    219 
    220     public MapKeySetGenerator(
    221         OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
    222             mapGenerator) {
    223       this.mapGenerator = mapGenerator;
    224       final SampleElements<Map.Entry<K, V>> mapSamples =
    225           this.mapGenerator.samples();
    226       this.samples = new SampleElements<K>(
    227           mapSamples.e0.getKey(),
    228           mapSamples.e1.getKey(),
    229           mapSamples.e2.getKey(),
    230           mapSamples.e3.getKey(),
    231           mapSamples.e4.getKey());
    232     }
    233 
    234     @Override
    235     public SampleElements<K> samples() {
    236       return samples;
    237     }
    238 
    239     @Override
    240     public Set<K> create(Object... elements) {
    241       @SuppressWarnings("unchecked")
    242       K[] keysArray = (K[]) elements;
    243 
    244       // Start with a suitably shaped collection of entries
    245       Collection<Map.Entry<K, V>> originalEntries =
    246           mapGenerator.getSampleElements(elements.length);
    247 
    248       // Create a copy of that, with the desired value for each key
    249       Collection<Map.Entry<K, V>> entries =
    250           new ArrayList<Entry<K, V>>(elements.length);
    251       int i = 0;
    252       for (Map.Entry<K, V> entry : originalEntries) {
    253         entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue()));
    254       }
    255 
    256       return mapGenerator.create(entries.toArray()).keySet();
    257     }
    258 
    259     @Override
    260     public K[] createArray(int length) {
    261       // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps
    262       // tidy this up and get rid of the casts here and in
    263       // MapValueCollectionGenerator.
    264 
    265       return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
    266           .createKeyArray(length);
    267     }
    268 
    269     @Override
    270     public Iterable<K> order(List<K> insertionOrder) {
    271       return insertionOrder;
    272     }
    273   }
    274 
    275   private static class MapValueCollectionGenerator<K, V>
    276       implements TestCollectionGenerator<V> {
    277     private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
    278         mapGenerator;
    279     private final SampleElements<V> samples;
    280 
    281     public MapValueCollectionGenerator(
    282         OneSizeTestContainerGenerator<
    283             Map<K, V>, Map.Entry<K, V>> mapGenerator) {
    284       this.mapGenerator = mapGenerator;
    285       final SampleElements<Map.Entry<K, V>> mapSamples =
    286           this.mapGenerator.samples();
    287       this.samples = new SampleElements<V>(
    288           mapSamples.e0.getValue(),
    289           mapSamples.e1.getValue(),
    290           mapSamples.e2.getValue(),
    291           mapSamples.e3.getValue(),
    292           mapSamples.e4.getValue());
    293     }
    294 
    295     @Override
    296     public SampleElements<V> samples() {
    297       return samples;
    298     }
    299 
    300     @Override
    301     public Collection<V> create(Object... elements) {
    302       @SuppressWarnings("unchecked")
    303       V[] valuesArray = (V[]) elements;
    304 
    305       // Start with a suitably shaped collection of entries
    306       Collection<Map.Entry<K, V>> originalEntries =
    307           mapGenerator.getSampleElements(elements.length);
    308 
    309       // Create a copy of that, with the desired value for each value
    310       Collection<Map.Entry<K, V>> entries =
    311           new ArrayList<Entry<K, V>>(elements.length);
    312       int i = 0;
    313       for (Map.Entry<K, V> entry : originalEntries) {
    314         entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++]));
    315       }
    316 
    317       return mapGenerator.create(entries.toArray()).values();
    318     }
    319 
    320     @Override
    321     public V[] createArray(int length) {
    322       //noinspection UnnecessaryLocalVariable
    323       final V[] vs = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
    324           .createValueArray(length);
    325       return vs;
    326     }
    327 
    328     @Override
    329     public Iterable<V> order(List<V> insertionOrder) {
    330       return insertionOrder;
    331     }
    332   }
    333 }
    334