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.MapContainsKeyTester; 030import com.google.common.collect.testing.testers.MapContainsValueTester; 031import com.google.common.collect.testing.testers.MapCreationTester; 032import com.google.common.collect.testing.testers.MapEntrySetTester; 033import com.google.common.collect.testing.testers.MapEqualsTester; 034import com.google.common.collect.testing.testers.MapGetTester; 035import com.google.common.collect.testing.testers.MapHashCodeTester; 036import com.google.common.collect.testing.testers.MapIsEmptyTester; 037import com.google.common.collect.testing.testers.MapPutAllTester; 038import com.google.common.collect.testing.testers.MapPutTester; 039import com.google.common.collect.testing.testers.MapRemoveTester; 040import com.google.common.collect.testing.testers.MapSerializationTester; 041import com.google.common.collect.testing.testers.MapSizeTester; 042import com.google.common.collect.testing.testers.MapToStringTester; 043import com.google.common.testing.SerializableTester; 044import java.util.Arrays; 045import java.util.HashSet; 046import java.util.List; 047import java.util.Map; 048import java.util.Map.Entry; 049import java.util.Set; 050import junit.framework.TestSuite; 051 052/** 053 * Creates, based on your criteria, a JUnit test suite that exhaustively tests a Map implementation. 054 * 055 * @author George van den Driessche 056 */ 057@GwtIncompatible 058public class MapTestSuiteBuilder<K, V> 059 extends PerCollectionSizeTestSuiteBuilder< 060 MapTestSuiteBuilder<K, V>, TestMapGenerator<K, V>, Map<K, V>, Entry<K, V>> { 061 public static <K, V> MapTestSuiteBuilder<K, V> using(TestMapGenerator<K, V> generator) { 062 return new MapTestSuiteBuilder<K, V>().usingGenerator(generator); 063 } 064 065 @SuppressWarnings("unchecked") // Class parameters must be raw. 066 @Override 067 protected List<Class<? extends AbstractTester>> getTesters() { 068 return Arrays.<Class<? extends AbstractTester>>asList( 069 MapClearTester.class, 070 MapContainsKeyTester.class, 071 MapContainsValueTester.class, 072 MapCreationTester.class, 073 MapEntrySetTester.class, 074 MapEqualsTester.class, 075 MapGetTester.class, 076 MapHashCodeTester.class, 077 MapIsEmptyTester.class, 078 MapPutTester.class, 079 MapPutAllTester.class, 080 MapRemoveTester.class, 081 MapSerializationTester.class, 082 MapSizeTester.class, 083 MapToStringTester.class); 084 } 085 086 @Override 087 protected List<TestSuite> createDerivedSuites( 088 FeatureSpecificTestSuiteBuilder< 089 ?, ? extends OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>>> 090 parentBuilder) { 091 // TODO: Once invariant support is added, supply invariants to each of the 092 // derived suites, to check that mutations to the derived collections are 093 // reflected in the underlying map. 094 095 List<TestSuite> derivedSuites = super.createDerivedSuites(parentBuilder); 096 097 if (parentBuilder.getFeatures().contains(CollectionFeature.SERIALIZABLE)) { 098 derivedSuites.add( 099 MapTestSuiteBuilder.using( 100 new ReserializedMapGenerator<K, V>(parentBuilder.getSubjectGenerator())) 101 .withFeatures(computeReserializedMapFeatures(parentBuilder.getFeatures())) 102 .named(parentBuilder.getName() + " reserialized") 103 .suppressing(parentBuilder.getSuppressedTests()) 104 .createTestSuite()); 105 } 106 107 derivedSuites.add( 108 createDerivedEntrySetSuite( 109 new MapEntrySetGenerator<K, V>(parentBuilder.getSubjectGenerator())) 110 .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures())) 111 .named(parentBuilder.getName() + " entrySet") 112 .suppressing(parentBuilder.getSuppressedTests()) 113 .createTestSuite()); 114 115 derivedSuites.add( 116 createDerivedKeySetSuite(keySetGenerator(parentBuilder.getSubjectGenerator())) 117 .withFeatures(computeKeySetFeatures(parentBuilder.getFeatures())) 118 .named(parentBuilder.getName() + " keys") 119 .suppressing(parentBuilder.getSuppressedTests()) 120 .createTestSuite()); 121 122 derivedSuites.add( 123 createDerivedValueCollectionSuite( 124 new MapValueCollectionGenerator<K, V>(parentBuilder.getSubjectGenerator())) 125 .named(parentBuilder.getName() + " values") 126 .withFeatures(computeValuesCollectionFeatures(parentBuilder.getFeatures())) 127 .suppressing(parentBuilder.getSuppressedTests()) 128 .createTestSuite()); 129 130 return derivedSuites; 131 } 132 133 protected SetTestSuiteBuilder<Entry<K, V>> createDerivedEntrySetSuite( 134 TestSetGenerator<Entry<K, V>> entrySetGenerator) { 135 return SetTestSuiteBuilder.using(entrySetGenerator); 136 } 137 138 protected SetTestSuiteBuilder<K> createDerivedKeySetSuite(TestSetGenerator<K> keySetGenerator) { 139 return SetTestSuiteBuilder.using(keySetGenerator); 140 } 141 142 protected CollectionTestSuiteBuilder<V> createDerivedValueCollectionSuite( 143 TestCollectionGenerator<V> valueCollectionGenerator) { 144 return CollectionTestSuiteBuilder.using(valueCollectionGenerator); 145 } 146 147 private static Set<Feature<?>> computeReserializedMapFeatures(Set<Feature<?>> mapFeatures) { 148 Set<Feature<?>> derivedFeatures = Helpers.copyToSet(mapFeatures); 149 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 150 derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS); 151 return derivedFeatures; 152 } 153 154 private static Set<Feature<?>> computeEntrySetFeatures(Set<Feature<?>> mapFeatures) { 155 Set<Feature<?>> entrySetFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 156 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_ENTRY_QUERIES)) { 157 entrySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 158 } 159 return entrySetFeatures; 160 } 161 162 private static Set<Feature<?>> computeKeySetFeatures(Set<Feature<?>> mapFeatures) { 163 Set<Feature<?>> keySetFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 164 165 // TODO(lowasser): make this trigger only if the map is a submap 166 // currently, the KeySetGenerator won't work properly for a subset of a keyset of a submap 167 keySetFeatures.add(CollectionFeature.SUBSET_VIEW); 168 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEYS)) { 169 keySetFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); 170 } else if (mapFeatures.contains(MapFeature.ALLOWS_NULL_KEY_QUERIES)) { 171 keySetFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 172 } 173 174 return keySetFeatures; 175 } 176 177 private static Set<Feature<?>> computeValuesCollectionFeatures(Set<Feature<?>> mapFeatures) { 178 Set<Feature<?>> valuesCollectionFeatures = computeCommonDerivedCollectionFeatures(mapFeatures); 179 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUE_QUERIES)) { 180 valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES); 181 } 182 if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) { 183 valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES); 184 } 185 186 return valuesCollectionFeatures; 187 } 188 189 public static Set<Feature<?>> computeCommonDerivedCollectionFeatures( 190 Set<Feature<?>> mapFeatures) { 191 mapFeatures = new HashSet<>(mapFeatures); 192 Set<Feature<?>> derivedFeatures = new HashSet<>(); 193 mapFeatures.remove(CollectionFeature.SERIALIZABLE); 194 if (mapFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { 195 derivedFeatures.add(CollectionFeature.SERIALIZABLE); 196 } 197 if (mapFeatures.contains(MapFeature.SUPPORTS_REMOVE)) { 198 derivedFeatures.add(CollectionFeature.SUPPORTS_REMOVE); 199 } 200 if (mapFeatures.contains(MapFeature.REJECTS_DUPLICATES_AT_CREATION)) { 201 derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION); 202 } 203 if (mapFeatures.contains(MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION)) { 204 derivedFeatures.add(CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION); 205 } 206 // add the intersection of CollectionFeature.values() and mapFeatures 207 for (CollectionFeature feature : CollectionFeature.values()) { 208 if (mapFeatures.contains(feature)) { 209 derivedFeatures.add(feature); 210 } 211 } 212 // add the intersection of CollectionSize.values() and mapFeatures 213 for (CollectionSize size : CollectionSize.values()) { 214 if (mapFeatures.contains(size)) { 215 derivedFeatures.add(size); 216 } 217 } 218 return derivedFeatures; 219 } 220 221 private static class ReserializedMapGenerator<K, V> implements TestMapGenerator<K, V> { 222 private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator; 223 224 public ReserializedMapGenerator( 225 OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) { 226 this.mapGenerator = mapGenerator; 227 } 228 229 @Override 230 public SampleElements<Entry<K, V>> samples() { 231 return mapGenerator.samples(); 232 } 233 234 @Override 235 public Entry<K, V>[] createArray(int length) { 236 return mapGenerator.createArray(length); 237 } 238 239 @Override 240 public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) { 241 return mapGenerator.order(insertionOrder); 242 } 243 244 @Override 245 public Map<K, V> create(Object... elements) { 246 return SerializableTester.reserialize(mapGenerator.create(elements)); 247 } 248 249 @Override 250 public K[] createKeyArray(int length) { 251 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createKeyArray(length); 252 } 253 254 @Override 255 public V[] createValueArray(int length) { 256 return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createValueArray(length); 257 } 258 } 259}