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