All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.testing.DerivedCollectionGenerators Maven / Gradle / Ivy

Go to download

Guava testlib is a set of java classes used for more convenient unit testing - particularly to assist the tests for Guava itself.

There is a newer version: 33.1.0-jre
Show newest version
/*
 * Copyright (C) 2008 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;

import static com.google.common.collect.testing.Helpers.castOrCopyToList;
import static com.google.common.collect.testing.Helpers.equal;
import static com.google.common.collect.testing.Helpers.mapEntry;
import static java.util.Collections.sort;

import com.google.common.annotations.GwtCompatible;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;

/**
 * Derived suite generators, split out of the suite builders so that they are available to GWT.
 *
 * @author George van den Driessche
 */
@GwtCompatible
public final class DerivedCollectionGenerators {
  public static class MapEntrySetGenerator
      implements TestSetGenerator>, DerivedGenerator {
    private final OneSizeTestContainerGenerator, Entry> mapGenerator;

    public MapEntrySetGenerator(
        OneSizeTestContainerGenerator, Entry> mapGenerator) {
      this.mapGenerator = mapGenerator;
    }

    @Override
    public SampleElements> samples() {
      return mapGenerator.samples();
    }

    @Override
    public Set> create(Object... elements) {
      return mapGenerator.create(elements).entrySet();
    }

    @Override
    public Entry[] createArray(int length) {
      return mapGenerator.createArray(length);
    }

    @Override
    public Iterable> order(List> insertionOrder) {
      return mapGenerator.order(insertionOrder);
    }

    @Override
    public OneSizeTestContainerGenerator, Entry> getInnerGenerator() {
      return mapGenerator;
    }
  }

  // TODO: investigate some API changes to SampleElements that would tidy up
  // parts of the following classes.

  static  TestSetGenerator keySetGenerator(
      OneSizeTestContainerGenerator, Entry> mapGenerator) {
    TestContainerGenerator, Entry> generator = mapGenerator.getInnerGenerator();
    if (generator instanceof TestSortedMapGenerator
        && ((TestSortedMapGenerator) generator).create().keySet() instanceof SortedSet) {
      return new MapSortedKeySetGenerator<>(mapGenerator);
    } else {
      return new MapKeySetGenerator<>(mapGenerator);
    }
  }

  public static class MapKeySetGenerator implements TestSetGenerator, DerivedGenerator {
    private final OneSizeTestContainerGenerator, Entry> mapGenerator;
    private final SampleElements samples;

    public MapKeySetGenerator(OneSizeTestContainerGenerator, Entry> mapGenerator) {
      this.mapGenerator = mapGenerator;
      final SampleElements> mapSamples = this.mapGenerator.samples();
      this.samples =
          new SampleElements(
              mapSamples.e0().getKey(),
              mapSamples.e1().getKey(),
              mapSamples.e2().getKey(),
              mapSamples.e3().getKey(),
              mapSamples.e4().getKey());
    }

    @Override
    public SampleElements samples() {
      return samples;
    }

    @Override
    public Set create(Object... elements) {
      @SuppressWarnings("unchecked")
      K[] keysArray = (K[]) elements;

      // Start with a suitably shaped collection of entries
      Collection> originalEntries = mapGenerator.getSampleElements(elements.length);

      // Create a copy of that, with the desired value for each key
      Collection> entries = new ArrayList<>(elements.length);
      int i = 0;
      for (Entry entry : originalEntries) {
        entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue()));
      }

