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