View Javadoc
1   /*
2    * Copyright (C) 2009 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.testing;
18  
19  import static java.util.Arrays.asList;
20  
21  import com.google.common.annotations.GwtIncompatible;
22  import com.google.common.collect.testing.features.CollectionFeature;
23  import com.google.common.collect.testing.features.CollectionSize;
24  import com.google.common.collect.testing.features.MapFeature;
25  import com.google.common.collect.testing.testers.MapEntrySetTester;
26  import java.io.Serializable;
27  import java.lang.reflect.Method;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.Comparator;
31  import java.util.EnumMap;
32  import java.util.HashMap;
33  import java.util.Hashtable;
34  import java.util.LinkedHashMap;
35  import java.util.Map;
36  import java.util.Map.Entry;
37  import java.util.NavigableMap;
38  import java.util.SortedMap;
39  import java.util.TreeMap;
40  import java.util.concurrent.ConcurrentHashMap;
41  import java.util.concurrent.ConcurrentSkipListMap;
42  import junit.framework.Test;
43  import junit.framework.TestSuite;
44  
45  /**
46   * Generates a test suite covering the {@link Map} implementations in the
47   * {@link java.util} package. Can be subclassed to specify tests that should
48   * be suppressed.
49   *
50   * @author Kevin Bourrillion
51   */
52  @GwtIncompatible
53  public class TestsForMapsInJavaUtil {
54  
55    public static Test suite() {
56      return new TestsForMapsInJavaUtil().allTests();
57    }
58  
59    public Test allTests() {
60      TestSuite suite = new TestSuite("java.util Maps");
61      suite.addTest(testsForCheckedMap());
62      suite.addTest(testsForCheckedNavigableMap());
63      suite.addTest(testsForCheckedSortedMap());
64      suite.addTest(testsForEmptyMap());
65      suite.addTest(testsForEmptyNavigableMap());
66      suite.addTest(testsForEmptySortedMap());
67      suite.addTest(testsForSingletonMap());
68      suite.addTest(testsForHashMap());
69      suite.addTest(testsForHashtable());
70      suite.addTest(testsForLinkedHashMap());
71      suite.addTest(testsForSynchronizedNavigableMap());
72      suite.addTest(testsForTreeMapNatural());
73      suite.addTest(testsForTreeMapWithComparator());
74      suite.addTest(testsForUnmodifiableMap());
75      suite.addTest(testsForUnmodifiableNavigableMap());
76      suite.addTest(testsForUnmodifiableSortedMap());
77      suite.addTest(testsForEnumMap());
78      suite.addTest(testsForConcurrentHashMap());
79      suite.addTest(testsForConcurrentSkipListMapNatural());
80      suite.addTest(testsForConcurrentSkipListMapWithComparator());
81      return suite;
82    }
83  
84    protected Collection<Method> suppressForCheckedMap() {
85      return Collections.emptySet();
86    }
87  
88    protected Collection<Method> suppressForCheckedNavigableMap() {
89      return Collections.emptySet();
90    }
91  
92    protected Collection<Method> suppressForCheckedSortedMap() {
93      return Collections.emptySet();
94    }
95  
96    protected Collection<Method> suppressForEmptyMap() {
97      return Collections.emptySet();
98    }
99  
100   private Collection<Method> suppressForEmptyNavigableMap() {
101     return Collections.emptySet();
102   }
103 
104   private Collection<Method> suppressForEmptySortedMap() {
105     return Collections.emptySet();
106   }
107 
108   protected Collection<Method> suppressForSingletonMap() {
109     return Collections.emptySet();
110   }
111 
112   protected Collection<Method> suppressForHashMap() {
113     return Collections.emptySet();
114   }
115 
116   protected Collection<Method> suppressForHashtable() {
117     return Collections.emptySet();
118   }
119 
120   protected Collection<Method> suppressForLinkedHashMap() {
121     return Collections.emptySet();
122   }
123 
124   protected Collection<Method> suppressForSynchronizedNavigableMap() {
125     return Collections.emptySet();
126   }
127 
128   protected Collection<Method> suppressForTreeMapNatural() {
129     return Collections.emptySet();
130   }
131 
132   protected Collection<Method> suppressForTreeMapWithComparator() {
133     return Collections.emptySet();
134   }
135 
136   protected Collection<Method> suppressForUnmodifiableMap() {
137     return Collections.emptySet();
138   }
139 
140   protected Collection<Method> suppressForUnmodifiableNavigableMap() {
141     return Collections.emptySet();
142   }
143 
144   protected Collection<Method> suppressForUnmodifiableSortedMap() {
145     return Collections.emptySet();
146   }
147 
148   protected Collection<Method> suppressForEnumMap() {
149     return Collections.emptySet();
150   }
151 
152   protected Collection<Method> suppressForConcurrentHashMap() {
153     return Collections.emptySet();
154   }
155 
156   protected Collection<Method> suppressForConcurrentSkipListMap() {
157     return asList(MapEntrySetTester.getSetValueMethod());
158   }
159 
160   public Test testsForCheckedMap() {
161     return MapTestSuiteBuilder.using(
162         new TestStringMapGenerator() {
163           @Override
164           protected Map<String, String> create(Entry<String, String>[] entries) {
165             Map<String, String> map = populate(new HashMap<String, String>(), entries);
166             return Collections.checkedMap(map, String.class, String.class);
167           }
168         })
169         .named("checkedMap/HashMap")
170         .withFeatures(
171             MapFeature.GENERAL_PURPOSE,
172             MapFeature.ALLOWS_NULL_KEYS,
173             MapFeature.ALLOWS_NULL_VALUES,
174             MapFeature.ALLOWS_ANY_NULL_QUERIES,
175             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
176             MapFeature.RESTRICTS_KEYS,
177             MapFeature.RESTRICTS_VALUES,
178             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
179             CollectionFeature.SERIALIZABLE,
180             CollectionSize.ANY)
181         .suppressing(suppressForCheckedMap())
182         .createTestSuite();
183   }
184 
185   public Test testsForCheckedNavigableMap() {
186     return SortedMapTestSuiteBuilder.using(
187         new TestStringSortedMapGenerator() {
188           @Override
189           protected NavigableMap<String, String> create(Entry<String, String>[] entries) {
190             NavigableMap<String, String> map = populate(new TreeMap<String, String>(), entries);
191             return Collections.checkedNavigableMap(map, String.class, String.class);
192           }
193         })
194         .named("checkedNavigableMap/TreeMap, natural")
195         .withFeatures(
196             MapFeature.GENERAL_PURPOSE,
197             MapFeature.ALLOWS_NULL_VALUES,
198             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
199             MapFeature.RESTRICTS_KEYS,
200             MapFeature.RESTRICTS_VALUES,
201             CollectionFeature.KNOWN_ORDER,
202             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
203             CollectionFeature.SERIALIZABLE,
204             CollectionSize.ANY)
205         .suppressing(suppressForCheckedNavigableMap())
206         .createTestSuite();
207   }
208 
209   public Test testsForCheckedSortedMap() {
210     return SortedMapTestSuiteBuilder.using(
211         new TestStringSortedMapGenerator() {
212           @Override
213           protected SortedMap<String, String> create(Entry<String, String>[] entries) {
214             SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries);
215             return Collections.checkedSortedMap(map, String.class, String.class);
216           }
217         })
218         .named("checkedSortedMap/TreeMap, natural")
219         .withFeatures(
220             MapFeature.GENERAL_PURPOSE,
221             MapFeature.ALLOWS_NULL_VALUES,
222             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
223             MapFeature.RESTRICTS_KEYS,
224             MapFeature.RESTRICTS_VALUES,
225             CollectionFeature.KNOWN_ORDER,
226             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
227             CollectionFeature.SERIALIZABLE,
228             CollectionSize.ANY)
229         .suppressing(suppressForCheckedSortedMap())
230         .createTestSuite();
231   }
232 
233   public Test testsForEmptyMap() {
234     return MapTestSuiteBuilder.using(
235             new TestStringMapGenerator() {
236               @Override
237               protected Map<String, String> create(Entry<String, String>[] entries) {
238                 return Collections.emptyMap();
239               }
240             })
241         .named("emptyMap")
242         .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO)
243         .suppressing(suppressForEmptyMap())
244         .createTestSuite();
245   }
246 
247   public Test testsForEmptyNavigableMap() {
248     return MapTestSuiteBuilder.using(
249         new TestStringSortedMapGenerator() {
250           @Override
251           protected NavigableMap<String, String> create(Entry<String, String>[] entries) {
252             return Collections.emptyNavigableMap();
253           }
254         })
255         .named("emptyNavigableMap")
256         .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO)
257         .suppressing(suppressForEmptyNavigableMap())
258         .createTestSuite();
259 
260   }
261 
262   public Test testsForEmptySortedMap() {
263     return MapTestSuiteBuilder.using(
264         new TestStringSortedMapGenerator() {
265           @Override
266           protected SortedMap<String, String> create(Entry<String, String>[] entries) {
267             return Collections.emptySortedMap();
268           }
269         })
270         .named("emptySortedMap")
271         .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO)
272         .suppressing(suppressForEmptySortedMap())
273         .createTestSuite();
274 
275   }
276 
277   public Test testsForSingletonMap() {
278     return MapTestSuiteBuilder.using(
279             new TestStringMapGenerator() {
280               @Override
281               protected Map<String, String> create(Entry<String, String>[] entries) {
282                 return Collections.singletonMap(entries[0].getKey(), entries[0].getValue());
283               }
284             })
285         .named("singletonMap")
286         .withFeatures(
287             MapFeature.ALLOWS_NULL_KEYS,
288             MapFeature.ALLOWS_NULL_VALUES,
289             MapFeature.ALLOWS_ANY_NULL_QUERIES,
290             CollectionFeature.SERIALIZABLE,
291             CollectionSize.ONE)
292         .suppressing(suppressForSingletonMap())
293         .createTestSuite();
294   }
295 
296   public Test testsForHashMap() {
297     return MapTestSuiteBuilder.using(
298             new TestStringMapGenerator() {
299               @Override
300               protected Map<String, String> create(Entry<String, String>[] entries) {
301                 return toHashMap(entries);
302               }
303             })
304         .named("HashMap")
305         .withFeatures(
306             MapFeature.GENERAL_PURPOSE,
307             MapFeature.ALLOWS_NULL_KEYS,
308             MapFeature.ALLOWS_NULL_VALUES,
309             MapFeature.ALLOWS_ANY_NULL_QUERIES,
310             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
311             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
312             CollectionFeature.SERIALIZABLE,
313             CollectionSize.ANY)
314         .suppressing(suppressForHashMap())
315         .createTestSuite();
316   }
317 
318   public Test testsForHashtable() {
319       return MapTestSuiteBuilder.using(
320           new TestStringMapGenerator() {
321             @Override
322             protected Map<String, String> create(Entry<String, String>[] entries) {
323               return populate(new Hashtable<String, String>(), entries);
324             }
325           })
326           .withFeatures(
327               MapFeature.GENERAL_PURPOSE,
328               MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
329               MapFeature.RESTRICTS_KEYS,
330               MapFeature.SUPPORTS_REMOVE,
331               CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
332               CollectionFeature.SERIALIZABLE,
333               CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
334               CollectionFeature.SUPPORTS_REMOVE,
335               CollectionSize.ANY
336           )
337           .named("Hashtable")
338           .suppressing(suppressForHashtable())
339           .createTestSuite();
340   }
341 
342   public Test testsForLinkedHashMap() {
343     return MapTestSuiteBuilder.using(
344             new TestStringMapGenerator() {
345               @Override
346               protected Map<String, String> create(Entry<String, String>[] entries) {
347                 return populate(new LinkedHashMap<String, String>(), entries);
348               }
349             })
350         .named("LinkedHashMap")
351         .withFeatures(
352             MapFeature.GENERAL_PURPOSE,
353             MapFeature.ALLOWS_NULL_KEYS,
354             MapFeature.ALLOWS_NULL_VALUES,
355             MapFeature.ALLOWS_ANY_NULL_QUERIES,
356             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
357             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
358             CollectionFeature.KNOWN_ORDER,
359             CollectionFeature.SERIALIZABLE,
360             CollectionSize.ANY)
361         .suppressing(suppressForLinkedHashMap())
362         .createTestSuite();
363   }
364 
365   /**
366    * Tests regular NavigableMap behavior of synchronizedNavigableMap(treeMap);
367    * does not test the fact that it's synchronized.
368    */
369   public Test testsForSynchronizedNavigableMap() {
370     return NavigableMapTestSuiteBuilder.using(
371             new TestStringSortedMapGenerator() {
372               @Override
373               protected SortedMap<String, String> create(Entry<String, String>[] entries) {
374                 NavigableMap<String, String> delegate = populate(new TreeMap<>(), entries);
375                 return Collections.synchronizedNavigableMap(delegate);
376               }
377             })
378         .named("synchronizedNavigableMap/TreeMap, natural")
379         .withFeatures(
380             MapFeature.GENERAL_PURPOSE,
381             MapFeature.ALLOWS_NULL_VALUES,
382             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
383             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
384             CollectionFeature.KNOWN_ORDER,
385             CollectionFeature.SERIALIZABLE,
386             CollectionSize.ANY)
387         .suppressing(suppressForSynchronizedNavigableMap())
388         .createTestSuite();
389   }
390 
391   public Test testsForTreeMapNatural() {
392     return NavigableMapTestSuiteBuilder.using(
393             new TestStringSortedMapGenerator() {
394               @Override
395               protected SortedMap<String, String> create(Entry<String, String>[] entries) {
396                 /*
397                  * TODO(cpovirk): it would be nice to create an input Map and use
398                  * the copy constructor here and in the other tests
399                  */
400                 return populate(new TreeMap<String, String>(), entries);
401               }
402             })
403         .named("TreeMap, natural")
404         .withFeatures(
405             MapFeature.GENERAL_PURPOSE,
406             MapFeature.ALLOWS_NULL_VALUES,
407             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
408             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
409             CollectionFeature.KNOWN_ORDER,
410             CollectionFeature.SERIALIZABLE,
411             CollectionSize.ANY)
412         .suppressing(suppressForTreeMapNatural())
413         .createTestSuite();
414   }
415 
416   public Test testsForTreeMapWithComparator() {
417     return NavigableMapTestSuiteBuilder.using(
418             new TestStringSortedMapGenerator() {
419               @Override
420               protected SortedMap<String, String> create(Entry<String, String>[] entries) {
421                 return populate(
422                     new TreeMap<String, String>(arbitraryNullFriendlyComparator()), entries);
423               }
424             })
425         .named("TreeMap, with comparator")
426         .withFeatures(
427             MapFeature.GENERAL_PURPOSE,
428             MapFeature.ALLOWS_NULL_KEYS,
429             MapFeature.ALLOWS_NULL_VALUES,
430             MapFeature.ALLOWS_ANY_NULL_QUERIES,
431             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
432             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
433             CollectionFeature.KNOWN_ORDER,
434             CollectionFeature.SERIALIZABLE,
435             CollectionSize.ANY)
436         .suppressing(suppressForTreeMapWithComparator())
437         .createTestSuite();
438   }
439 
440   public Test testsForUnmodifiableMap() {
441     return MapTestSuiteBuilder.using(
442         new TestStringMapGenerator() {
443           @Override
444           protected Map<String, String> create(Entry<String, String>[] entries) {
445             return Collections.unmodifiableMap(toHashMap(entries));
446           }
447         })
448         .named("unmodifiableMap/HashMap")
449         .withFeatures(
450             MapFeature.ALLOWS_NULL_KEYS,
451             MapFeature.ALLOWS_NULL_VALUES,
452             MapFeature.ALLOWS_ANY_NULL_QUERIES,
453             CollectionFeature.SERIALIZABLE,
454             CollectionSize.ANY)
455         .suppressing(suppressForUnmodifiableMap())
456         .createTestSuite();
457   }
458 
459   public Test testsForUnmodifiableNavigableMap() {
460     return MapTestSuiteBuilder.using(
461         new TestStringSortedMapGenerator() {
462           @Override
463           protected NavigableMap<String, String> create(Entry<String, String>[] entries) {
464             return Collections.unmodifiableNavigableMap(populate(new TreeMap<>(), entries));
465           }
466         })
467         .named("unmodifiableNavigableMap/TreeMap, natural")
468         .withFeatures(
469             MapFeature.ALLOWS_NULL_VALUES,
470             CollectionFeature.KNOWN_ORDER,
471             CollectionFeature.SERIALIZABLE,
472             CollectionSize.ANY)
473         .suppressing(suppressForUnmodifiableNavigableMap())
474         .createTestSuite();
475   }
476 
477   public Test testsForUnmodifiableSortedMap() {
478     return MapTestSuiteBuilder.using(
479         new TestStringSortedMapGenerator() {
480           @Override
481           protected SortedMap<String, String> create(Entry<String, String>[] entries) {
482             SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries);
483             return Collections.unmodifiableSortedMap(map);
484           }
485         })
486         .named("unmodifiableSortedMap/TreeMap, natural")
487         .withFeatures(
488             MapFeature.ALLOWS_NULL_VALUES,
489             CollectionFeature.KNOWN_ORDER,
490             CollectionFeature.SERIALIZABLE,
491             CollectionSize.ANY)
492         .suppressing(suppressForUnmodifiableSortedMap())
493         .createTestSuite();
494   }
495 
496   public Test testsForEnumMap() {
497     return MapTestSuiteBuilder.using(
498             new TestEnumMapGenerator() {
499               @Override
500               protected Map<AnEnum, String> create(Entry<AnEnum, String>[] entries) {
501                 return populate(new EnumMap<AnEnum, String>(AnEnum.class), entries);
502               }
503             })
504         .named("EnumMap")
505         .withFeatures(
506             MapFeature.GENERAL_PURPOSE,
507             MapFeature.ALLOWS_NULL_VALUES,
508             MapFeature.RESTRICTS_KEYS,
509             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
510             CollectionFeature.KNOWN_ORDER,
511             CollectionFeature.SERIALIZABLE,
512             CollectionSize.ANY)
513         .suppressing(suppressForEnumMap())
514         .createTestSuite();
515   }
516 
517   public Test testsForConcurrentHashMap() {
518     return MapTestSuiteBuilder.using(
519             new TestStringMapGenerator() {
520               @Override
521               protected Map<String, String> create(Entry<String, String>[] entries) {
522                 return populate(new ConcurrentHashMap<String, String>(), entries);
523               }
524             })
525         .named("ConcurrentHashMap")
526         .withFeatures(
527             MapFeature.GENERAL_PURPOSE,
528             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
529             CollectionFeature.SERIALIZABLE,
530             CollectionSize.ANY)
531         .suppressing(suppressForConcurrentHashMap())
532         .createTestSuite();
533   }
534 
535   public Test testsForConcurrentSkipListMapNatural() {
536     return NavigableMapTestSuiteBuilder.using(
537             new TestStringSortedMapGenerator() {
538               @Override
539               protected SortedMap<String, String> create(Entry<String, String>[] entries) {
540                 return populate(new ConcurrentSkipListMap<String, String>(), entries);
541               }
542             })
543         .named("ConcurrentSkipListMap, natural")
544         .withFeatures(
545             MapFeature.GENERAL_PURPOSE,
546             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
547             CollectionFeature.KNOWN_ORDER,
548             CollectionFeature.SERIALIZABLE,
549             CollectionSize.ANY)
550         .suppressing(suppressForConcurrentSkipListMap())
551         .createTestSuite();
552   }
553 
554   public Test testsForConcurrentSkipListMapWithComparator() {
555     return NavigableMapTestSuiteBuilder.using(
556             new TestStringSortedMapGenerator() {
557               @Override
558               protected SortedMap<String, String> create(Entry<String, String>[] entries) {
559                 return populate(
560                     new ConcurrentSkipListMap<String, String>(arbitraryNullFriendlyComparator()),
561                     entries);
562               }
563             })
564         .named("ConcurrentSkipListMap, with comparator")
565         .withFeatures(
566             MapFeature.GENERAL_PURPOSE,
567             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
568             CollectionFeature.KNOWN_ORDER,
569             CollectionFeature.SERIALIZABLE,
570             CollectionSize.ANY)
571         .suppressing(suppressForConcurrentSkipListMap())
572         .createTestSuite();
573   }
574 
575   // TODO: IdentityHashMap, AbstractMap
576 
577   private static Map<String, String> toHashMap(Entry<String, String>[] entries) {
578     return populate(new HashMap<String, String>(), entries);
579   }
580 
581   // TODO: call conversion constructors or factory methods instead of using
582   // populate() on an empty map
583   private static <T, M extends Map<T, String>> M populate(M map, Entry<T, String>[] entries) {
584     for (Entry<T, String> entry : entries) {
585       map.put(entry.getKey(), entry.getValue());
586     }
587     return map;
588   }
589 
590   static <T> Comparator<T> arbitraryNullFriendlyComparator() {
591     return new NullFriendlyComparator<T>();
592   }
593 
594   private static final class NullFriendlyComparator<T> implements Comparator<T>, Serializable {
595     @Override
596     public int compare(T left, T right) {
597       return String.valueOf(left).compareTo(String.valueOf(right));
598     }
599   }
600 }