Home | History | Annotate | Download | only in base
      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.base;
     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 testSneakyThrow() throws Exception {
     79     Iterator<Integer> iter = new AbstractIterator<Integer>() {
     80       boolean haveBeenCalled;
     81       @Override public Integer computeNext() {
     82         if (haveBeenCalled) {
     83           fail("Should not have been called again");
     84         } else {
     85           haveBeenCalled = true;
     86           sneakyThrow(new SomeCheckedException());
     87         }
     88         return null; // never reached
     89       }
     90     };
     91 
     92     // The first time, the sneakily-thrown exception comes out
     93     try {
     94       iter.hasNext();
     95       fail("No exception thrown");
     96     } catch (Exception e) {
     97       if (!(e instanceof SomeCheckedException)) {
     98         throw e;
     99       }
    100     }
    101 
    102     // But the second time, AbstractIterator itself throws an ISE
    103     try {
    104       iter.hasNext();
    105       fail("No exception thrown");
    106     } catch (IllegalStateException expected) {
    107     }
    108   }
    109 
    110   public void testException() {
    111     final SomeUncheckedException exception = new SomeUncheckedException();
    112     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    113       @Override public Integer computeNext() {
    114         throw exception;
    115       }
    116     };
    117 
    118     // It should pass through untouched
    119     try {
    120       iter.hasNext();
    121       fail("No exception thrown");
    122     } catch (SomeUncheckedException e) {
    123       assertSame(exception, e);
    124     }
    125   }
    126 
    127   public void testExceptionAfterEndOfData() {
    128     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    129       @Override public Integer computeNext() {
    130         endOfData();
    131         throw new SomeUncheckedException();
    132       }
    133     };
    134     try {
    135       iter.hasNext();
    136       fail("No exception thrown");
    137     } catch (SomeUncheckedException expected) {
    138     }
    139   }
    140 
    141   public void testCantRemove() {
    142     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    143       boolean haveBeenCalled;
    144       @Override public Integer computeNext() {
    145         if (haveBeenCalled) {
    146           endOfData();
    147         }
    148         haveBeenCalled = true;
    149         return 0;
    150       }
    151     };
    152 
    153     assertEquals(0, (int) iter.next());
    154 
    155     try {
    156       iter.remove();
    157       fail("No exception thrown");
    158     } catch (UnsupportedOperationException expected) {
    159     }
    160   }
    161 
    162   public void testReentrantHasNext() {
    163     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    164       @Override protected Integer computeNext() {
    165         hasNext();
    166         return null;
    167       }
    168     };
    169     try {
    170       iter.hasNext();
    171       fail();
    172     } catch (IllegalStateException expected) {
    173     }
    174   }
    175 
    176   // Technically we should test other reentrant scenarios (4 combinations of
    177   // hasNext/next), but we'll cop out for now, knowing that
    178   // next() both start by invoking hasNext() anyway.
    179 
    180   /**
    181    * Throws a undeclared checked exception.
    182    */
    183   private static void sneakyThrow(Throwable t) {
    184     class SneakyThrower<T extends Throwable> {
    185       @SuppressWarnings("unchecked") // not really safe, but that's the point
    186       void throwIt(Throwable t) throws T {
    187         throw (T) t;
    188       }
    189     }
    190     new SneakyThrower<Error>().throwIt(t);
    191   }
    192 
    193   private static class SomeCheckedException extends Exception {
    194   }
    195 
    196   private static class SomeUncheckedException extends RuntimeException {
    197   }
    198 }
    199