AbstractIteratorTest.java revision 1d580d0f6ee4f21eb309ba7b509d2c6d671c4044
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
17package com.google.common.collect;
18
19import com.google.common.annotations.GwtCompatible;
20
21import junit.framework.TestCase;
22
23import java.util.Iterator;
24import 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?
34public class AbstractIteratorTest extends TestCase {
35
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
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
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
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
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
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
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
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   */
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