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