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.assertEqualIgnoringOrder;
020import static com.google.common.collect.testing.Helpers.copyToList;
021import static com.google.common.collect.testing.Helpers.mapEntry;
022import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE;
023import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE;
024import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES;
025import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER;
026import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE;
027import static com.google.common.collect.testing.features.CollectionSize.ZERO;
028import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows;
029import static java.util.Arrays.asList;
030
031import com.google.common.annotations.GwtCompatible;
032import com.google.common.collect.testing.AbstractCollectionTester;
033import com.google.common.collect.testing.IteratorFeature;
034import com.google.common.collect.testing.IteratorTester;
035import com.google.common.collect.testing.features.CollectionFeature;
036import com.google.common.collect.testing.features.CollectionSize;
037import java.util.ArrayList;
038import java.util.Iterator;
039import java.util.List;
040import java.util.Map.Entry;
041import java.util.NoSuchElementException;
042import java.util.Set;
043import org.jspecify.annotations.NullMarked;
044import org.jspecify.annotations.Nullable;
045import org.junit.Ignore;
046
047/**
048 * A generic JUnit test which tests {@code iterator} operations on a collection. Can't be invoked
049 * directly; please see {@link com.google.common.collect.testing.CollectionTestSuiteBuilder}.
050 *
051 * @author Chris Povirk
052 */
053@GwtCompatible(emulated = true)
054@Ignore("test runners must not instantiate and run this directly, only via suites we build")
055// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
056@SuppressWarnings("JUnit4ClassUsedInJUnit3")
057@NullMarked
058public class CollectionIteratorTester<E extends @Nullable Object>
059    extends AbstractCollectionTester<E> {
060  public void testIterator() {
061    List<E> iteratorElements = new ArrayList<>();
062    for (E element : collection) { // uses iterator()
063      iteratorElements.add(element);
064    }
065    assertEqualIgnoringOrder(asList(createSamplesArray()), iteratorElements);
066  }
067
068  @CollectionFeature.Require(KNOWN_ORDER)
069  public void testIterationOrdering() {
070    List<E> iteratorElements = new ArrayList<>();
071    for (E element : collection) { // uses iterator()
072      iteratorElements.add(element);
073    }
074    List<E> expected = copyToList(getOrderedElements());
075    assertEquals("Different ordered iteration", expected, iteratorElements);
076  }
077
078  @CollectionFeature.Require(ALLOWS_NULL_VALUES)
079  @CollectionSize.Require(absent = ZERO)
080  public void testIterator_nullElement() {
081    initCollectionWithNullElement();
082    List<E> iteratorElements = new ArrayList<>();
083    for (E element : collection) { // uses iterator()
084      iteratorElements.add(element);
085    }
086    assertEqualIgnoringOrder(asList(createArrayWithNullElement()), iteratorElements);
087  }
088
089  @CollectionFeature.Require(SUPPORTS_ITERATOR_REMOVE)
090  @CollectionSize.Require(absent = ZERO)
091  public void testIterator_removeAffectsBackingCollection() {
092    int originalSize = collection.size();
093    Iterator<E> iterator = collection.iterator();
094    Object element = iterator.next();
095    // If it's an Entry, it may become invalid once it's removed from the Map. Copy it.
096    if (element instanceof Entry) {
097      Entry<?, ?> entry = (Entry<?, ?>) element;
098      element = mapEntry(entry.getKey(), entry.getValue());
099    }
100    assertTrue(collection.contains(element)); // sanity check
101    iterator.remove();
102    assertFalse(collection.contains(element));
103    assertEquals(originalSize - 1, collection.size());
104  }
105
106  @CollectionFeature.Require({KNOWN_ORDER, SUPPORTS_ITERATOR_REMOVE})
107  public void testIterator_knownOrderRemoveSupported() {
108    runIteratorTest(MODIFIABLE, IteratorTester.KnownOrder.KNOWN_ORDER, getOrderedElements());
109  }
110
111  @CollectionFeature.Require(value = KNOWN_ORDER, absent = SUPPORTS_ITERATOR_REMOVE)
112  public void testIterator_knownOrderRemoveUnsupported() {
113    runIteratorTest(UNMODIFIABLE, IteratorTester.KnownOrder.KNOWN_ORDER, getOrderedElements());
114  }
115
116  @CollectionFeature.Require(absent = KNOWN_ORDER, value = SUPPORTS_ITERATOR_REMOVE)
117  public void testIterator_unknownOrderRemoveSupported() {
118    runIteratorTest(MODIFIABLE, IteratorTester.KnownOrder.UNKNOWN_ORDER, getSampleElements());
119  }
120
121  @CollectionFeature.Require(absent = {KNOWN_ORDER, SUPPORTS_ITERATOR_REMOVE})
122  public void testIterator_unknownOrderRemoveUnsupported() {
123    runIteratorTest(UNMODIFIABLE, IteratorTester.KnownOrder.UNKNOWN_ORDER, getSampleElements());
124  }
125
126  private void runIteratorTest(
127      Set<IteratorFeature> features, IteratorTester.KnownOrder knownOrder, Iterable<E> elements) {
128    new IteratorTester<E>(
129        Platform.collectionIteratorTesterNumIterations(), features, elements, knownOrder) {
130      @Override
131      protected Iterator<E> newTargetIterator() {
132        resetCollection();
133        return collection.iterator();
134      }
135
136      @Override
137      protected void verify(List<E> elements) {
138        expectContents(elements);
139      }
140    }.test();
141  }
142
143  public void testIteratorNoSuchElementException() {
144    Iterator<E> iterator = collection.iterator();
145    while (iterator.hasNext()) {
146      iterator.next();
147    }
148
149    assertThrows(NoSuchElementException.class, iterator::next);
150  }
151}