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