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