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 com.google.common.collect.testing.features.CollectionFeature; 20 import com.google.common.collect.testing.features.CollectionSize; 21 import com.google.common.collect.testing.features.Feature; 22 import com.google.common.collect.testing.features.MapFeature; 23 import com.google.common.collect.testing.testers.MapClearTester; 24 import com.google.common.collect.testing.testers.MapContainsKeyTester; 25 import com.google.common.collect.testing.testers.MapContainsValueTester; 26 import com.google.common.collect.testing.testers.MapCreationTester; 27 import com.google.common.collect.testing.testers.MapEqualsTester; 28 import com.google.common.collect.testing.testers.MapGetTester; 29 import com.google.common.collect.testing.testers.MapHashCodeTester; 30 import com.google.common.collect.testing.testers.MapIsEmptyTester; 31 import com.google.common.collect.testing.testers.MapPutAllTester; 32 import com.google.common.collect.testing.testers.MapPutTester; 33 import com.google.common.collect.testing.testers.MapRemoveTester; 34 import com.google.common.collect.testing.testers.MapSizeTester; 35 36 import junit.framework.TestSuite; 37 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collection; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Map.Entry; 45 import java.util.Set; 46 47 /** 48 * Creates, based on your criteria, a JUnit test suite that exhaustively tests 49 * a Map implementation. 50 * 51 * @author George van den Driessche 52 */ 53 public class MapTestSuiteBuilder<K, V> 54 extends PerCollectionSizeTestSuiteBuilder< 55 MapTestSuiteBuilder<K, V>, 56 TestMapGenerator<K, V>, Map<K, V>, Map.Entry<K, V>> { using( TestMapGenerator<K, V> generator)57 public static <K, V> MapTestSuiteBuilder<K, V> using( 58 TestMapGenerator<K, V> generator) { 59 return new MapTestSuiteBuilder<K, V>().usingGenerator(generator); 60 } 61 62 @SuppressWarnings("unchecked") // Class parameters must be raw. getTesters()63 @Override protected List<Class<? extends AbstractTester>> getTesters() { 64 return Arrays.<Class<? extends AbstractTester>>asList( 65 MapClearTester.class, 66 MapContainsKeyTester.class, 67 MapContainsValueTester.class, 68 MapCreationTester.class, 69 MapEqualsTester.class, 70 MapGetTester.class, 71 MapHashCodeTester.class, 72 MapIsEmptyTester.class, 73 MapPutTester.class, 74 MapPutAllTester.class, 75 MapRemoveTester.class, 76 MapSizeTester.class 77 ); 78 } 79 createDerivedSuites( FeatureSpecificTestSuiteBuilder< ?, ? extends OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>> parentBuilder)80 @Override List<TestSuite> createDerivedSuites( 81 FeatureSpecificTestSuiteBuilder< 82 ?, 83 ? extends OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>>> 84 parentBuilder) { 85 // TODO: Once invariant support is added, supply invariants to each of the 86 // derived suites, to check that mutations to the derived collections are 87 // reflected in the underlying map. 88 89 List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder); 90 91 derivedSuites.add(SetTestSuiteBuilder.using( 92 new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator())) 93 .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures())) 94 .named(parentBuilder.getName() + " entrySet") 95 .suppressing(parentBuilder.getSuppressedTests()) 96 .createTestSuite()); 97 98 derivedSuites.add(createDerivedKeySetSuite( 99 new MapKeySetGenerator<K, V>(parentBuilder.getSubjectGenerator())) 100 .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures())) 101 .named(parentBuilder.getName() + " keys") 102 .suppressing(parentBuilder.getSuppressedTests()) 103 .createTestSuite()); 104 105 derivedSuites.add(CollectionTestSuiteBuilder.using( 106 new MapValueCollectionGenerator<K, V>( 107 parentBuilder.getSubjectGenerator())) 108 .named(parentBuilder.getName() + " values") 109 .withFeatures(computeValuesCollectionFeatures( 110 parentBuilder.getFeatures())) 111 .suppressing(parentBuilder.getSuppressedTests()) 112 .createTestSuite()); 113 114 return derivedSuites; 115 } 116 createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator)117 protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) { 118 return SetTestSuiteBuilder.using(keySetGenerator); 119 } 120 computeEntrySetFeatures( Set<Feature<?>> mapFeatures)121 private static Set<Feature<?>> computeEntrySetFeatures( 122 Set<Feature<?>> mapFeatures) { 123 Set<Feature<?>> entrySetFeatures = 124 computeCommonDerivedCollectionFeatures(mapFeatures); 125 entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 126 return entrySetFeatures; 127 } 128 computeKeySetFeatures( Set<Feature<?>> mapFeatures)129 private static Set<Feature<?>> computeKeySetFeatures( 130 Set<Feature<?>> mapFeatures) { 131 Set<Feature<?>> keySetFeatures = 132 computeCommonDerivedCollectionFeatures(mapFeatures); 133 134 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) { 135 keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); 136 } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_QUERIES)) { 137 keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 138 } 139 140 return keySetFeatures; 141 } 142 computeValuesCollectionFeatures( Set<Feature<?>> mapFeatures)143 private static Set<Feature<?>> computeValuesCollectionFeatures( 144 Set<Feature<?>> mapFeatures) { 145 Set<Feature<?>> valuesCollectionFeatures = 146 computeCommonDerivedCollectionFeatures(mapFeatures); 147 valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 148 149 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) { 150 valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); 151 } 152 153 return valuesCollectionFeatures; 154 } 155 computeCommonDerivedCollectionFeatures( Set<Feature<?>> mapFeatures)156 private static Set<Feature<?>> computeCommonDerivedCollectionFeatures( 157 Set<Feature<?>> mapFeatures) { 158 Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>(); 159 if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) { 160 derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE); 161 derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE_ALL); 162 derivedFeatures.add(CollectionFeature.SUPPORTS_RETAIN_ALL); 163 } 164 if (mapFeatures.contains(MapFeature.SUPPORTS_CLEAR)) { 165 derivedFeatures.add(CollectionFeature.SUPPORTS_CLEAR); 166 } 167 if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) { 168 derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION); 169 } 170 // add the intersection of CollectionSize.values() and mapFeatures 171 for (CollectionSize size : CollectionSize.values()) { 172 if (mapFeatures.contains(size)) { 173 derivedFeatures.add(size); 174 } 175 } 176 return derivedFeatures; 177 } 178 179 private static class MapEntrySetGenerator<K, V> 180 implements TestSetGenerator<Map.Entry<K, V>> { 181 private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> 182 mapGenerator; 183 MapEntrySetGenerator( OneSizeTestContainerGenerator< Map<K, V>, Map.Entry<K, V>> mapGenerator)184 public MapEntrySetGenerator( 185 OneSizeTestContainerGenerator< 186 Map<K, V>, Map.Entry<K, V>> mapGenerator) { 187 this.mapGenerator = mapGenerator; 188 } 189 190 @Override samples()191 public SampleElements<Map.Entry<K, V>> samples() { 192 return mapGenerator.samples(); 193 } 194 195 @Override create(Object... elements)196 public Set<Map.Entry<K, V>> create(Object... elements) { 197 return mapGenerator.create(elements).entrySet(); 198 } 199 200 @Override createArray(int length)201 public Map.Entry<K, V>[] createArray(int length) { 202 return mapGenerator.createArray(length); 203 } 204 205 @Override order( List<Map.Entry<K, V>> insertionOrder)206 public Iterable<Map.Entry<K, V>> order( 207 List<Map.Entry<K, V>> insertionOrder) { 208 return mapGenerator.order(insertionOrder); 209 } 210 } 211 212 // TODO: investigate some API changes to SampleElements that would tidy up 213 // parts of the following classes. 214 215 private static class MapKeySetGenerator<K, V> implements TestSetGenerator<K> { 216 private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> 217 mapGenerator; 218 private final SampleElements<K> samples; 219 MapKeySetGenerator( OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> mapGenerator)220 public MapKeySetGenerator( 221 OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> 222 mapGenerator) { 223 this.mapGenerator = mapGenerator; 224 final SampleElements<Map.Entry<K, V>> mapSamples = 225 this.mapGenerator.samples(); 226 this.samples = new SampleElements<K>( 227 mapSamples.e0.getKey(), 228 mapSamples.e1.getKey(), 229 mapSamples.e2.getKey(), 230 mapSamples.e3.getKey(), 231 mapSamples.e4.getKey()); 232 } 233 234 @Override samples()235 public SampleElements<K> samples() { 236 return samples; 237 } 238 239 @Override create(Object... elements)240 public Set<K> create(Object... elements) { 241 @SuppressWarnings("unchecked") 242 K[] keysArray = (K[]) elements; 243 244 // Start with a suitably shaped collection of entries 245 Collection<Map.Entry<K, V>> originalEntries = 246 mapGenerator.getSampleElements(elements.length); 247 248 // Create a copy of that, with the desired value for each key 249 Collection<Map.Entry<K, V>> entries = 250 new ArrayList<Entry<K, V>>(elements.length); 251 int i = 0; 252 for (Map.Entry<K, V> entry : originalEntries) { 253 entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue())); 254 } 255 256 return mapGenerator.create(entries.toArray()).keySet(); 257 } 258 259 @Override createArray(int length)260 public K[] createArray(int length) { 261 // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps 262 // tidy this up and get rid of the casts here and in 263 // MapValueCollectionGenerator. 264 265 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()) 266 .createKeyArray(length); 267 } 268 269 @Override order(List<K> insertionOrder)270 public Iterable<K> order(List<K> insertionOrder) { 271 return insertionOrder; 272 } 273 } 274 275 private static class MapValueCollectionGenerator<K, V> 276 implements TestCollectionGenerator<V> { 277 private final OneSizeTestContainerGenerator<Map<K, V>, Map.Entry<K, V>> 278 mapGenerator; 279 private final SampleElements<V> samples; 280 MapValueCollectionGenerator( OneSizeTestContainerGenerator< Map<K, V>, Map.Entry<K, V>> mapGenerator)281 public MapValueCollectionGenerator( 282 OneSizeTestContainerGenerator< 283 Map<K, V>, Map.Entry<K, V>> mapGenerator) { 284 this.mapGenerator = mapGenerator; 285 final SampleElements<Map.Entry<K, V>> mapSamples = 286 this.mapGenerator.samples(); 287 this.samples = new SampleElements<V>( 288 mapSamples.e0.getValue(), 289 mapSamples.e1.getValue(), 290 mapSamples.e2.getValue(), 291 mapSamples.e3.getValue(), 292 mapSamples.e4.getValue()); 293 } 294 295 @Override samples()296 public SampleElements<V> samples() { 297 return samples; 298 } 299 300 @Override create(Object... elements)301 public Collection<V> create(Object... elements) { 302 @SuppressWarnings("unchecked") 303 V[] valuesArray = (V[]) elements; 304 305 // Start with a suitably shaped collection of entries 306 Collection<Map.Entry<K, V>> originalEntries = 307 mapGenerator.getSampleElements(elements.length); 308 309 // Create a copy of that, with the desired value for each value 310 Collection<Map.Entry<K, V>> entries = 311 new ArrayList<Entry<K, V>>(elements.length); 312 int i = 0; 313 for (Map.Entry<K, V> entry : originalEntries) { 314 entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++])); 315 } 316 317 return mapGenerator.create(entries.toArray()).values(); 318 } 319 320 @Override createArray(int length)321 public V[] createArray(int length) { 322 //noinspection UnnecessaryLocalVariable 323 final V[] vs = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()) 324 .createValueArray(length); 325 return vs; 326 } 327 328 @Override order(List<V> insertionOrder)329 public Iterable<V> order(List<V> insertionOrder) { 330 return insertionOrder; 331 } 332 } 333 } 334