blob: 0a22d7d5fe461db72586ec7fee63774d8286cd50 [file] [log] [blame]
/*
* 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;
}
}
}