| /******************************************************************************* |
| * 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; |
| } |
| } |
| |
| } |