• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
populateMultimapForGet( Multimap<Integer, String> multimap, String[] elements)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 
populateMultimapForKeySet( Multimap<String, Integer> multimap, String[] elements)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 
populateMultimapForValues( Multimap<Integer, String> multimap, String[] elements)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 
populateMultimapForKeys( Multimap<String, Integer> multimap, String[] elements)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 
create()143     static <K, V> PopulatableMapAsMultimap<K, V> create() {
144       return new PopulatableMapAsMultimap<K, V>();
145     }
146 
147     @SuppressWarnings("unchecked")  // all methods throw immediately
PopulatableMapAsMultimap()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 
delegate()162     @Override protected Multimap<K, V> delegate() {
163       return unusableDelegate;
164     }
165 
put(K key, V value)166     @Override public boolean put(K key, V value) {
167       map.put(key, value);
168       return true;
169     }
170 
build()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
samples()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
create(Object... elements)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 
createMultimap()199     abstract Multimap<String, Integer> createMultimap();
200 
201     @Override
202     @SuppressWarnings("unchecked")
createArray(int length)203     public Entry<String, Integer>[] createArray(int length) {
204       return (Entry<String, Integer>[]) new Entry<?, ?>[length];
205     }
206 
207     @Override
order( List<Entry<String, Integer>> insertionOrder)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>> {
create(Object... elements)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 
suite()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
createKeyArray(int length)318     public String[] createKeyArray(int length) {
319       return new String[length];
320     }
321 
322     @Override
createValueArray(int length)323     public String[] createValueArray(int length) {
324       return new String[length];
325     }
326 
327     @Override
sampleKeys()328     public SampleElements<String> sampleKeys() {
329       return new SampleElements<String>("one", "two", "three", "four", "five");
330     }
331 
332     @Override
sampleValues()333     public SampleElements<String> sampleValues() {
334       return new SampleElements<String>("january", "february", "march", "april", "may");
335     }
336 
337     @Override
createCollection(Iterable<? extends String> values)338     public Collection<String> createCollection(Iterable<? extends String> values) {
339       return Lists.newArrayList(values);
340     }
341 
342     @Override
samples()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
create(Object... elements)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 
transform(Multimap<String, String> multimap)363     abstract M transform(Multimap<String, String> multimap);
364 
365     @SuppressWarnings("unchecked")
366     @Override
createArray(int length)367     public Entry<String, String>[] createArray(int length) {
368       return new Entry[length];
369     }
370 
371     @Override
order(List<Entry<String, String>> insertionOrder)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 
transformSuite()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
samples()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
createArray(int length)479     public Entry<String, Integer>[] createArray(int length) {
480       return new Entry[length];
481     }
482 
483     @Override
order(List<Entry<String, Integer>> insertionOrder)484     public Iterable<Entry<String, Integer>> order(List<Entry<String, Integer>> insertionOrder) {
485       return insertionOrder;
486     }
487 
488     @Override
createKeyArray(int length)489     public String[] createKeyArray(int length) {
490       return new String[length];
491     }
492 
493     @Override
createValueArray(int length)494     public Integer[] createValueArray(int length) {
495       return new Integer[length];
496     }
497 
498     @Override
sampleKeys()499     public SampleElements<String> sampleKeys() {
500       return new SampleElements<String>("one", "two", "three", "four", "five");
501     }
502 
503     @Override
sampleValues()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 
filter(SetMultimap<String, Integer> multimap)514     abstract SetMultimap<String, Integer> filter(SetMultimap<String, Integer> multimap);
515 
516     @Override
create(Object... elements)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
createCollection(Iterable<? extends Integer> values)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
create(Object... elements)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 
filter(ListMultimap<String, Integer> multimap)548     abstract ListMultimap<String, Integer> filter(ListMultimap<String, Integer> multimap);
549 
550     @Override
createCollection(Iterable<? extends Integer> values)551     public Collection<Integer> createCollection(Iterable<? extends Integer> values) {
552       return Lists.newArrayList(values);
553     }
554   }
555 
filterSuite()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