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.collect; 18 19 import com.google.common.annotations.GwtCompatible; 20 import com.google.common.annotations.GwtIncompatible; 21 import com.google.common.annotations.J2ktIncompatible; 22 import com.google.common.testing.GcFinalization; 23 import java.lang.ref.WeakReference; 24 import java.util.Iterator; 25 import java.util.NoSuchElementException; 26 import junit.framework.TestCase; 27 import org.checkerframework.checker.nullness.qual.Nullable; 28 29 /** 30 * Unit test for {@code AbstractIterator}. 31 * 32 * @author Kevin Bourrillion 33 */ 34 @SuppressWarnings("serial") // No serialization is used in this test 35 @GwtCompatible(emulated = true) 36 @ElementTypesAreNonnullByDefault 37 public class AbstractIteratorTest extends TestCase { 38 testDefaultBehaviorOfNextAndHasNext()39 public void testDefaultBehaviorOfNextAndHasNext() { 40 41 // This sample AbstractIterator returns 0 on the first call, 1 on the 42 // second, then signals that it's reached the end of the data 43 Iterator<Integer> iter = 44 new AbstractIterator<Integer>() { 45 private int rep; 46 47 @Override 48 public @Nullable Integer computeNext() { 49 switch (rep++) { 50 case 0: 51 return 0; 52 case 1: 53 return 1; 54 case 2: 55 return endOfData(); 56 default: 57 throw new AssertionError("Should not have been invoked again"); 58 } 59 } 60 }; 61 62 assertTrue(iter.hasNext()); 63 assertEquals(0, (int) iter.next()); 64 65 // verify idempotence of hasNext() 66 assertTrue(iter.hasNext()); 67 assertTrue(iter.hasNext()); 68 assertTrue(iter.hasNext()); 69 assertEquals(1, (int) iter.next()); 70 71 assertFalse(iter.hasNext()); 72 73 // Make sure computeNext() doesn't get invoked again 74 assertFalse(iter.hasNext()); 75 76 try { 77 iter.next(); 78 fail("no exception thrown"); 79 } catch (NoSuchElementException expected) { 80 } 81 } 82 testDefaultBehaviorOfPeek()83 public void testDefaultBehaviorOfPeek() { 84 /* 85 * This sample AbstractIterator returns 0 on the first call, 1 on the 86 * second, then signals that it's reached the end of the data 87 */ 88 AbstractIterator<Integer> iter = 89 new AbstractIterator<Integer>() { 90 private int rep; 91 92 @Override 93 public @Nullable Integer computeNext() { 94 switch (rep++) { 95 case 0: 96 return 0; 97 case 1: 98 return 1; 99 case 2: 100 return endOfData(); 101 default: 102 throw new AssertionError("Should not have been invoked again"); 103 } 104 } 105 }; 106 107 assertEquals(0, (int) iter.peek()); 108 assertEquals(0, (int) iter.peek()); 109 assertTrue(iter.hasNext()); 110 assertEquals(0, (int) iter.peek()); 111 assertEquals(0, (int) iter.next()); 112 113 assertEquals(1, (int) iter.peek()); 114 assertEquals(1, (int) iter.next()); 115 116 try { 117 iter.peek(); 118 fail("peek() should throw NoSuchElementException at end"); 119 } catch (NoSuchElementException expected) { 120 } 121 122 try { 123 iter.peek(); 124 fail("peek() should continue to throw NoSuchElementException at end"); 125 } catch (NoSuchElementException expected) { 126 } 127 128 try { 129 iter.next(); 130 fail("next() should throw NoSuchElementException as usual"); 131 } catch (NoSuchElementException expected) { 132 } 133 134 try { 135 iter.peek(); 136 fail("peek() should still throw NoSuchElementException after next()"); 137 } catch (NoSuchElementException expected) { 138 } 139 } 140 141 142 @J2ktIncompatible // weak references, details of GC 143 @GwtIncompatible // weak references 144 @AndroidIncompatible // depends on details of GC testFreesNextReference()145 public void testFreesNextReference() { 146 Iterator<Object> itr = 147 new AbstractIterator<Object>() { 148 @Override 149 public Object computeNext() { 150 return new Object(); 151 } 152 }; 153 WeakReference<Object> ref = new WeakReference<>(itr.next()); 154 GcFinalization.awaitClear(ref); 155 } 156 testDefaultBehaviorOfPeekForEmptyIteration()157 public void testDefaultBehaviorOfPeekForEmptyIteration() { 158 159 AbstractIterator<Integer> empty = 160 new AbstractIterator<Integer>() { 161 private boolean alreadyCalledEndOfData; 162 163 @Override 164 public @Nullable Integer computeNext() { 165 if (alreadyCalledEndOfData) { 166 fail("Should not have been invoked again"); 167 } 168 alreadyCalledEndOfData = true; 169 return endOfData(); 170 } 171 }; 172 173 try { 174 empty.peek(); 175 fail("peek() should throw NoSuchElementException at end"); 176 } catch (NoSuchElementException expected) { 177 } 178 179 try { 180 empty.peek(); 181 fail("peek() should continue to throw NoSuchElementException at end"); 182 } catch (NoSuchElementException expected) { 183 } 184 } 185 testSneakyThrow()186 public void testSneakyThrow() throws Exception { 187 Iterator<Integer> iter = 188 new AbstractIterator<Integer>() { 189 boolean haveBeenCalled; 190 191 @Override 192 public Integer computeNext() { 193 if (haveBeenCalled) { 194 throw new AssertionError("Should not have been called again"); 195 } else { 196 haveBeenCalled = true; 197 sneakyThrow(new SomeCheckedException()); 198 throw new AssertionError(); // unreachable 199 } 200 } 201 }; 202 203 // The first time, the sneakily-thrown exception comes out 204 try { 205 iter.hasNext(); 206 fail("No exception thrown"); 207 } catch (Exception e) { 208 if (!(e instanceof SomeCheckedException)) { 209 throw e; 210 } 211 } 212 213 // But the second time, AbstractIterator itself throws an ISE 214 try { 215 iter.hasNext(); 216 fail("No exception thrown"); 217 } catch (IllegalStateException expected) { 218 } 219 } 220 testException()221 public void testException() { 222 final SomeUncheckedException exception = new SomeUncheckedException(); 223 Iterator<Integer> iter = 224 new AbstractIterator<Integer>() { 225 @Override 226 public Integer computeNext() { 227 throw exception; 228 } 229 }; 230 231 // It should pass through untouched 232 try { 233 iter.hasNext(); 234 fail("No exception thrown"); 235 } catch (SomeUncheckedException e) { 236 assertSame(exception, e); 237 } 238 } 239 testExceptionAfterEndOfData()240 public void testExceptionAfterEndOfData() { 241 Iterator<Integer> iter = 242 new AbstractIterator<Integer>() { 243 @Override 244 public Integer computeNext() { 245 endOfData(); 246 throw new SomeUncheckedException(); 247 } 248 }; 249 try { 250 iter.hasNext(); 251 fail("No exception thrown"); 252 } catch (SomeUncheckedException expected) { 253 } 254 } 255 256 @SuppressWarnings("DoNotCall") testCantRemove()257 public void testCantRemove() { 258 Iterator<Integer> iter = 259 new AbstractIterator<Integer>() { 260 boolean haveBeenCalled; 261 262 @Override 263 public Integer computeNext() { 264 if (haveBeenCalled) { 265 endOfData(); 266 } 267 haveBeenCalled = true; 268 return 0; 269 } 270 }; 271 272 assertEquals(0, (int) iter.next()); 273 274 try { 275 iter.remove(); 276 fail("No exception thrown"); 277 } catch (UnsupportedOperationException expected) { 278 } 279 } 280 testReentrantHasNext()281 public void testReentrantHasNext() { 282 Iterator<Integer> iter = 283 new AbstractIterator<Integer>() { 284 @Override 285 protected Integer computeNext() { 286 boolean unused = hasNext(); 287 throw new AssertionError(); 288 } 289 }; 290 try { 291 iter.hasNext(); 292 fail(); 293 } catch (IllegalStateException expected) { 294 } 295 } 296 297 // Technically we should test other reentrant scenarios (9 combinations of 298 // hasNext/next/peek), but we'll cop out for now, knowing that peek() and 299 // next() both start by invoking hasNext() anyway. 300 301 /** Throws an undeclared checked exception. */ sneakyThrow(Throwable t)302 private static void sneakyThrow(Throwable t) { 303 class SneakyThrower<T extends Throwable> { 304 @SuppressWarnings("unchecked") // not really safe, but that's the point 305 void throwIt(Throwable t) throws T { 306 throw (T) t; 307 } 308 } 309 new SneakyThrower<Error>().throwIt(t); 310 } 311 312 private static class SomeCheckedException extends Exception {} 313 314 private static class SomeUncheckedException extends RuntimeException {} 315 } 316