private TestSuite createDescendingSuite(SortedMultisetTestSuiteBuilder<E> parentBuilder) {
    final TestMultisetGenerator<E> delegate =
        (TestMultisetGenerator<E>) parentBuilder.getSubjectGenerator();

    Set<Feature<?>> features = new HashSet<Feature<?>>();
    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<Feature<?>>();
    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();
  }
  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;
  }
  public static Test suite() {
    TestSuite suite = new TestSuite();
    suite.addTestSuite(ImmutableSortedMultisetTest.class);

    suite.addTest(
        SortedMultisetTestSuiteBuilder.using(
                new TestStringMultisetGenerator() {
                  @Override
                  protected Multiset<String> create(String[] elements) {
                    return ImmutableSortedMultiset.copyOf(elements);
                  }

                  @Override
                  public List<String> order(List<String> insertionOrder) {
                    return Ordering.natural().sortedCopy(insertionOrder);
                  }
                })
            .named("ImmutableSortedMultiset")
            .withFeatures(
                CollectionSize.ANY,
                CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS,
                CollectionFeature.ALLOWS_NULL_QUERIES)
            .createTestSuite());

    suite.addTest(
        SetTestSuiteBuilder.using(
                new TestStringSetGenerator() {
                  @Override
                  protected Set<String> create(String[] elements) {
                    return SerializableTester.reserialize(
                        ImmutableSortedMultiset.copyOf(elements).elementSet());
                  }

                  @Override
                  public List<String> order(List<String> insertionOrder) {
                    return Ordering.natural().immutableSortedCopy(insertionOrder);
                  }
                })
            .named("ImmutableSortedMultiset, element set")
            .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES)
            .createTestSuite());

    return suite;
  }
 public static <E> SortedMultisetTestSuiteBuilder<E> using(TestMultisetGenerator<E> generator) {
   SortedMultisetTestSuiteBuilder<E> result = new SortedMultisetTestSuiteBuilder<E>();
   result.usingGenerator(generator);
   return result;
 }
  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<Feature<?>>();
    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();
  }