AbstractContainerTester.java revision dbd967a6e5c96cc1a97c5521f88dc1564ba2f81b
1/*
2 * Copyright (C) 2008 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect.testing;
18
19import java.util.ArrayList;
20import java.util.Arrays;
21import java.util.Collection;
22import java.util.Collections;
23import java.util.List;
24
25/**
26 * Base class for testers of classes (including {@link Collection}
27 * and {@link java.util.Map Map}) that contain elements.
28 *
29 * <p>This class is GWT compatible.
30 *
31 * @param <C> the type of the container
32 * @param <E> the type of the container's contents
33 *
34 * @author George van den Driessche
35 */
36public abstract class AbstractContainerTester<C, E>
37    extends AbstractTester<OneSizeTestContainerGenerator<C, E>> {
38  protected SampleElements<E> samples;
39  protected C container;
40
41  @Override public void setUp() throws Exception {
42    super.setUp();
43    samples = this.getSubjectGenerator().samples();
44    resetContainer();
45  }
46
47  /**
48   * @return the contents of the container under test, for use by
49   * {@link #expectContents(Object[]) expectContents(E...)} and its friends.
50   */
51  protected abstract Collection<E> actualContents();
52
53  /**
54   * Replaces the existing container under test with a new container created
55   * by the subject generator.
56   *
57   * @see #resetContainer(Object) resetContainer(C)
58   *
59   * @return the new container instance.
60   */
61  protected C resetContainer() {
62    return resetContainer(getSubjectGenerator().createTestSubject());
63  }
64
65  /**
66   * Replaces the existing container under test with a new container.
67   * This is useful when a single test method needs to create multiple
68   * containers while retaining the ability to use
69   * {@link #expectContents(Object[]) expectContents(E...)} and other
70   * convenience methods. The creation of multiple containers in a single
71   * method is discouraged in most cases, but it is vital to the iterator tests.
72   *
73   * @return the new container instance
74   * @param newValue the new container instance
75   */
76  protected C resetContainer(C newValue) {
77    container = newValue;
78    return container;
79  }
80
81  /**
82   * @see #expectContents(java.util.Collection)
83   *
84   * @param elements expected contents of {@link #container}
85   */
86  protected final void expectContents(E... elements) {
87    expectContents(Arrays.asList(elements));
88  }
89
90  /**
91   * Asserts that the collection under test contains exactly the given elements,
92   * respecting cardinality but not order. Subclasses may override this method
93   * to provide stronger assertions, e.g., to check ordering in lists, but
94   * realize that <strong>unless a test extends
95   * {@link com.google.common.collect.testing.testers.AbstractListTester
96   * AbstractListTester}, a call to {@code expectContents()} invokes this
97   * version</strong>.
98   *
99   * @param expected expected value of {@link #container}
100   */
101  /*
102   * TODO: improve this and other implementations and move out of this framework
103   * for wider use
104   *
105   * TODO: could we incorporate the overriding logic from AbstractListTester, by
106   * examining whether the features include KNOWN_ORDER?
107   */
108  protected void expectContents(Collection<E> expected) {
109    Helpers.assertEqualIgnoringOrder(expected, actualContents());
110  }
111
112  protected void expectUnchanged() {
113    expectContents(getSampleElements());
114  }
115
116  /**
117   * Asserts that the collection under test contains exactly the elements it was
118   * initialized with plus the given elements, according to
119   * {@link #expectContents(java.util.Collection)}. In other words, for the
120   * default {@code expectContents()} implementation, the number of occurrences
121   * of each given element has increased by one since the test collection was
122   * created, and the number of occurrences of all other elements has not
123   * changed.
124   *
125   * <p>Note: This means that a test like the following will fail if
126   * {@code collection} is a {@code Set}:
127   *
128   * <pre>
129   * collection.add(existingElement);
130   * expectAdded(existingElement);</pre>
131   *
132   * In this case, {@code collection} was not modified as a result of the
133   * {@code add()} call, and the test will fail because the number of
134   * occurrences of {@code existingElement} is unchanged.
135   *
136   * @param elements expected additional contents of {@link #container}
137   */
138  protected final void expectAdded(E... elements) {
139    List<E> expected = Helpers.copyToList(getSampleElements());
140    expected.addAll(Arrays.asList(elements));
141    expectContents(expected);
142  }
143
144  protected final void expectAdded(int index, E... elements) {
145    expectAdded(index, Arrays.asList(elements));
146  }
147
148  protected final void expectAdded(int index, Collection<E> elements) {
149    List<E> expected = Helpers.copyToList(getSampleElements());
150    expected.addAll(index, elements);
151    expectContents(expected);
152  }
153
154  /*
155   * TODO: if we're testing a list, we could check indexOf(). (Doing it in
156   * AbstractListTester isn't enough because many tests that run on lists don't
157   * extends AbstractListTester.) We could also iterate over all elements to
158   * verify absence
159   */
160  protected void expectMissing(E... elements) {
161    for (E element : elements) {
162      assertFalse("Should not contain " + element,
163          actualContents().contains(element));
164    }
165  }
166
167  protected E[] createSamplesArray() {
168    E[] array = getSubjectGenerator().createArray(getNumElements());
169    getSampleElements().toArray(array);
170    return array;
171  }
172
173  public static class ArrayWithDuplicate<E> {
174    public final E[] elements;
175    public final E duplicate;
176
177    private ArrayWithDuplicate(E[] elements, E duplicate) {
178      this.elements = elements;
179      this.duplicate = duplicate;
180    }
181  }
182
183  /**
184   * @return an array of the proper size with a duplicate element.
185   * The size must be at least three.
186   */
187  protected ArrayWithDuplicate<E> createArrayWithDuplicateElement() {
188    E[] elements = createSamplesArray();
189    E duplicate = elements[(elements.length / 2) - 1];
190    elements[(elements.length / 2) + 1] = duplicate;
191    return new ArrayWithDuplicate<E>(elements, duplicate);
192  }
193
194  // Helper methods to improve readability of derived classes
195
196  protected int getNumElements() {
197    return getSubjectGenerator().getCollectionSize().getNumElements();
198  }
199
200  protected Collection<E> getSampleElements(int howMany) {
201    return getSubjectGenerator().getSampleElements(howMany);
202  }
203
204  protected Collection<E> getSampleElements() {
205    return getSampleElements(getNumElements());
206  }
207
208  /**
209   * Returns the {@linkplain #getSampleElements() sample elements} as ordered by
210   * {@link TestContainerGenerator#order(List)}. Tests should used this method
211   * only if they declare requirement {@link
212   * com.google.common.collect.testing.features.CollectionFeature#KNOWN_ORDER}.
213   */
214  protected List<E> getOrderedElements() {
215    List<E> list = new ArrayList<E>();
216    for (E e : getSubjectGenerator().order(
217        new ArrayList<E>(getSampleElements()))) {
218      list.add(e);
219    }
220    return Collections.unmodifiableList(list);
221  }
222
223  /**
224   * @return a suitable location for a null element, to use when initializing
225   * containers for tests that involve a null element being present.
226   */
227  protected int getNullLocation() {
228    return getNumElements() / 2;
229  }
230
231  @SuppressWarnings("unchecked")
232  protected MinimalCollection<E> createDisjointCollection() {
233    return MinimalCollection.of(samples.e3, samples.e4);
234  }
235
236  @SuppressWarnings("unchecked")
237  protected MinimalCollection<E> emptyCollection() {
238    return MinimalCollection.<E>of();
239  }
240}
241