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 @GwtIncompatible // weak references 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 fail("Should not have been called again"); 191 } else { 192 haveBeenCalled = true; 193 sneakyThrow(new SomeCheckedException()); 194 } 195 return null; // never reached 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 testCantRemove()252 public void testCantRemove() { 253 Iterator<Integer> iter = 254 new AbstractIterator<Integer>() { 255 boolean haveBeenCalled; 256 257 @Override 258 public Integer computeNext() { 259 if (haveBeenCalled) { 260 endOfData(); 261 } 262 haveBeenCalled = true; 263 return 0; 264 } 265 }; 266 267 assertEquals(0, (int) iter.next()); 268 269 try { 270 iter.remove(); 271 fail("No exception thrown"); 272 } catch (UnsupportedOperationException expected) { 273 } 274 } 275 testReentrantHasNext()276 public void testReentrantHasNext() { 277 Iterator<Integer> iter = 278 new AbstractIterator<Integer>() { 279 @Override 280 protected Integer computeNext() { 281 boolean unused = hasNext(); 282 return null; 283 } 284 }; 285 try { 286 iter.hasNext(); 287 fail(); 288 } catch (IllegalStateException expected) { 289 } 290 } 291 292 // Technically we should test other reentrant scenarios (9 combinations of 293 // hasNext/next/peek), but we'll cop out for now, knowing that peek() and 294 // next() both start by invoking hasNext() anyway. 295 296 /** Throws a undeclared checked exception. */ sneakyThrow(Throwable t)297 private static void sneakyThrow(Throwable t) { 298 class SneakyThrower<T extends Throwable> { 299 @SuppressWarnings("unchecked") // not really safe, but that's the point 300 void throwIt(Throwable t) throws T { 301 throw (T) t; 302 } 303 } 304 new SneakyThrower<Error>().throwIt(t); 305 } 306 307 private static class SomeCheckedException extends Exception {} 308 309 private static class SomeUncheckedException extends RuntimeException {} 310 } 311