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 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 @GwtCompatible(emulated = true)
     35 // TODO(cpovirk): why is this slow (>1m/test) under GWT when fully optimized?
     36 public class AbstractIteratorTest extends TestCase {
     37 
     38   public void testDefaultBehaviorOfNextAndHasNext() {
     39 
     40     // This sample AbstractIterator returns 0 on the first call, 1 on the
     41     // second, then signals that it's reached the end of the data
     42     Iterator<Integer> iter = new AbstractIterator<Integer>() {
     43       private int rep;
     44       @Override public Integer computeNext() {
     45         switch (rep++) {
     46           case 0:
     47             return 0;
     48           case 1:
     49             return 1;
     50           case 2:
     51             return endOfData();
     52           default:
     53             fail("Should not have been invoked again");
     54             return null;
     55         }
     56       }
     57     };
     58 
     59     assertTrue(iter.hasNext());
     60     assertEquals(0, (int) iter.next());
     61 
     62     // verify idempotence of hasNext()
     63     assertTrue(iter.hasNext());
     64     assertTrue(iter.hasNext());
     65     assertTrue(iter.hasNext());
     66     assertEquals(1, (int) iter.next());
     67 
     68     assertFalse(iter.hasNext());
     69 
     70     // Make sure computeNext() doesn't get invoked again
     71     assertFalse(iter.hasNext());
     72 
     73     try {
     74       iter.next();
     75       fail("no exception thrown");
     76     } catch (NoSuchElementException expected) {
     77     }
     78   }
     79 
     80   public void testSneakyThrow() throws Exception {
     81     Iterator<Integer> iter = new AbstractIterator<Integer>() {
     82       boolean haveBeenCalled;
     83       @Override public Integer computeNext() {
     84         if (haveBeenCalled) {
     85           fail("Should not have been called again");
     86         } else {
     87           haveBeenCalled = true;
     88           sneakyThrow(new SomeCheckedException());
     89         }
     90         return null; // never reached
     91       }
     92     };
     93 
     94     // The first time, the sneakily-thrown exception comes out
     95     try {
     96       iter.hasNext();
     97       fail("No exception thrown");
     98     } catch (Exception e) {
     99       if (!(e instanceof SomeCheckedException)) {
    100         throw e;
    101       }
    102     }
    103 
    104     // But the second time, AbstractIterator itself throws an ISE
    105     try {
    106       iter.hasNext();
    107       fail("No exception thrown");
    108     } catch (IllegalStateException expected) {
    109     }
    110   }
    111 
    112   public void testException() {
    113     final SomeUncheckedException exception = new SomeUncheckedException();
    114     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    115       @Override public Integer computeNext() {
    116         throw exception;
    117       }
    118     };
    119 
    120     // It should pass through untouched
    121     try {
    122       iter.hasNext();
    123       fail("No exception thrown");
    124     } catch (SomeUncheckedException e) {
    125       assertSame(exception, e);
    126     }
    127   }
    128 
    129   public void testExceptionAfterEndOfData() {
    130     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    131       @Override public Integer computeNext() {
    132         endOfData();
    133         throw new SomeUncheckedException();
    134       }
    135     };
    136     try {
    137       iter.hasNext();
    138       fail("No exception thrown");
    139     } catch (SomeUncheckedException expected) {
    140     }
    141   }
    142 
    143   public void testCantRemove() {
    144     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    145       boolean haveBeenCalled;
    146       @Override public Integer computeNext() {
    147         if (haveBeenCalled) {
    148           endOfData();
    149         }
    150         haveBeenCalled = true;
    151         return 0;
    152       }
    153     };
    154 
    155     assertEquals(0, (int) iter.next());
    156 
    157     try {
    158       iter.remove();
    159       fail("No exception thrown");
    160     } catch (UnsupportedOperationException expected) {
    161     }
    162   }
    163 
    164   @GwtIncompatible("weak references")
    165   public void testFreesNextReference() {
    166     Iterator<Object> itr = new AbstractIterator<Object>() {
    167       @Override public Object computeNext() {
    168         return new Object();
    169       }
    170     };
    171     WeakReference<Object> ref = new WeakReference<Object>(itr.next());
    172     GcFinalization.awaitClear(ref);
    173   }
    174 
    175   public void testReentrantHasNext() {
    176     Iterator<Integer> iter = new AbstractIterator<Integer>() {
    177       @Override protected Integer computeNext() {
    178         hasNext();
    179         return null;
    180       }
    181     };
    182     try {
    183       iter.hasNext();
    184       fail();
    185     } catch (IllegalStateException expected) {
    186     }
    187   }
    188 
    189   // Technically we should test other reentrant scenarios (4 combinations of
    190   // hasNext/next), but we'll cop out for now, knowing that
    191   // next() both start by invoking hasNext() anyway.
    192 
    193   /**
    194    * Throws a undeclared checked exception.
    195    */
    196   private static void sneakyThrow(Throwable t) {
    197     class SneakyThrower<T extends Throwable> {
    198       @SuppressWarnings("unchecked") // intentionally unsafe for test
    199       void throwIt(Throwable t) throws T {
    200         throw (T) t;
    201       }
    202     }
    203     new SneakyThrower<Error>().throwIt(t);
    204   }
    205 
    206   private static class SomeCheckedException extends Exception {
    207   }
    208 
    209   private static class SomeUncheckedException extends RuntimeException {
    210   }
    211 }
    212