blob: 44652f3819a72a3963aad4f6973454446b776531 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2009 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.utility.tests.internal.iterators;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import junit.framework.TestCase;
import org.eclipse.jpt.utility.internal.iterators.CloneListIterator;
import org.eclipse.jpt.utility.tests.internal.TestTools;
@SuppressWarnings("nls")
public class CloneListIteratorTests extends TestCase {
List<String> originalList;
private boolean concurrentProblem;
private List<String> concurrentList;
public CloneListIteratorTests(String name) {
super(name);
}
@Override
protected void setUp() throws Exception {
super.setUp();
this.originalList = this.buildList();
}
@Override
protected void tearDown() throws Exception {
TestTools.clear(this);
super.tearDown();
}
public void testHasNext() {
int originalSize = this.originalList.size();
int i = 0;
for (ListIterator<String> stream = this.buildCloneListIterator(); stream.hasNext();) {
stream.next();
// should allow concurrent modification
this.originalList.add("foo");
i++;
}
assertTrue(originalSize != this.originalList.size());
assertEquals(originalSize, i);
}
public void testNext() {
ListIterator<String> nestedListIterator = this.buildNestedListIterator();
for (ListIterator<String> stream = this.buildCloneListIterator(); stream.hasNext();) {
assertEquals("bogus element", nestedListIterator.next(), stream.next());
}
}
public void testIndex() {
ListIterator<String> cloneListIterator = this.buildCloneListIterator();
ListIterator<String> nestedListIterator = this.buildNestedListIterator();
for (int i = 0; i < 7; i++) {
nestedListIterator.next();
cloneListIterator.next();
assertEquals("bogus index", nestedListIterator.nextIndex(), cloneListIterator.nextIndex());
assertEquals("bogus index", nestedListIterator.previousIndex(), cloneListIterator.previousIndex());
}
for (int i = 0; i < 3; i++) {
nestedListIterator.previous();
cloneListIterator.previous();
assertEquals("bogus index", nestedListIterator.nextIndex(), cloneListIterator.nextIndex());
assertEquals("bogus index", nestedListIterator.previousIndex(), cloneListIterator.previousIndex());
}
while (nestedListIterator.hasNext()) {
nestedListIterator.next();
cloneListIterator.next();
assertEquals("bogus index", nestedListIterator.nextIndex(), cloneListIterator.nextIndex());
assertEquals("bogus index", nestedListIterator.previousIndex(), cloneListIterator.previousIndex());
}
}
public void testHasPrevious() {
int originalSize = this.originalList.size();
int i = 0;
ListIterator<String> stream = this.buildCloneListIterator();
while (stream.hasNext()) {
stream.next();
this.originalList.add("foo");
i++;
}
assertTrue(originalSize != this.originalList.size());
originalSize = this.originalList.size();
while (stream.hasPrevious()) {
stream.previous();
// should allow concurrent modification
this.originalList.add("bar");
i--;
}
assertTrue(originalSize != this.originalList.size());
assertEquals(0, i);
}
public void testPrevious() {
ListIterator<String> nestedListIterator = this.buildNestedListIterator();
ListIterator<String> stream = this.buildCloneListIterator();
while (stream.hasNext()) {
nestedListIterator.next();
stream.next();
}
while (stream.hasPrevious()) {
assertEquals("bogus element", nestedListIterator.previous(), stream.previous());
}
}
public void testNoSuchElementException() {
boolean exCaught = false;
ListIterator<String> stream = this.buildCloneListIterator();
String string = null;
while (stream.hasNext()) {
string = stream.next();
}
try {
string = stream.next();
} catch (NoSuchElementException ex) {
exCaught = true;
}
assertTrue("NoSuchElementException not thrown: " + string, exCaught);
exCaught = false;
while (stream.hasPrevious()) {
string = stream.previous();
}
try {
string = stream.previous();
} catch (NoSuchElementException ex) {
exCaught = true;
}
assertTrue("NoSuchElementException not thrown: " + string, exCaught);
}
public void testModifyDefault() {
boolean exCaught = false;
for (ListIterator<String> stream = this.buildCloneListIterator(); stream.hasNext();) {
if (stream.next().equals("three")) {
try {
stream.remove();
} catch (UnsupportedOperationException ex) {
exCaught = true;
}
}
}
assertTrue("UnsupportedOperationException not thrown", exCaught);
exCaught = false;
for (ListIterator<String> stream = this.buildCloneListIterator(); stream.hasNext();) {
if (stream.next().equals("three")) {
try {
stream.add("three and a half");
} catch (UnsupportedOperationException ex) {
exCaught = true;
}
}
}
assertTrue("UnsupportedOperationException not thrown", exCaught);
exCaught = false;
for (ListIterator<String> stream = this.buildCloneListIterator(); stream.hasNext();) {
if (stream.next().equals("three")) {
try {
stream.set("another three");
} catch (UnsupportedOperationException ex) {
exCaught = true;
}
}
}
assertTrue("UnsupportedOperationException not thrown", exCaught);
}
public void testModifyMutatorNext() {
this.verifyModifyNext(new CloneListIterator<String>(this.originalList, this.buildMutator()));
}
public void testModifyMutatorPrevious() {
this.verifyModifyPrevious(new CloneListIterator<String>(this.originalList, this.buildMutator()));
}
private CloneListIterator.Mutator<String> buildMutator() {
return new CloneListIterator.Mutator<String>() {
public void add(int index, String o) {
CloneListIteratorTests.this.originalList.add(index, o);
}
public void remove(int index) {
CloneListIteratorTests.this.originalList.remove(index);
}
public void set(int index, String o) {
CloneListIteratorTests.this.originalList.set(index, o);
}
};
}
public void testModifySubclassNext() {
this.verifyModifyNext(this.buildSubclass());
}
public void testModifySubclassPrevious() {
this.verifyModifyPrevious(this.buildSubclass());
}
private ListIterator<String> buildSubclass() {
return new CloneListIterator<String>(this.originalList) {
@Override
protected void add(int currentIndex, String o) {
CloneListIteratorTests.this.originalList.add(currentIndex, o);
}
@Override
protected void remove(int currentIndex) {
CloneListIteratorTests.this.originalList.remove(currentIndex);
}
@Override
protected void set(int currentIndex, String o) {
CloneListIteratorTests.this.originalList.set(currentIndex, o);
}
};
}
private void verifyModifyNext(ListIterator<String> iterator) {
String removed = "three";
String addedAfter = "five";
String added = "five and a half";
String replaced = "seven";
String replacement = "another seven";
assertTrue(this.originalList.contains(removed));
assertTrue(this.originalList.contains(addedAfter));
assertTrue(this.originalList.contains(replaced));
// try to remove before calling #next()
boolean exCaught = false;
try {
iterator.remove();
} catch (IllegalStateException ex) {
exCaught = true;
}
assertTrue(exCaught);
while (iterator.hasNext()) {
String next = iterator.next();
if (next.equals(addedAfter)) {
iterator.add(added);
}
if (next.equals(removed)) {
iterator.remove();
// try to remove twice
exCaught = false;
try {
iterator.remove();
} catch (IllegalStateException ex) {
exCaught = true;
}
assertTrue(exCaught);
}
if (next.equals(replaced)) {
iterator.set(replacement);
}
}
assertTrue(this.originalList.contains(added));
assertFalse(this.originalList.contains(removed));
assertFalse(this.originalList.contains(replaced));
assertTrue(this.originalList.contains(replacement));
}
private void verifyModifyPrevious(ListIterator<String> iterator) {
String removed = "three";
String addedBefore = "five";
String added = "four and a half";
String replaced = "seven";
String replacement = "another seven";
assertTrue(this.originalList.contains(removed));
assertTrue(this.originalList.contains(addedBefore));
assertTrue(this.originalList.contains(replaced));
while (iterator.hasNext()) {
iterator.next();
}
while (iterator.hasPrevious()) {
Object previous = iterator.previous();
if (previous.equals(addedBefore)) {
iterator.add(added);
}
if (previous.equals(removed)) {
iterator.remove();
// try to remove twice
boolean exCaught = false;
try {
iterator.remove();
} catch (IllegalStateException ex) {
exCaught = true;
}
assertTrue("IllegalStateException not thrown", exCaught);
}
if (previous.equals(replaced)) {
iterator.set(replacement);
}
}
assertTrue(this.originalList.contains(added));
assertFalse(this.originalList.contains(removed));
assertFalse(this.originalList.contains(replaced));
assertTrue(this.originalList.contains(replacement));
}
private ListIterator<String> buildCloneListIterator() {
return this.buildCloneListIterator(this.originalList);
}
private ListIterator<String> buildCloneListIterator(List<String> list) {
return new CloneListIterator<String>(list);
}
private ListIterator<String> buildNestedListIterator() {
return this.originalList.listIterator();
}
private List<String> buildList() {
List<String> list = this.buildEmptyList();
this.populateList(list);
return list;
}
private void populateList(List<String> list) {
list.add("zero");
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add("six");
list.add("seven");
list.add("eight");
list.add("nine");
}
protected List<String> buildEmptyList() {
return new ArrayList<String>();
}
/**
* Test concurrent access: First build a clone iterator in a separate thread
* that hangs momentarily during its construction; then modify the shared
* collection in this thread. This would cause a
* ConcurrentModificationException in the other thread if the clone iterator
* were not synchronized on the original collection.
*/
public void testConcurrentAccess() throws Exception {
CloneIteratorTests.SlowCollection<String> slow = new CloneIteratorTests.SlowCollection<String>();
this.populateList(slow);
// using the unsynchronized list will cause the test to fail
// this.originalList = slow;
this.originalList = Collections.synchronizedList(slow);
this.concurrentProblem = false;
this.concurrentList = new ArrayList<String>();
Thread thread = new Thread(this.buildRunnable());
thread.start();
while (!slow.hasStartedClone()) {
// wait for the other thread to start the clone...
Thread.yield();
}
// ...then sneak in an extra element
this.originalList.add("seventeen");
while (thread.isAlive()) {
// wait for the other thread to finish
Thread.yield();
}
assertFalse(this.concurrentProblem);
List<String> expected = new ArrayList<String>();
this.populateList(expected);
assertEquals(expected, this.concurrentList);
}
private Runnable buildRunnable() {
return new Runnable() {
public void run() {
CloneListIteratorTests.this.loopWithCloneListIterator();
}
};
}
/**
* use a clone iterator to loop over the "slow" collection and copy its
* contents to the concurrent collection
*/
void loopWithCloneListIterator() {
try {
for (ListIterator<String> stream = this.buildCloneListIterator(); stream.hasNext();) {
this.concurrentList.add(stream.next());
}
} catch (Throwable t) {
this.concurrentProblem = true;
}
}
}