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