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@GwtCompatible(emulated = true)
32// TODO(cpovirk): why is this slow (>1m/test) under GWT when fully optimized?
33public class AbstractIteratorTest extends TestCase {
34
35  public void testDefaultBehaviorOfNextAndHasNext() {
36
37    // This sample AbstractIterator returns 0 on the first call, 1 on the
38    // second, then signals that it's reached the end of the data
39    Iterator<Integer> iter = new AbstractIterator<Integer>() {
40      private int rep;
41      @Override public Integer computeNext() {
42        switch (rep++) {
43          case 0:
44            return 0;
45          case 1:
46            return 1;
47          case 2:
48            return endOfData();
49          default:
50            fail("Should not have been invoked again");
51            return null;
52        }
53      }
54    };
55
56    assertTrue(iter.hasNext());
57    assertEquals(0, (int) iter.next());
58
59    // verify idempotence of hasNext()
60    assertTrue(iter.hasNext());
61    assertTrue(iter.hasNext());
62    assertTrue(iter.hasNext());
63    assertEquals(1, (int) iter.next());
64
65    assertFalse(iter.hasNext());
66
67    // Make sure computeNext() doesn't get invoked again
68    assertFalse(iter.hasNext());
69
70    try {
71      iter.next();
72      fail("no exception thrown");
73    } catch (NoSuchElementException expected) {
74    }
75  }
76
77  public void testSneakyThrow() throws Exception {
78    Iterator<Integer> iter = new AbstractIterator<Integer>() {
79      boolean haveBeenCalled;
80      @Override public Integer computeNext() {
81        if (haveBeenCalled) {
82          fail("Should not have been called again");
83        } else {
84          haveBeenCalled = true;
85          sneakyThrow(new SomeCheckedException());
86        }
87        return null; // never reached
88      }
89    };
90
91    // The first time, the sneakily-thrown exception comes out
92    try {
93      iter.hasNext();
94      fail("No exception thrown");
95    } catch (Exception e) {
96      if (!(e instanceof SomeCheckedException)) {
97        throw e;
98      }
99    }
100
101    // But the second time, AbstractIterator itself throws an ISE
102    try {
103      iter.hasNext();
104      fail("No exception thrown");
105    } catch (IllegalStateException expected) {
106    }
107  }
108
109  public void testException() {
110    final SomeUncheckedException exception = new SomeUncheckedException();
111    Iterator<Integer> iter = new AbstractIterator<Integer>() {
112      @Override public Integer computeNext() {
113        throw exception;
114      }
115    };
116
117    // It should pass through untouched
118    try {
119      iter.hasNext();
120      fail("No exception thrown");
121    } catch (SomeUncheckedException e) {
122      assertSame(exception, e);
123    }
124  }
125
126  public void testExceptionAfterEndOfData() {
127    Iterator<Integer> iter = new AbstractIterator<Integer>() {
128      @Override public Integer computeNext() {
129        endOfData();
130        throw new SomeUncheckedException();
131      }
132    };
133    try {
134      iter.hasNext();
135      fail("No exception thrown");
136    } catch (SomeUncheckedException expected) {
137    }
138  }
139
140  public void testCantRemove() {
141    Iterator<Integer> iter = new AbstractIterator<Integer>() {
142      boolean haveBeenCalled;
143      @Override public Integer computeNext() {
144        if (haveBeenCalled) {
145          endOfData();
146        }
147        haveBeenCalled = true;
148        return 0;
149      }
150    };
151
152    assertEquals(0, (int) iter.next());
153
154    try {
155      iter.remove();
156      fail("No exception thrown");
157    } catch (UnsupportedOperationException expected) {
158    }
159  }
160
161  public void testReentrantHasNext() {
162    Iterator<Integer> iter = new AbstractIterator<Integer>() {
163      @Override protected Integer computeNext() {
164        hasNext();
165        return null;
166      }
167    };
168    try {
169      iter.hasNext();
170      fail();
171    } catch (IllegalStateException expected) {
172    }
173  }
174
175  // Technically we should test other reentrant scenarios (4 combinations of
176  // hasNext/next), but we'll cop out for now, knowing that
177  // next() both start by invoking hasNext() anyway.
178
179  /**
180   * Throws a undeclared checked exception.
181   */
182  private static void sneakyThrow(Throwable t) {
183    class SneakyThrower<T extends Throwable> {
184      @SuppressWarnings("unchecked") // intentionally unsafe for test
185      void throwIt(Throwable t) throws T {
186        throw (T) t;
187      }
188    }
189    new SneakyThrower<Error>().throwIt(t);
190  }
191
192  private static class SomeCheckedException extends Exception {
193  }
194
195  private static class SomeUncheckedException extends RuntimeException {
196  }
197}
198
199