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 17package com.google.common.collect.testing.google; 18 19import static com.google.common.base.Preconditions.checkArgument; 20 21import com.google.common.collect.Multiset; 22import com.google.common.collect.Multiset.Entry; 23import com.google.common.collect.Multisets; 24import com.google.common.collect.testing.AbstractCollectionTestSuiteBuilder; 25import com.google.common.collect.testing.AbstractTester; 26import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; 27import com.google.common.collect.testing.Helpers; 28import com.google.common.collect.testing.OneSizeTestContainerGenerator; 29import com.google.common.collect.testing.SampleElements; 30import com.google.common.collect.testing.SetTestSuiteBuilder; 31import com.google.common.collect.testing.TestSetGenerator; 32import com.google.common.collect.testing.features.CollectionFeature; 33import com.google.common.collect.testing.features.Feature; 34import com.google.common.collect.testing.testers.CollectionSerializationEqualTester; 35import com.google.common.testing.SerializableTester; 36 37import junit.framework.TestSuite; 38 39import java.util.ArrayList; 40import java.util.Collection; 41import java.util.Collections; 42import java.util.HashSet; 43import java.util.LinkedHashMap; 44import java.util.LinkedHashSet; 45import java.util.List; 46import java.util.Map; 47import java.util.Set; 48 49/** 50 * Creates, based on your criteria, a JUnit test suite that exhaustively tests 51 * a {@code Multiset} implementation. 52 * 53 * @author Jared Levy 54 * @author Louis Wasserman 55 */ 56public class MultisetTestSuiteBuilder<E> extends 57 AbstractCollectionTestSuiteBuilder<MultisetTestSuiteBuilder<E>, E> { 58 public static <E> MultisetTestSuiteBuilder<E> using( 59 TestMultisetGenerator<E> generator) { 60 return new MultisetTestSuiteBuilder<E>().usingGenerator(generator); 61 } 62 63 public enum NoRecurse implements Feature<Void> { 64 NO_ENTRY_SET; 65 66 @Override 67 public Set<Feature<? super Void>> getImpliedFeatures() { 68 return Collections.emptySet(); 69 } 70 } 71 72 @Override protected List<Class<? extends AbstractTester>> getTesters() { 73 List<Class<? extends AbstractTester>> testers 74 = Helpers.copyToList(super.getTesters()); 75 testers.add(CollectionSerializationEqualTester.class); 76 testers.add(MultisetAddTester.class); 77 testers.add(MultisetContainsTester.class); 78 testers.add(MultisetCountTester.class); 79 testers.add(MultisetElementSetTester.class); 80 testers.add(MultisetEqualsTester.class); 81 testers.add(MultisetReadsTester.class); 82 testers.add(MultisetSetCountConditionallyTester.class); 83 testers.add(MultisetSetCountUnconditionallyTester.class); 84 testers.add(MultisetRemoveTester.class); 85 testers.add(MultisetEntrySetTester.class); 86 testers.add(MultisetIteratorTester.class); 87 testers.add(MultisetSerializationTester.class); 88 return testers; 89 } 90 91 private static Set<Feature<?>> computeEntrySetFeatures( 92 Set<Feature<?>> features) { 93 Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>(); 94 derivedFeatures.addAll(features); 95 derivedFeatures.remove(CollectionFeature.GENERAL_PURPOSE); 96 derivedFeatures.remove(CollectionFeature.SUPPORTS_ADD); 97 derivedFeatures.remove(CollectionFeature.ALLOWS_NULL_VALUES); 98 derivedFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION); 99 if (!derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { 100 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 101 } 102 return derivedFeatures; 103 } 104 105 static Set<Feature<?>> computeElementSetFeatures( 106 Set<Feature<?>> features) { 107 Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>(); 108 derivedFeatures.addAll(features); 109 derivedFeatures.remove(CollectionFeature.GENERAL_PURPOSE); 110 derivedFeatures.remove(CollectionFeature.SUPPORTS_ADD); 111 if (!derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { 112 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 113 } 114 return derivedFeatures; 115 } 116 117 private static Set<Feature<?>> computeReserializedMultisetFeatures( 118 Set<Feature<?>> features) { 119 Set<Feature<?>> derivedFeatures = new HashSet<Feature<?>>(); 120 derivedFeatures.addAll(features); 121 derivedFeatures.remove(CollectionFeature.SERIALIZABLE); 122 derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS); 123 return derivedFeatures; 124 } 125 126 @Override 127 protected List<TestSuite> createDerivedSuites(FeatureSpecificTestSuiteBuilder< 128 ?, ? extends OneSizeTestContainerGenerator<Collection<E>, E>> parentBuilder) { 129 List<TestSuite> derivedSuites = new ArrayList<TestSuite>( 130 super.createDerivedSuites(parentBuilder)); 131 132 derivedSuites.add(createElementSetTestSuite(parentBuilder)); 133 134 if (!parentBuilder.getFeatures().contains(NoRecurse.NO_ENTRY_SET)) { 135 derivedSuites.add( 136 SetTestSuiteBuilder.using(new EntrySetGenerator<E>(parentBuilder.getSubjectGenerator())) 137 .named(getName() + ".entrySet") 138 .withFeatures(computeEntrySetFeatures(parentBuilder.getFeatures())) 139 .suppressing(parentBuilder.getSuppressedTests()) 140 .createTestSuite()); 141 } 142 143 if (parentBuilder.getFeatures().contains(CollectionFeature.SERIALIZABLE)) { 144 derivedSuites.add(MultisetTestSuiteBuilder 145 .using(new ReserializedMultisetGenerator<E>(parentBuilder.getSubjectGenerator())) 146 .named(getName() + " reserialized") 147 .withFeatures(computeReserializedMultisetFeatures(parentBuilder.getFeatures())) 148 .suppressing(parentBuilder.getSuppressedTests()) 149 .createTestSuite()); 150 } 151 return derivedSuites; 152 } 153 154 TestSuite createElementSetTestSuite(FeatureSpecificTestSuiteBuilder< 155 ?, ? extends OneSizeTestContainerGenerator<Collection<E>, E>> parentBuilder) { 156 return SetTestSuiteBuilder 157 .using(new ElementSetGenerator<E>(parentBuilder.getSubjectGenerator())) 158 .named(getName() + ".elementSet") 159 .withFeatures(computeElementSetFeatures(parentBuilder.getFeatures())) 160 .suppressing(parentBuilder.getSuppressedTests()) 161 .createTestSuite(); 162 } 163 164 static class ElementSetGenerator<E> implements TestSetGenerator<E> { 165 final OneSizeTestContainerGenerator<Collection<E>, E> gen; 166 167 ElementSetGenerator(OneSizeTestContainerGenerator<Collection<E>, E> gen) { 168 this.gen = gen; 169 } 170 171 @Override 172 public SampleElements<E> samples() { 173 return gen.samples(); 174 } 175 176 @Override 177 public Set<E> create(Object... elements) { 178 Object[] duplicated = new Object[elements.length * 2]; 179 for (int i = 0; i < elements.length; i++) { 180 duplicated[i] = elements[i]; 181 duplicated[i + elements.length] = elements[i]; 182 } 183 return ((Multiset<E>) gen.create(duplicated)).elementSet(); 184 } 185 186 @Override 187 public E[] createArray(int length) { 188 return gen.createArray(length); 189 } 190 191 @Override 192 public Iterable<E> order(List<E> insertionOrder) { 193 return gen.order(new ArrayList<E>(new LinkedHashSet<E>(insertionOrder))); 194 } 195 } 196 197 static class EntrySetGenerator<E> implements TestSetGenerator<Multiset.Entry<E>> { 198 final OneSizeTestContainerGenerator<Collection<E>, E> gen; 199 200 private EntrySetGenerator(OneSizeTestContainerGenerator<Collection<E>, E> gen) { 201 this.gen = gen; 202 } 203 204 @Override 205 public SampleElements<Multiset.Entry<E>> samples() { 206 SampleElements<E> samples = gen.samples(); 207 return new SampleElements<Multiset.Entry<E>>( 208 Multisets.immutableEntry(samples.e0, 3), 209 Multisets.immutableEntry(samples.e1, 4), 210 Multisets.immutableEntry(samples.e2, 1), 211 Multisets.immutableEntry(samples.e3, 5), 212 Multisets.immutableEntry(samples.e4, 2)); 213 } 214 215 @Override 216 public Set<Multiset.Entry<E>> create(Object... entries) { 217 List<Object> contents = new ArrayList<Object>(); 218 Set<E> elements = new HashSet<E>(); 219 for (Object o : entries) { 220 @SuppressWarnings("unchecked") 221 Multiset.Entry<E> entry = (Entry<E>) o; 222 checkArgument(elements.add(entry.getElement()), 223 "Duplicate keys not allowed in EntrySetGenerator"); 224 for (int i = 0; i < entry.getCount(); i++) { 225 contents.add(entry.getElement()); 226 } 227 } 228 return ((Multiset<E>) gen.create(contents.toArray())).entrySet(); 229 } 230 231 @SuppressWarnings("unchecked") 232 @Override 233 public Multiset.Entry<E>[] createArray(int length) { 234 return new Multiset.Entry[length]; 235 } 236 237 @Override 238 public Iterable<Entry<E>> order(List<Entry<E>> insertionOrder) { 239 // We mimic the order from gen. 240 Map<E, Entry<E>> map = new LinkedHashMap<E, Entry<E>>(); 241 for (Entry<E> entry : insertionOrder) { 242 map.put(entry.getElement(), entry); 243 } 244 245 Set<E> seen = new HashSet<E>(); 246 List<Entry<E>> order = new ArrayList<Entry<E>>(); 247 for (E e : gen.order(new ArrayList<E>(map.keySet()))) { 248 if (seen.add(e)) { 249 order.add(map.get(e)); 250 } 251 } 252 return order; 253 } 254 } 255 256 static class ReserializedMultisetGenerator<E> implements TestMultisetGenerator<E>{ 257 final OneSizeTestContainerGenerator<Collection<E>, E> gen; 258 259 private ReserializedMultisetGenerator(OneSizeTestContainerGenerator<Collection<E>, E> gen) { 260 this.gen = gen; 261 } 262 263 @Override 264 public SampleElements<E> samples() { 265 return gen.samples(); 266 } 267 268 @Override 269 public Multiset<E> create(Object... elements) { 270 return (Multiset<E>) SerializableTester.reserialize(gen.create(elements)); 271 } 272 273 @Override 274 public E[] createArray(int length) { 275 return gen.createArray(length); 276 } 277 278 @Override 279 public Iterable<E> order(List<E> insertionOrder) { 280 return gen.order(insertionOrder); 281 } 282 } 283} 284 285