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