Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2007 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;
     18 
     19 import com.google.common.annotations.GwtCompatible;
     20 
     21 import junit.framework.TestCase;
     22 
     23 import java.util.Iterator;
     24 import java.util.NoSuchElementException;
     25 
     26 /**
     27  * Unit test for {@code AbstractIterator}.
     28  *
     29  * @author Kevin Bourrillion
     30  */
     31 @SuppressWarnings("serial") // No serialization is used in this test
     32 @GwtCompatible
     33 // TODO(cpovirk): why is this slow (>1m/test) under GWT when fully optimized?
     34 public class AbstractIteratorTest extends TestCase {
     35 
     36   public void testDefaultBehaviorOfNextAndHasNext() {
     37 
     38     // This sample AbstractIterator returns 0 on the first call, 1 on the
     39     // second, then signals that it's reached the end of the data
     40     Iterator<Integer> iter = new AbstractIterator<Integer>() {
     41       private int rep;
     42       @Override public Integer computeNext() {
     43         switch (rep++) {
     44           case 0:
     45             return 0;
     46           case 1:
     47             return 1;
     48           case 2:
     49             return endOfData();
     50           default:
     51             fail("Should not have been invoked again");
     52             return null;
     53         }
     54       }
     55     };
     56 
     57     assertTrue(iter.hasNext());
     58     assertEquals(0, (int) iter.next());
     59 
     60     // verify idempotence of hasNext()
     61     assertTrue(iter.hasNext());
     62     assertTrue(iter.hasNext());
     63     assertTrue(iter.hasNext());
     64     assertEquals(1, (int) iter.next());
     65 
     66     assertFalse(iter.hasNext());
     67 
     68     // Make sure computeNext() doesn't get invoked again
     69     assertFalse(iter.hasNext());
     70 
     71     try {
     72       iter.next();
     73       fail("no exception thrown");
     74     } catch (NoSuchElementException expected) {
     75     }
     76   }
     77 
     78   public void testDefaultBehaviorOfPeek() {
     79     /*
     80      * This sample AbstractIterator returns 0 on the first call, 1 on the
     81      * second, then signals that it's reached the end of the data
     82      */
     83     AbstractIterator<Integer> iter = new AbstractIterator<Integer>() {
     84       private int rep;
     85       @Override public Integer computeNext() {
     86         switch (rep++) {
     87           case 0:
     88             return 0;
     89           case 1:
     90             return 1;
     91           case 2:
     92             return endOfData();
     93           default:
     94             fail("Should not have been invoked again");
     95             return null;
     96         }
     97       }
     98     };
     99 
    100     assertEquals(0, (int) iter.peek());
    101     assertEquals(0, (int) iter.peek());
    102     assertTrue(iter.hasNext());
    103     assertEquals(0, (int) iter.peek());
    104     assertEquals(0, (int) iter.next());
    105 
    106     assertEquals(1, (int) iter.peek());
    107     assertEquals(1, (int) iter.next());
    108 
    109     try {
    110       iter.peek();
    111       fail("peek() should throw NoSuchElementException at end");
    112     } catch (NoSuchElementException expected) {
    113     }
    114 
    115     try {
    116       iter.peek();
    117       fail("peek() should continue to throw NoSuchElementException at end");
    118     } catch (NoSuchElementException expected) {
    119     }
    120 
    121     try {
    122       iter.next();
    123       fail("next() should throw NoSuchElementException as usual");
    124     } catch (NoSuchElementException expected) {
    125     }
    126 
    127     try {
    128       iter.peek();
    129       fail("peek() should still throw NoSuchElementException after next()");
    130     } catch (NoSuchElementException expected) {
    131     }
    132   }
    133 
    134   public void testDefaultBehaviorOfPeekForEmptyIteration() {
    135 
    136     AbstractIterator<Integer> empty = new AbstractIterator<Integer>() {
    137       private boolean alreadyCalledEndOfData;
    138       @Override public Integer computeNext() {
    139         if (alreadyCalledEndOfData) {
    140           fail("Should not have been invoked again");
    141         }
    142         alreadyCalledEndOfData = true;
    143         return endOfData();
    144       }
    145     };
    146 
    147     try {
    148       empty.peek();
    149       fail("peek() should throw NoSuchElementException at end");
    150     } catch (NoSuchElementException expected) {
    151     }
    152 
    153     try {
    154       empty.peek();
    155       fail("peek() should continue to throw NoSuchElementException at end");
    156     } catch (NoSuchElementException expected) {
    157     }
    158   }
    159 
    160   public void testSneakyThrow() throws Exception {
    161     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    162       boolean haveBeenCalled;
    163       @Override public Integer computeNext() {
    164         if (haveBeenCalled) {
    165           fail("Should not have been called again");
    166         } else {
    167           haveBeenCalled = true;
    168           sneakyThrow(new SomeCheckedException());
    169         }
    170         return null; // never reached
    171       }
    172     };
    173 
    174     // The first time, the sneakily-thrown exception comes out
    175     try {
    176       iter.hasNext();
    177       fail("No exception thrown");
    178     } catch (Exception e) {
    179       if (!(e instanceof SomeCheckedException)) {
    180         throw e;
    181       }
    182     }
    183 
    184     // But the second time, AbstractIterator itself throws an ISE
    185     try {
    186       iter.hasNext();
    187       fail("No exception thrown");
    188     } catch (IllegalStateException expected) {
    189     }
    190   }
    191 
    192   public void testException() {
    193     final SomeUncheckedException exception = new SomeUncheckedException();
    194     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    195       @Override public Integer computeNext() {
    196         throw exception;
    197       }
    198     };
    199 
    200     // It should pass through untouched
    201     try {
    202       iter.hasNext();
    203       fail("No exception thrown");
    204     } catch (SomeUncheckedException e) {
    205       assertSame(exception, e);
    206     }
    207   }
    208 
    209   public void testExceptionAfterEndOfData() {
    210     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    211       @Override public Integer computeNext() {
    212         endOfData();
    213         throw new SomeUncheckedException();
    214       }
    215     };
    216     try {
    217       iter.hasNext();
    218       fail("No exception thrown");
    219     } catch (SomeUncheckedException expected) {
    220     }
    221   }
    222 
    223   public void testCantRemove() {
    224     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    225       boolean haveBeenCalled;
    226       @Override public Integer computeNext() {
    227         if (haveBeenCalled) {
    228           endOfData();
    229         }
    230         haveBeenCalled = true;
    231         return 0;
    232       }
    233     };
    234 
    235     assertEquals(0, (int) iter.next());
    236 
    237     try {
    238       iter.remove();
    239       fail("No exception thrown");
    240     } catch (UnsupportedOperationException expected) {
    241     }
    242   }
    243 
    244   public void testReentrantHasNext() {
    245     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    246       @Override protected Integer computeNext() {
    247         hasNext();
    248         return null;
    249       }
    250     };
    251     try {
    252       iter.hasNext();
    253       fail();
    254     } catch (IllegalStateException expected) {
    255     }
    256   }
    257 
    258   // Technically we should test other reentrant scenarios (9 combinations of
    259   // hasNext/next/peek), but we'll cop out for now, knowing that peek() and
    260   // next() both start by invoking hasNext() anyway.
    261 
    262   /**
    263    * Throws a undeclared checked exception.
    264    */
    265   private static void sneakyThrow(Throwable t) {
    266     class SneakyThrower<T extends Throwable> {
    267       @SuppressWarnings("unchecked") // not really safe, but that's the point
    268       void throwIt(Throwable t) throws T {
    269         throw (T) t;
    270       }
    271     }
    272     new SneakyThrower<Error>().throwIt(t);
    273   }
    274 
    275   private static class SomeCheckedException extends Exception {
    276   }
    277 
    278   private static class SomeUncheckedException extends RuntimeException {
    279   }
    280 }
    281