Home | History | Annotate | Download | only in testing
      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 
     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 
     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 
     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 
     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 
     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;
    113     IteratorWithSunJavaBug6529795(Iterator<T> iterator) {
    114       this.iterator = iterator;
    115     }
    116 
    117     @Override
    118     public boolean hasNext() {
    119       return iterator.hasNext();
    120     }
    121 
    122     @Override
    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
    133     public void remove() {
    134       if (nextThrewException) {
    135         throw new IllegalStateException();
    136       }
    137       iterator.remove();
    138     }
    139   }
    140 
    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 
    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> {
    178     TesterThatCountsCalls() {
    179       super(STEPS, MODIFIABLE, newArrayList(1),
    180           IteratorTester.KnownOrder.KNOWN_ORDER);
    181     }
    182     int numCallsToNewTargetIterator;
    183     int numCallsToVerify;
    184     @Override protected Iterator<Integer> newTargetIterator() {
    185       numCallsToNewTargetIterator++;
    186       return Lists.newArrayList(1).iterator();
    187     }
    188     @Override protected void verify(List<Integer> elements) {
    189       numCallsToVerify++;
    190       super.verify(elements);
    191     }
    192   }
    193 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    329     private ThrowingIterator(RuntimeException ex) {
    330       this.ex = ex;
    331     }
    332 
    333     @Override
    334     public boolean hasNext() {
    335       // IteratorTester doesn't expect exceptions for hasNext().
    336       return true;
    337     }
    338 
    339     @Override
    340     public E next() {
    341       ex.fillInStackTrace();
    342       throw ex;
    343     }
    344 
    345     @Override
    346     public void remove() {
    347       ex.fillInStackTrace();
    348       throw ex;
    349     }
    350   }
    351 }
    352