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 com.google.common.collect.testing.features.CollectionFeature;
20 import com.google.common.collect.testing.features.CollectionSize;
21 import com.google.common.collect.testing.features.MapFeature;
22 
23 import junit.framework.Test;
24 import junit.framework.TestSuite;
25 
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.LinkedHashMap;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.SortedMap;
37 import java.util.TreeMap;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.concurrent.ConcurrentSkipListMap;
40 
41 /**
42  * Generates a test suite covering the {@link Map} implementations in the
43  * {@link java.util} package. Can be subclassed to specify tests that should
44  * be suppressed.
45  *
46  * @author Kevin Bourrillion
47  */
48 public class TestsForMapsInJavaUtil {
49 
50   public static Test suite() {
51     return new TestsForMapsInJavaUtil().allTests();
52   }
53 
54   public Test allTests() {
55     TestSuite suite = new TestSuite("java.util Maps");
56     suite.addTest(testsForEmptyMap());
57     suite.addTest(testsForSingletonMap());
58     suite.addTest(testsForHashMap());
59     suite.addTest(testsForLinkedHashMap());
60     suite.addTest(testsForTreeMapNatural());
61     suite.addTest(testsForTreeMapWithComparator());
62     suite.addTest(testsForEnumMap());
63     suite.addTest(testsForConcurrentHashMap());
64     suite.addTest(testsForConcurrentSkipListMapNatural());
65     suite.addTest(testsForConcurrentSkipListMapWithComparator());
66     return suite;
67   }
68 
69   protected Collection<Method> suppressForEmptyMap() {
70     return Collections.emptySet();
71   }
72   protected Collection<Method> suppressForSingletonMap() {
73     return Collections.emptySet();
74   }
75   protected Collection<Method> suppressForHashMap() {
76     return Collections.emptySet();
77   }
78   protected Collection<Method> suppressForLinkedHashMap() {
79     return Collections.emptySet();
80   }
81   protected Collection<Method> suppressForTreeMapNatural() {
82     return Collections.emptySet();
83   }
84   protected Collection<Method> suppressForTreeMapWithComparator() {
85     return Collections.emptySet();
86   }
87   protected Collection<Method> suppressForEnumMap() {
88     return Collections.emptySet();
89   }
90   protected Collection<Method> suppressForConcurrentHashMap() {
91     return Collections.emptySet();
92   }
93   protected Collection<Method> suppressForConcurrentSkipListMap() {
94     return Collections.emptySet();
95   }
96 
97   public Test testsForEmptyMap() {
98     return MapTestSuiteBuilder
99         .using(new TestStringMapGenerator() {
100             @Override protected Map<String, String> create(
101                 Entry<String, String>[] entries) {
102               return Collections.emptyMap();
103             }
104           })
105         .named("emptyMap")
106         .withFeatures(
107             CollectionFeature.SERIALIZABLE,
108             CollectionSize.ZERO)
109         .suppressing(suppressForEmptyMap())
110         .createTestSuite();
111   }
112 
113   public Test testsForSingletonMap() {
114     return MapTestSuiteBuilder
115         .using(new TestStringMapGenerator() {
116             @Override protected Map<String, String> create(
117                 Entry<String, String>[] entries) {
118               return Collections.singletonMap(
119                   entries[0].getKey(), entries[0].getValue());
120             }
121           })
122         .named("singletonMap")
123         .withFeatures(
124             MapFeature.ALLOWS_NULL_KEYS,
125             MapFeature.ALLOWS_NULL_VALUES,
126             MapFeature.ALLOWS_ANY_NULL_QUERIES,
127             CollectionFeature.SERIALIZABLE,
128             CollectionSize.ONE)
129         .suppressing(suppressForSingletonMap())
130         .createTestSuite();
131   }
132 
133   public Test testsForHashMap() {
134     return MapTestSuiteBuilder
135         .using(new TestStringMapGenerator() {
136             @Override protected Map<String, String> create(
137                 Entry<String, String>[] entries) {
138               return toHashMap(entries);
139             }
140           })
141         .named("HashMap")
142         .withFeatures(
143             MapFeature.GENERAL_PURPOSE,
144             MapFeature.ALLOWS_NULL_KEYS,
145             MapFeature.ALLOWS_NULL_VALUES,
146             MapFeature.ALLOWS_ANY_NULL_QUERIES,
147             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
148             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
149             CollectionFeature.SERIALIZABLE,
150             CollectionSize.ANY)
151         .suppressing(suppressForHashMap())
152         .createTestSuite();
153   }
154 
155   public Test testsForLinkedHashMap() {
156     return MapTestSuiteBuilder
157         .using(new TestStringMapGenerator() {
158             @Override protected Map<String, String> create(
159                 Entry<String, String>[] entries) {
160               return populate(new LinkedHashMap<String, String>(), entries);
161             }
162           })
163         .named("LinkedHashMap")
164         .withFeatures(
165             MapFeature.GENERAL_PURPOSE,
166             MapFeature.ALLOWS_NULL_KEYS,
167             MapFeature.ALLOWS_NULL_VALUES,
168             MapFeature.ALLOWS_ANY_NULL_QUERIES,
169             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
170             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
171             CollectionFeature.KNOWN_ORDER,
172             CollectionFeature.SERIALIZABLE,
173             CollectionSize.ANY)
174         .suppressing(suppressForLinkedHashMap())
175         .createTestSuite();
176   }
177 
178   public Test testsForTreeMapNatural() {
179     return NavigableMapTestSuiteBuilder
180         .using(new TestStringSortedMapGenerator() {
181             @Override protected SortedMap<String, String> create(
182                 Entry<String, String>[] entries) {
183               /*
184                * TODO(cpovirk): it would be nice to create an input Map and use
185                * the copy constructor here and in the other tests
186                */
187               return populate(new TreeMap<String, String>(), entries);
188             }
189           })
190         .named("TreeMap, natural")
191         .withFeatures(
192             MapFeature.GENERAL_PURPOSE,
193             MapFeature.ALLOWS_NULL_VALUES,
194             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
195             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
196             CollectionFeature.KNOWN_ORDER,
197             CollectionFeature.SERIALIZABLE,
198             CollectionSize.ANY)
199         .suppressing(suppressForTreeMapNatural())
200         .createTestSuite();
201   }
202 
203   public Test testsForTreeMapWithComparator() {
204     return NavigableMapTestSuiteBuilder
205         .using(new TestStringSortedMapGenerator() {
206             @Override protected SortedMap<String, String> create(
207                 Entry<String, String>[] entries) {
208               return populate(new TreeMap<String, String>(
209                   arbitraryNullFriendlyComparator()), entries);
210             }
211           })
212         .named("TreeMap, with comparator")
213         .withFeatures(
214             MapFeature.GENERAL_PURPOSE,
215             MapFeature.ALLOWS_NULL_KEYS,
216             MapFeature.ALLOWS_NULL_VALUES,
217             MapFeature.ALLOWS_ANY_NULL_QUERIES,
218             MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
219             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
220             CollectionFeature.KNOWN_ORDER,
221             CollectionFeature.SERIALIZABLE,
222             CollectionSize.ANY)
223         .suppressing(suppressForTreeMapWithComparator())
224         .createTestSuite();
225   }
226 
227   public Test testsForEnumMap() {
228     return MapTestSuiteBuilder
229         .using(new TestEnumMapGenerator() {
230             @Override protected Map<AnEnum, String> create(
231                 Entry<AnEnum, String>[] entries) {
232               return populate(
233                   new EnumMap<AnEnum, String>(AnEnum.class), entries);
234             }
235           })
236         .named("EnumMap")
237         .withFeatures(
238             MapFeature.GENERAL_PURPOSE,
239             MapFeature.ALLOWS_NULL_VALUES,
240             MapFeature.RESTRICTS_KEYS,
241             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
242             CollectionFeature.KNOWN_ORDER,
243             CollectionFeature.SERIALIZABLE,
244             CollectionSize.ANY)
245         .suppressing(suppressForEnumMap())
246         .createTestSuite();
247   }
248 
249   public Test testsForConcurrentHashMap() {
250     return MapTestSuiteBuilder
251         .using(new TestStringMapGenerator() {
252           @Override protected Map<String, String> create(
253               Entry<String, String>[] entries) {
254             return populate(new ConcurrentHashMap<String, String>(), entries);
255           }
256         })
257         .named("ConcurrentHashMap")
258         .withFeatures(
259             MapFeature.GENERAL_PURPOSE,
260             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
261             CollectionFeature.SERIALIZABLE,
262             CollectionSize.ANY)
263         .suppressing(suppressForConcurrentHashMap())
264         .createTestSuite();
265   }
266 
267   public Test testsForConcurrentSkipListMapNatural() {
268     return NavigableMapTestSuiteBuilder
269         .using(new TestStringSortedMapGenerator() {
270           @Override protected SortedMap<String, String> create(
271               Entry<String, String>[] entries) {
272             return populate(new ConcurrentSkipListMap<String, String>(), entries);
273           }
274         })
275         .named("ConcurrentSkipListMap, natural")
276         .withFeatures(
277             MapFeature.GENERAL_PURPOSE,
278             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
279             CollectionFeature.KNOWN_ORDER,
280             CollectionFeature.SERIALIZABLE,
281             CollectionSize.ANY)
282         .suppressing(suppressForConcurrentSkipListMap())
283         .createTestSuite();
284   }
285 
286   public Test testsForConcurrentSkipListMapWithComparator() {
287     return NavigableMapTestSuiteBuilder
288         .using(new TestStringSortedMapGenerator() {
289           @Override protected SortedMap<String, String> create(
290               Entry<String, String>[] entries) {
291             return populate(new ConcurrentSkipListMap<String, String>(
292                 arbitraryNullFriendlyComparator()), entries);
293           }
294         })
295         .named("ConcurrentSkipListMap, with comparator")
296         .withFeatures(
297             MapFeature.GENERAL_PURPOSE,
298             CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
299             CollectionFeature.KNOWN_ORDER,
300             CollectionFeature.SERIALIZABLE,
301             CollectionSize.ANY)
302         .suppressing(suppressForConcurrentSkipListMap())
303         .createTestSuite();
304   }
305 
306   // TODO: IdentityHashMap, AbstractMap
307 
308   private static Map<String, String> toHashMap(
309       Entry<String, String>[] entries) {
310     return populate(new HashMap<String, String>(), entries);
311   }
312 
313   // TODO: call conversion constructors or factory methods instead of using
314   // populate() on an empty map
315   private static <T, M extends Map<T, String>> M populate(
316       M map, Entry<T, String>[] entries) {
317     for (Entry<T, String> entry : entries) {
318       map.put(entry.getKey(), entry.getValue());
319     }
320     return map;
321   }
322 
323   static <T> Comparator<T> arbitraryNullFriendlyComparator() {
324     return new NullFriendlyComparator<T>();
325   }
326 
327   private static final class NullFriendlyComparator<T> implements Comparator<T>, Serializable {
328     @Override
329     public int compare(T left, T right) {
330       return String.valueOf(left).compareTo(String.valueOf(right));
331     }
332   }
333 }
334