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