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.base; 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 @GwtCompatible(emulated = true) 33 public class AbstractIteratorTest extends TestCase { 34 testDefaultBehaviorOfNextAndHasNext()35 public void testDefaultBehaviorOfNextAndHasNext() { 36 37 // This sample AbstractIterator returns 0 on the first call, 1 on the 38 // second, then signals that it's reached the end of the data 39 Iterator<Integer> iter = 40 new AbstractIterator<Integer>() { 41 private int rep; 42 43 @Override 44 public Integer computeNext() { 45 switch (rep++) { 46 case 0: 47 return 0; 48 case 1: 49 return 1; 50 case 2: 51 return endOfData(); 52 default: 53 fail("Should not have been invoked again"); 54 return null; 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 testSneakyThrow()80 public void testSneakyThrow() throws Exception { 81 Iterator<Integer> iter = 82 new AbstractIterator<Integer>() { 83 boolean haveBeenCalled; 84 85 @Override 86 public Integer computeNext() { 87 if (haveBeenCalled) { 88 fail("Should not have been called again"); 89 } else { 90 haveBeenCalled = true; 91 sneakyThrow(new SomeCheckedException()); 92 } 93 return null; // never reached 94 } 95 }; 96 97 // The first time, the sneakily-thrown exception comes out 98 try { 99 iter.hasNext(); 100 fail("No exception thrown"); 101 } catch (Exception e) { 102 if (!(e instanceof SomeCheckedException)) { 103 throw e; 104 } 105 } 106 107 // But the second time, AbstractIterator itself throws an ISE 108 try { 109 iter.hasNext(); 110 fail("No exception thrown"); 111 } catch (IllegalStateException expected) { 112 } 113 } 114 testException()115 public void testException() { 116 final SomeUncheckedException exception = new SomeUncheckedException(); 117 Iterator<Integer> iter = 118 new AbstractIterator<Integer>() { 119 @Override 120 public Integer computeNext() { 121 throw exception; 122 } 123 }; 124 125 // It should pass through untouched 126 try { 127 iter.hasNext(); 128 fail("No exception thrown"); 129 } catch (SomeUncheckedException e) { 130 assertSame(exception, e); 131 } 132 } 133 testExceptionAfterEndOfData()134 public void testExceptionAfterEndOfData() { 135 Iterator<Integer> iter = 136 new AbstractIterator<Integer>() { 137 @Override 138 public Integer computeNext() { 139 endOfData(); 140 throw new SomeUncheckedException(); 141 } 142 }; 143 try { 144 iter.hasNext(); 145 fail("No exception thrown"); 146 } catch (SomeUncheckedException expected) { 147 } 148 } 149 testCantRemove()150 public void testCantRemove() { 151 Iterator<Integer> iter = 152 new AbstractIterator<Integer>() { 153 boolean haveBeenCalled; 154 155 @Override 156 public Integer computeNext() { 157 if (haveBeenCalled) { 158 endOfData(); 159 } 160 haveBeenCalled = true; 161 return 0; 162 } 163 }; 164 165 assertEquals(0, (int) iter.next()); 166 167 try { 168 iter.remove(); 169 fail("No exception thrown"); 170 } catch (UnsupportedOperationException expected) { 171 } 172 } 173 174 @GwtIncompatible // weak references testFreesNextReference()175 public void testFreesNextReference() { 176 Iterator<Object> itr = 177 new AbstractIterator<Object>() { 178 @Override 179 public Object computeNext() { 180 return new Object(); 181 } 182 }; 183 WeakReference<Object> ref = new WeakReference<>(itr.next()); 184 GcFinalization.awaitClear(ref); 185 } 186 testReentrantHasNext()187 public void testReentrantHasNext() { 188 Iterator<Integer> iter = 189 new AbstractIterator<Integer>() { 190 @Override 191 protected Integer computeNext() { 192 boolean unused = hasNext(); 193 return null; 194 } 195 }; 196 try { 197 iter.hasNext(); 198 fail(); 199 } catch (IllegalStateException expected) { 200 } 201 } 202 203 // Technically we should test other reentrant scenarios (4 combinations of 204 // hasNext/next), but we'll cop out for now, knowing that 205 // next() both start by invoking hasNext() anyway. 206 207 /** Throws a undeclared checked exception. */ sneakyThrow(Throwable t)208 private static void sneakyThrow(Throwable t) { 209 class SneakyThrower<T extends Throwable> { 210 @SuppressWarnings("unchecked") // intentionally unsafe for test 211 void throwIt(Throwable t) throws T { 212 throw (T) t; 213 } 214 } 215 new SneakyThrower<Error>().throwIt(t); 216 } 217 218 private static class SomeCheckedException extends Exception {} 219 220 private static class SomeUncheckedException extends RuntimeException {} 221 } 222