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; 021import static com.google.common.testing.SerializableTester.reserialize; 022import static java.util.Arrays.asList; 023 024import com.google.common.annotations.GwtIncompatible; 025import com.google.common.collect.testing.DerivedCollectionGenerators.MapEntrySetGenerator; 026import com.google.common.collect.testing.DerivedCollectionGenerators.MapValueCollectionGenerator; 027import com.google.common.collect.testing.features.CollectionFeature; 028import com.google.common.collect.testing.features.CollectionSize; 029import com.google.common.collect.testing.features.Feature; 030import com.google.common.collect.testing.features.MapFeature; 031import com.google.common.collect.testing.testers.MapClearTester; 032import com.google.common.collect.testing.testers.MapComputeIfAbsentTester; 033import com.google.common.collect.testing.testers.MapComputeIfPresentTester; 034import com.google.common.collect.testing.testers.MapComputeTester; 035import com.google.common.collect.testing.testers.MapContainsKeyTester; 036import com.google.common.collect.testing.testers.MapContainsValueTester; 037import com.google.common.collect.testing.testers.MapCreationTester; 038import com.google.common.collect.testing.testers.MapEntrySetTester; 039import com.google.common.collect.testing.testers.MapEqualsTester; 040import com.google.common.collect.testing.testers.MapForEachTester; 041import com.google.common.collect.testing.testers.MapGetOrDefaultTester; 042import com.google.common.collect.testing.testers.MapGetTester; 043import com.google.common.collect.testing.testers.MapHashCodeTester; 044import com.google.common.collect.testing.testers.MapIsEmptyTester; 045import com.google.common.collect.testing.testers.MapMergeTester; 046import com.google.common.collect.testing.testers.MapPutAllTester; 047import com.google.common.collect.testing.testers.MapPutIfAbsentTester; 048import com.google.common.collect.testing.testers.MapPutTester; 049import com.google.common.collect.testing.testers.MapRemoveEntryTester; 050import com.google.common.collect.testing.testers.MapRemoveTester; 051import com.google.common.collect.testing.testers.MapReplaceAllTester; 052import com.google.common.collect.testing.testers.MapReplaceEntryTester; 053import com.google.common.collect.testing.testers.MapReplaceTester; 054import com.google.common.collect.testing.testers.MapSerializationTester; 055import com.google.common.collect.testing.testers.MapSizeTester; 056import com.google.common.collect.testing.testers.MapToStringTester; 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 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 using(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 = 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 final class ReserializedMapGenerator<K, V> implements TestMapGenerator<K, V> { 252 private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator; 253 254 ReserializedMapGenerator(OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 255 this.mapGenerator = mapGenerator; 256 } 257 258 @Override 259 public SampleElements<Entry<K, V>> samples() { 260 return mapGenerator.samples(); 261 } 262 263 @Override 264 public Entry<K, V>[] createArray(int length) { 265 return mapGenerator.createArray(length); 266 } 267 268 @Override 269 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 270 return mapGenerator.order(insertionOrder); 271 } 272 273 @Override 274 public Map<K, V> create(Object... elements) { 275 return reserialize(mapGenerator.create(elements)); 276 } 277 278 @Override 279 public K[] createKeyArray(int length) { 280 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createKeyArray(length); 281 } 282 283 @Override 284 public V[] createValueArray(int length) { 285 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createValueArray(length); 286 } 287 } 288}