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 static com.google.common.collect.testing.Helpers.castOrCopyToList;
     20 import static com.google.common.collect.testing.Helpers.equal;
     21 import static com.google.common.collect.testing.Helpers.mapEntry;
     22 import static java.util.Collections.sort;
     23 
     24 import com.google.common.annotations.GwtCompatible;
     25 
     26 import java.util.ArrayList;
     27 import java.util.Arrays;
     28 import java.util.Collection;
     29 import java.util.Collections;
     30 import java.util.Comparator;
     31 import java.util.List;
     32 import java.util.Map;
     33 import java.util.Map.Entry;
     34 import java.util.Set;
     35 import java.util.SortedMap;
     36 import java.util.SortedSet;
     37 
     38 /**
     39  * Derived suite generators, split out of the suite builders so that they are available to GWT.
     40  *
     41  * @author George van den Driessche
     42  */
     43 @GwtCompatible
     44 public final class DerivedCollectionGenerators {
     45   public static class MapEntrySetGenerator<K, V>
     46       implements TestSetGenerator<Map.Entry<K, V>>, DerivedGenerator {
     47     private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
     48         mapGenerator;
     49 
     50     public MapEntrySetGenerator(
     51         OneSizeTestContainerGenerator<
     52             Map<K, V>, Map.Entry<K, V>> mapGenerator) {
     53       this.mapGenerator = mapGenerator;
     54     }
     55 
     56     @Override
     57     public SampleElements<Map.Entry<K, V>> samples() {
     58       return mapGenerator.samples();
     59     }
     60 
     61     @Override
     62     public Set<Map.Entry<K, V>> create(Object... elements) {
     63       return mapGenerator.create(elements).entrySet();
     64     }
     65 
     66     @Override
     67     public Map.Entry<K, V>[] createArray(int length) {
     68       return mapGenerator.createArray(length);
     69     }
     70 
     71     @Override
     72     public Iterable<Map.Entry<K, V>> order(
     73         List<Map.Entry<K, V>> insertionOrder) {
     74       return mapGenerator.order(insertionOrder);
     75     }
     76 
     77     @Override
     78     public OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> getInnerGenerator() {
     79       return mapGenerator;
     80     }
     81   }
     82 
     83   // TODO: investigate some API changes to SampleElements that would tidy up
     84   // parts of the following classes.
     85 
     86   static <K, V> TestSetGenerator<K> keySetGenerator(
     87       OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> mapGenerator) {
     88     TestContainerGenerator<Map<K, V>, Entry<K, V>> generator = mapGenerator.getInnerGenerator();
     89     if (generator instanceof TestSortedMapGenerator
     90         && ((TestSortedMapGenerator<K, V>) generator).create().keySet() instanceof SortedSet) {
     91       return new MapSortedKeySetGenerator<K, V>(mapGenerator);
     92     } else {
     93       return new MapKeySetGenerator<K, V>(mapGenerator);
     94     }
     95   }
     96 
     97   public static class MapKeySetGenerator<K, V>
     98       implements TestSetGenerator<K>, DerivedGenerator {
     99     private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
    100         mapGenerator;
    101     private final SampleElements<K> samples;
    102 
    103     public MapKeySetGenerator(
    104         OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
    105             mapGenerator) {
    106       this.mapGenerator = mapGenerator;
    107       final SampleElements<Map.Entry<K, V>> mapSamples =
    108           this.mapGenerator.samples();
    109       this.samples = new SampleElements<K>(
    110           mapSamples.e0.getKey(),
    111           mapSamples.e1.getKey(),
    112           mapSamples.e2.getKey(),
    113           mapSamples.e3.getKey(),
    114           mapSamples.e4.getKey());
    115     }
    116 
    117     @Override
    118     public SampleElements<K> samples() {
    119       return samples;
    120     }
    121 
    122     @Override
    123     public Set<K> create(Object... elements) {
    124       @SuppressWarnings("unchecked")
    125       K[] keysArray = (K[]) elements;
    126 
    127       // Start with a suitably shaped collection of entries
    128       Collection<Map.Entry<K, V>> originalEntries =
    129           mapGenerator.getSampleElements(elements.length);
    130 
    131       // Create a copy of that, with the desired value for each key
    132       Collection<Map.Entry<K, V>> entries =
    133           new ArrayList<Entry<K, V>>(elements.length);
    134       int i = 0;
    135       for (Map.Entry<K, V> entry : originalEntries) {
    136         entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue()));
    137       }
    138 
    139       return mapGenerator.create(entries.toArray()).keySet();
    140     }
    141 
    142     @Override
    143     public K[] createArray(int length) {
    144       // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps
    145       // tidy this up and get rid of the casts here and in
    146       // MapValueCollectionGenerator.
    147 
    148       return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
    149           .createKeyArray(length);
    150     }
    151 
    152     @Override
    153     public Iterable<K> order(List<K> insertionOrder) {
    154       V v = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).samples().e0.getValue();
    155       List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>();
    156       for (K element : insertionOrder) {
    157         entries.add(mapEntry(element, v));
    158       }
    159 
    160       List<K> keys = new ArrayList<K>();
    161       for (Entry<K, V> entry : mapGenerator.order(entries)) {
    162         keys.add(entry.getKey());
    163       }
    164       return keys;
    165     }
    166 
    167     @Override
    168     public OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> getInnerGenerator() {
    169       return mapGenerator;
    170     }
    171   }
    172 
    173   public static class MapSortedKeySetGenerator<K, V> extends MapKeySetGenerator<K, V>
    174       implements TestSortedSetGenerator<K>, DerivedGenerator {
    175     private final TestSortedMapGenerator<K, V> delegate;
    176 
    177     public MapSortedKeySetGenerator(
    178         OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) {
    179       super(mapGenerator);
    180       this.delegate = (TestSortedMapGenerator<K, V>) mapGenerator.getInnerGenerator();
    181     }
    182 
    183     @Override
    184     public SortedSet<K> create(Object... elements) {
    185       return (SortedSet<K>) super.create(elements);
    186     }
    187 
    188     @Override
    189     public K belowSamplesLesser() {
    190       return delegate.belowSamplesLesser().getKey();
    191     }
    192 
    193     @Override
    194     public K belowSamplesGreater() {
    195       return delegate.belowSamplesGreater().getKey();
    196     }
    197 
    198     @Override
    199     public K aboveSamplesLesser() {
    200       return delegate.aboveSamplesLesser().getKey();
    201     }
    202 
    203     @Override
    204     public K aboveSamplesGreater() {
    205       return delegate.aboveSamplesGreater().getKey();
    206     }
    207 
    208   }
    209 
    210   public static class MapValueCollectionGenerator<K, V>
    211       implements TestCollectionGenerator<V>, DerivedGenerator {
    212     private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
    213         mapGenerator;
    214     private final SampleElements<V> samples;
    215 
    216     public MapValueCollectionGenerator(
    217         OneSizeTestContainerGenerator<
    218             Map<K, V>, Map.Entry<K, V>> mapGenerator) {
    219       this.mapGenerator = mapGenerator;
    220       final SampleElements<Map.Entry<K, V>> mapSamples =
    221           this.mapGenerator.samples();
    222       this.samples = new SampleElements<V>(
    223           mapSamples.e0.getValue(),
    224           mapSamples.e1.getValue(),
    225           mapSamples.e2.getValue(),
    226           mapSamples.e3.getValue(),
    227           mapSamples.e4.getValue());
    228     }
    229 
    230     @Override
    231     public SampleElements<V> samples() {
    232       return samples;
    233     }
    234 
    235     @Override
    236     public Collection<V> create(Object... elements) {
    237       @SuppressWarnings("unchecked")
    238       V[] valuesArray = (V[]) elements;
    239 
    240       // Start with a suitably shaped collection of entries
    241       Collection<Map.Entry<K, V>> originalEntries =
    242           mapGenerator.getSampleElements(elements.length);
    243 
    244       // Create a copy of that, with the desired value for each value
    245       Collection<Map.Entry<K, V>> entries =
    246           new ArrayList<Entry<K, V>>(elements.length);
    247       int i = 0;
    248       for (Map.Entry<K, V> entry : originalEntries) {
    249         entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++]));
    250       }
    251 
    252       return mapGenerator.create(entries.toArray()).values();
    253     }
    254 
    255     @Override
    256     public V[] createArray(int length) {
    257       //noinspection UnnecessaryLocalVariable
    258       final V[] vs = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
    259           .createValueArray(length);
    260       return vs;
    261     }
    262 
    263     @Override
    264     public Iterable<V> order(List<V> insertionOrder) {
    265       final List<Entry<K, V>> orderedEntries =
    266           castOrCopyToList(mapGenerator.order(castOrCopyToList(
    267               mapGenerator.getSampleElements(5))));
    268       sort(insertionOrder, new Comparator<V>() {
    269         @Override public int compare(V left, V right) {
    270           // The indexes are small enough for the subtraction trick to be safe.
    271           return indexOfEntryWithValue(left) - indexOfEntryWithValue(right);
    272         }
    273 
    274         int indexOfEntryWithValue(V value) {
    275           for (int i = 0; i < orderedEntries.size(); i++) {
    276             if (equal(orderedEntries.get(i).getValue(), value)) {
    277               return i;
    278             }
    279           }
    280           throw new IllegalArgumentException("Map.values generator can order only sample values");
    281         }
    282       });
    283       return insertionOrder;
    284     }
    285 
    286     @Override
    287     public OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> getInnerGenerator() {
    288       return mapGenerator;
    289     }
    290   }
    291 
    292   // TODO(cpovirk): could something like this be used elsewhere, e.g., ReserializedListGenerator?
    293   static class ForwardingTestMapGenerator<K, V> implements TestMapGenerator<K, V> {
    294     TestMapGenerator<K, V> delegate;
    295 
    296     ForwardingTestMapGenerator(TestMapGenerator<K, V> delegate) {
    297       this.delegate = delegate;
    298     }
    299 
    300     @Override
    301     public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) {
    302       return delegate.order(insertionOrder);
    303     }
    304 
    305     @Override
    306     public K[] createKeyArray(int length) {
    307       return delegate.createKeyArray(length);
    308     }
    309 
    310     @Override
    311     public V[] createValueArray(int length) {
    312       return delegate.createValueArray(length);
    313     }
    314 
    315     @Override
    316     public SampleElements<Entry<K, V>> samples() {
    317       return delegate.samples();
    318     }
    319 
    320     @Override
    321     public Map<K, V> create(Object... elements) {
    322       return delegate.create(elements);
    323     }
    324 
    325     @Override
    326     public Entry<K, V>[] createArray(int length) {
    327       return delegate.createArray(length);
    328     }
    329   }
    330 
    331   /**
    332    * Two bounds (from and to) define how to build a subMap.
    333    */
    334   public enum Bound {
    335     INCLUSIVE,
    336     EXCLUSIVE,
    337     NO_BOUND;
    338   }
    339 
    340   public static class SortedSetSubsetTestSetGenerator<E>
    341       implements TestSortedSetGenerator<E> {
    342     final Bound to;
    343     final Bound from;
    344     final E firstInclusive;
    345     final E lastInclusive;
    346     private final Comparator<? super E> comparator;
    347     private final TestSortedSetGenerator<E> delegate;
    348 
    349     public SortedSetSubsetTestSetGenerator(
    350         TestSortedSetGenerator<E> delegate, Bound to, Bound from) {
    351       this.to = to;
    352       this.from = from;
    353       this.delegate = delegate;
    354 
    355       SortedSet<E> emptySet = delegate.create();
    356       this.comparator = emptySet.comparator();
    357 
    358       SampleElements<E> samples = delegate.samples();
    359       List<E> samplesList = new ArrayList<E>(samples.asList());
    360       Collections.sort(samplesList, comparator);
    361       this.firstInclusive = samplesList.get(0);
    362       this.lastInclusive = samplesList.get(samplesList.size() - 1);
    363     }
    364 
    365     public final TestSortedSetGenerator<E> getInnerGenerator() {
    366       return delegate;
    367     }
    368 
    369     public final Bound getTo() {
    370       return to;
    371     }
    372 
    373     public final Bound getFrom() {
    374       return from;
    375     }
    376 
    377     @Override
    378     public SampleElements<E> samples() {
    379       return delegate.samples();
    380     }
    381 
    382     @Override
    383     public E[] createArray(int length) {
    384       return delegate.createArray(length);
    385     }
    386 
    387     @Override
    388     public Iterable<E> order(List<E> insertionOrder) {
    389       return delegate.order(insertionOrder);
    390     }
    391 
    392     @Override
    393     public SortedSet<E> create(Object... elements) {
    394       @SuppressWarnings("unchecked") // set generators must pass SampleElements values
    395       List<E> normalValues = (List) Arrays.asList(elements);
    396       List<E> extremeValues = new ArrayList<E>();
    397 
    398       // nulls are usually out of bounds for a subset, so ban them altogether
    399       for (Object o : elements) {
    400         if (o == null) {
    401           throw new NullPointerException();
    402         }
    403       }
    404 
    405       // prepare extreme values to be filtered out of view
    406       E firstExclusive = delegate.belowSamplesGreater();
    407       E lastExclusive = delegate.aboveSamplesLesser();
    408       if (from != Bound.NO_BOUND) {
    409         extremeValues.add(delegate.belowSamplesLesser());
    410         extremeValues.add(delegate.belowSamplesGreater());
    411       }
    412       if (to != Bound.NO_BOUND) {
    413         extremeValues.add(delegate.aboveSamplesLesser());
    414         extremeValues.add(delegate.aboveSamplesGreater());
    415       }
    416 
    417       // the regular values should be visible after filtering
    418       List<E> allEntries = new ArrayList<E>();
    419       allEntries.addAll(extremeValues);
    420       allEntries.addAll(normalValues);
    421       SortedSet<E> map = delegate.create(allEntries.toArray());
    422 
    423       return createSubSet(map, firstExclusive, lastExclusive);
    424     }
    425 
    426     /**
    427      * Calls the smallest subSet overload that filters out the extreme values.
    428      */
    429     SortedSet<E> createSubSet(SortedSet<E> set, E firstExclusive, E lastExclusive) {
    430       if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) {
    431         return set.headSet(lastExclusive);
    432       } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) {
    433         return set.tailSet(firstInclusive);
    434       } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) {
    435         return set.subSet(firstInclusive, lastExclusive);
    436       } else {
    437         throw new IllegalArgumentException();
    438       }
    439     }
    440 
    441     @Override
    442     public E belowSamplesLesser() {
    443       throw new UnsupportedOperationException();
    444     }
    445 
    446     @Override
    447     public E belowSamplesGreater() {
    448       throw new UnsupportedOperationException();
    449     }
    450 
    451     @Override
    452     public E aboveSamplesLesser() {
    453       throw new UnsupportedOperationException();
    454     }
    455 
    456     @Override
    457     public E aboveSamplesGreater() {
    458       throw new UnsupportedOperationException();
    459     }
    460   }
    461 
    462   /*
    463    * TODO(cpovirk): surely we can find a less ugly solution than a class that accepts 3 parameters,
    464    * exposes as many getters, does work in the constructor, and has both a superclass and a subclass
    465    */
    466   public static class SortedMapSubmapTestMapGenerator<K, V>
    467       extends ForwardingTestMapGenerator<K, V> implements TestSortedMapGenerator<K, V> {
    468     final Bound to;
    469     final Bound from;
    470     final K firstInclusive;
    471     final K lastInclusive;
    472     private final Comparator<Entry<K, V>> entryComparator;
    473 
    474     public SortedMapSubmapTestMapGenerator(
    475         TestSortedMapGenerator<K, V> delegate, Bound to, Bound from) {
    476       super(delegate);
    477       this.to = to;
    478       this.from = from;
    479 
    480       SortedMap<K, V> emptyMap = delegate.create();
    481       this.entryComparator = Helpers.entryComparator(emptyMap.comparator());
    482 
    483       // derive values for inclusive filtering from the input samples
    484       SampleElements<Entry<K, V>> samples = delegate.samples();
    485       @SuppressWarnings("unchecked") // no elements are inserted into the array
    486       List<Entry<K, V>> samplesList = Arrays.asList(
    487           samples.e0, samples.e1, samples.e2, samples.e3, samples.e4);
    488       Collections.sort(samplesList, entryComparator);
    489       this.firstInclusive = samplesList.get(0).getKey();
    490       this.lastInclusive = samplesList.get(samplesList.size() - 1).getKey();
    491     }
    492 
    493     @Override public SortedMap<K, V> create(Object... entries) {
    494       @SuppressWarnings("unchecked") // map generators must past entry objects
    495       List<Entry<K, V>> normalValues = (List) Arrays.asList(entries);
    496       List<Entry<K, V>> extremeValues = new ArrayList<Entry<K, V>>();
    497 
    498       // prepare extreme values to be filtered out of view
    499       K firstExclusive = getInnerGenerator().belowSamplesGreater().getKey();
    500       K lastExclusive = getInnerGenerator().aboveSamplesLesser().getKey();
    501       if (from != Bound.NO_BOUND) {
    502         extremeValues.add(getInnerGenerator().belowSamplesLesser());
    503         extremeValues.add(getInnerGenerator().belowSamplesGreater());
    504       }
    505       if (to != Bound.NO_BOUND) {
    506         extremeValues.add(getInnerGenerator().aboveSamplesLesser());
    507         extremeValues.add(getInnerGenerator().aboveSamplesGreater());
    508       }
    509 
    510       // the regular values should be visible after filtering
    511       List<Entry<K, V>> allEntries = new ArrayList<Entry<K, V>>();
    512       allEntries.addAll(extremeValues);
    513       allEntries.addAll(normalValues);
    514       SortedMap<K, V> map = (SortedMap<K, V>)
    515           delegate.create((Object[])
    516               allEntries.toArray(new Entry<?, ?>[allEntries.size()]));
    517 
    518       return createSubMap(map, firstExclusive, lastExclusive);
    519     }
    520 
    521     /**
    522      * Calls the smallest subMap overload that filters out the extreme values. This method is
    523      * overridden in NavigableMapTestSuiteBuilder.
    524      */
    525     SortedMap<K, V> createSubMap(SortedMap<K, V> map, K firstExclusive, K lastExclusive) {
    526       if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) {
    527         return map.headMap(lastExclusive);
    528       } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) {
    529         return map.tailMap(firstInclusive);
    530       } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) {
    531         return map.subMap(firstInclusive, lastExclusive);
    532       } else {
    533         throw new IllegalArgumentException();
    534       }
    535     }
    536 
    537     public final Bound getTo() {
    538       return to;
    539     }
    540 
    541     public final Bound getFrom() {
    542       return from;
    543     }
    544 
    545     public final TestSortedMapGenerator<K, V> getInnerGenerator() {
    546       return (TestSortedMapGenerator<K, V>) delegate;
    547     }
    548 
    549     @Override
    550     public Entry<K, V> belowSamplesLesser() {
    551       // should never reach here!
    552       throw new UnsupportedOperationException();
    553     }
    554 
    555     @Override
    556     public Entry<K, V> belowSamplesGreater() {
    557       // should never reach here!
    558       throw new UnsupportedOperationException();
    559     }
    560 
    561     @Override
    562     public Entry<K, V> aboveSamplesLesser() {
    563       // should never reach here!
    564       throw new UnsupportedOperationException();
    565     }
    566 
    567     @Override
    568     public Entry<K, V> aboveSamplesGreater() {
    569       // should never reach here!
    570       throw new UnsupportedOperationException();
    571     }
    572   }
    573 
    574   private DerivedCollectionGenerators() {}
    575 }
    576