MapTestSuiteBuilder.java revision 0888a09821a98ac0680fad765217302858e70fa4
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.testing;
18
19import static com.google.common.collect.testing.DerivedCollectionGenerators.keySetGenerator;
20
21import com.google.common.collect.testing.DerivedCollectionGenerators.MapEntrySetGenerator;
22import com.google.common.collect.testing.DerivedCollectionGenerators.MapValueCollectionGenerator;
23import com.google.common.collect.testing.features.CollectionFeature;
24import com.google.common.collect.testing.features.CollectionSize;
25import com.google.common.collect.testing.features.Feature;
26import com.google.common.collect.testing.features.MapFeature;
27import com.google.common.collect.testing.testers.MapClearTester;
28import com.google.common.collect.testing.testers.MapContainsKeyTester;
29import com.google.common.collect.testing.testers.MapContainsValueTester;
30import com.google.common.collect.testing.testers.MapCreationTester;
31import com.google.common.collect.testing.testers.MapEntrySetTester;
32import com.google.common.collect.testing.testers.MapEqualsTester;
33import com.google.common.collect.testing.testers.MapGetTester;
34import com.google.common.collect.testing.testers.MapHashCodeTester;
35import com.google.common.collect.testing.testers.MapIsEmptyTester;
36import com.google.common.collect.testing.testers.MapPutAllTester;
37import com.google.common.collect.testing.testers.MapPutTester;
38import com.google.common.collect.testing.testers.MapRemoveTester;
39import com.google.common.collect.testing.testers.MapSerializationTester;
40import com.google.common.collect.testing.testers.MapSizeTester;
41import com.google.common.collect.testing.testers.MapToStringTester;
42import com.google.common.testing.SerializableTester;
43
44import junit.framework.TestSuite;
45
46import java.util.Arrays;
47import java.util.HashSet;
48import java.util.List;
49import java.util.Map;
50import java.util.Set;
51
52/**
53 * Creates, based on your criteria, a JUnit test suite that exhaustively tests
54 * a Map implementation.
55 *
56 * @author George van den Driessche
57 */
58public class MapTestSuiteBuilder<K, V>
59    extends PerCollectionSizeTestSuiteBuilder<
60        MapTestSuiteBuilder<K, V>,
61        TestMapGenerator<K, V>, Map<K, V>, Map.Entry<K, V>> {
62  public static <K, V> MapTestSuiteBuilder<K, V> using(
63      TestMapGenerator<K, V> generator) {
64    return new MapTestSuiteBuilder<K, V>().usingGenerator(generator);
65  }
66
67  @SuppressWarnings("unchecked") // Class parameters must be raw.
68  @Override protected List<Class<? extends AbstractTester>> getTesters() {
69    return Arrays.<Class<? extends AbstractTester>>asList(
70        MapClearTester.class,
71        MapContainsKeyTester.class,
72        MapContainsValueTester.class,
73        MapCreationTester.class,
74        MapEntrySetTester.class,
75        MapEqualsTester.class,
76        MapGetTester.class,
77        MapHashCodeTester.class,
78        MapIsEmptyTester.class,
79        MapPutTester.class,
80        MapPutAllTester.class,
81        MapRemoveTester.class,
82        MapSerializationTester.class,
83        MapSizeTester.class,
84        MapToStringTester.class
85    );
86  }
87
88  @Override
89  protected List<TestSuite> createDerivedSuites(
90      FeatureSpecificTestSuiteBuilder<
91          ?,
92          ? extends OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>>
93      parentBuilder) {
94    // TODO: Once invariant support is added, supply invariants to each of the
95    // derived suites, to check that mutations to the derived collections are
96    // reflected in the underlying map.
97
98    List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder);
99
100    if (parentBuilder.getFeatures().contains(CollectionFeature.SERIALIZABLE)) {
101      derivedSuites.add(MapTestSuiteBuilder.using(
102              new ReserializedMapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
103          .withFeatures(computeReserializedMapFeatures(parentBuilder.getFeatures()))
104          .named(parentBuilder.getName() + " reserialized")
105          .suppressing(parentBuilder.getSuppressedTests())
106          .createTestSuite());
107    }
108
109    derivedSuites.add(SetTestSuiteBuilder.using(
110            new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
111        .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures()))
112        .named(parentBuilder.getName() + " entrySet")
113        .suppressing(parentBuilder.getSuppressedTests())
114        .createTestSuite());
115
116    derivedSuites.add(createDerivedKeySetSuite(
117            keySetGenerator(parentBuilder.getSubjectGenerator()))
118        .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures()))
119        .named(parentBuilder.getName() + " keys")
120        .suppressing(parentBuilder.getSuppressedTests())
121        .createTestSuite());
122
123    derivedSuites.add(CollectionTestSuiteBuilder.using(
124            new MapValueCollectionGenerator<K, V>(
125                parentBuilder.getSubjectGenerator()))
126        .named(parentBuilder.getName() + " values")
127        .withFeatures(computeValuesCollectionFeatures(
128            parentBuilder.getFeatures()))
129        .suppressing(parentBuilder.getSuppressedTests())
130        .createTestSuite());
131
132    return derivedSuites;
133  }
134
135  protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) {
136    return SetTestSuiteBuilder.using(keySetGenerator);
137  }
138
139  private static Set<Feature<?>> computeReserializedMapFeatures(
140      Set<Feature<?>> mapFeatures) {
141    Set<Feature<?>> derivedFeatures = Helpers.copyToSet(mapFeatures);
142    derivedFeatures.remove(CollectionFeature.SERIALIZABLE);
143    derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS);
144    return derivedFeatures;
145  }
146
147  private static Set<Feature<?>> computeEntrySetFeatures(
148      Set<Feature<?>> mapFeatures) {
149    Set<Feature<?>> entrySetFeatures =
150        computeCommonDerivedCollectionFeatures(mapFeatures);
151    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_ENTRY_QUERIES)) {
152      entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
153    }
154    return entrySetFeatures;
155  }
156
157  private static Set<Feature<?>> computeKeySetFeatures(
158      Set<Feature<?>> mapFeatures) {
159    Set<Feature<?>> keySetFeatures =
160        computeCommonDerivedCollectionFeatures(mapFeatures);
161
162    // TODO(user): make this trigger only if the map is a submap
163    // currently, the KeySetGenerator won't work properly for a subset of a keyset of a submap
164    keySetFeatures.add(CollectionFeature.SUBSET_VIEW);
165    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) {
166      keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
167    } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEY_QUERIES)) {
168      keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
169    }
170
171    return keySetFeatures;
172  }
173
174  private static Set<Feature<?>> computeValuesCollectionFeatures(
175      Set<Feature<?>> mapFeatures) {
176    Set<Feature<?>> valuesCollectionFeatures =
177        computeCommonDerivedCollectionFeatures(mapFeatures);
178    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUE_QUERIES)) {
179      valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
180    }
181    if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
182      valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
183    }
184
185    return valuesCollectionFeatures;
186  }
187
188  public static Set<Feature<?>> computeCommonDerivedCollectionFeatures(
189      Set<Feature<?>> mapFeatures) {
190    mapFeatures = new HashSet<Feature<?>>(mapFeatures);
191    Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>();
192    mapFeatures.remove(CollectionFeature.SERIALIZABLE);
193    if (mapFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) {
194      derivedFeatures.add(CollectionFeature.SERIALIZABLE);
195    }
196    if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) {
197      derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE);
198    }
199    if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) {
200      derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
201    }
202    if (mapFeatures.contains(MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION)) {
203      derivedFeatures.add(CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION);
204    }
205    // add the intersection of CollectionFeature.values() and mapFeatures
206    for (CollectionFeature feature : CollectionFeature.values()) {
207      if (mapFeatures.contains(feature)) {
208        derivedFeatures.add(feature);
209      }
210    }
211    // add the intersection of CollectionSize.values() and mapFeatures
212    for (CollectionSize size : CollectionSize.values()) {
213      if (mapFeatures.contains(size)) {
214        derivedFeatures.add(size);
215      }
216    }
217    return derivedFeatures;
218  }
219
220  private static class ReserializedMapGenerator<K, V>
221      implements TestMapGenerator<K, V> {
222    private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>
223        mapGenerator;
224
225    public ReserializedMapGenerator(
226        OneSizeTestContainerGenerator<
227            Map<K, V>, Map.Entry<K, V>> mapGenerator) {
228      this.mapGenerator = mapGenerator;
229    }
230
231    @Override
232    public SampleElements<Map.Entry<K, V>> samples() {
233      return mapGenerator.samples();
234    }
235
236    @Override
237    public Map.Entry<K, V>[] createArray(int length) {
238      return mapGenerator.createArray(length);
239    }
240
241    @Override
242    public Iterable<Map.Entry<K, V>> order(
243        List<Map.Entry<K, V>> insertionOrder) {
244      return mapGenerator.order(insertionOrder);
245    }
246
247    @Override
248    public Map<K, V> create(Object... elements) {
249      return SerializableTester.reserialize(mapGenerator.create(elements));
250    }
251
252    @Override
253    public K[] createKeyArray(int length) {
254      return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
255          .createKeyArray(length);
256    }
257
258    @Override
259    public V[] createValueArray(int length) {
260      return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
261        .createValueArray(length);
262    }
263  }
264}
265