• 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                 throw new AssertionError("Should not have been invoked again");
55             }
56           }
57         };
58 
59     assertTrue(iter.hasNext());
60     assertEquals(0, (int) iter.next());
61 
62     // verify idempotence of hasNext()
63     assertTrue(iter.hasNext());
64     assertTrue(iter.hasNext());
65     assertTrue(iter.hasNext());
66     assertEquals(1, (int) iter.next());
67 
68     assertFalse(iter.hasNext());
69 
70     // Make sure computeNext() doesn't get invoked again
71     assertFalse(iter.hasNext());
72 
73     try {
74       iter.next();
75       fail("no exception thrown");
76     } catch (NoSuchElementException expected) {
77     }
78   }
79 
testDefaultBehaviorOfPeek()80   public void testDefaultBehaviorOfPeek() {
81     /*
82      * This sample AbstractIterator returns 0 on the first call, 1 on the
83      * second, then signals that it's reached the end of the data
84      */
85     AbstractIterator<Integer> iter =
86         new AbstractIterator<Integer>() {
87           private int rep;
88 
89           @Override
90           public Integer computeNext() {
91             switch (rep++) {
92               case 0:
93                 return 0;
94               case 1:
95                 return 1;
96               case 2:
97                 return endOfData();
98               default:
99                 throw new AssertionError("Should not have been invoked again");
100             }
101           }
102         };
103 
104     assertEquals(0, (int) iter.peek());
105     assertEquals(0, (int) iter.peek());
106     assertTrue(iter.hasNext());
107     assertEquals(0, (int) iter.peek());
108     assertEquals(0, (int) iter.next());
109 
110     assertEquals(1, (int) iter.peek());
111     assertEquals(1, (int) iter.next());
112 
113     try {
114       iter.peek();
115       fail("peek() should throw NoSuchElementException at end");
116     } catch (NoSuchElementException expected) {
117     }
118 
119     try {
120       iter.peek();
121       fail("peek() should continue to throw NoSuchElementException at end");
122     } catch (NoSuchElementException expected) {
123     }
124 
125     try {
126       iter.next();
127       fail("next() should throw NoSuchElementException as usual");
128     } catch (NoSuchElementException expected) {
129     }
130 
131     try {
132       iter.peek();
133       fail("peek() should still throw NoSuchElementException after next()");
134     } catch (NoSuchElementException expected) {
135     }
136   }
137 
138 
139   @GwtIncompatible // weak references
140   @AndroidIncompatible // depends on details of GC
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               throw new AssertionError("Should not have been called again");
191             } else {
192               haveBeenCalled = true;
193               sneakyThrow(new SomeCheckedException());
194               throw new AssertionError(); // unreachable
195             }
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 
252   @SuppressWarnings("DoNotCall")
testCantRemove()253   public void testCantRemove() {
254     Iterator<Integer> iter =
255         new AbstractIterator<Integer>() {
256           boolean haveBeenCalled;
257 
258           @Override
259           public Integer computeNext() {
260             if (haveBeenCalled) {
261               endOfData();
262             }
263             haveBeenCalled = true;
264             return 0;
265           }
266         };
267 
268     assertEquals(0, (int) iter.next());
269 
270     try {
271       iter.remove();
272       fail("No exception thrown");
273     } catch (UnsupportedOperationException expected) {
274     }
275   }
276 
testReentrantHasNext()277   public void testReentrantHasNext() {
278     Iterator<Integer> iter =
279         new AbstractIterator<Integer>() {
280           @Override
281           protected Integer computeNext() {
282             boolean unused = hasNext();
283             throw new AssertionError();
284           }
285         };
286     try {
287       iter.hasNext();
288       fail();
289     } catch (IllegalStateException expected) {
290     }
291   }
292 
293   // Technically we should test other reentrant scenarios (9 combinations of
294   // hasNext/next/peek), but we'll cop out for now, knowing that peek() and
295   // next() both start by invoking hasNext() anyway.
296 
297   /** Throws an undeclared checked exception. */
sneakyThrow(Throwable t)298   private static void sneakyThrow(Throwable t) {
299     class SneakyThrower<T extends Throwable> {
300       @SuppressWarnings("unchecked") // not really safe, but that's the point
301       void throwIt(Throwable t) throws T {
302         throw (T) t;
303       }
304     }
305     new SneakyThrower<Error>().throwIt(t);
306   }
307 
308   private static class SomeCheckedException extends Exception {}
309 
310   private static class SomeUncheckedException extends RuntimeException {}
311 }
312