• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.annotations.GwtIncompatible;
21 import com.google.common.annotations.J2ktIncompatible;
22 import com.google.common.testing.GcFinalization;
23 import java.lang.ref.WeakReference;
24 import java.util.Iterator;
25 import java.util.NoSuchElementException;
26 import junit.framework.TestCase;
27 import org.checkerframework.checker.nullness.qual.Nullable;
28 
29 /**
30  * Unit test for {@code AbstractIterator}.
31  *
32  * @author Kevin Bourrillion
33  */
34 @SuppressWarnings("serial") // No serialization is used in this test
35 @GwtCompatible(emulated = true)
36 @ElementTypesAreNonnullByDefault
37 public class AbstractIteratorTest extends TestCase {
38 
testDefaultBehaviorOfNextAndHasNext()39   public void testDefaultBehaviorOfNextAndHasNext() {
40 
41     // This sample AbstractIterator returns 0 on the first call, 1 on the
42     // second, then signals that it's reached the end of the data
43     Iterator<Integer> iter =
44         new AbstractIterator<Integer>() {
45           private int rep;
46 
47           @Override
48           public @Nullable Integer computeNext() {
49             switch (rep++) {
50               case 0:
51                 return 0;
52               case 1:
53                 return 1;
54               case 2:
55                 return endOfData();
56               default:
57                 throw new AssertionError("Should not have been invoked again");
58             }
59           }
60         };
61 
62     assertTrue(iter.hasNext());
63     assertEquals(0, (int) iter.next());
64 
65     // verify idempotence of hasNext()
66     assertTrue(iter.hasNext());
67     assertTrue(iter.hasNext());
68     assertTrue(iter.hasNext());
69     assertEquals(1, (int) iter.next());
70 
71     assertFalse(iter.hasNext());
72 
73     // Make sure computeNext() doesn't get invoked again
74     assertFalse(iter.hasNext());
75 
76     try {
77       iter.next();
78       fail("no exception thrown");
79     } catch (NoSuchElementException expected) {
80     }
81   }
82 
testDefaultBehaviorOfPeek()83   public void testDefaultBehaviorOfPeek() {
84     /*
85      * This sample AbstractIterator returns 0 on the first call, 1 on the
86      * second, then signals that it's reached the end of the data
87      */
88     AbstractIterator<Integer> iter =
89         new AbstractIterator<Integer>() {
90           private int rep;
91 
92           @Override
93           public @Nullable Integer computeNext() {
94             switch (rep++) {
95               case 0:
96                 return 0;
97               case 1:
98                 return 1;
99               case 2:
100                 return endOfData();
101               default:
102                 throw new AssertionError("Should not have been invoked again");
103             }
104           }
105         };
106 
107     assertEquals(0, (int) iter.peek());
108     assertEquals(0, (int) iter.peek());
109     assertTrue(iter.hasNext());
110     assertEquals(0, (int) iter.peek());
111     assertEquals(0, (int) iter.next());
112 
113     assertEquals(1, (int) iter.peek());
114     assertEquals(1, (int) iter.next());
115 
116     try {
117       iter.peek();
118       fail("peek() should throw NoSuchElementException at end");
119     } catch (NoSuchElementException expected) {
120     }
121 
122     try {
123       iter.peek();
124       fail("peek() should continue to throw NoSuchElementException at end");
125     } catch (NoSuchElementException expected) {
126     }
127 
128     try {
129       iter.next();
130       fail("next() should throw NoSuchElementException as usual");
131     } catch (NoSuchElementException expected) {
132     }
133 
134     try {
135       iter.peek();
136       fail("peek() should still throw NoSuchElementException after next()");
137     } catch (NoSuchElementException expected) {
138     }
139   }
140 
141 
142   @J2ktIncompatible // weak references, details of GC
143   @GwtIncompatible // weak references
144   @AndroidIncompatible // depends on details of GC
testFreesNextReference()145   public void testFreesNextReference() {
146     Iterator<Object> itr =
147         new AbstractIterator<Object>() {
148           @Override
149           public Object computeNext() {
150             return new Object();
151           }
152         };
153     WeakReference<Object> ref = new WeakReference<>(itr.next());
154     GcFinalization.awaitClear(ref);
155   }
156 
testDefaultBehaviorOfPeekForEmptyIteration()157   public void testDefaultBehaviorOfPeekForEmptyIteration() {
158 
159     AbstractIterator<Integer> empty =
160         new AbstractIterator<Integer>() {
161           private boolean alreadyCalledEndOfData;
162 
163           @Override
164           public @Nullable Integer computeNext() {
165             if (alreadyCalledEndOfData) {
166               fail("Should not have been invoked again");
167             }
168             alreadyCalledEndOfData = true;
169             return endOfData();
170           }
171         };
172 
173     try {
174       empty.peek();
175       fail("peek() should throw NoSuchElementException at end");
176     } catch (NoSuchElementException expected) {
177     }
178 
179     try {
180       empty.peek();
181       fail("peek() should continue to throw NoSuchElementException at end");
182     } catch (NoSuchElementException expected) {
183     }
184   }
185 
testSneakyThrow()186   public void testSneakyThrow() throws Exception {
187     Iterator<Integer> iter =
188         new AbstractIterator<Integer>() {
189           boolean haveBeenCalled;
190 
191           @Override
192           public Integer computeNext() {
193             if (haveBeenCalled) {
194               throw new AssertionError("Should not have been called again");
195             } else {
196               haveBeenCalled = true;
197               sneakyThrow(new SomeCheckedException());
198               throw new AssertionError(); // unreachable
199             }
200           }
201         };
202 
203     // The first time, the sneakily-thrown exception comes out
204     try {
205       iter.hasNext();
206       fail("No exception thrown");
207     } catch (Exception e) {
208       if (!(e instanceof SomeCheckedException)) {
209         throw e;
210       }
211     }
212 
213     // But the second time, AbstractIterator itself throws an ISE
214     try {
215       iter.hasNext();
216       fail("No exception thrown");
217     } catch (IllegalStateException expected) {
218     }
219   }
220 
testException()221   public void testException() {
222     final SomeUncheckedException exception = new SomeUncheckedException();
223     Iterator<Integer> iter =
224         new AbstractIterator<Integer>() {
225           @Override
226           public Integer computeNext() {
227             throw exception;
228           }
229         };
230 
231     // It should pass through untouched
232     try {
233       iter.hasNext();
234       fail("No exception thrown");
235     } catch (SomeUncheckedException e) {
236       assertSame(exception, e);
237     }
238   }
239 
testExceptionAfterEndOfData()240   public void testExceptionAfterEndOfData() {
241     Iterator<Integer> iter =
242         new AbstractIterator<Integer>() {
243           @Override
244           public Integer computeNext() {
245             endOfData();
246             throw new SomeUncheckedException();
247           }
248         };
249     try {
250       iter.hasNext();
251       fail("No exception thrown");
252     } catch (SomeUncheckedException expected) {
253     }
254   }
255 
256   @SuppressWarnings("DoNotCall")
testCantRemove()257   public void testCantRemove() {
258     Iterator<Integer> iter =
259         new AbstractIterator<Integer>() {
260           boolean haveBeenCalled;
261 
262           @Override
263           public Integer computeNext() {
264             if (haveBeenCalled) {
265               endOfData();
266             }
267             haveBeenCalled = true;
268             return 0;
269           }
270         };
271 
272     assertEquals(0, (int) iter.next());
273 
274     try {
275       iter.remove();
276       fail("No exception thrown");
277     } catch (UnsupportedOperationException expected) {
278     }
279   }
280 
testReentrantHasNext()281   public void testReentrantHasNext() {
282     Iterator<Integer> iter =
283         new AbstractIterator<Integer>() {
284           @Override
285           protected Integer computeNext() {
286             boolean unused = hasNext();
287             throw new AssertionError();
288           }
289         };
290     try {
291       iter.hasNext();
292       fail();
293     } catch (IllegalStateException expected) {
294     }
295   }
296 
297   // Technically we should test other reentrant scenarios (9 combinations of
298   // hasNext/next/peek), but we'll cop out for now, knowing that peek() and
299   // next() both start by invoking hasNext() anyway.
300 
301   /** Throws an undeclared checked exception. */
sneakyThrow(Throwable t)302   private static void sneakyThrow(Throwable t) {
303     class SneakyThrower<T extends Throwable> {
304       @SuppressWarnings("unchecked") // not really safe, but that's the point
305       void throwIt(Throwable t) throws T {
306         throw (T) t;
307       }
308     }
309     new SneakyThrower<Error>().throwIt(t);
310   }
311 
312   private static class SomeCheckedException extends Exception {}
313 
314   private static class SomeUncheckedException extends RuntimeException {}
315 }
316