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 static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; 20 21 import com.google.common.annotations.GwtCompatible; 22 import com.google.common.annotations.GwtIncompatible; 23 import com.google.common.annotations.J2ktIncompatible; 24 import com.google.common.base.TestExceptions.SomeCheckedException; 25 import com.google.common.base.TestExceptions.SomeUncheckedException; 26 import com.google.common.testing.GcFinalization; 27 import java.lang.ref.WeakReference; 28 import java.util.Iterator; 29 import java.util.NoSuchElementException; 30 import junit.framework.TestCase; 31 32 /** 33 * Unit test for {@code AbstractIterator}. 34 * 35 * @author Kevin Bourrillion 36 */ 37 @GwtCompatible(emulated = true) 38 public class AbstractIteratorTest extends TestCase { 39 testDefaultBehaviorOfNextAndHasNext()40 public void testDefaultBehaviorOfNextAndHasNext() { 41 42 // This sample AbstractIterator returns 0 on the first call, 1 on the 43 // second, then signals that it's reached the end of the data 44 Iterator<Integer> iter = 45 new AbstractIterator<Integer>() { 46 private int rep; 47 48 @Override 49 public Integer computeNext() { 50 switch (rep++) { 51 case 0: 52 return 0; 53 case 1: 54 return 1; 55 case 2: 56 return endOfData(); 57 default: 58 throw new AssertionError("Should not have been invoked again"); 59 } 60 } 61 }; 62 63 assertTrue(iter.hasNext()); 64 assertEquals(0, (int) iter.next()); 65 66 // verify idempotence of hasNext() 67 assertTrue(iter.hasNext()); 68 assertTrue(iter.hasNext()); 69 assertTrue(iter.hasNext()); 70 assertEquals(1, (int) iter.next()); 71 72 assertFalse(iter.hasNext()); 73 74 // Make sure computeNext() doesn't get invoked again 75 assertFalse(iter.hasNext()); 76 77 assertThrows(NoSuchElementException.class, iter::next); 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 throw new AssertionError("Should not have been called again"); 89 } else { 90 haveBeenCalled = true; 91 sneakyThrow(new SomeCheckedException()); 92 throw new AssertionError(); // unreachable 93 } 94 } 95 }; 96 97 // The first time, the sneakily-thrown exception comes out 98 assertThrows(SomeCheckedException.class, iter::hasNext); 99 // But the second time, AbstractIterator itself throws an ISE 100 assertThrows(IllegalStateException.class, iter::hasNext); 101 } 102 testException()103 public void testException() { 104 final SomeUncheckedException exception = new SomeUncheckedException(); 105 Iterator<Integer> iter = 106 new AbstractIterator<Integer>() { 107 @Override 108 public Integer computeNext() { 109 throw exception; 110 } 111 }; 112 113 // It should pass through untouched 114 SomeUncheckedException e = assertThrows(SomeUncheckedException.class, iter::hasNext); 115 assertSame(exception, e); 116 } 117 testExceptionAfterEndOfData()118 public void testExceptionAfterEndOfData() { 119 Iterator<Integer> iter = 120 new AbstractIterator<Integer>() { 121 @Override 122 public Integer computeNext() { 123 endOfData(); 124 throw new SomeUncheckedException(); 125 } 126 }; 127 assertThrows(SomeUncheckedException.class, iter::hasNext); 128 } 129 testCantRemove()130 public void testCantRemove() { 131 Iterator<Integer> iter = 132 new AbstractIterator<Integer>() { 133 boolean haveBeenCalled; 134 135 @Override 136 public Integer computeNext() { 137 if (haveBeenCalled) { 138 endOfData(); 139 } 140 haveBeenCalled = true; 141 return 0; 142 } 143 }; 144 145 assertEquals(0, (int) iter.next()); 146 147 assertThrows(UnsupportedOperationException.class, iter::remove); 148 } 149 150 151 @GwtIncompatible // weak references 152 @J2ktIncompatible 153 @AndroidIncompatible // depends on details of GC testFreesNextReference()154 public void testFreesNextReference() { 155 Iterator<Object> itr = 156 new AbstractIterator<Object>() { 157 @Override 158 public Object computeNext() { 159 return new Object(); 160 } 161 }; 162 WeakReference<Object> ref = new WeakReference<>(itr.next()); 163 GcFinalization.awaitClear(ref); 164 } 165 testReentrantHasNext()166 public void testReentrantHasNext() { 167 Iterator<Integer> iter = 168 new AbstractIterator<Integer>() { 169 @Override 170 protected Integer computeNext() { 171 boolean unused = hasNext(); 172 throw new AssertionError(); 173 } 174 }; 175 assertThrows(IllegalStateException.class, iter::hasNext); 176 } 177 178 // Technically we should test other reentrant scenarios (4 combinations of 179 // hasNext/next), but we'll cop out for now, knowing that 180 // next() both start by invoking hasNext() anyway. 181 182 /** Throws an undeclared checked exception. */ sneakyThrow(Throwable t)183 private static void sneakyThrow(Throwable t) { 184 class SneakyThrower<T extends Throwable> { 185 @SuppressWarnings("unchecked") // intentionally unsafe for test 186 void throwIt(Throwable t) throws T { 187 throw (T) t; 188 } 189 } 190 new SneakyThrower<Error>().throwIt(t); 191 } 192 } 193