Home | History | Annotate | Download | only in collect
      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;
     18 
     19 import static com.google.common.collect.Maps.newHashMap;
     20 import static com.google.common.collect.testing.Helpers.mapEntry;
     21 import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES;
     22 import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE;
     23 import static com.google.common.collect.testing.google.AbstractMultisetSetCountTester.getSetCountDuplicateInitializingMethods;
     24 import static com.google.common.collect.testing.google.MultisetCountTester.getCountDuplicateInitializingMethods;
     25 import static com.google.common.collect.testing.google.MultisetIteratorTester.getIteratorDuplicateInitializingMethods;
     26 import static com.google.common.collect.testing.google.MultisetRemoveTester.getRemoveDuplicateInitializingMethods;
     27 import static java.lang.reflect.Proxy.newProxyInstance;
     28 
     29 import com.google.common.annotations.GwtIncompatible;
     30 import com.google.common.base.Ascii;
     31 import com.google.common.base.Function;
     32 import com.google.common.base.Predicate;
     33 import com.google.common.base.Predicates;
     34 import com.google.common.base.Supplier;
     35 import com.google.common.collect.Maps.EntryTransformer;
     36 import com.google.common.collect.testing.SampleElements;
     37 import com.google.common.collect.testing.SetTestSuiteBuilder;
     38 import com.google.common.collect.testing.TestCollectionGenerator;
     39 import com.google.common.collect.testing.TestListGenerator;
     40 import com.google.common.collect.testing.TestStringSetGenerator;
     41 import com.google.common.collect.testing.features.CollectionFeature;
     42 import com.google.common.collect.testing.features.CollectionSize;
     43 import com.google.common.collect.testing.features.Feature;
     44 import com.google.common.collect.testing.features.MapFeature;
     45 import com.google.common.collect.testing.google.ListMultimapTestSuiteBuilder;
     46 import com.google.common.collect.testing.google.MultimapFeature;
     47 import com.google.common.collect.testing.google.MultimapTestSuiteBuilder;
     48 import com.google.common.collect.testing.google.MultisetTestSuiteBuilder;
     49 import com.google.common.collect.testing.google.SetMultimapTestSuiteBuilder;
     50 import com.google.common.collect.testing.google.TestListMultimapGenerator;
     51 import com.google.common.collect.testing.google.TestMultimapGenerator;
     52 import com.google.common.collect.testing.google.TestSetMultimapGenerator;
     53 import com.google.common.collect.testing.google.TestStringListMultimapGenerator;
     54 import com.google.common.collect.testing.google.TestStringMultisetGenerator;
     55 
     56 import junit.framework.Test;
     57 import junit.framework.TestCase;
     58 import junit.framework.TestSuite;
     59 
     60 import java.lang.reflect.InvocationHandler;
     61 import java.lang.reflect.Method;
     62 import java.util.Collection;
     63 import java.util.List;
     64 import java.util.Map;
     65 import java.util.Map.Entry;
     66 import java.util.Set;
     67 import java.util.TreeSet;
     68 
     69 /**
     70  * Run collection tests on wrappers from {@link Multimaps}.
     71  *
     72  * @author Jared Levy
     73  */
     74 @GwtIncompatible("suite") // TODO(cpovirk): set up collect/gwt/suites version
     75 public class MultimapsCollectionTest extends TestCase {
     76 
     77   private static final Feature<?>[] FOR_MAP_FEATURES_ONE = {
     78     CollectionSize.ONE,
     79     ALLOWS_NULL_VALUES,
     80     SUPPORTS_REMOVE,
     81     CollectionFeature.SUPPORTS_ITERATOR_REMOVE
     82   };
     83 
     84   private static final Feature<?>[] FOR_MAP_FEATURES_ANY = {
     85     CollectionSize.ANY,
     86     ALLOWS_NULL_VALUES,
     87     SUPPORTS_REMOVE,
     88     CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
     89     MultisetTestSuiteBuilder.NoRecurse.NO_ENTRY_SET,  // Cannot create entries with count > 1
     90   };
     91 
     92   static final Supplier<TreeSet<String>> STRING_TREESET_FACTORY = new Supplier<TreeSet<String>>() {
     93     @Override
     94     public TreeSet<String> get() {
     95       return new TreeSet<String>(Ordering.natural().nullsLast());
     96     }
     97   };
     98 
     99   static void populateMultimapForGet(
    100       Multimap<Integer, String> multimap, String[] elements) {
    101     multimap.put(2, "foo");
    102     for (String element : elements) {
    103       multimap.put(3, element);
    104     }
    105   }
    106 
    107   static void populateMultimapForKeySet(
    108       Multimap<String, Integer> multimap, String[] elements) {
    109     for (String element : elements) {
    110       multimap.put(element, 2);
    111       multimap.put(element, 3);
    112     }
    113   }
    114 
    115   static void populateMultimapForValues(
    116       Multimap<Integer, String> multimap, String[] elements) {
    117     for (int i = 0; i < elements.length; i++) {
    118       multimap.put(i % 2, elements[i]);
    119     }
    120   }
    121 
    122   static void populateMultimapForKeys(
    123       Multimap<String, Integer> multimap, String[] elements) {
    124     for (int i = 0; i < elements.length; i++) {
    125       multimap.put(elements[i], i);
    126     }
    127   }
    128 
    129   /**
    130    * Implements {@code Multimap.put()} -- and no other methods -- for a {@code
    131    * Map} by ignoring all but the latest value for each key. This class exists
    132    * only so that we can use
    133    * {@link MultimapsCollectionTest#populateMultimapForGet(Multimap, String[])}
    134    * and similar methods to populate a map to be passed to
    135    * {@link Multimaps#forMap(Map)}. All tests should run against the result of
    136    * {@link #build()}.
    137    */
    138   private static final class PopulatableMapAsMultimap<K, V>
    139       extends ForwardingMultimap<K, V> {
    140     final Map<K, V> map;
    141     final SetMultimap<K, V> unusableDelegate;
    142 
    143     static <K, V> PopulatableMapAsMultimap<K, V> create() {
    144       return new PopulatableMapAsMultimap<K, V>();
    145     }
    146 
    147     @SuppressWarnings("unchecked")  // all methods throw immediately
    148     PopulatableMapAsMultimap() {
    149       this.map = newHashMap();
    150       this.unusableDelegate = (SetMultimap<K, V>) newProxyInstance(
    151           SetMultimap.class.getClassLoader(),
    152           new Class<?>[] {SetMultimap.class},
    153           new InvocationHandler() {
    154             @Override
    155             public Object invoke(Object proxy, Method method, Object[] args)
    156                 throws Throwable {
    157               throw new UnsupportedOperationException();
    158             }
    159           });
    160     }
    161 
    162     @Override protected Multimap<K, V> delegate() {
    163       return unusableDelegate;
    164     }
    165 
    166     @Override public boolean put(K key, V value) {
    167       map.put(key, value);
    168       return true;
    169     }
    170 
    171     SetMultimap<K, V> build() {
    172       return Multimaps.forMap(map);
    173     }
    174   }
    175 
    176   abstract static class TestEntriesGenerator
    177       implements TestCollectionGenerator<Entry<String, Integer>> {
    178     @Override
    179     public SampleElements<Entry<String, Integer>> samples() {
    180       return new SampleElements<Entry<String, Integer>>(
    181           Maps.immutableEntry("bar", 1),
    182           Maps.immutableEntry("bar", 2),
    183           Maps.immutableEntry("foo", 3),
    184           Maps.immutableEntry("bar", 3),
    185           Maps.immutableEntry("cat", 2));
    186     }
    187 
    188     @Override
    189     public Collection<Entry<String, Integer>> create(Object... elements) {
    190       Multimap<String, Integer> multimap = createMultimap();
    191       for (Object element : elements) {
    192         @SuppressWarnings("unchecked")
    193         Entry<String, Integer> entry = (Entry<String, Integer>) element;
    194         multimap.put(entry.getKey(), entry.getValue());
    195       }
    196       return multimap.entries();
    197     }
    198 
    199     abstract Multimap<String, Integer> createMultimap();
    200 
    201     @Override
    202     @SuppressWarnings("unchecked")
    203     public Entry<String, Integer>[] createArray(int length) {
    204       return (Entry<String, Integer>[]) new Entry<?, ?>[length];
    205     }
    206 
    207     @Override
    208     public List<Entry<String, Integer>> order(
    209         List<Entry<String, Integer>> insertionOrder) {
    210       return insertionOrder;
    211     }
    212   }
    213 
    214   public abstract static class TestEntriesListGenerator
    215       extends TestEntriesGenerator
    216       implements TestListGenerator<Entry<String, Integer>> {
    217     @Override public List<Entry<String, Integer>> create(Object... elements) {
    218       return (List<Entry<String, Integer>>) super.create(elements);
    219     }
    220   }
    221 
    222   private static final Predicate<Map.Entry<Integer, String>> FILTER_GET_PREDICATE
    223       = new Predicate<Map.Entry<Integer, String>>() {
    224         @Override public boolean apply(Entry<Integer, String> entry) {
    225           return !"badvalue".equals(entry.getValue()) && 55556 != entry.getKey();
    226         }
    227     };
    228 
    229   private static final Predicate<Map.Entry<String, Integer>> FILTER_KEYSET_PREDICATE
    230     = new Predicate<Map.Entry<String, Integer>>() {
    231       @Override public boolean apply(Entry<String, Integer> entry) {
    232         return !"badkey".equals(entry.getKey()) && 55556 != entry.getValue();
    233       }
    234   };
    235 
    236   public static Test suite() {
    237     TestSuite suite = new TestSuite();
    238 
    239     suite.addTest(transformSuite());
    240     suite.addTest(filterSuite());
    241 
    242     suite.addTest(ListMultimapTestSuiteBuilder.using(new TestStringListMultimapGenerator() {
    243         @Override
    244         protected ListMultimap<String, String> create(Entry<String, String>[] entries) {
    245           ListMultimap<String, String> multimap = Multimaps.synchronizedListMultimap(
    246               ArrayListMultimap.<String, String> create());
    247           for (Entry<String, String> entry : entries) {
    248             multimap.put(entry.getKey(), entry.getValue());
    249           }
    250           return multimap;
    251         }
    252       })
    253       .named("synchronized ArrayListMultimap")
    254       .withFeatures(
    255           MapFeature.ALLOWS_NULL_KEYS,
    256           MapFeature.ALLOWS_NULL_VALUES,
    257           MapFeature.ALLOWS_ANY_NULL_QUERIES,
    258           MapFeature.GENERAL_PURPOSE,
    259           MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
    260           CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
    261           CollectionSize.ANY)
    262       .createTestSuite());
    263 
    264     suite.addTest(SetTestSuiteBuilder.using(
    265         new TestStringSetGenerator() {
    266           @Override protected Set<String> create(String[] elements) {
    267             PopulatableMapAsMultimap<Integer, String> multimap
    268                 = PopulatableMapAsMultimap.create();
    269             populateMultimapForGet(multimap, elements);
    270             return multimap.build().get(3);
    271           }
    272         })
    273         .named("Multimaps.forMap.get")
    274         .withFeatures(FOR_MAP_FEATURES_ONE)
    275         .createTestSuite());
    276 
    277     suite.addTest(SetTestSuiteBuilder.using(
    278         new TestStringSetGenerator() {
    279           @Override protected Set<String> create(String[] elements) {
    280             PopulatableMapAsMultimap<String, Integer> multimap
    281                 = PopulatableMapAsMultimap.create();
    282             populateMultimapForKeySet(multimap, elements);
    283             return multimap.build().keySet();
    284           }
    285         })
    286         .named("Multimaps.forMap.keySet")
    287         .withFeatures(FOR_MAP_FEATURES_ANY)
    288         .createTestSuite());
    289 
    290     // TODO: use collection testers on Multimaps.forMap.values
    291 
    292     suite.addTest(MultisetTestSuiteBuilder.using(
    293         new TestStringMultisetGenerator() {
    294           @Override protected Multiset<String> create(String[] elements) {
    295             PopulatableMapAsMultimap<String, Integer> multimap
    296                 = PopulatableMapAsMultimap.create();
    297             populateMultimapForKeys(multimap, elements);
    298             return multimap.build().keys();
    299           }
    300         })
    301         .named("Multimaps.forMap.keys")
    302         .withFeatures(FOR_MAP_FEATURES_ANY)
    303         .suppressing(getCountDuplicateInitializingMethods())
    304         .suppressing(getSetCountDuplicateInitializingMethods())
    305         .suppressing(getIteratorDuplicateInitializingMethods())
    306         .suppressing(getRemoveDuplicateInitializingMethods())
    307         .createTestSuite());
    308 
    309     // TODO: use collection testers on Multimaps.forMap.entries
    310 
    311     return suite;
    312   }
    313 
    314   static abstract class TransformedMultimapGenerator<M extends Multimap<String, String>>
    315       implements TestMultimapGenerator<String, String, M> {
    316 
    317     @Override
    318     public String[] createKeyArray(int length) {
    319       return new String[length];
    320     }
    321 
    322     @Override
    323     public String[] createValueArray(int length) {
    324       return new String[length];
    325     }
    326 
    327     @Override
    328     public SampleElements<String> sampleKeys() {
    329       return new SampleElements<String>("one", "two", "three", "four", "five");
    330     }
    331 
    332     @Override
    333     public SampleElements<String> sampleValues() {
    334       return new SampleElements<String>("january", "february", "march", "april", "may");
    335     }
    336 
    337     @Override
    338     public Collection<String> createCollection(Iterable<? extends String> values) {
    339       return Lists.newArrayList(values);
    340     }
    341 
    342     @Override
    343     public SampleElements<Entry<String, String>> samples() {
    344       return new SampleElements<Entry<String, String>>(
    345           mapEntry("one", "january"),
    346           mapEntry("two", "february"),
    347           mapEntry("three", "march"),
    348           mapEntry("four", "april"),
    349           mapEntry("five", "may"));
    350     }
    351 
    352     @Override
    353     public M create(Object... elements) {
    354       Multimap<String, String> multimap = ArrayListMultimap.create();
    355       for (Object o : elements) {
    356         @SuppressWarnings("unchecked")
    357         Entry<String, String> entry = (Entry<String, String>) o;
    358         multimap.put(entry.getKey(), Ascii.toUpperCase(entry.getValue()));
    359       }
    360       return transform(multimap);
    361     }
    362 
    363     abstract M transform(Multimap<String, String> multimap);
    364 
    365     @SuppressWarnings("unchecked")
    366     @Override
    367     public Entry<String, String>[] createArray(int length) {
    368       return new Entry[length];
    369     }
    370 
    371     @Override
    372     public Iterable<Entry<String, String>> order(List<Entry<String, String>> insertionOrder) {
    373       return insertionOrder;
    374     }
    375 
    376     static final Function<String, String> FUNCTION = new Function<String, String>() {
    377       @Override
    378       public String apply(String value) {
    379         return Ascii.toLowerCase(value);
    380       }
    381     };
    382 
    383     static final EntryTransformer<String, String, String> ENTRY_TRANSFORMER =
    384         new EntryTransformer<String, String, String>() {
    385       @Override
    386       public String transformEntry(String key, String value) {
    387         return Ascii.toLowerCase(value);
    388       }
    389     };
    390   }
    391 
    392   static abstract class TransformedListMultimapGenerator
    393       extends TransformedMultimapGenerator<ListMultimap<String, String>>
    394       implements TestListMultimapGenerator<String, String> {
    395   }
    396 
    397   private static Test transformSuite() {
    398     TestSuite suite = new TestSuite("Multimaps.transform*");
    399     suite.addTest(MultimapTestSuiteBuilder.using(
    400         new TransformedMultimapGenerator<Multimap<String,String>>() {
    401           @Override
    402           Multimap<String, String> transform(Multimap<String, String> multimap) {
    403             return Multimaps.transformValues(multimap, FUNCTION);
    404           }
    405         })
    406         .named("Multimaps.transformValues[Multimap]")
    407         .withFeatures(
    408             CollectionSize.ANY,
    409             MapFeature.SUPPORTS_REMOVE,
    410             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
    411             MapFeature.ALLOWS_NULL_KEYS,
    412             MapFeature.ALLOWS_ANY_NULL_QUERIES)
    413         .createTestSuite());
    414     suite.addTest(MultimapTestSuiteBuilder.using(
    415         new TransformedMultimapGenerator<Multimap<String,String>>() {
    416           @Override
    417           Multimap<String, String> transform(Multimap<String, String> multimap) {
    418             return Multimaps.transformEntries(multimap, ENTRY_TRANSFORMER);
    419           }
    420         })
    421         .named("Multimaps.transformEntries[Multimap]")
    422         .withFeatures(
    423             CollectionSize.ANY,
    424             MapFeature.SUPPORTS_REMOVE,
    425             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
    426             MapFeature.ALLOWS_NULL_KEYS,
    427             MapFeature.ALLOWS_ANY_NULL_QUERIES)
    428         .createTestSuite());
    429     suite.addTest(ListMultimapTestSuiteBuilder.using(new TransformedListMultimapGenerator() {
    430           @Override
    431           ListMultimap<String, String> transform(Multimap<String, String> multimap) {
    432             return Multimaps.transformValues((ListMultimap<String, String>) multimap, FUNCTION);
    433           }
    434         })
    435         .named("Multimaps.transformValues[ListMultimap]")
    436         .withFeatures(
    437             CollectionSize.ANY,
    438             MapFeature.SUPPORTS_REMOVE,
    439             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
    440             MapFeature.ALLOWS_NULL_KEYS,
    441             MapFeature.ALLOWS_ANY_NULL_QUERIES)
    442         .createTestSuite());
    443     suite.addTest(ListMultimapTestSuiteBuilder.using(new TransformedListMultimapGenerator() {
    444           @Override
    445           ListMultimap<String, String> transform(Multimap<String, String> multimap) {
    446             return Multimaps.transformEntries(
    447                 (ListMultimap<String, String>) multimap, ENTRY_TRANSFORMER);
    448           }
    449         })
    450         .named("Multimaps.transformEntries[ListMultimap]")
    451         .withFeatures(
    452             CollectionSize.ANY,
    453             MapFeature.SUPPORTS_REMOVE,
    454             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
    455             MapFeature.ALLOWS_NULL_KEYS,
    456             MapFeature.ALLOWS_ANY_NULL_QUERIES)
    457         .createTestSuite());
    458 
    459     // TODO: use collection testers on Multimaps.forMap.entries
    460 
    461     return suite;
    462   }
    463 
    464   static abstract class TestFilteredMultimapGenerator<M extends Multimap<String, Integer>>
    465       implements TestMultimapGenerator<String, Integer, M> {
    466 
    467     @Override
    468     public SampleElements<Entry<String, Integer>> samples() {
    469       return new SampleElements<Entry<String, Integer>>(
    470           mapEntry("one", 114),
    471           mapEntry("two", 37),
    472           mapEntry("three", 42),
    473           mapEntry("four", 19),
    474           mapEntry("five", 82));
    475     }
    476 
    477     @SuppressWarnings("unchecked")
    478     @Override
    479     public Entry<String, Integer>[] createArray(int length) {
    480       return new Entry[length];
    481     }
    482 
    483     @Override
    484     public Iterable<Entry<String, Integer>> order(List<Entry<String, Integer>> insertionOrder) {
    485       return insertionOrder;
    486     }
    487 
    488     @Override
    489     public String[] createKeyArray(int length) {
    490       return new String[length];
    491     }
    492 
    493     @Override
    494     public Integer[] createValueArray(int length) {
    495       return new Integer[length];
    496     }
    497 
    498     @Override
    499     public SampleElements<String> sampleKeys() {
    500       return new SampleElements<String>("one", "two", "three", "four", "five");
    501     }
    502 
    503     @Override
    504     public SampleElements<Integer> sampleValues() {
    505       return new SampleElements<Integer>(114, 37, 42, 19, 82);
    506     }
    507   }
    508 
    509   static abstract class FilteredSetMultimapGenerator
    510       extends TestFilteredMultimapGenerator<SetMultimap<String, Integer>>
    511       implements TestSetMultimapGenerator<String, Integer> {
    512 
    513 
    514     abstract SetMultimap<String, Integer> filter(SetMultimap<String, Integer> multimap);
    515 
    516     @Override
    517     public SetMultimap<String, Integer> create(Object... elements) {
    518       SetMultimap<String, Integer> multimap = LinkedHashMultimap.create();
    519       for (Object o : elements) {
    520         @SuppressWarnings("unchecked")
    521         Entry<String, Integer> entry = (Entry<String, Integer>) o;
    522         multimap.put(entry.getKey(), entry.getValue());
    523       }
    524       return filter(multimap);
    525     }
    526 
    527     @Override
    528     public Collection<Integer> createCollection(Iterable<? extends Integer> values) {
    529       return Sets.newLinkedHashSet(values);
    530     }
    531   }
    532 
    533   static abstract class FilteredListMultimapGenerator
    534       extends TestFilteredMultimapGenerator<ListMultimap<String, Integer>>
    535       implements TestListMultimapGenerator<String, Integer> {
    536 
    537     @Override
    538     public ListMultimap<String, Integer> create(Object... elements) {
    539       ListMultimap<String, Integer> multimap = LinkedListMultimap.create();
    540       for (Object o : elements) {
    541         @SuppressWarnings("unchecked")
    542         Entry<String, Integer> entry = (Entry<String, Integer>) o;
    543         multimap.put(entry.getKey(), entry.getValue());
    544       }
    545       return filter(multimap);
    546     }
    547 
    548     abstract ListMultimap<String, Integer> filter(ListMultimap<String, Integer> multimap);
    549 
    550     @Override
    551     public Collection<Integer> createCollection(Iterable<? extends Integer> values) {
    552       return Lists.newArrayList(values);
    553     }
    554   }
    555 
    556   private static Test filterSuite() {
    557     TestSuite suite = new TestSuite("Multimaps.filter*");
    558     suite.addTest(SetMultimapTestSuiteBuilder.using(new FilteredSetMultimapGenerator() {
    559         @Override
    560         SetMultimap<String, Integer> filter(SetMultimap<String, Integer> multimap) {
    561           multimap.put("foo", 17);
    562           multimap.put("bar", 32);
    563           multimap.put("foo", 16);
    564           return Multimaps.filterKeys(multimap,
    565               Predicates.not(Predicates.in(ImmutableSet.of("foo", "bar"))));
    566         }
    567       })
    568       .named("Multimaps.filterKeys[SetMultimap, Predicate]")
    569       .withFeatures(
    570           CollectionSize.ANY,
    571           MultimapFeature.VALUE_COLLECTIONS_SUPPORT_ITERATOR_REMOVE,
    572           MapFeature.GENERAL_PURPOSE,
    573           MapFeature.ALLOWS_NULL_KEYS,
    574           MapFeature.ALLOWS_NULL_VALUES,
    575           MapFeature.ALLOWS_ANY_NULL_QUERIES)
    576       .createTestSuite());
    577 
    578     suite.addTest(ListMultimapTestSuiteBuilder.using(new FilteredListMultimapGenerator() {
    579         @Override
    580         ListMultimap<String, Integer> filter(ListMultimap<String, Integer> multimap) {
    581           multimap.put("foo", 17);
    582           multimap.put("bar", 32);
    583           multimap.put("foo", 16);
    584           return Multimaps.filterKeys(multimap,
    585               Predicates.not(Predicates.in(ImmutableSet.of("foo", "bar"))));
    586         }
    587       })
    588       .named("Multimaps.filterKeys[ListMultimap, Predicate]")
    589       .withFeatures(
    590           CollectionSize.ANY,
    591           MultimapFeature.VALUE_COLLECTIONS_SUPPORT_ITERATOR_REMOVE,
    592           MapFeature.GENERAL_PURPOSE,
    593           MapFeature.ALLOWS_NULL_KEYS,
    594           MapFeature.ALLOWS_NULL_VALUES,
    595           MapFeature.ALLOWS_ANY_NULL_QUERIES)
    596       .createTestSuite());
    597     suite.addTest(ListMultimapTestSuiteBuilder.using(new FilteredListMultimapGenerator() {
    598         @Override
    599         ListMultimap<String, Integer> filter(ListMultimap<String, Integer> multimap) {
    600           multimap.put("foo", 17);
    601           multimap.put("bar", 32);
    602           multimap.put("foo", 16);
    603           multimap = Multimaps.filterKeys(multimap, Predicates.not(Predicates.equalTo("foo")));
    604           return Multimaps.filterKeys(multimap, Predicates.not(Predicates.equalTo("bar")));
    605         }
    606       })
    607       .named("Multimaps.filterKeys[Multimaps.filterKeys[ListMultimap], Predicate]")
    608       .withFeatures(
    609           CollectionSize.ANY,
    610           MultimapFeature.VALUE_COLLECTIONS_SUPPORT_ITERATOR_REMOVE,
    611           MapFeature.GENERAL_PURPOSE,
    612           MapFeature.ALLOWS_NULL_KEYS,
    613           MapFeature.ALLOWS_NULL_VALUES,
    614           MapFeature.ALLOWS_ANY_NULL_QUERIES)
    615       .createTestSuite());
    616     suite.addTest(SetMultimapTestSuiteBuilder.using(new FilteredSetMultimapGenerator() {
    617         @Override
    618         SetMultimap<String, Integer> filter(SetMultimap<String, Integer> multimap) {
    619           multimap.put("one", 314);
    620           multimap.put("two", 159);
    621           multimap.put("one", 265);
    622           return Multimaps.filterValues(multimap,
    623               Predicates.not(Predicates.in(ImmutableSet.of(314, 159, 265))));
    624         }
    625       })
    626       .named("Multimaps.filterValues[SetMultimap, Predicate]")
    627       .withFeatures(
    628           CollectionSize.ANY,
    629           MapFeature.GENERAL_PURPOSE,
    630           MapFeature.ALLOWS_NULL_KEYS,
    631           MapFeature.ALLOWS_NULL_VALUES,
    632           MapFeature.ALLOWS_ANY_NULL_QUERIES)
    633       .createTestSuite());
    634     suite.addTest(SetMultimapTestSuiteBuilder.using(new FilteredSetMultimapGenerator() {
    635         @Override
    636         SetMultimap<String, Integer> filter(SetMultimap<String, Integer> multimap) {
    637           ImmutableSetMultimap<String, Integer> badEntries =
    638               ImmutableSetMultimap.of("foo", 314, "one", 159, "two", 265, "bar", 358);
    639           multimap.putAll(badEntries);
    640           return Multimaps.filterEntries(multimap,
    641               Predicates.not(Predicates.in(badEntries.entries())));
    642         }
    643       })
    644       .named("Multimaps.filterEntries[SetMultimap, Predicate]")
    645       .withFeatures(
    646           CollectionSize.ANY,
    647           MapFeature.GENERAL_PURPOSE,
    648           MapFeature.ALLOWS_NULL_KEYS,
    649           MapFeature.ALLOWS_NULL_VALUES,
    650           MapFeature.ALLOWS_ANY_NULL_QUERIES)
    651       .createTestSuite());
    652     suite.addTest(SetMultimapTestSuiteBuilder.using(new FilteredSetMultimapGenerator() {
    653         @Override
    654         SetMultimap<String, Integer> filter(SetMultimap<String, Integer> multimap) {
    655           ImmutableSetMultimap<String, Integer> badEntries =
    656               ImmutableSetMultimap.of("foo", 314, "one", 159, "two", 265, "bar", 358);
    657           multimap.putAll(badEntries);
    658           multimap = Multimaps.filterKeys(multimap,
    659               Predicates.not(Predicates.in(ImmutableSet.of("foo", "bar"))));
    660           return Multimaps.filterEntries(multimap,
    661               Predicates.not(Predicates.in(badEntries.entries())));
    662         }
    663       })
    664       .named("Multimaps.filterEntries[Multimaps.filterKeys[SetMultimap]]")
    665       .withFeatures(
    666           CollectionSize.ANY,
    667           MapFeature.GENERAL_PURPOSE,
    668           MapFeature.ALLOWS_NULL_KEYS,
    669           MapFeature.ALLOWS_NULL_VALUES,
    670           MapFeature.ALLOWS_ANY_NULL_QUERIES)
    671       .createTestSuite());
    672     suite.addTest(SetMultimapTestSuiteBuilder.using(new FilteredSetMultimapGenerator() {
    673         @Override
    674         SetMultimap<String, Integer> filter(SetMultimap<String, Integer> multimap) {
    675           ImmutableSetMultimap<String, Integer> badEntries =
    676               ImmutableSetMultimap.of("foo", 314, "one", 159, "two", 265, "bar", 358);
    677           multimap.putAll(badEntries);
    678           multimap = Multimaps.filterEntries(multimap,
    679               Predicates.not(Predicates.in(ImmutableMap.of("one", 159, "two", 265).entrySet())));
    680           return Multimaps.filterKeys(multimap,
    681               Predicates.not(Predicates.in(ImmutableSet.of("foo", "bar"))));
    682         }
    683       })
    684       .named("Multimaps.filterKeys[Multimaps.filterEntries[SetMultimap]]")
    685       .withFeatures(
    686           CollectionSize.ANY,
    687           MapFeature.GENERAL_PURPOSE,
    688           MapFeature.ALLOWS_NULL_KEYS,
    689           MapFeature.ALLOWS_NULL_VALUES,
    690           MapFeature.ALLOWS_ANY_NULL_QUERIES)
    691       .createTestSuite());
    692     suite.addTest(SetMultimapTestSuiteBuilder.using(new FilteredSetMultimapGenerator() {
    693         @Override
    694         SetMultimap<String, Integer> filter(SetMultimap<String, Integer> multimap) {
    695           ImmutableSetMultimap<String, Integer> badEntries =
    696               ImmutableSetMultimap.of("foo", 314, "bar", 358);
    697           multimap.putAll(badEntries);
    698           multimap = Multimaps.filterKeys(multimap, Predicates.not(Predicates.equalTo("foo")));
    699           multimap = Multimaps.filterKeys(multimap, Predicates.not(Predicates.equalTo("bar")));
    700           return multimap;
    701         }
    702       })
    703       .named("Multimaps.filterKeys[Multimaps.filterKeys[SetMultimap]]")
    704       .withFeatures(
    705           CollectionSize.ANY,
    706           MultimapFeature.VALUE_COLLECTIONS_SUPPORT_ITERATOR_REMOVE,
    707           MapFeature.GENERAL_PURPOSE,
    708           MapFeature.ALLOWS_NULL_KEYS,
    709           MapFeature.ALLOWS_NULL_VALUES,
    710           MapFeature.ALLOWS_ANY_NULL_QUERIES)
    711       .createTestSuite());
    712     return suite;
    713   }
    714 }
    715