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