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