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
17package com.google.common.collect.testing;
18
19import com.google.common.collect.testing.features.CollectionFeature;
20import com.google.common.collect.testing.features.CollectionSize;
21import com.google.common.collect.testing.features.MapFeature;
22
23import junit.framework.Test;
24import junit.framework.TestSuite;
25
26import java.io.Serializable;
27import java.lang.reflect.Method;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.Comparator;
31import java.util.EnumMap;
32import java.util.HashMap;
33import java.util.LinkedHashMap;
34import java.util.Map;
35import java.util.Map.Entry;
36import java.util.SortedMap;
37import java.util.TreeMap;
38import java.util.concurrent.ConcurrentHashMap;
39import 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 */
48public 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