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.base;
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 testSneakyThrow() throws Exception {
79    Iterator<Integer> iter = new AbstractIterator<Integer>() {
80      boolean haveBeenCalled;
81      @Override public Integer computeNext() {
82        if (haveBeenCalled) {
83          fail("Should not have been called again");
84        } else {
85          haveBeenCalled = true;
86          sneakyThrow(new SomeCheckedException());
87        }
88        return null; // never reached
89      }
90    };
91
92    // The first time, the sneakily-thrown exception comes out
93    try {
94      iter.hasNext();
95      fail("No exception thrown");
96    } catch (Exception e) {
97      if (!(e instanceof SomeCheckedException)) {
98        throw e;
99      }
100    }
101
102    // But the second time, AbstractIterator itself throws an ISE
103    try {
104      iter.hasNext();
105      fail("No exception thrown");
106    } catch (IllegalStateException expected) {
107    }
108  }
109
110  public void testException() {
111    final SomeUncheckedException exception = new SomeUncheckedException();
112    Iterator<Integer> iter = new AbstractIterator<Integer>() {
113      @Override public Integer computeNext() {
114        throw exception;
115      }
116    };
117
118    // It should pass through untouched
119    try {
120      iter.hasNext();
121      fail("No exception thrown");
122    } catch (SomeUncheckedException e) {
123      assertSame(exception, e);
124    }
125  }
126
127  public void testExceptionAfterEndOfData() {
128    Iterator<Integer> iter = new AbstractIterator<Integer>() {
129      @Override public Integer computeNext() {
130        endOfData();
131        throw new SomeUncheckedException();
132      }
133    };
134    try {
135      iter.hasNext();
136      fail("No exception thrown");
137    } catch (SomeUncheckedException expected) {
138    }
139  }
140
141  public void testCantRemove() {
142    Iterator<Integer> iter = new AbstractIterator<Integer>() {
143      boolean haveBeenCalled;
144      @Override public Integer computeNext() {
145        if (haveBeenCalled) {
146          endOfData();
147        }
148        haveBeenCalled = true;
149        return 0;
150      }
151    };
152
153    assertEquals(0, (int) iter.next());
154
155    try {
156      iter.remove();
157      fail("No exception thrown");
158    } catch (UnsupportedOperationException expected) {
159    }
160  }
161
162  public void testReentrantHasNext() {
163    Iterator<Integer> iter = new AbstractIterator<Integer>() {
164      @Override protected Integer computeNext() {
165        hasNext();
166        return null;
167      }
168    };
169    try {
170      iter.hasNext();
171      fail();
172    } catch (IllegalStateException expected) {
173    }
174  }
175
176  // Technically we should test other reentrant scenarios (4 combinations of
177  // hasNext/next), but we'll cop out for now, knowing that
178  // next() both start by invoking hasNext() anyway.
179
180  /**
181   * Throws a undeclared checked exception.
182   */
183  private static void sneakyThrow(Throwable t) {
184    class SneakyThrower<T extends Throwable> {
185      @SuppressWarnings("unchecked") // not really safe, but that's the point
186      void throwIt(Throwable t) throws T {
187        throw (T) t;
188      }
189    }
190    new SneakyThrower<Error>().throwIt(t);
191  }
192
193  private static class SomeCheckedException extends Exception {
194  }
195
196  private static class SomeUncheckedException extends RuntimeException {
197  }
198}
199