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