      return mapGenerator.create(entries.toArray()).keySet();
    }

    @Override
    public K[] createArray(int length) {
      // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps
      // tidy this up and get rid of the casts here and in
      // MapValueCollectionGenerator.

      return ((TestMapGenerator) mapGenerator.getInnerGenerator()).createKeyArray(length);
    }

    @Override
    public Iterable order(List insertionOrder) {
      V v = ((TestMapGenerator) mapGenerator.getInnerGenerator()).samples().e0().getValue();
      List> entries = new ArrayList<>();
      for (K element : insertionOrder) {
        entries.add(mapEntry(element, v));
      }

      List keys = new ArrayList<>();
      for (Entry entry : mapGenerator.order(entries)) {
        keys.add(entry.getKey());
      }
      return keys;
    }

    @Override
    public OneSizeTestContainerGenerator, Entry> getInnerGenerator() {
      return mapGenerator;
    }
  }

  public static class MapSortedKeySetGenerator extends MapKeySetGenerator
      implements TestSortedSetGenerator, DerivedGenerator {
    private final TestSortedMapGenerator delegate;

    public MapSortedKeySetGenerator(
        OneSizeTestContainerGenerator, Entry> mapGenerator) {
      super(mapGenerator);
      this.delegate = (TestSortedMapGenerator) mapGenerator.getInnerGenerator();
    }

    @Override
    public SortedSet create(Object... elements) {
      return (SortedSet) super.create(elements);
    }

    @Override
    public K belowSamplesLesser() {
      return delegate.belowSamplesLesser().getKey();
    }

    @Override
    public K belowSamplesGreater() {
      return delegate.belowSamplesGreater().getKey();
    }

    @Override
    public K aboveSamplesLesser() {
      return delegate.aboveSamplesLesser().getKey();
    }

    @Override
    public K aboveSamplesGreater() {
      return delegate.aboveSamplesGreater().getKey();
    }
  }

  public static class MapValueCollectionGenerator
      implements TestCollectionGenerator, DerivedGenerator {
    private final OneSizeTestContainerGenerator, Entry> mapGenerator;
    private final SampleElements samples;

    public MapValueCollectionGenerator(
        OneSizeTestContainerGenerator, Entry> mapGenerator) {
      this.mapGenerator = mapGenerator;
      final SampleElements> mapSamples = this.mapGenerator.samples();
      this.samples =
          new SampleElements(
              mapSamples.e0().getValue(),
              mapSamples.e1().getValue(),
              mapSamples.e2().getValue(),
              mapSamples.e3().getValue(),
              mapSamples.e4().getValue());
    }

    @Override
    public SampleElements samples() {
      return samples;
    }

    @Override
    public Collection create(Object... elements) {
      @SuppressWarnings("unchecked")
      V[] valuesArray = (V[]) elements;

      // Start with a suitably shaped collection of entries
      Collection> originalEntries = mapGenerator.getSampleElements(elements.length);

      // Create a copy of that, with the desired value for each value
      Collection> entries = new ArrayList<>(elements.length);
      int i = 0;
      for (Entry entry : originalEntries) {
        entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++]));
      }

      return mapGenerator.create(entries.toArray()).values();
    }

    @Override
    public V[] createArray(int length) {
      // noinspection UnnecessaryLocalVariable
      final V[] vs =
          ((TestMapGenerator) mapGenerator.getInnerGenerator()).createValueArray(length);
      return vs;
    }

    @Override
    public Iterable order(List insertionOrder) {
      final List> orderedEntries =
          castOrCopyToList(mapGenerator.order(castOrCopyToList(mapGenerator.getSampleElements(5))));
      sort(
          insertionOrder,
          new Comparator() {
            @Override
            public int compare(V left, V right) {
              // The indexes are small enough for the subtraction trick to be safe.
              return indexOfEntryWithValue(left) - indexOfEntryWithValue(right);
            }

            int indexOfEntryWithValue(V value) {
              for (int i = 0; i < orderedEntries.size(); i++) {
                if (equal(orderedEntries.get(i).getValue(), value)) {
                  return i;
                }
              }
              throw new IllegalArgumentException(
                  "Map.values generator can order only sample values");
            }
          });
      return insertionOrder;
    }

    @Override
    public OneSizeTestContainerGenerator, Entry> getInnerGenerator() {
      return mapGenerator;
    }
  }

  // TODO(cpovirk): could something like this be used elsewhere, e.g., ReserializedListGenerator?
  static class ForwardingTestMapGenerator implements TestMapGenerator {
    TestMapGenerator delegate;

    ForwardingTestMapGenerator(TestMapGenerator delegate) {
      this.delegate = delegate;
    }

    @Override
    public Iterable> order(List> insertionOrder) {
      return delegate.order(insertionOrder);
    }

    @Override
    public K[] createKeyArray(int length) {
      return delegate.createKeyArray(length);
    }

    @Override
    public V[] createValueArray(int length) {
      return delegate.createValueArray(length);
    }

    @Override
    public SampleElements> samples() {
      return delegate.samples();
    }

    @Override
    public Map create(Object... elements) {
      return delegate.create(elements);
    }

    @Override
    public Entry[] createArray(int length) {
      return delegate.createArray(length);
    }
  }

  /** Two bounds (from and to) define how to build a subMap. */
  public enum Bound {
    INCLUSIVE,
    EXCLUSIVE,
    NO_BOUND;
  }

  public static class SortedSetSubsetTestSetGenerator implements TestSortedSetGenerator {
    final Bound to;
    final Bound from;
    final E firstInclusive;
    final E lastInclusive;
    private final Comparator comparator;
    private final TestSortedSetGenerator delegate;

    public SortedSetSubsetTestSetGenerator(
        TestSortedSetGenerator delegate, Bound to, Bound from) {
      this.to = to;
      this.from = from;
      this.delegate = delegate;

      SortedSet emptySet = delegate.create();
      this.comparator = emptySet.comparator();

      SampleElements samples = delegate.samples();
      List samplesList = new ArrayList<>(samples.asList());
      Collections.sort(samplesList, comparator);
      this.firstInclusive = samplesList.get(0);
      this.lastInclusive = samplesList.get(samplesList.size() - 1);
    }

    public final TestSortedSetGenerator getInnerGenerator() {
      return delegate;
    }

    public final Bound getTo() {
      return to;
    }

    public final Bound getFrom() {
      return from;
    }

    @Override
    public SampleElements samples() {
      return delegate.samples();
    }

    @Override
    public E[] createArray(int length) {
      return delegate.createArray(length);
    }

    @Override
    public Iterable order(List insertionOrder) {
      return delegate.order(insertionOrder);
    }

    @Override
    public SortedSet create(Object... elements) {
      List normalValues = (List) Arrays.asList(elements);
      List extremeValues = new ArrayList<>();

      // nulls are usually out of bounds for a subset, so ban them altogether
      for (Object o : elements) {
        if (o == null) {
          throw new NullPointerException();
        }
      }

      // prepare extreme values to be filtered out of view
      E firstExclusive = delegate.belowSamplesGreater();
      E lastExclusive = delegate.aboveSamplesLesser();
      if (from != Bound.NO_BOUND) {
        extremeValues.add(delegate.belowSamplesLesser());
        extremeValues.add(delegate.belowSamplesGreater());
      }
      if (to != Bound.NO_BOUND) {
        extremeValues.add(delegate.aboveSamplesLesser());
        extremeValues.add(delegate.aboveSamplesGreater());
      }

      // the regular values should be visible after filtering
      List allEntries = new ArrayList<>();
      allEntries.addAll(extremeValues);
      allEntries.addAll(normalValues);
      SortedSet set = delegate.create(allEntries.toArray());

      return createSubSet(set, firstExclusive, lastExclusive);
    }

    /** Calls the smallest subSet overload that filters out the extreme values. */
    SortedSet createSubSet(SortedSet set, E firstExclusive, E lastExclusive) {
      if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) {
        return set.headSet(lastExclusive);
      } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) {
        return set.tailSet(firstInclusive);
      } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) {
        return set.subSet(firstInclusive, lastExclusive);
      } else {
        throw new IllegalArgumentException();
      }
    }

    @Override
    public E belowSamplesLesser() {
      throw new UnsupportedOperationException();
    }

    @Override
    public E belowSamplesGreater() {
      throw new UnsupportedOperationException();
    }

    @Override
    public E aboveSamplesLesser() {
      throw new UnsupportedOperationException();
    }

    @Override
    public E aboveSamplesGreater() {
      throw new UnsupportedOperationException();
    }
  }

  /*
   * TODO(cpovirk): surely we can find a less ugly solution than a class that accepts 3 parameters,
   * exposes as many getters, does work in the constructor, and has both a superclass and a subclass
   */
  public static class SortedMapSubmapTestMapGenerator extends ForwardingTestMapGenerator
      implements TestSortedMapGenerator {
    final Bound to;
    final Bound from;
    final K firstInclusive;
    final K lastInclusive;
    private final Comparator> entryComparator;

    public SortedMapSubmapTestMapGenerator(
        TestSortedMapGenerator delegate, Bound to, Bound from) {
      super(delegate);
      this.to = to;
      this.from = from;

      SortedMap emptyMap = delegate.create();
      this.entryComparator = Helpers.entryComparator(emptyMap.comparator());

      // derive values for inclusive filtering from the input samples
      SampleElements> samples = delegate.samples();
      @SuppressWarnings("unchecked") // no elements are inserted into the array
      List> samplesList =
          Arrays.asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4());
      Collections.sort(samplesList, entryComparator);
      this.firstInclusive = samplesList.get(0).getKey();
      this.lastInclusive = samplesList.get(samplesList.size() - 1).getKey();
    }

    @Override
    public SortedMap create(Object... entries) {
      List> extremeValues = new ArrayList<>();

      // prepare extreme values to be filtered out of view
      K firstExclusive = getInnerGenerator().belowSamplesGreater().getKey();
      K lastExclusive = getInnerGenerator().aboveSamplesLesser().getKey();
      if (from != Bound.NO_BOUND) {
        extremeValues.add(getInnerGenerator().belowSamplesLesser());
        extremeValues.add(getInnerGenerator().belowSamplesGreater());
      }
      if (to != Bound.NO_BOUND) {
        extremeValues.add(getInnerGenerator().aboveSamplesLesser());
        extremeValues.add(getInnerGenerator().aboveSamplesGreater());
      }

      // the regular values should be visible after filtering
      List> allEntries = new ArrayList<>();
      allEntries.addAll(extremeValues);
      for (Object entry : entries) {
        allEntries.add((Entry) entry);
      }
      SortedMap map = (SortedMap) delegate.create(allEntries.toArray());

      return createSubMap(map, firstExclusive, lastExclusive);
    }

    /**
     * Calls the smallest subMap overload that filters out the extreme values. This method is
     * overridden in NavigableMapTestSuiteBuilder.
     */
    SortedMap createSubMap(SortedMap map, K firstExclusive, K lastExclusive) {
      if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) {
        return map.headMap(lastExclusive);
      } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) {
        return map.tailMap(firstInclusive);
      } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) {
        return map.subMap(firstInclusive, lastExclusive);
      } else {
        throw new IllegalArgumentException();
      }
    }

    public final Bound getTo() {
      return to;
    }

    public final Bound getFrom() {
      return from;
    }

    public final TestSortedMapGenerator getInnerGenerator() {
      return (TestSortedMapGenerator) delegate;
    }

    @Override
    public Entry belowSamplesLesser() {
      // should never reach here!
      throw new UnsupportedOperationException();
    }

    @Override
    public Entry belowSamplesGreater() {
      // should never reach here!
      throw new UnsupportedOperationException();
    }

    @Override
    public Entry aboveSamplesLesser() {
      // should never reach here!
      throw new UnsupportedOperationException();
    }

    @Override
    public Entry aboveSamplesGreater() {
      // should never reach here!
      throw new UnsupportedOperationException();
    }
  }

  private DerivedCollectionGenerators() {}
}