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