| /* |
| * Copyright (C) 2008 The Guava Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.common.collect.testing; |
| |
| import static com.google.common.collect.Lists.newArrayList; |
| import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; |
| import static java.util.Collections.emptyList; |
| |
| import com.google.common.annotations.GwtCompatible; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| |
| import junit.framework.AssertionFailedError; |
| import junit.framework.TestCase; |
| |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| |
| /** |
| * Unit test for IteratorTester. |
| * |
| * @author Mick Killianey |
| */ |
| @GwtCompatible |
| @SuppressWarnings("serial") // No serialization is used in this test |
| public class IteratorTesterTest extends TestCase { |
| |
| public void testCanCatchDifferentLengthOfIteration() { |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(4, MODIFIABLE, newArrayList(1, 2, 3), |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return Lists.newArrayList(1, 2, 3, 4).iterator(); |
| } |
| }; |
| assertFailure(tester); |
| } |
| |
| public void testCanCatchDifferentContents() { |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(3, MODIFIABLE, newArrayList(1, 2, 3), |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return Lists.newArrayList(1, 3, 2).iterator(); |
| } |
| }; |
| assertFailure(tester); |
| } |
| |
| public void testCanCatchDifferentRemoveBehaviour() { |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(3, MODIFIABLE, newArrayList(1, 2), |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return ImmutableList.of(1, 2).iterator(); |
| } |
| }; |
| assertFailure(tester); |
| } |
| |
| public void testUnknownOrder() { |
| new IteratorTester<Integer>(3, MODIFIABLE, newArrayList(1, 2), |
| IteratorTester.KnownOrder.UNKNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return newArrayList(2, 1).iterator(); |
| } |
| }.test(); |
| } |
| |
| public void testUnknownOrderUnrecognizedElement() { |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(3, MODIFIABLE, newArrayList(1, 2, 50), |
| IteratorTester.KnownOrder.UNKNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return newArrayList(2, 1, 3).iterator(); |
| } |
| }; |
| assertFailure(tester); |
| } |
| |
| /** |
| * This Iterator wraps another iterator and gives it a bug found |
| * in JDK6. |
| * |
| * <p>This bug is this: if you create an iterator from a TreeSet |
| * and call next() on that iterator when hasNext() is false, so |
| * that next() throws a NoSuchElementException, then subsequent |
| * calls to remove() will incorrectly throw an IllegalStateException, |
| * instead of removing the last element returned. |
| * |
| * <p>See |
| * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6529795"> |
| * Sun bug 6529795</a> |
| */ |
| static class IteratorWithSunJavaBug6529795<T> implements Iterator<T> { |
| Iterator<T> iterator; |
| boolean nextThrewException; |
| IteratorWithSunJavaBug6529795(Iterator<T> iterator) { |
| this.iterator = iterator; |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return iterator.hasNext(); |
| } |
| |
| @Override |
| public T next() { |
| try { |
| return iterator.next(); |
| } catch (NoSuchElementException e) { |
| nextThrewException = true; |
| throw e; |
| } |
| } |
| |
| @Override |
| public void remove() { |
| if (nextThrewException) { |
| throw new IllegalStateException(); |
| } |
| iterator.remove(); |
| } |
| } |
| |
| public void testCanCatchSunJavaBug6529795InTargetIterator() { |
| try { |
| /* Choose 4 steps to get sequence [next, next, next, remove] */ |
| new IteratorTester<Integer>(4, MODIFIABLE, newArrayList(1, 2), |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| Iterator<Integer> iterator = Lists.newArrayList(1, 2).iterator(); |
| return new IteratorWithSunJavaBug6529795<Integer>(iterator); |
| } |
| }.test(); |
| } catch (AssertionFailedError e) { |
| return; |
| } |
| fail("Should have caught jdk6 bug in target iterator"); |
| } |
| |
| public void testCanWorkAroundSunJavaBug6529795InTargetIterator() { |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(4, MODIFIABLE, newArrayList(1, 2), |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| Iterator<Integer> iterator = Lists.newArrayList(1, 2).iterator(); |
| return new IteratorWithSunJavaBug6529795<Integer>(iterator); |
| } |
| }; |
| |
| /* |
| * Calling this method on an IteratorTester should avoid flagging |
| * the bug exposed by the preceding test. |
| */ |
| tester.ignoreSunJavaBug6529795(); |
| |
| tester.test(); |
| } |
| |
| private static final int STEPS = 3; |
| static class TesterThatCountsCalls extends IteratorTester<Integer> { |
| TesterThatCountsCalls() { |
| super(STEPS, MODIFIABLE, newArrayList(1), |
| IteratorTester.KnownOrder.KNOWN_ORDER); |
| } |
| int numCallsToNewTargetIterator; |
| int numCallsToVerify; |
| @Override protected Iterator<Integer> newTargetIterator() { |
| numCallsToNewTargetIterator++; |
| return Lists.newArrayList(1).iterator(); |
| } |
| @Override protected void verify(List<Integer> elements) { |
| numCallsToVerify++; |
| super.verify(elements); |
| } |
| } |
| |
| public void testVerifyGetsCalled() { |
| TesterThatCountsCalls tester = new TesterThatCountsCalls(); |
| |
| tester.test(); |
| |
| assertEquals("Should have verified once per stimulus executed", |
| tester.numCallsToVerify, |
| tester.numCallsToNewTargetIterator * STEPS); |
| } |
| |
| public void testVerifyCanThrowAssertionThatFailsTest() { |
| final String message = "Important info about why verify failed"; |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(1, MODIFIABLE, newArrayList(1, 2, 3), |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return Lists.newArrayList(1, 2, 3).iterator(); |
| } |
| |
| @Override protected void verify(List<Integer> elements) { |
| throw new AssertionFailedError(message); |
| } |
| }; |
| AssertionFailedError actual = null; |
| try { |
| tester.test(); |
| } catch (AssertionFailedError e) { |
| actual = e; |
| } |
| assertNotNull("verify() should be able to cause test failure", actual); |
| assertTrue("AssertionFailedError should have info about why test failed", |
| actual.getCause().getMessage().contains(message)); |
| } |
| |
| public void testMissingException() { |
| List<Integer> emptyList = newArrayList(); |
| |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(1, MODIFIABLE, emptyList, |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return new Iterator<Integer>() { |
| @Override |
| public void remove() { |
| // We should throw here, but we won't! |
| } |
| @Override |
| public Integer next() { |
| // We should throw here, but we won't! |
| return null; |
| } |
| @Override |
| public boolean hasNext() { |
| return false; |
| } |
| }; |
| } |
| }; |
| assertFailure(tester); |
| } |
| |
| public void testUnexpectedException() { |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(1, MODIFIABLE, newArrayList(1), |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return new ThrowingIterator<Integer>(new IllegalStateException()); |
| } |
| }; |
| assertFailure(tester); |
| } |
| |
| public void testSimilarException() { |
| List<Integer> emptyList = emptyList(); |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(1, MODIFIABLE, emptyList, |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return new Iterator<Integer>() { |
| @Override |
| public void remove() { |
| throw new IllegalStateException() { /* subclass */}; |
| } |
| @Override |
| public Integer next() { |
| throw new NoSuchElementException() { /* subclass */}; |
| } |
| @Override |
| public boolean hasNext() { |
| return false; |
| } |
| }; |
| } |
| }; |
| tester.test(); |
| } |
| |
| public void testMismatchedException() { |
| List<Integer> emptyList = emptyList(); |
| IteratorTester<Integer> tester = |
| new IteratorTester<Integer>(1, MODIFIABLE, emptyList, |
| IteratorTester.KnownOrder.KNOWN_ORDER) { |
| @Override protected Iterator<Integer> newTargetIterator() { |
| return new Iterator<Integer>() { |
| @Override |
| public void remove() { |
| // Wrong exception type. |
| throw new IllegalArgumentException(); |
| } |
| @Override |
| public Integer next() { |
| // Wrong exception type. |
| throw new UnsupportedOperationException(); |
| } |
| @Override |
| public boolean hasNext() { |
| return false; |
| } |
| }; |
| } |
| }; |
| assertFailure(tester); |
| } |
| |
| private static void assertFailure(IteratorTester<?> tester) { |
| try { |
| tester.test(); |
| fail(); |
| } catch (AssertionFailedError expected) { |
| } |
| } |
| |
| private static final class ThrowingIterator<E> implements Iterator<E> { |
| private final RuntimeException ex; |
| |
| private ThrowingIterator(RuntimeException ex) { |
| this.ex = ex; |
| } |
| |
| @Override |
| public boolean hasNext() { |
| // IteratorTester doesn't expect exceptions for hasNext(). |
| return true; |
| } |
| |
| @Override |
| public E next() { |
| ex.fillInStackTrace(); |
| throw ex; |
| } |
| |
| @Override |
| public void remove() { |
| ex.fillInStackTrace(); |
| throw ex; |
| } |
| } |
| } |