001/* 002 * Copyright (C) 2008 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect.testing; 018 019import static com.google.common.collect.testing.DerivedCollectionGenerators.keySetGenerator; 020import static com.google.common.collect.testing.Helpers.copyToSet; 021 022import com.google.common.annotations.GwtIncompatible; 023import com.google.common.collect.testing.DerivedCollectionGenerators.MapEntrySetGenerator; 024import com.google.common.collect.testing.DerivedCollectionGenerators.MapValueCollectionGenerator; 025import com.google.common.collect.testing.features.CollectionFeature; 026import com.google.common.collect.testing.features.CollectionSize; 027import com.google.common.collect.testing.features.Feature; 028import com.google.common.collect.testing.features.MapFeature; 029import com.google.common.collect.testing.testers.MapClearTester; 030import com.google.common.collect.testing.testers.MapComputeIfAbsentTester; 031import com.google.common.collect.testing.testers.MapComputeIfPresentTester; 032import com.google.common.collect.testing.testers.MapComputeTester; 033import com.google.common.collect.testing.testers.MapContainsKeyTester; 034import com.google.common.collect.testing.testers.MapContainsValueTester; 035import com.google.common.collect.testing.testers.MapCreationTester; 036import com.google.common.collect.testing.testers.MapEntrySetTester; 037import com.google.common.collect.testing.testers.MapEqualsTester; 038import com.google.common.collect.testing.testers.MapForEachTester; 039import com.google.common.collect.testing.testers.MapGetOrDefaultTester; 040import com.google.common.collect.testing.testers.MapGetTester; 041import com.google.common.collect.testing.testers.MapHashCodeTester; 042import com.google.common.collect.testing.testers.MapIsEmptyTester; 043import com.google.common.collect.testing.testers.MapMergeTester; 044import com.google.common.collect.testing.testers.MapPutAllTester; 045import com.google.common.collect.testing.testers.MapPutIfAbsentTester; 046import com.google.common.collect.testing.testers.MapPutTester; 047import com.google.common.collect.testing.testers.MapRemoveEntryTester; 048import com.google.common.collect.testing.testers.MapRemoveTester; 049import com.google.common.collect.testing.testers.MapReplaceAllTester; 050import com.google.common.collect.testing.testers.MapReplaceEntryTester; 051import com.google.common.collect.testing.testers.MapReplaceTester; 052import com.google.common.collect.testing.testers.MapSerializationTester; 053import com.google.common.collect.testing.testers.MapSizeTester; 054import com.google.common.collect.testing.testers.MapToStringTester; 055import com.google.common.testing.SerializableTester; 056import java.util.Arrays; 057import java.util.HashSet; 058import java.util.List; 059import java.util.Map; 060import java.util.Map.Entry; 061import java.util.Set; 062import junit.framework.TestSuite; 063 064/** 065 * Creates, based on your criteria, a JUnit test suite that exhaustively tests a Map implementation. 066 * 067 * @author George van den Driessche 068 */ 069@GwtIncompatible 070public class MapTestSuiteBuilder<K, V> 071 extends PerCollectionSizeTestSuiteBuilder< 072 MapTestSuiteBuilder<K, V>, TestMapGenerator<K, V>, Map<K, V>, Entry<K, V>> { 073 public static <K, V> MapTestSuiteBuilder<K, V> using(TestMapGenerator<K, V> generator) { 074 return new MapTestSuiteBuilder<K, V>().usingGenerator(generator); 075 } 076 077 @SuppressWarnings("rawtypes") // class literals 078 @Override 079 protected List<Class<? extends AbstractTester>> getTesters() { 080 return Arrays.<Class<? extends AbstractTester>>asList( 081 MapClearTester.class, 082 MapComputeTester.class, 083 MapComputeIfAbsentTester.class, 084 MapComputeIfPresentTester.class, 085 MapContainsKeyTester.class, 086 MapContainsValueTester.class, 087 MapCreationTester.class, 088 MapEntrySetTester.class, 089 MapEqualsTester.class, 090 MapForEachTester.class, 091 MapGetTester.class, 092 MapGetOrDefaultTester.class, 093 MapHashCodeTester.class, 094 MapIsEmptyTester.class, 095 MapMergeTester.class, 096 MapPutTester.class, 097 MapPutAllTester.class, 098 MapPutIfAbsentTester.class, 099 MapRemoveTester.class, 100 MapRemoveEntryTester.class, 101 MapReplaceTester.class, 102 MapReplaceAllTester.class, 103 MapReplaceEntryTester.class, 104 MapSerializationTester.class, 105 MapSizeTester.class, 106 MapToStringTester.class); 107 } 108 109 @Override 110 protected List<TestSuite> createDerivedSuites( 111 FeatureSpecificTestSuiteBuilder< 112 ?, ? extends OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>>> 113 parentBuilder) { 114 // TODO: Once invariant support is added, supply invariants to each of the 115 // derived suites, to check that mutations to the derived collections are 116 // reflected in the underlying map. 117 118 List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder); 119 120 if (parentBuilder.getFeatures().contains(CollectionFeature.SERIALIZABLE)) { 121 derivedSuites.add( 122 MapTestSuiteBuilder.using( 123 new ReserializedMapGenerator<K, V>(parentBuilder.getSubjectGenerator())) 124 .withFeatures(computeReserializedMapFeatures(parentBuilder.getFeatures())) 125 .named(parentBuilder.getName() + " reserialized") 126 .suppressing(parentBuilder.getSuppressedTests()) 127 .withSetUp(parentBuilder.getSetUp()) 128 .withTearDown(parentBuilder.getTearDown()) 129 .createTestSuite()); 130 } 131 132 derivedSuites.add( 133 createDerivedEntrySetSuite( 134 new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator())) 135 .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures())) 136 .named(parentBuilder.getName() + " entrySet") 137 .suppressing(parentBuilder.getSuppressedTests()) 138 .withSetUp(parentBuilder.getSetUp()) 139 .withTearDown(parentBuilder.getTearDown()) 140 .createTestSuite()); 141 142 derivedSuites.add( 143 createDerivedKeySetSuite(keySetGenerator(parentBuilder.getSubjectGenerator())) 144 .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures())) 145 .named(parentBuilder.getName() + " keys") 146 .suppressing(parentBuilder.getSuppressedTests()) 147 .withSetUp(parentBuilder.getSetUp()) 148 .withTearDown(parentBuilder.getTearDown()) 149 .createTestSuite()); 150 151 derivedSuites.add( 152 createDerivedValueCollectionSuite( 153 new MapValueCollectionGenerator<K, V>(parentBuilder.getSubjectGenerator())) 154 .named(parentBuilder.getName() + " values") 155 .withFeatures(computeValuesCollectionFeatures(parentBuilder.getFeatures())) 156 .suppressing(parentBuilder.getSuppressedTests()) 157 .withSetUp(parentBuilder.getSetUp()) 158 .withTearDown(parentBuilder.getTearDown()) 159 .createTestSuite()); 160 161 return derivedSuites; 162 } 163 164 protected SetTestSuiteBuilder<Entry<K, V>> createDerivedEntrySetSuite( 165 TestSetGenerator<Entry<K, V>> entrySetGenerator) { 166 return SetTestSuiteBuilder.using(entrySetGenerator); 167 } 168 169 protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) { 170 return SetTestSuiteBuilder.using(keySetGenerator); 171 } 172 173 protected CollectionTestSuiteBuilder<V> createDerivedValueCollectionSuite( 174 TestCollectionGenerator<V> valueCollectionGenerator) { 175 return CollectionTestSuiteBuilder.using(valueCollectionGenerator); 176 } 177 178 private static Set<Feature<?>> computeReserializedMapFeatures(Set<Feature<?>> mapFeatures) { 179 Set<Feature<?>> derivedFeatures = copyToSet(mapFeatures); 180 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 181 derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS); 182 return derivedFeatures; 183 } 184 185 private static Set<Feature<?>> computeEntrySetFeatures(Set<Feature<?>> mapFeatures) { 186 Set<Feature<?>> entrySetFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 187 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_ENTRY_QUERIES)) { 188 entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 189 } 190 return entrySetFeatures; 191 } 192 193 private static Set<Feature<?>> computeKeySetFeatures(Set<Feature<?>> mapFeatures) { 194 Set<Feature<?>> keySetFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 195 196 // TODO(lowasser): make this trigger only if the map is a submap 197 // currently, the KeySetGenerator won't work properly for a subset of a keyset of a submap 198 keySetFeatures.add(CollectionFeature.SUBSET_VIEW); 199 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) { 200 keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); 201 } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEY_QUERIES)) { 202 keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 203 } 204 205 return keySetFeatures; 206 } 207 208 private static Set<Feature<?>> computeValuesCollectionFeatures(Set<Feature<?>> mapFeatures) { 209 Set<Feature<?>> valuesCollectionFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 210 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUE_QUERIES)) { 211 valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 212 } 213 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) { 214 valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); 215 } 216 217 return valuesCollectionFeatures; 218 } 219 220 public static Set<Feature<?>> computeCommonDerivedCollectionFeatures( 221 Set<Feature<?>> mapFeatures) { 222 mapFeatures = new HashSet<>(mapFeatures); 223 Set<Feature<?>> derivedFeatures = new HashSet<>(); 224 mapFeatures.remove(CollectionFeature.SERIALIZABLE); 225 if (mapFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { 226 derivedFeatures.add(CollectionFeature.SERIALIZABLE); 227 } 228 if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) { 229 derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE); 230 } 231 if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) { 232 derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION); 233 } 234 if (mapFeatures.contains(MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION)) { 235 derivedFeatures.add(CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION); 236 } 237 // add the intersection of CollectionFeature.values() and mapFeatures 238 for (CollectionFeature feature : CollectionFeature.values()) { 239 if (mapFeatures.contains(feature)) { 240 derivedFeatures.add(feature); 241 } 242 } 243 // add the intersection of CollectionSize.values() and mapFeatures 244 for (CollectionSize size : CollectionSize.values()) { 245 if (mapFeatures.contains(size)) { 246 derivedFeatures.add(size); 247 } 248 } 249 return derivedFeatures; 250 } 251 252 private static class ReserializedMapGenerator<K, V> implements TestMapGenerator<K, V> { 253 private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator; 254 255 public ReserializedMapGenerator( 256 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 257 this.mapGenerator = mapGenerator; 258 } 259 260 @Override 261 public SampleElements<Entry<K, V>> samples() { 262 return mapGenerator.samples(); 263 } 264 265 @Override 266 public Entry<K, V>[] createArray(int length) { 267 return mapGenerator.createArray(length); 268 } 269 270 @Override 271 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 272 return mapGenerator.order(insertionOrder); 273 } 274 275 @Override 276 public Map<K, V> create(Object... elements) { 277 return SerializableTester.reserialize(mapGenerator.create(elements)); 278 } 279 280 @Override 281 public K[] createKeyArray(int length) { 282 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createKeyArray(length); 283 } 284 285 @Override 286 public V[] createValueArray(int length) { 287 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createValueArray(length); 288 } 289 } 290}