• 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 
141   @GwtIncompatible // weak references
testFreesNextReference()142   public void testFreesNextReference() {
143     Iterator<Object> itr =
144         new AbstractIterator<Object>() {
145           @Override
146           public Object computeNext() {
147             return new Object();
148           }
149         };
150     WeakReference<Object> ref = new WeakReference<>(itr.next());
151     GcFinalization.awaitClear(ref);
152   }
153 
testDefaultBehaviorOfPeekForEmptyIteration()154   public void testDefaultBehaviorOfPeekForEmptyIteration() {
155 
156     AbstractIterator<Integer> empty =
157         new AbstractIterator<Integer>() {
158           private boolean alreadyCalledEndOfData;
159 
160           @Override
161           public Integer computeNext() {
162             if (alreadyCalledEndOfData) {
163               fail("Should not have been invoked again");
164             }
165             alreadyCalledEndOfData = true;
166             return endOfData();
167           }
168         };
169 
170     try {
171       empty.peek();
172       fail("peek() should throw NoSuchElementException at end");
173     } catch (NoSuchElementException expected) {
174     }
175 
176     try {
177       empty.peek();
178       fail("peek() should continue to throw NoSuchElementException at end");
179     } catch (NoSuchElementException expected) {
180     }
181   }
182 
testSneakyThrow()183   public void testSneakyThrow() throws Exception {
184     Iterator<Integer> iter =
185         new AbstractIterator<Integer>() {
186           boolean haveBeenCalled;
187 
188           @Override
189           public Integer computeNext() {
190             if (haveBeenCalled) {
191               fail("Should not have been called again");
192             } else {
193               haveBeenCalled = true;
194               sneakyThrow(new SomeCheckedException());
195             }
196             return null; // never reached
197           }
198         };
199 
200     // The first time, the sneakily-thrown exception comes out
201     try {
202       iter.hasNext();
203       fail("No exception thrown");
204     } catch (Exception e) {
205       if (!(e instanceof SomeCheckedException)) {
206         throw e;
207       }
208     }
209 
210     // But the second time, AbstractIterator itself throws an ISE
211     try {
212       iter.hasNext();
213       fail("No exception thrown");
214     } catch (IllegalStateException expected) {
215     }
216   }
217 
testException()218   public void testException() {
219     final SomeUncheckedException exception = new SomeUncheckedException();
220     Iterator<Integer> iter =
221         new AbstractIterator<Integer>() {
222           @Override
223           public Integer computeNext() {
224             throw exception;
225           }
226         };
227 
228     // It should pass through untouched
229     try {
230       iter.hasNext();
231       fail("No exception thrown");
232     } catch (SomeUncheckedException e) {
233       assertSame(exception, e);
234     }
235   }
236 
testExceptionAfterEndOfData()237   public void testExceptionAfterEndOfData() {
238     Iterator<Integer> iter =
239         new AbstractIterator<Integer>() {
240           @Override
241           public Integer computeNext() {
242             endOfData();
243             throw new SomeUncheckedException();
244           }
245         };
246     try {
247       iter.hasNext();
248       fail("No exception thrown");
249     } catch (SomeUncheckedException expected) {
250     }
251   }
252 
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             return null;
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 a 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