• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.google.common.collect.testing.google;
18 
19 import com.google.common.collect.BoundType;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.Lists;
22 import com.google.common.collect.Multiset;
23 import com.google.common.collect.SortedMultiset;
24 import com.google.common.collect.testing.AbstractTester;
25 import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder;
26 import com.google.common.collect.testing.Helpers;
27 import com.google.common.collect.testing.OneSizeTestContainerGenerator;
28 import com.google.common.collect.testing.SampleElements;
29 import com.google.common.collect.testing.SetTestSuiteBuilder;
30 import com.google.common.collect.testing.features.CollectionFeature;
31 import com.google.common.collect.testing.features.Feature;
32 import com.google.common.testing.SerializableTester;
33 
34 import junit.framework.TestSuite;
35 
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.Comparator;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Set;
44 
45 /**
46  * Creates, based on your criteria, a JUnit test suite that exhaustively tests a
47  * {@code SortedMultiset} implementation.
48  *
49  * <p><b>Warning:</b> expects that {@code E} is a String.
50  *
51  * @author Louis Wasserman
52  */
53 public class SortedMultisetTestSuiteBuilder<E> extends
54     MultisetTestSuiteBuilder<E> {
using( TestMultisetGenerator<E> generator)55   public static <E> SortedMultisetTestSuiteBuilder<E> using(
56       TestMultisetGenerator<E> generator) {
57     SortedMultisetTestSuiteBuilder<E> result =
58         new SortedMultisetTestSuiteBuilder<E>();
59     result.usingGenerator(generator);
60     return result;
61   }
62 
63   @Override
createTestSuite()64   public TestSuite createTestSuite() {
65     withFeatures(CollectionFeature.KNOWN_ORDER);
66     TestSuite suite = super.createTestSuite();
67     for (TestSuite subSuite : createDerivedSuites(this)) {
68       suite.addTest(subSuite);
69     }
70     return suite;
71   }
72 
73   @Override
getTesters()74   protected List<Class<? extends AbstractTester>> getTesters() {
75     List<Class<? extends AbstractTester>> testers =
76         Helpers.copyToList(super.getTesters());
77     testers.add(MultisetNavigationTester.class);
78     return testers;
79   }
80 
81   @Override
createElementSetTestSuite(FeatureSpecificTestSuiteBuilder< ?, ? extends OneSizeTestContainerGenerator<Collection<E>, E>> parentBuilder)82   TestSuite createElementSetTestSuite(FeatureSpecificTestSuiteBuilder<
83       ?, ? extends OneSizeTestContainerGenerator<Collection<E>, E>> parentBuilder) {
84     // TODO(user): make a SortedElementSetGenerator
85     return SetTestSuiteBuilder
86         .using(new ElementSetGenerator<E>(parentBuilder.getSubjectGenerator()))
87         .named(getName() + ".elementSet")
88         .withFeatures(computeElementSetFeatures(parentBuilder.getFeatures()))
89         .suppressing(parentBuilder.getSuppressedTests())
90         .createTestSuite();
91   }
92 
93   /**
94    * To avoid infinite recursion, test suites with these marker features won't
95    * have derived suites created for them.
96    */
97   enum NoRecurse implements Feature<Void> {
98     SUBMULTISET, DESCENDING;
99 
100     @Override
getImpliedFeatures()101     public Set<Feature<? super Void>> getImpliedFeatures() {
102       return Collections.emptySet();
103     }
104   }
105 
106   /**
107    * Two bounds (from and to) define how to build a subMultiset.
108    */
109   enum Bound {
110     INCLUSIVE, EXCLUSIVE, NO_BOUND;
111   }
112 
createDerivedSuites( SortedMultisetTestSuiteBuilder<E> parentBuilder)113   List<TestSuite> createDerivedSuites(
114       SortedMultisetTestSuiteBuilder<E> parentBuilder) {
115     List<TestSuite> derivedSuites = Lists.newArrayList();
116 
117     if (!parentBuilder.getFeatures().contains(NoRecurse.DESCENDING)) {
118       derivedSuites.add(createDescendingSuite(parentBuilder));
119     }
120 
121     if (parentBuilder.getFeatures().contains(CollectionFeature.SERIALIZABLE)) {
122       derivedSuites.add(createReserializedSuite(parentBuilder));
123     }
124 
125     if (!parentBuilder.getFeatures().contains(NoRecurse.SUBMULTISET)) {
126       derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.NO_BOUND,
127           Bound.EXCLUSIVE));
128       derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.NO_BOUND,
129           Bound.INCLUSIVE));
130       derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.EXCLUSIVE,
131           Bound.NO_BOUND));
132       derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.EXCLUSIVE,
133           Bound.EXCLUSIVE));
134       derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.EXCLUSIVE,
135           Bound.INCLUSIVE));
136       derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.INCLUSIVE,
137           Bound.NO_BOUND));
138       derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.INCLUSIVE,
139           Bound.EXCLUSIVE));
140       derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.INCLUSIVE,
141           Bound.INCLUSIVE));
142     }
143 
144     return derivedSuites;
145   }
146 
createSubMultisetSuite( SortedMultisetTestSuiteBuilder<E> parentBuilder, final Bound from, final Bound to)147   private TestSuite createSubMultisetSuite(
148       SortedMultisetTestSuiteBuilder<E> parentBuilder, final Bound from,
149       final Bound to) {
150     final TestMultisetGenerator<E> delegate =
151         (TestMultisetGenerator<E>) parentBuilder.getSubjectGenerator();
152 
153     Set<Feature<?>> features = new HashSet<Feature<?>>();
154     features.add(NoRecurse.SUBMULTISET);
155     features.add(CollectionFeature.RESTRICTS_ELEMENTS);
156     features.addAll(parentBuilder.getFeatures());
157 
158     if (!features.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) {
159       features.remove(CollectionFeature.SERIALIZABLE);
160     }
161 
162     SortedMultiset<E> emptyMultiset = (SortedMultiset<E>) delegate.create();
163     final Comparator<? super E> comparator = emptyMultiset.comparator();
164     SampleElements<E> samples = delegate.samples();
165     @SuppressWarnings("unchecked")
166     List<E> samplesList =
167         Arrays.asList(samples.e0, samples.e1, samples.e2, samples.e3,
168             samples.e4);
169 
170     Collections.sort(samplesList, comparator);
171     final E firstInclusive = samplesList.get(0);
172     final E lastInclusive = samplesList.get(samplesList.size() - 1);
173 
174     return SortedMultisetTestSuiteBuilder
175         .using(new ForwardingTestMultisetGenerator<E>(delegate) {
176           @Override
177           public SortedMultiset<E> create(Object... entries) {
178             @SuppressWarnings("unchecked")
179             // we dangerously assume E is a string
180             List<E> extremeValues = (List) getExtremeValues();
181             @SuppressWarnings("unchecked")
182             // map generators must past entry objects
183             List<E> normalValues = (List) Arrays.asList(entries);
184 
185             // prepare extreme values to be filtered out of view
186             Collections.sort(extremeValues, comparator);
187             E firstExclusive = extremeValues.get(1);
188             E lastExclusive = extremeValues.get(2);
189             if (from == Bound.NO_BOUND) {
190               extremeValues.remove(0);
191               extremeValues.remove(0);
192             }
193             if (to == Bound.NO_BOUND) {
194               extremeValues.remove(extremeValues.size() - 1);
195               extremeValues.remove(extremeValues.size() - 1);
196             }
197 
198             // the regular values should be visible after filtering
199             List<E> allEntries = new ArrayList<E>();
200             allEntries.addAll(extremeValues);
201             allEntries.addAll(normalValues);
202             SortedMultiset<E> multiset =
203                 (SortedMultiset<E>) delegate.create(allEntries.toArray());
204 
205             // call the smallest subMap overload that filters out the extreme
206             // values
207             if (from == Bound.INCLUSIVE) {
208               multiset =
209                   multiset.tailMultiset(firstInclusive, BoundType.CLOSED);
210             } else if (from == Bound.EXCLUSIVE) {
211               multiset = multiset.tailMultiset(firstExclusive, BoundType.OPEN);
212             }
213 
214             if (to == Bound.INCLUSIVE) {
215               multiset = multiset.headMultiset(lastInclusive, BoundType.CLOSED);
216             } else if (to == Bound.EXCLUSIVE) {
217               multiset = multiset.headMultiset(lastExclusive, BoundType.OPEN);
218             }
219 
220             return multiset;
221           }
222         })
223         .named(parentBuilder.getName() + " subMultiset " + from + "-" + to)
224         .withFeatures(features)
225         .suppressing(parentBuilder.getSuppressedTests())
226         .createTestSuite();
227   }
228 
229   /**
230    * Returns an array of four bogus elements that will always be too high or too
231    * low for the display. This includes two values for each extreme.
232    *
233    * <p>
234    * This method (dangerously) assume that the strings {@code "!! a"} and
235    * {@code "~~ z"} will work for this purpose, which may cause problems for
236    * navigable maps with non-string or unicode generators.
237    */
238   private List<String> getExtremeValues() {
239     List<String> result = new ArrayList<String>();
240     result.add("!! a");
241     result.add("!! b");
242     result.add("~~ y");
243     result.add("~~ z");
244     return result;
245   }
246 
247   private TestSuite createDescendingSuite(
248       SortedMultisetTestSuiteBuilder<E> parentBuilder) {
249     final TestMultisetGenerator<E> delegate =
250         (TestMultisetGenerator<E>) parentBuilder.getSubjectGenerator();
251 
252     Set<Feature<?>> features = new HashSet<Feature<?>>();
253     features.add(NoRecurse.DESCENDING);
254     features.addAll(parentBuilder.getFeatures());
255     if (!features.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) {
256       features.remove(CollectionFeature.SERIALIZABLE);
257     }
258 
259     return SortedMultisetTestSuiteBuilder
260         .using(new ForwardingTestMultisetGenerator<E>(delegate) {
261           @Override
262           public SortedMultiset<E> create(Object... entries) {
263             return ((SortedMultiset<E>) super.create(entries))
264                 .descendingMultiset();
265           }
266 
267           @Override
268           public Iterable<E> order(List<E> insertionOrder) {
269             return ImmutableList.copyOf(super.order(insertionOrder)).reverse();
270           }
271         })
272         .named(parentBuilder.getName() + " descending")
273         .withFeatures(features)
274         .suppressing(parentBuilder.getSuppressedTests())
275         .createTestSuite();
276   }
277 
278   private TestSuite createReserializedSuite(
279       SortedMultisetTestSuiteBuilder<E> parentBuilder) {
280     final TestMultisetGenerator<E> delegate =
281         (TestMultisetGenerator<E>) parentBuilder.getSubjectGenerator();
282 
283     Set<Feature<?>> features = new HashSet<Feature<?>>();
284     features.addAll(parentBuilder.getFeatures());
285     features.remove(CollectionFeature.SERIALIZABLE);
286     features.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS);
287 
288     return SortedMultisetTestSuiteBuilder
289         .using(new ForwardingTestMultisetGenerator<E>(delegate) {
290           @Override
291           public SortedMultiset<E> create(Object... entries) {
292             return SerializableTester.reserialize(((SortedMultiset<E>) super.create(entries)));
293           }
294         })
295         .named(parentBuilder.getName() + " reserialized")
296         .withFeatures(features)
297         .suppressing(parentBuilder.getSuppressedTests())
298         .createTestSuite();
299   }
300 
301   private static class ForwardingTestMultisetGenerator<E>
302       implements TestMultisetGenerator<E> {
303     private final TestMultisetGenerator<E> delegate;
304 
305     ForwardingTestMultisetGenerator(TestMultisetGenerator<E> delegate) {
306       this.delegate = delegate;
307     }
308 
309     @Override
310     public SampleElements<E> samples() {
311       return delegate.samples();
312     }
313 
314     @Override
315     public E[] createArray(int length) {
316       return delegate.createArray(length);
317     }
318 
319     @Override
320     public Iterable<E> order(List<E> insertionOrder) {
321       return delegate.order(insertionOrder);
322     }
323 
324     @Override
325     public Multiset<E> create(Object... elements) {
326       return delegate.create(elements);
327     }
328   }
329 }
330