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