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.testers;
018
019import static com.google.common.collect.testing.Helpers.copyToList;
020import static com.google.common.collect.testing.Helpers.getMethod;
021import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS;
022import static com.google.common.collect.testing.features.CollectionSize.ONE;
023import static com.google.common.collect.testing.features.CollectionSize.ZERO;
024import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_ADD_WITH_INDEX;
025import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_REMOVE_WITH_INDEX;
026import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_SET;
027import static com.google.common.testing.SerializableTester.reserializeAndAssert;
028import static java.util.Arrays.asList;
029import static java.util.Collections.emptyList;
030import static java.util.Collections.singletonList;
031import static org.junit.Assert.assertThrows;
032
033import com.google.common.annotations.GwtCompatible;
034import com.google.common.annotations.GwtIncompatible;
035import com.google.common.annotations.J2ktIncompatible;
036import com.google.common.collect.testing.features.CollectionFeature;
037import com.google.common.collect.testing.features.CollectionSize;
038import com.google.common.collect.testing.features.ListFeature;
039import java.lang.reflect.Method;
040import java.util.List;
041import java.util.concurrent.CopyOnWriteArrayList;
042
043/**
044 * A generic JUnit test which tests {@code subList()} operations on a list. Can't be invoked
045 * directly; please see {@link com.google.common.collect.testing.ListTestSuiteBuilder}.
046 *
047 * @author Chris Povirk
048 */
049@GwtCompatible
050public class ListSubListTester<E> extends AbstractListTester<E> {
051  public void testSubList_startNegative() {
052    assertThrows(IndexOutOfBoundsException.class, () -> getList().subList(-1, 0));
053  }
054
055  public void testSubList_endTooLarge() {
056    assertThrows(IndexOutOfBoundsException.class, () -> getList().subList(0, getNumElements() + 1));
057  }
058
059  public void testSubList_startGreaterThanEnd() {
060    RuntimeException expected = assertThrows(RuntimeException.class, () -> getList().subList(1, 0));
061    /*
062     * The subList() docs claim that this should be an IndexOutOfBoundsException, but many JDK
063     * implementations throw IllegalArgumentException: https://bugs.openjdk.org/browse/JDK-4506427
064     */
065    if (!(expected instanceof IndexOutOfBoundsException
066        || expected instanceof IllegalArgumentException)) {
067      throw expected;
068    }
069  }
070
071  @SuppressWarnings("EmptyList") // ImmutableList doesn't support nullable element types
072  public void testSubList_empty() {
073    assertEquals("subList(0, 0) should be empty", emptyList(), getList().subList(0, 0));
074  }
075
076  public void testSubList_entireList() {
077    assertEquals(
078        "subList(0, size) should be equal to the original list",
079        getList(),
080        getList().subList(0, getNumElements()));
081  }
082
083  @ListFeature.Require(SUPPORTS_REMOVE_WITH_INDEX)
084  @CollectionSize.Require(absent = ZERO)
085  public void testSubList_subListRemoveAffectsOriginal() {
086    List<E> subList = getList().subList(0, 1);
087    subList.remove(0);
088    List<E> expected = asList(createSamplesArray()).subList(1, getNumElements());
089    expectContents(expected);
090  }
091
092  @ListFeature.Require(SUPPORTS_REMOVE_WITH_INDEX)
093  @CollectionSize.Require(absent = ZERO)
094  public void testSubList_subListClearAffectsOriginal() {
095    List<E> subList = getList().subList(0, 1);
096    subList.clear();
097    List<E> expected = asList(createSamplesArray()).subList(1, getNumElements());
098    expectContents(expected);
099  }
100
101  @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX)
102  public void testSubList_subListAddAffectsOriginal() {
103    List<E> subList = getList().subList(0, 0);
104    subList.add(e3());
105    expectAdded(0, e3());
106  }
107
108  @ListFeature.Require(SUPPORTS_SET)
109  @CollectionSize.Require(absent = ZERO)
110  public void testSubList_subListSetAffectsOriginal() {
111    List<E> subList = getList().subList(0, 1);
112    subList.set(0, e3());
113    List<E> expected = copyToList(createSamplesArray());
114    expected.set(0, e3());
115    expectContents(expected);
116  }
117
118  @ListFeature.Require(SUPPORTS_SET)
119  @CollectionSize.Require(absent = ZERO)
120  public void testSubList_originalListSetAffectsSubList() {
121    List<E> subList = getList().subList(0, 1);
122    getList().set(0, e3());
123    assertEquals(
124        "A set() call to a list after a sublist has been created "
125            + "should be reflected in the sublist",
126        singletonList(e3()),
127        subList);
128  }
129
130  @ListFeature.Require(SUPPORTS_REMOVE_WITH_INDEX)
131  @CollectionSize.Require(absent = {ZERO, ONE})
132  public void testSubList_subListRemoveAffectsOriginalLargeList() {
133    List<E> subList = getList().subList(1, 3);
134    subList.remove(e2());
135    List<E> expected = copyToList(createSamplesArray());
136    expected.remove(2);
137    expectContents(expected);
138  }
139
140  @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX)
141  @CollectionSize.Require(absent = {ZERO, ONE})
142  public void testSubList_subListAddAtIndexAffectsOriginalLargeList() {
143    List<E> subList = getList().subList(2, 3);
144    subList.add(0, e3());
145    expectAdded(2, e3());
146  }
147
148  @ListFeature.Require(SUPPORTS_SET)
149  @CollectionSize.Require(absent = {ZERO, ONE})
150  public void testSubList_subListSetAffectsOriginalLargeList() {
151    List<E> subList = getList().subList(1, 2);
152    subList.set(0, e3());
153    List<E> expected = copyToList(createSamplesArray());
154    expected.set(1, e3());
155    expectContents(expected);
156  }
157
158  @ListFeature.Require(SUPPORTS_SET)
159  @CollectionSize.Require(absent = {ZERO, ONE})
160  public void testSubList_originalListSetAffectsSubListLargeList() {
161    List<E> subList = getList().subList(1, 3);
162    getList().set(1, e3());
163    assertEquals(
164        "A set() call to a list after a sublist has been created "
165            + "should be reflected in the sublist",
166        asList(e3(), e2()),
167        subList);
168  }
169
170  @SuppressWarnings("EmptyList") // ImmutableList doesn't support nullable element types
171  public void testSubList_ofSubListEmpty() {
172    List<E> subList = getList().subList(0, 0).subList(0, 0);
173    assertEquals("subList(0, 0).subList(0, 0) should be an empty list", emptyList(), subList);
174  }
175
176  @CollectionSize.Require(absent = {ZERO, ONE})
177  public void testSubList_ofSubListNonEmpty() {
178    List<E> subList = getList().subList(0, 2).subList(1, 2);
179    assertEquals(
180        "subList(0, 2).subList(1, 2) "
181            + "should be a single-element list of the element at index 1",
182        singletonList(getOrderedElements().get(1)),
183        subList);
184  }
185
186  @CollectionSize.Require(absent = {ZERO})
187  public void testSubList_size() {
188    List<E> list = getList();
189    int size = getNumElements();
190    assertEquals(size, list.subList(0, size).size());
191    assertEquals(size - 1, list.subList(0, size - 1).size());
192    assertEquals(size - 1, list.subList(1, size).size());
193    assertEquals(0, list.subList(size, size).size());
194    assertEquals(0, list.subList(0, 0).size());
195  }
196
197  @CollectionSize.Require(absent = {ZERO})
198  public void testSubList_isEmpty() {
199    List<E> list = getList();
200    int size = getNumElements();
201    for (List<E> subList :
202        asList(
203            list.subList(0, size),
204            list.subList(0, size - 1),
205            list.subList(1, size),
206            list.subList(0, 0),
207            list.subList(size, size))) {
208      assertEquals(subList.size() == 0, subList.isEmpty());
209    }
210  }
211
212  @CollectionSize.Require(absent = {ZERO, ONE})
213  public void testSubList_get() {
214    List<E> list = getList();
215    int size = getNumElements();
216    List<E> copy = list.subList(0, size);
217    List<E> head = list.subList(0, size - 1);
218    List<E> tail = list.subList(1, size);
219    assertEquals(list.get(0), copy.get(0));
220    assertEquals(list.get(size - 1), copy.get(size - 1));
221    assertEquals(list.get(1), tail.get(0));
222    assertEquals(list.get(size - 1), tail.get(size - 2));
223    assertEquals(list.get(0), head.get(0));
224    assertEquals(list.get(size - 2), head.get(size - 2));
225    for (List<E> subList : asList(copy, head, tail)) {
226      for (int index : asList(-1, subList.size())) {
227        assertThrows(IndexOutOfBoundsException.class, () -> subList.get(index));
228      }
229    }
230  }
231
232  @CollectionSize.Require(absent = {ZERO, ONE})
233  public void testSubList_contains() {
234    List<E> list = getList();
235    int size = getNumElements();
236    List<E> copy = list.subList(0, size);
237    List<E> head = list.subList(0, size - 1);
238    List<E> tail = list.subList(1, size);
239    assertTrue(copy.contains(list.get(0)));
240    assertTrue(head.contains(list.get(0)));
241    assertTrue(tail.contains(list.get(1)));
242    // The following assumes all elements are distinct.
243    assertTrue(copy.contains(list.get(size - 1)));
244    assertTrue(head.contains(list.get(size - 2)));
245    assertTrue(tail.contains(list.get(size - 1)));
246    assertFalse(head.contains(list.get(size - 1)));
247    assertFalse(tail.contains(list.get(0)));
248  }
249
250  @CollectionSize.Require(absent = {ZERO, ONE})
251  public void testSubList_indexOf() {
252    List<E> list = getList();
253    int size = getNumElements();
254    List<E> copy = list.subList(0, size);
255    List<E> head = list.subList(0, size - 1);
256    List<E> tail = list.subList(1, size);
257    assertEquals(0, copy.indexOf(list.get(0)));
258    assertEquals(0, head.indexOf(list.get(0)));
259    assertEquals(0, tail.indexOf(list.get(1)));
260    // The following assumes all elements are distinct.
261    assertEquals(size - 1, copy.indexOf(list.get(size - 1)));
262    assertEquals(size - 2, head.indexOf(list.get(size - 2)));
263    assertEquals(size - 2, tail.indexOf(list.get(size - 1)));
264    assertEquals(-1, head.indexOf(list.get(size - 1)));
265    assertEquals(-1, tail.indexOf(list.get(0)));
266  }
267
268  @CollectionSize.Require(absent = {ZERO, ONE})
269  public void testSubList_lastIndexOf() {
270    List<E> list = getList();
271    int size = list.size();
272    List<E> copy = list.subList(0, size);
273    List<E> head = list.subList(0, size - 1);
274    List<E> tail = list.subList(1, size);
275    assertEquals(size - 1, copy.lastIndexOf(list.get(size - 1)));
276    assertEquals(size - 2, head.lastIndexOf(list.get(size - 2)));
277    assertEquals(size - 2, tail.lastIndexOf(list.get(size - 1)));
278    // The following assumes all elements are distinct.
279    assertEquals(0, copy.lastIndexOf(list.get(0)));
280    assertEquals(0, head.lastIndexOf(list.get(0)));
281    assertEquals(0, tail.lastIndexOf(list.get(1)));
282    assertEquals(-1, head.lastIndexOf(list.get(size - 1)));
283    assertEquals(-1, tail.lastIndexOf(list.get(0)));
284  }
285
286  @CollectionFeature.Require(SERIALIZABLE_INCLUDING_VIEWS)
287  public void testReserializeWholeSubList() {
288    reserializeAndAssert(getList().subList(0, getNumElements()));
289  }
290
291  @CollectionFeature.Require(SERIALIZABLE_INCLUDING_VIEWS)
292  public void testReserializeEmptySubList() {
293    reserializeAndAssert(getList().subList(0, 0));
294  }
295
296  @CollectionFeature.Require(SERIALIZABLE_INCLUDING_VIEWS)
297  @CollectionSize.Require(absent = {ZERO, ONE})
298  public void testReserializeSubList() {
299    reserializeAndAssert(getList().subList(0, 2));
300  }
301
302  /**
303   * Returns the {@link Method} instance for {@link #testSubList_originalListSetAffectsSubList()} so
304   * that tests of {@link CopyOnWriteArrayList} can suppress them with {@code
305   * FeatureSpecificTestSuiteBuilder.suppressing()} until <a
306   * href="https://bugs.openjdk.org/browse/JDK-6570631">JDK-6570631</a> is fixed.
307   */
308  @J2ktIncompatible
309  @GwtIncompatible // reflection
310  public static Method getSubListOriginalListSetAffectsSubListMethod() {
311    return getMethod(ListSubListTester.class, "testSubList_originalListSetAffectsSubList");
312  }
313
314  /**
315   * Returns the {@link Method} instance for {@link
316   * #testSubList_originalListSetAffectsSubListLargeList()} so that tests of {@link
317   * CopyOnWriteArrayList} can suppress them with {@code
318   * FeatureSpecificTestSuiteBuilder.suppressing()} until <a
319   * href="https://bugs.openjdk.org/browse/JDK-6570631">JDK-6570631</a> is fixed.
320   */
321  @J2ktIncompatible
322  @GwtIncompatible // reflection
323  public static Method getSubListOriginalListSetAffectsSubListLargeListMethod() {
324    return getMethod(ListSubListTester.class, "testSubList_originalListSetAffectsSubListLargeList");
325  }
326
327  /**
328   * Returns the {@link Method} instance for {@link
329   * #testSubList_subListRemoveAffectsOriginalLargeList()} so that tests of {@link
330   * CopyOnWriteArrayList} can suppress it with {@code
331   * FeatureSpecificTestSuiteBuilder.suppressing()} until <a
332   * href="https://bugs.openjdk.org/browse/JDK-6570575">JDK-6570575</a> is fixed.
333   */
334  @J2ktIncompatible
335  @GwtIncompatible // reflection
336  public static Method getSubListSubListRemoveAffectsOriginalLargeListMethod() {
337    return getMethod(ListSubListTester.class, "testSubList_subListRemoveAffectsOriginalLargeList");
338  }
339
340  /*
341   * TODO: perform all List tests on subList(), but beware infinite recursion
342   */
343}