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