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 
17 package com.google.common.collect.testing;
18 
19 import static com.google.common.collect.testing.DerivedCollectionGenerators.keySetGenerator;
20 
21 import com.google.common.collect.testing.DerivedCollectionGenerators.MapEntrySetGenerator;
22 import com.google.common.collect.testing.DerivedCollectionGenerators.MapValueCollectionGenerator;
23 import com.google.common.collect.testing.features.CollectionFeature;
24 import com.google.common.collect.testing.features.CollectionSize;
25 import com.google.common.collect.testing.features.Feature;
26 import com.google.common.collect.testing.features.MapFeature;
27 import com.google.common.collect.testing.testers.MapClearTester;
28 import com.google.common.collect.testing.testers.MapContainsKeyTester;
29 import com.google.common.collect.testing.testers.MapContainsValueTester;
30 import com.google.common.collect.testing.testers.MapCreationTester;
31 import com.google.common.collect.testing.testers.MapEntrySetTester;
32 import com.google.common.collect.testing.testers.MapEqualsTester;
33 import com.google.common.collect.testing.testers.MapGetTester;
34 import com.google.common.collect.testing.testers.MapHashCodeTester;
35 import com.google.common.collect.testing.testers.MapIsEmptyTester;
36 import com.google.common.collect.testing.testers.MapPutAllTester;
37 import com.google.common.collect.testing.testers.MapPutTester;
38 import com.google.common.collect.testing.testers.MapRemoveTester;
39 import com.google.common.collect.testing.testers.MapSerializationTester;
40 import com.google.common.collect.testing.testers.MapSizeTester;
41 import com.google.common.collect.testing.testers.MapToStringTester;
42 import com.google.common.testing.SerializableTester;
43 
44 import junit.framework.TestSuite;
45 
46 import java.util.Arrays;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Map;
50 import 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  */
58 public class MapTestSuiteBuilder<K, V>
59     extends PerCollectionSizeTestSuiteBuilder<
60         MapTestSuiteBuilder<K, V>,
61         TestMapGenerator<K, V>, Map<K, V>, Map.Entry<K, V>> {
using( TestMapGenerator<K, V> generator)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.
getTesters()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
createDerivedSuites( FeatureSpecificTestSuiteBuilder< ?, ? extends OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>> parentBuilder)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 
createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator)135   protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) {
136     return SetTestSuiteBuilder.using(keySetGenerator);
137   }
138 
computeReserializedMapFeatures( Set<Feature<?>> mapFeatures)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 
computeEntrySetFeatures( Set<Feature<?>> mapFeatures)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 
computeKeySetFeatures( Set<Feature<?>> mapFeatures)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 
computeValuesCollectionFeatures( Set<Feature<?>> mapFeatures)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 
computeCommonDerivedCollectionFeatures( Set<Feature<?>> mapFeatures)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 
ReserializedMapGenerator( OneSizeTestContainerGenerator< Map<K, V>, Map.Entry<K, V>> mapGenerator)225     public ReserializedMapGenerator(
226         OneSizeTestContainerGenerator<
227             Map<K, V>, Map.Entry<K, V>> mapGenerator) {
228       this.mapGenerator = mapGenerator;
229     }
230 
231     @Override
samples()232     public SampleElements<Map.Entry<K, V>> samples() {
233       return mapGenerator.samples();
234     }
235 
236     @Override
createArray(int length)237     public Map.Entry<K, V>[] createArray(int length) {
238       return mapGenerator.createArray(length);
239     }
240 
241     @Override
order( List<Map.Entry<K, V>> insertionOrder)242     public Iterable<Map.Entry<K, V>> order(
243         List<Map.Entry<K, V>> insertionOrder) {
244       return mapGenerator.order(insertionOrder);
245     }
246 
247     @Override
create(Object... elements)248     public Map<K, V> create(Object... elements) {
249       return SerializableTester.reserialize(mapGenerator.create(elements));
250     }
251 
252     @Override
createKeyArray(int length)253     public K[] createKeyArray(int length) {
254       return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
255           .createKeyArray(length);
256     }
257 
258     @Override
createValueArray(int length)259     public V[] createValueArray(int length) {
260       return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator())
261         .createValueArray(length);
262     }
263   }
264 }
265