| /* |
| * Copyright (C) 2011 The Guava Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package com.google.common.collect.testing.google; |
| |
| import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; |
| import static com.google.common.collect.testing.features.CollectionFeature.RESTRICTS_ELEMENTS; |
| import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; |
| import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS; |
| |
| import com.google.common.annotations.GwtIncompatible; |
| import com.google.common.collect.BoundType; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Multiset; |
| import com.google.common.collect.SortedMultiset; |
| import com.google.common.collect.testing.AbstractTester; |
| import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; |
| import com.google.common.collect.testing.Helpers; |
| import com.google.common.collect.testing.OneSizeTestContainerGenerator; |
| import com.google.common.collect.testing.SampleElements; |
| import com.google.common.collect.testing.SetTestSuiteBuilder; |
| import com.google.common.collect.testing.features.Feature; |
| import com.google.common.testing.SerializableTester; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import junit.framework.TestSuite; |
| |
| /** |
| * Creates, based on your criteria, a JUnit test suite that exhaustively tests a {@code |
| * SortedMultiset} implementation. |
| * |
| * <p><b>Warning:</b> expects that {@code E} is a String. |
| * |
| * @author Louis Wasserman |
| */ |
| @GwtIncompatible |
| public class SortedMultisetTestSuiteBuilder<E> extends MultisetTestSuiteBuilder<E> { |
| public static <E> SortedMultisetTestSuiteBuilder<E> using(TestMultisetGenerator<E> generator) { |
| SortedMultisetTestSuiteBuilder<E> result = new SortedMultisetTestSuiteBuilder<E>(); |
| result.usingGenerator(generator); |
| return result; |
| } |
| |
| @Override |
| public TestSuite createTestSuite() { |
| withFeatures(KNOWN_ORDER); |
| TestSuite suite = super.createTestSuite(); |
| for (TestSuite subSuite : createDerivedSuites(this)) { |
| suite.addTest(subSuite); |
| } |
| return suite; |
| } |
| |
| @Override |
| protected List<Class<? extends AbstractTester>> getTesters() { |
| List<Class<? extends AbstractTester>> testers = Helpers.copyToList(super.getTesters()); |
| testers.add(MultisetNavigationTester.class); |
| return testers; |
| } |
| |
| @Override |
| TestSuite createElementSetTestSuite( |
| FeatureSpecificTestSuiteBuilder<?, ? extends OneSizeTestContainerGenerator<Collection<E>, E>> |
| parentBuilder) { |
| // TODO(lowasser): make a SortedElementSetGenerator |
| return SetTestSuiteBuilder.using( |
| new ElementSetGenerator<E>(parentBuilder.getSubjectGenerator())) |
| .named(getName() + ".elementSet") |
| .withFeatures(computeElementSetFeatures(parentBuilder.getFeatures())) |
| .suppressing(parentBuilder.getSuppressedTests()) |
| .createTestSuite(); |
| } |
| |
| /** |
| * To avoid infinite recursion, test suites with these marker features won't have derived suites |
| * created for them. |
| */ |
| enum NoRecurse implements Feature<Void> { |
| SUBMULTISET, |
| DESCENDING; |
| |
| @Override |
| public Set<Feature<? super Void>> getImpliedFeatures() { |
| return Collections.emptySet(); |
| } |
| } |
| |
| /** Two bounds (from and to) define how to build a subMultiset. */ |
| enum Bound { |
| INCLUSIVE, |
| EXCLUSIVE, |
| NO_BOUND; |
| } |
| |
| List<TestSuite> createDerivedSuites(SortedMultisetTestSuiteBuilder<E> parentBuilder) { |
| List<TestSuite> derivedSuites = Lists.newArrayList(); |
| |
| if (!parentBuilder.getFeatures().contains(NoRecurse.DESCENDING)) { |
| derivedSuites.add(createDescendingSuite(parentBuilder)); |
| } |
| |
| if (parentBuilder.getFeatures().contains(SERIALIZABLE)) { |
| derivedSuites.add(createReserializedSuite(parentBuilder)); |
| } |
| |
| if (!parentBuilder.getFeatures().contains(NoRecurse.SUBMULTISET)) { |
| derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.NO_BOUND, Bound.EXCLUSIVE)); |
| derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.NO_BOUND, Bound.INCLUSIVE)); |
| derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.EXCLUSIVE, Bound.NO_BOUND)); |
| derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.EXCLUSIVE, Bound.EXCLUSIVE)); |
| derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.EXCLUSIVE, Bound.INCLUSIVE)); |
| derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.INCLUSIVE, Bound.NO_BOUND)); |
| derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.INCLUSIVE, Bound.EXCLUSIVE)); |
| derivedSuites.add(createSubMultisetSuite(parentBuilder, Bound.INCLUSIVE, Bound.INCLUSIVE)); |
| } |
| |
| return derivedSuites; |
| } |
| |
| private TestSuite createSubMultisetSuite( |
| SortedMultisetTestSuiteBuilder<E> parentBuilder, final Bound from, final Bound to) { |
| final TestMultisetGenerator<E> delegate = |
| (TestMultisetGenerator<E>) parentBuilder.getSubjectGenerator(); |
| |
| Set<Feature<?>> features = new HashSet<>(); |
| features.add(NoRecurse.SUBMULTISET); |
| features.add(RESTRICTS_ELEMENTS); |
| features.addAll(parentBuilder.getFeatures()); |
| |
| if (!features.remove(SERIALIZABLE_INCLUDING_VIEWS)) { |
| features.remove(SERIALIZABLE); |
| } |
| |
| SortedMultiset<E> emptyMultiset = (SortedMultiset<E>) delegate.create(); |
| final Comparator<? super E> comparator = emptyMultiset.comparator(); |
| SampleElements<E> samples = delegate.samples(); |
| @SuppressWarnings("unchecked") |
| List<E> samplesList = |
| Arrays.asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4()); |
| |
| Collections.sort(samplesList, comparator); |
| final E firstInclusive = samplesList.get(0); |
| final E lastInclusive = samplesList.get(samplesList.size() - 1); |
| |
| return SortedMultisetTestSuiteBuilder.using( |
| new ForwardingTestMultisetGenerator<E>(delegate) { |
| @Override |
| public SortedMultiset<E> create(Object... entries) { |
| @SuppressWarnings("unchecked") |
| // we dangerously assume E is a string |
| List<E> extremeValues = (List) getExtremeValues(); |
| @SuppressWarnings("unchecked") |
| // map generators must past entry objects |
| List<E> normalValues = (List) Arrays.asList(entries); |
| |
| // prepare extreme values to be filtered out of view |
| Collections.sort(extremeValues, comparator); |
| E firstExclusive = extremeValues.get(1); |
| E lastExclusive = extremeValues.get(2); |
| if (from == Bound.NO_BOUND) { |
| extremeValues.remove(0); |
| extremeValues.remove(0); |
| } |
| if (to == Bound.NO_BOUND) { |
| extremeValues.remove(extremeValues.size() - 1); |
| extremeValues.remove(extremeValues.size() - 1); |
| } |
| |
| // the regular values should be visible after filtering |
| List<E> allEntries = new ArrayList<E>(); |
| allEntries.addAll(extremeValues); |
| allEntries.addAll(normalValues); |
| SortedMultiset<E> multiset = |
| (SortedMultiset<E>) delegate.create(allEntries.toArray()); |
| |
| // call the smallest subMap overload that filters out the extreme |
| // values |
| if (from == Bound.INCLUSIVE) { |
| multiset = multiset.tailMultiset(firstInclusive, BoundType.CLOSED); |
| } else if (from == Bound.EXCLUSIVE) { |
| multiset = multiset.tailMultiset(firstExclusive, BoundType.OPEN); |
| } |
| |
| if (to == Bound.INCLUSIVE) { |
| multiset = multiset.headMultiset(lastInclusive, BoundType.CLOSED); |
| } else if (to == Bound.EXCLUSIVE) { |
| multiset = multiset.headMultiset(lastExclusive, BoundType.OPEN); |
| } |
| |
| return multiset; |
| } |
| }) |
| .named(parentBuilder.getName() + " subMultiset " + from + "-" + to) |
| .withFeatures(features) |
| .suppressing(parentBuilder.getSuppressedTests()) |
| .createTestSuite(); |
| } |
| |
| /** |
| * Returns an array of four bogus elements that will always be too high or too low for the |
| * display. This includes two values for each extreme. |
| * |
| * <p>This method (dangerously) assume that the strings {@code "!! a"} and {@code "~~ z"} will |
| * work for this purpose, which may cause problems for navigable maps with non-string or unicode |
| * generators. |
| */ |
| private List<String> getExtremeValues() { |
| List<String> result = new ArrayList<>(); |
| result.add("!! a"); |
| result.add("!! b"); |
| result.add("~~ y"); |
| result.add("~~ z"); |
| return result; |
| } |
| |
| private TestSuite createDescendingSuite(SortedMultisetTestSuiteBuilder<E> parentBuilder) { |
| final TestMultisetGenerator<E> delegate = |
| (TestMultisetGenerator<E>) parentBuilder.getSubjectGenerator(); |
| |
| Set<Feature<?>> features = new HashSet<>(); |
| features.add(NoRecurse.DESCENDING); |
| features.addAll(parentBuilder.getFeatures()); |
| if (!features.remove(SERIALIZABLE_INCLUDING_VIEWS)) { |
| features.remove(SERIALIZABLE); |
| } |
| |
| return SortedMultisetTestSuiteBuilder.using( |
| new ForwardingTestMultisetGenerator<E>(delegate) { |
| @Override |
| public SortedMultiset<E> create(Object... entries) { |
| return ((SortedMultiset<E>) super.create(entries)).descendingMultiset(); |
| } |
| |
| @Override |
| public Iterable<E> order(List<E> insertionOrder) { |
| return ImmutableList.copyOf(super.order(insertionOrder)).reverse(); |
| } |
| }) |
| .named(parentBuilder.getName() + " descending") |
| .withFeatures(features) |
| .suppressing(parentBuilder.getSuppressedTests()) |
| .createTestSuite(); |
| } |
| |
| private TestSuite createReserializedSuite(SortedMultisetTestSuiteBuilder<E> parentBuilder) { |
| final TestMultisetGenerator<E> delegate = |
| (TestMultisetGenerator<E>) parentBuilder.getSubjectGenerator(); |
| |
| Set<Feature<?>> features = new HashSet<>(); |
| features.addAll(parentBuilder.getFeatures()); |
| features.remove(SERIALIZABLE); |
| features.remove(SERIALIZABLE_INCLUDING_VIEWS); |
| |
| return SortedMultisetTestSuiteBuilder.using( |
| new ForwardingTestMultisetGenerator<E>(delegate) { |
| @Override |
| public SortedMultiset<E> create(Object... entries) { |
| return SerializableTester.reserialize(((SortedMultiset<E>) super.create(entries))); |
| } |
| }) |
| .named(parentBuilder.getName() + " reserialized") |
| .withFeatures(features) |
| .suppressing(parentBuilder.getSuppressedTests()) |
| .createTestSuite(); |
| } |
| |
| private static class ForwardingTestMultisetGenerator<E> implements TestMultisetGenerator<E> { |
| private final TestMultisetGenerator<E> delegate; |
| |
| ForwardingTestMultisetGenerator(TestMultisetGenerator<E> delegate) { |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public SampleElements<E> samples() { |
| return delegate.samples(); |
| } |
| |
| @Override |
| public E[] createArray(int length) { |
| return delegate.createArray(length); |
| } |
| |
| @Override |
| public Iterable<E> order(List<E> insertionOrder) { |
| return delegate.order(insertionOrder); |
| } |
| |
| @Override |
| public Multiset<E> create(Object... elements) { |
| return delegate.create(elements); |
| } |
| } |
| } |