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 fail("Should not have been invoked again"); 55 return null; 56 } 57 } 58 }; 59 60 assertTrue(iter.hasNext()); 61 assertEquals(0, (int) iter.next()); 62 63 // verify idempotence of hasNext() 64 assertTrue(iter.hasNext()); 65 assertTrue(iter.hasNext()); 66 assertTrue(iter.hasNext()); 67 assertEquals(1, (int) iter.next()); 68 69 assertFalse(iter.hasNext()); 70 71 // Make sure computeNext() doesn't get invoked again 72 assertFalse(iter.hasNext()); 73 74 try { 75 iter.next(); 76 fail("no exception thrown"); 77 } catch (NoSuchElementException expected) { 78 } 79 } 80 testDefaultBehaviorOfPeek()81 public void testDefaultBehaviorOfPeek() { 82 /* 83 * This sample AbstractIterator returns 0 on the first call, 1 on the 84 * second, then signals that it's reached the end of the data 85 */ 86 AbstractIterator<Integer> iter = 87 new AbstractIterator<Integer>() { 88 private int rep; 89 90 @Override 91 public Integer computeNext() { 92 switch (rep++) { 93 case 0: 94 return 0; 95 case 1: 96 return 1; 97 case 2: 98 return endOfData(); 99 default: 100 fail("Should not have been invoked again"); 101 return null; 102 } 103 } 104 }; 105 106 assertEquals(0, (int) iter.peek()); 107 assertEquals(0, (int) iter.peek()); 108 assertTrue(iter.hasNext()); 109 assertEquals(0, (int) iter.peek()); 110 assertEquals(0, (int) iter.next()); 111 112 assertEquals(1, (int) iter.peek()); 113 assertEquals(1, (int) iter.next()); 114 115 try { 116 iter.peek(); 117 fail("peek() should throw NoSuchElementException at end"); 118 } catch (NoSuchElementException expected) { 119 } 120 121 try { 122 iter.peek(); 123 fail("peek() should continue to throw NoSuchElementException at end"); 124 } catch (NoSuchElementException expected) { 125 } 126 127 try { 128 iter.next(); 129 fail("next() should throw NoSuchElementException as usual"); 130 } catch (NoSuchElementException expected) { 131 } 132 133 try { 134 iter.peek(); 135 fail("peek() should still throw NoSuchElementException after next()"); 136 } catch (NoSuchElementException expected) { 137 } 138 } 139 140 141 @GwtIncompatible // weak references testFreesNextReference()142 public void testFreesNextReference() { 143 Iterator<Object> itr = 144 new AbstractIterator<Object>() { 145 @Override 146 public Object computeNext() { 147 return new Object(); 148 } 149 }; 150 WeakReference<Object> ref = new WeakReference<>(itr.next()); 151 GcFinalization.awaitClear(ref); 152 } 153 testDefaultBehaviorOfPeekForEmptyIteration()154 public void testDefaultBehaviorOfPeekForEmptyIteration() { 155 156 AbstractIterator<Integer> empty = 157 new AbstractIterator<Integer>() { 158 private boolean alreadyCalledEndOfData; 159 160 @Override 161 public Integer computeNext() { 162 if (alreadyCalledEndOfData) { 163 fail("Should not have been invoked again"); 164 } 165 alreadyCalledEndOfData = true; 166 return endOfData(); 167 } 168 }; 169 170 try { 171 empty.peek(); 172 fail("peek() should throw NoSuchElementException at end"); 173 } catch (NoSuchElementException expected) { 174 } 175 176 try { 177 empty.peek(); 178 fail("peek() should continue to throw NoSuchElementException at end"); 179 } catch (NoSuchElementException expected) { 180 } 181 } 182 testSneakyThrow()183 public void testSneakyThrow() throws Exception { 184 Iterator<Integer> iter = 185 new AbstractIterator<Integer>() { 186 boolean haveBeenCalled; 187 188 @Override 189 public Integer computeNext() { 190 if (haveBeenCalled) { 191 fail("Should not have been called again"); 192 } else { 193 haveBeenCalled = true; 194 sneakyThrow(new SomeCheckedException()); 195 } 196 return null; // never reached 197 } 198 }; 199 200 // The first time, the sneakily-thrown exception comes out 201 try { 202 iter.hasNext(); 203 fail("No exception thrown"); 204 } catch (Exception e) { 205 if (!(e instanceof SomeCheckedException)) { 206 throw e; 207 } 208 } 209 210 // But the second time, AbstractIterator itself throws an ISE 211 try { 212 iter.hasNext(); 213 fail("No exception thrown"); 214 } catch (IllegalStateException expected) { 215 } 216 } 217 testException()218 public void testException() { 219 final SomeUncheckedException exception = new SomeUncheckedException(); 220 Iterator<Integer> iter = 221 new AbstractIterator<Integer>() { 222 @Override 223 public Integer computeNext() { 224 throw exception; 225 } 226 }; 227 228 // It should pass through untouched 229 try { 230 iter.hasNext(); 231 fail("No exception thrown"); 232 } catch (SomeUncheckedException e) { 233 assertSame(exception, e); 234 } 235 } 236 testExceptionAfterEndOfData()237 public void testExceptionAfterEndOfData() { 238 Iterator<Integer> iter = 239 new AbstractIterator<Integer>() { 240 @Override 241 public Integer computeNext() { 242 endOfData(); 243 throw new SomeUncheckedException(); 244 } 245 }; 246 try { 247 iter.hasNext(); 248 fail("No exception thrown"); 249 } catch (SomeUncheckedException expected) { 250 } 251 } 252 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 return null; 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 a 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