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