001/*
002 * Copyright (C) 2008 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.collect.testing;
018
019import com.google.common.annotations.GwtCompatible;
020import com.google.errorprone.annotations.CanIgnoreReturnValue;
021import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.List;
027import org.checkerframework.checker.nullness.qual.Nullable;
028import org.junit.Ignore;
029
030/**
031 * Base class for testers of classes (including {@link Collection} and {@link java.util.Map Map})
032 * that contain elements.
033 *
034 * @param <C> the type of the container
035 * @param <E> the type of the container's contents
036 * @author George van den Driessche
037 */
038@GwtCompatible
039@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
040@SuppressWarnings("JUnit4ClassUsedInJUnit3")
041@ElementTypesAreNonnullByDefault
042public abstract class AbstractContainerTester<C, E extends @Nullable Object>
043    extends AbstractTester<OneSizeTestContainerGenerator<C, E>> {
044  protected SampleElements<E> samples;
045  protected C container;
046
047  @Override
048  @OverridingMethodsMustInvokeSuper
049  public void setUp() throws Exception {
050    super.setUp();
051    samples = this.getSubjectGenerator().samples();
052    resetContainer();
053  }
054
055  /**
056   * @return the contents of the container under test, for use by {@link #expectContents(Object[])
057   *     expectContents(E...)} and its friends.
058   */
059  protected abstract Collection<E> actualContents();
060
061  /**
062   * Replaces the existing container under test with a new container created by the subject
063   * generator.
064   *
065   * @see #resetContainer(Object) resetContainer(C)
066   * @return the new container instance.
067   */
068  @CanIgnoreReturnValue
069  protected C resetContainer() {
070    return resetContainer(getSubjectGenerator().createTestSubject());
071  }
072
073  /**
074   * Replaces the existing container under test with a new container. This is useful when a single
075   * test method needs to create multiple containers while retaining the ability to use {@link
076   * #expectContents(Object[]) expectContents(E...)} and other convenience methods. The creation of
077   * multiple containers in a single method is discouraged in most cases, but it is vital to the
078   * iterator tests.
079   *
080   * @return the new container instance
081   * @param newValue the new container instance
082   */
083  @CanIgnoreReturnValue
084  protected C resetContainer(C newValue) {
085    container = newValue;
086    return container;
087  }
088
089  /**
090   * @see #expectContents(java.util.Collection)
091   * @param elements expected contents of {@link #container}
092   */
093  protected final void expectContents(E... elements) {
094    expectContents(Arrays.asList(elements));
095  }
096
097  /**
098   * Asserts that the collection under test contains exactly the given elements, respecting
099   * cardinality but not order. Subclasses may override this method to provide stronger assertions,
100   * e.g., to check ordering in lists, but realize that <strong>unless a test extends {@link
101   * com.google.common.collect.testing.testers.AbstractListTester AbstractListTester}, a call to
102   * {@code expectContents()} invokes this version</strong>.
103   *
104   * @param expected expected value of {@link #container}
105   */
106  /*
107   * TODO: improve this and other implementations and move out of this framework
108   * for wider use
109   *
110   * TODO: could we incorporate the overriding logic from AbstractListTester, by
111   * examining whether the features include KNOWN_ORDER?
112   */
113  protected void expectContents(Collection<E> expected) {
114    Helpers.assertEqualIgnoringOrder(expected, actualContents());
115  }
116
117  protected void expectUnchanged() {
118    expectContents(getOrderedElements());
119  }
120
121  /**
122   * Asserts that the collection under test contains exactly the elements it was initialized with
123   * plus the given elements, according to {@link #expectContents(java.util.Collection)}. In other
124   * words, for the default {@code expectContents()} implementation, the number of occurrences of
125   * each given element has increased by one since the test collection was created, and the number
126   * of occurrences of all other elements has not changed.
127   *
128   * <p>Note: This means that a test like the following will fail if {@code collection} is a {@code
129   * Set}:
130   *
131   * <pre>
132   * collection.add(existingElement);
133   * expectAdded(existingElement);</pre>
134   *
135   * <p>In this case, {@code collection} was not modified as a result of the {@code add()} call, and
136   * the test will fail because the number of occurrences of {@code existingElement} is unchanged.
137   *
138   * @param elements expected additional contents of {@link #container}
139   */
140  protected final void expectAdded(E... elements) {
141    List<E> expected = Helpers.copyToList(getSampleElements());
142    expected.addAll(Arrays.asList(elements));
143    expectContents(expected);
144  }
145
146  protected final void expectAdded(int index, E... elements) {
147    expectAdded(index, Arrays.asList(elements));
148  }
149
150  protected final void expectAdded(int index, Collection<E> elements) {
151    List<E> expected = Helpers.copyToList(getSampleElements());
152    expected.addAll(index, elements);
153    expectContents(expected);
154  }
155
156  /*
157   * TODO: if we're testing a list, we could check indexOf(). (Doing it in
158   * AbstractListTester isn't enough because many tests that run on lists don't
159   * extends AbstractListTester.) We could also iterate over all elements to
160   * verify absence
161   */
162  protected void expectMissing(E... elements) {
163    for (E element : elements) {
164      assertFalse("Should not contain " + element, actualContents().contains(element));
165    }
166  }
167
168  protected E[] createSamplesArray() {
169    E[] array = getSubjectGenerator().createArray(getNumElements());
170    getSampleElements().toArray(array);
171    return array;
172  }
173
174  protected E[] createOrderedArray() {
175    E[] array = getSubjectGenerator().createArray(getNumElements());
176    getOrderedElements().toArray(array);
177    return array;
178  }
179
180  public static class ArrayWithDuplicate<E extends @Nullable Object> {
181    public final E[] elements;
182    public final E duplicate;
183
184    private ArrayWithDuplicate(E[] elements, E duplicate) {
185      this.elements = elements;
186      this.duplicate = duplicate;
187    }
188  }
189
190  /**
191   * @return an array of the proper size with a duplicate element. The size must be at least three.
192   */
193  protected ArrayWithDuplicate<E> createArrayWithDuplicateElement() {
194    E[] elements = createSamplesArray();
195    E duplicate = elements[(elements.length / 2) - 1];
196    elements[(elements.length / 2) + 1] = duplicate;
197    return new ArrayWithDuplicate<>(elements, duplicate);
198  }
199
200  // Helper methods to improve readability of derived classes
201
202  protected int getNumElements() {
203    return getSubjectGenerator().getCollectionSize().getNumElements();
204  }
205
206  protected Collection<E> getSampleElements(int howMany) {
207    return getSubjectGenerator().getSampleElements(howMany);
208  }
209
210  protected Collection<E> getSampleElements() {
211    return getSampleElements(getNumElements());
212  }
213
214  /**
215   * Returns the {@linkplain #getSampleElements() sample elements} as ordered by {@link
216   * TestContainerGenerator#order(List)}. Tests should use this method only if they declare
217   * requirement {@link com.google.common.collect.testing.features.CollectionFeature#KNOWN_ORDER}.
218   */
219  protected List<E> getOrderedElements() {
220    List<E> list = new ArrayList<>();
221    for (E e : getSubjectGenerator().order(new ArrayList<E>(getSampleElements()))) {
222      list.add(e);
223    }
224    return Collections.unmodifiableList(list);
225  }
226
227  /**
228   * @return a suitable location for a null element, to use when initializing containers for tests
229   *     that involve a null element being present.
230   */
231  protected int getNullLocation() {
232    return getNumElements() / 2;
233  }
234
235  protected MinimalCollection<E> createDisjointCollection() {
236    return MinimalCollection.of(e3(), e4());
237  }
238
239  protected MinimalCollection<E> emptyCollection() {
240    return MinimalCollection.<E>of();
241  }
242
243  protected final E e0() {
244    return samples.e0();
245  }
246
247  protected final E e1() {
248    return samples.e1();
249  }
250
251  protected final E e2() {
252    return samples.e2();
253  }
254
255  protected final E e3() {
256    return samples.e3();
257  }
258
259  protected final E e4() {
260    return samples.e4();
261  }
262}