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