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