1 /* 2 * Copyright (C) 2008 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.testing; 18 19 import static com.google.common.collect.Lists.newArrayList; 20 import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; 21 import static java.util.Collections.emptyList; 22 23 import com.google.common.annotations.GwtCompatible; 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.Lists; 26 import java.util.Iterator; 27 import java.util.List; 28 import java.util.NoSuchElementException; 29 import junit.framework.AssertionFailedError; 30 import junit.framework.TestCase; 31 32 /** 33 * Unit test for IteratorTester. 34 * 35 * @author Mick Killianey 36 */ 37 @GwtCompatible 38 @SuppressWarnings("serial") // No serialization is used in this test 39 public class IteratorTesterTest extends TestCase { 40 testCanCatchDifferentLengthOfIteration()41 public void testCanCatchDifferentLengthOfIteration() { 42 IteratorTester<Integer> tester = 43 new IteratorTester<Integer>( 44 4, MODIFIABLE, newArrayList(1, 2, 3), IteratorTester.KnownOrder.KNOWN_ORDER) { 45 @Override 46 protected Iterator<Integer> newTargetIterator() { 47 return Lists.newArrayList(1, 2, 3, 4).iterator(); 48 } 49 }; 50 assertFailure(tester); 51 } 52 testCanCatchDifferentContents()53 public void testCanCatchDifferentContents() { 54 IteratorTester<Integer> tester = 55 new IteratorTester<Integer>( 56 3, MODIFIABLE, newArrayList(1, 2, 3), IteratorTester.KnownOrder.KNOWN_ORDER) { 57 @Override 58 protected Iterator<Integer> newTargetIterator() { 59 return Lists.newArrayList(1, 3, 2).iterator(); 60 } 61 }; 62 assertFailure(tester); 63 } 64 testCanCatchDifferentRemoveBehaviour()65 public void testCanCatchDifferentRemoveBehaviour() { 66 IteratorTester<Integer> tester = 67 new IteratorTester<Integer>( 68 3, MODIFIABLE, newArrayList(1, 2), IteratorTester.KnownOrder.KNOWN_ORDER) { 69 @Override 70 protected Iterator<Integer> newTargetIterator() { 71 return ImmutableList.of(1, 2).iterator(); 72 } 73 }; 74 assertFailure(tester); 75 } 76 testUnknownOrder()77 public void testUnknownOrder() { 78 new IteratorTester<Integer>( 79 3, MODIFIABLE, newArrayList(1, 2), IteratorTester.KnownOrder.UNKNOWN_ORDER) { 80 @Override 81 protected Iterator<Integer> newTargetIterator() { 82 return newArrayList(2, 1).iterator(); 83 } 84 }.test(); 85 } 86 testUnknownOrderUnrecognizedElement()87 public void testUnknownOrderUnrecognizedElement() { 88 IteratorTester<Integer> tester = 89 new IteratorTester<Integer>( 90 3, MODIFIABLE, newArrayList(1, 2, 50), IteratorTester.KnownOrder.UNKNOWN_ORDER) { 91 @Override 92 protected Iterator<Integer> newTargetIterator() { 93 return newArrayList(2, 1, 3).iterator(); 94 } 95 }; 96 assertFailure(tester); 97 } 98 99 /** 100 * This Iterator wraps another iterator and gives it a bug found in JDK6. 101 * 102 * <p>This bug is this: if you create an iterator from a TreeSet and call next() on that iterator 103 * when hasNext() is false, so that next() throws a NoSuchElementException, then subsequent calls 104 * to remove() will incorrectly throw an IllegalStateException, instead of removing the last 105 * element returned. 106 * 107 * <p>See <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6529795">Sun bug 6529795</a> 108 */ 109 static class IteratorWithSunJavaBug6529795<T> implements Iterator<T> { 110 Iterator<T> iterator; 111 boolean nextThrewException; 112 IteratorWithSunJavaBug6529795(Iterator<T> iterator)113 IteratorWithSunJavaBug6529795(Iterator<T> iterator) { 114 this.iterator = iterator; 115 } 116 117 @Override hasNext()118 public boolean hasNext() { 119 return iterator.hasNext(); 120 } 121 122 @Override next()123 public T next() { 124 try { 125 return iterator.next(); 126 } catch (NoSuchElementException e) { 127 nextThrewException = true; 128 throw e; 129 } 130 } 131 132 @Override remove()133 public void remove() { 134 if (nextThrewException) { 135 throw new IllegalStateException(); 136 } 137 iterator.remove(); 138 } 139 } 140 testCanCatchSunJavaBug6529795InTargetIterator()141 public void testCanCatchSunJavaBug6529795InTargetIterator() { 142 try { 143 /* Choose 4 steps to get sequence [next, next, next, remove] */ 144 new IteratorTester<Integer>( 145 4, MODIFIABLE, newArrayList(1, 2), IteratorTester.KnownOrder.KNOWN_ORDER) { 146 @Override 147 protected Iterator<Integer> newTargetIterator() { 148 Iterator<Integer> iterator = Lists.newArrayList(1, 2).iterator(); 149 return new IteratorWithSunJavaBug6529795<>(iterator); 150 } 151 }.test(); 152 } catch (AssertionFailedError e) { 153 return; 154 } 155 fail("Should have caught jdk6 bug in target iterator"); 156 } 157 158 private static final int STEPS = 3; 159 160 static class TesterThatCountsCalls extends IteratorTester<Integer> { TesterThatCountsCalls()161 TesterThatCountsCalls() { 162 super(STEPS, MODIFIABLE, newArrayList(1), IteratorTester.KnownOrder.KNOWN_ORDER); 163 } 164 165 int numCallsToNewTargetIterator; 166 int numCallsToVerify; 167 168 @Override newTargetIterator()169 protected Iterator<Integer> newTargetIterator() { 170 numCallsToNewTargetIterator++; 171 return Lists.newArrayList(1).iterator(); 172 } 173 174 @Override verify(List<Integer> elements)175 protected void verify(List<Integer> elements) { 176 numCallsToVerify++; 177 super.verify(elements); 178 } 179 } 180 testVerifyGetsCalled()181 public void testVerifyGetsCalled() { 182 TesterThatCountsCalls tester = new TesterThatCountsCalls(); 183 184 tester.test(); 185 186 assertEquals( 187 "Should have verified once per stimulus executed", 188 tester.numCallsToVerify, 189 tester.numCallsToNewTargetIterator * STEPS); 190 } 191 testVerifyCanThrowAssertionThatFailsTest()192 public void testVerifyCanThrowAssertionThatFailsTest() { 193 final String message = "Important info about why verify failed"; 194 IteratorTester<Integer> tester = 195 new IteratorTester<Integer>( 196 1, MODIFIABLE, newArrayList(1, 2, 3), IteratorTester.KnownOrder.KNOWN_ORDER) { 197 @Override 198 protected Iterator<Integer> newTargetIterator() { 199 return Lists.newArrayList(1, 2, 3).iterator(); 200 } 201 202 @Override 203 protected void verify(List<Integer> elements) { 204 throw new AssertionFailedError(message); 205 } 206 }; 207 AssertionFailedError actual = null; 208 try { 209 tester.test(); 210 } catch (AssertionFailedError e) { 211 actual = e; 212 } 213 assertNotNull("verify() should be able to cause test failure", actual); 214 assertTrue( 215 "AssertionFailedError should have info about why test failed", 216 actual.getCause().getMessage().contains(message)); 217 } 218 testMissingException()219 public void testMissingException() { 220 List<Integer> emptyList = newArrayList(); 221 222 IteratorTester<Integer> tester = 223 new IteratorTester<Integer>( 224 1, MODIFIABLE, emptyList, IteratorTester.KnownOrder.KNOWN_ORDER) { 225 @Override 226 protected Iterator<Integer> newTargetIterator() { 227 return new Iterator<Integer>() { 228 @Override 229 public void remove() { 230 // We should throw here, but we won't! 231 } 232 233 @Override 234 public Integer next() { 235 // We should throw here, but we won't! 236 return null; 237 } 238 239 @Override 240 public boolean hasNext() { 241 return false; 242 } 243 }; 244 } 245 }; 246 assertFailure(tester); 247 } 248 testUnexpectedException()249 public void testUnexpectedException() { 250 IteratorTester<Integer> tester = 251 new IteratorTester<Integer>( 252 1, MODIFIABLE, newArrayList(1), IteratorTester.KnownOrder.KNOWN_ORDER) { 253 @Override 254 protected Iterator<Integer> newTargetIterator() { 255 return new ThrowingIterator<>(new IllegalStateException()); 256 } 257 }; 258 assertFailure(tester); 259 } 260 testSimilarException()261 public void testSimilarException() { 262 List<Integer> emptyList = emptyList(); 263 IteratorTester<Integer> tester = 264 new IteratorTester<Integer>( 265 1, MODIFIABLE, emptyList, IteratorTester.KnownOrder.KNOWN_ORDER) { 266 @Override 267 protected Iterator<Integer> newTargetIterator() { 268 return new Iterator<Integer>() { 269 @Override 270 public void remove() { 271 throw new IllegalStateException() { 272 /* subclass */ 273 }; 274 } 275 276 @Override 277 public Integer next() { 278 throw new NoSuchElementException() { 279 /* subclass */ 280 }; 281 } 282 283 @Override 284 public boolean hasNext() { 285 return false; 286 } 287 }; 288 } 289 }; 290 tester.test(); 291 } 292 testMismatchedException()293 public void testMismatchedException() { 294 List<Integer> emptyList = emptyList(); 295 IteratorTester<Integer> tester = 296 new IteratorTester<Integer>( 297 1, MODIFIABLE, emptyList, IteratorTester.KnownOrder.KNOWN_ORDER) { 298 @Override 299 protected Iterator<Integer> newTargetIterator() { 300 return new Iterator<Integer>() { 301 @Override 302 public void remove() { 303 // Wrong exception type. 304 throw new IllegalArgumentException(); 305 } 306 307 @Override 308 public Integer next() { 309 // Wrong exception type. 310 throw new UnsupportedOperationException(); 311 } 312 313 @Override 314 public boolean hasNext() { 315 return false; 316 } 317 }; 318 } 319 }; 320 assertFailure(tester); 321 } 322 assertFailure(IteratorTester<?> tester)323 private static void assertFailure(IteratorTester<?> tester) { 324 try { 325 tester.test(); 326 } catch (AssertionFailedError expected) { 327 return; 328 } 329 fail(); 330 } 331 332 private static final class ThrowingIterator<E> implements Iterator<E> { 333 private final RuntimeException ex; 334 ThrowingIterator(RuntimeException ex)335 private ThrowingIterator(RuntimeException ex) { 336 this.ex = ex; 337 } 338 339 @Override hasNext()340 public boolean hasNext() { 341 // IteratorTester doesn't expect exceptions for hasNext(). 342 return true; 343 } 344 345 @Override next()346 public E next() { 347 ex.fillInStackTrace(); 348 throw ex; 349 } 350 351 @Override remove()352 public void remove() { 353 ex.fillInStackTrace(); 354 throw ex; 355 } 356 } 357 } 358