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