View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.iterators;
18  
19  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.util.ArrayList;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.NoSuchElementException;
30  
31  import org.apache.commons.collections4.AbstractObjectTest;
32  import org.apache.commons.collections4.IteratorUtils;
33  import org.junit.jupiter.api.Test;
34  
35  /**
36   * Abstract class for testing the Iterator interface.
37   * <p>
38   * This class provides a framework for testing an implementation of Iterator.
39   * Concrete subclasses must provide the iterator to be tested.
40   * They must also specify certain details of how the iterator operates by
41   * overriding the supportsXxx() methods if necessary.
42   * </p>
43   *
44   * @param <E> the type of elements tested by this iterator.
45   */
46  public abstract class AbstractIteratorTest<E> extends AbstractObjectTest {
47  
48      /**
49       * Implement this method to return an iterator over an empty collection.
50       *
51       * @return an empty iterator
52       */
53      public abstract Iterator<E> makeEmptyIterator();
54  
55      /**
56       * Implements the abstract superclass method to return the full iterator.
57       *
58       * @return a full iterator
59       */
60      @Override
61      public abstract Iterator<E> makeObject();
62  
63      /**
64       * Tests whether or not we are testing an iterator that can be empty.
65       * Default is true.
66       *
67       * @return true if Iterator can be empty
68       */
69      public boolean supportsEmptyIterator() {
70          return true;
71      }
72  
73      /**
74       * Tests whether or not we are testing an iterator that can contain elements.
75       * Default is true.
76       *
77       * @return true if Iterator can be full
78       */
79      public boolean supportsFullIterator() {
80          return true;
81      }
82  
83      /**
84       * Tests whether or not we are testing an iterator that supports remove().
85       * Default is true.
86       *
87       * @return true if Iterator supports remove
88       */
89      public boolean supportsRemove() {
90          return true;
91      }
92  
93      /**
94       * Test the empty iterator.
95       */
96      @Test
97      public void testEmptyIterator() {
98          if (!supportsEmptyIterator()) {
99              return;
100         }
101 
102         final Iterator<E> it = makeEmptyIterator();
103 
104         // hasNext() should return false
105         assertFalse(it.hasNext(), "hasNext() should return false for empty iterators");
106 
107         // next() should throw a NoSuchElementException
108         assertThrows(NoSuchElementException.class, () -> it.next(),
109                 "NoSuchElementException must be thrown when Iterator is exhausted");
110         verify();
111 
112         assertNotNull(it.toString());
113     }
114 
115     /**
116      * Tests {@link Iterator#forEachRemaining(java.util.function.Consumer)}.
117      */
118     @Test
119     public void testForEachRemaining() {
120         final List<E> expected = IteratorUtils.toList(makeObject());
121         final Iterator<E> it = makeObject();
122         final List<E> actual = new ArrayList<>();
123         it.forEachRemaining(actual::add);
124         assertEquals(expected, actual);
125     }
126 
127     /**
128      * Test normal iteration behavior.
129      */
130     @Test
131     public void testFullIterator() {
132         if (!supportsFullIterator()) {
133             return;
134         }
135 
136         final Iterator<E> it = makeObject();
137 
138         // hasNext() must be true (ensure makeFullIterator is correct!)
139         assertTrue(it.hasNext(), "hasNext() should return true for at least one element");
140 
141         // next() must not throw exception (ensure makeFullIterator is correct!)
142         assertDoesNotThrow(it::next, "Full iterators must have at least one element");
143 
144         // iterate through
145         while (it.hasNext()) {
146             it.next();
147             verify();
148         }
149 
150         // next() must throw NoSuchElementException now
151         assertThrows(NoSuchElementException.class, () -> it.next(),
152                 "NoSuchElementException must be thrown when Iterator is exhausted");
153 
154         assertNotNull(it.toString());
155     }
156 
157     /**
158      * Test remove behavior.
159      */
160     @Test
161     public void testRemove() {
162         final Iterator<E> it = makeObject();
163 
164         if (!supportsRemove()) {
165             // check for UnsupportedOperationException if not supported
166             assertThrows(UnsupportedOperationException.class, it::remove);
167             return;
168         }
169 
170         // should throw IllegalStateException before next() called
171         assertThrows(IllegalStateException.class, () -> it.remove());
172         verify();
173 
174         // remove after next should be fine
175         it.next();
176         it.remove();
177 
178         // should throw IllegalStateException for second remove()
179         assertThrows(IllegalStateException.class, () -> it.remove());
180     }
181 
182     /**
183      * Allows subclasses to add complex cross verification
184      */
185     public void verify() {
186         // do nothing
187     }
188 
189 }