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