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