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