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; 020 021import com.google.common.annotations.GwtIncompatible; 022import com.google.common.collect.testing.DerivedCollectionGenerators.MapEntrySetGenerator; 023import com.google.common.collect.testing.DerivedCollectionGenerators.MapValueCollectionGenerator; 024import com.google.common.collect.testing.features.CollectionFeature; 025import com.google.common.collect.testing.features.CollectionSize; 026import com.google.common.collect.testing.features.Feature; 027import com.google.common.collect.testing.features.MapFeature; 028import com.google.common.collect.testing.testers.MapClearTester; 029import com.google.common.collect.testing.testers.MapComputeIfAbsentTester; 030import com.google.common.collect.testing.testers.MapComputeIfPresentTester; 031import com.google.common.collect.testing.testers.MapComputeTester; 032import com.google.common.collect.testing.testers.MapContainsKeyTester; 033import com.google.common.collect.testing.testers.MapContainsValueTester; 034import com.google.common.collect.testing.testers.MapCreationTester; 035import com.google.common.collect.testing.testers.MapEntrySetTester; 036import com.google.common.collect.testing.testers.MapEqualsTester; 037import com.google.common.collect.testing.testers.MapForEachTester; 038import com.google.common.collect.testing.testers.MapGetOrDefaultTester; 039import com.google.common.collect.testing.testers.MapGetTester; 040import com.google.common.collect.testing.testers.MapHashCodeTester; 041import com.google.common.collect.testing.testers.MapIsEmptyTester; 042import com.google.common.collect.testing.testers.MapMergeTester; 043import com.google.common.collect.testing.testers.MapPutAllTester; 044import com.google.common.collect.testing.testers.MapPutIfAbsentTester; 045import com.google.common.collect.testing.testers.MapPutTester; 046import com.google.common.collect.testing.testers.MapRemoveEntryTester; 047import com.google.common.collect.testing.testers.MapRemoveTester; 048import com.google.common.collect.testing.testers.MapReplaceAllTester; 049import com.google.common.collect.testing.testers.MapReplaceEntryTester; 050import com.google.common.collect.testing.testers.MapReplaceTester; 051import com.google.common.collect.testing.testers.MapSerializationTester; 052import com.google.common.collect.testing.testers.MapSizeTester; 053import com.google.common.collect.testing.testers.MapToStringTester; 054import com.google.common.testing.SerializableTester; 055import java.util.Arrays; 056import java.util.HashSet; 057import java.util.List; 058import java.util.Map; 059import java.util.Map.Entry; 060import java.util.Set; 061import junit.framework.TestSuite; 062 063/** 064 * Creates, based on your criteria, a JUnit test suite that exhaustively tests a Map implementation. 065 * 066 * @author George van den Driessche 067 */ 068@GwtIncompatible 069public class MapTestSuiteBuilder<K, V> 070 extends PerCollectionSizeTestSuiteBuilder< 071 MapTestSuiteBuilder<K, V>, TestMapGenerator<K, V>, Map<K, V>, Entry<K, V>> { 072 public static <K, V> MapTestSuiteBuilder<K, V> using(TestMapGenerator<K, V> generator) { 073 return new MapTestSuiteBuilder<K, V>().usingGenerator(generator); 074 } 075 076 @SuppressWarnings("rawtypes") // class literals 077 @Override 078 protected List<Class<? extends AbstractTester>> getTesters() { 079 return Arrays.<Class<? extends AbstractTester>>asList( 080 MapClearTester.class, 081 MapComputeTester.class, 082 MapComputeIfAbsentTester.class, 083 MapComputeIfPresentTester.class, 084 MapContainsKeyTester.class, 085 MapContainsValueTester.class, 086 MapCreationTester.class, 087 MapEntrySetTester.class, 088 MapEqualsTester.class, 089 MapForEachTester.class, 090 MapGetTester.class, 091 MapGetOrDefaultTester.class, 092 MapHashCodeTester.class, 093 MapIsEmptyTester.class, 094 MapMergeTester.class, 095 MapPutTester.class, 096 MapPutAllTester.class, 097 MapPutIfAbsentTester.class, 098 MapRemoveTester.class, 099 MapRemoveEntryTester.class, 100 MapReplaceTester.class, 101 MapReplaceAllTester.class, 102 MapReplaceEntryTester.class, 103 MapSerializationTester.class, 104 MapSizeTester.class, 105 MapToStringTester.class); 106 } 107 108 @Override 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 163 protected SetTestSuiteBuilder<Entry<K, V>> createDerivedEntrySetSuite( 164 TestSetGenerator<Entry<K, V>> entrySetGenerator) { 165 return SetTestSuiteBuilder.using(entrySetGenerator); 166 } 167 168 protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) { 169 return SetTestSuiteBuilder.using(keySetGenerator); 170 } 171 172 protected CollectionTestSuiteBuilder<V> createDerivedValueCollectionSuite( 173 TestCollectionGenerator<V> valueCollectionGenerator) { 174 return CollectionTestSuiteBuilder.using(valueCollectionGenerator); 175 } 176 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 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 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 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 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 254 public ReserializedMapGenerator( 255 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 256 this.mapGenerator = mapGenerator; 257 } 258 259 @Override 260 public SampleElements<Entry<K, V>> samples() { 261 return mapGenerator.samples(); 262 } 263 264 @Override 265 public Entry<K, V>[] createArray(int length) { 266 return mapGenerator.createArray(length); 267 } 268 269 @Override 270 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 271 return mapGenerator.order(insertionOrder); 272 } 273 274 @Override 275 public Map<K, V> create(Object... elements) { 276 return SerializableTester.reserialize(mapGenerator.create(elements)); 277 } 278 279 @Override 280 public K[] createKeyArray(int length) { 281 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createKeyArray(length); 282 } 283 284 @Override 285 public V[] createValueArray(int length) { 286 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createValueArray(length); 287 } 288 } 289}