Skip to main content
summaryrefslogblamecommitdiffstats
blob: 69c1cd825494f853bcfbb54cdf4f75afb1f73e5a (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                                                                                
                                                        













                                                                                
 
                                                                    
                                                                    

                                                        
                        


                                     

                                  











                                                     
















































































































































































































                                                                                                                           
                                     













                                                                    
                                                     

































































































                                                                                                                 
                                                              
                                                                       
                               
                                                   




                                                                          
                              





                                                                


                                                                









                                                                                        

                                                                                                      



                 
/*******************************************************************************
 * Copyright (c) 2005, 2010 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 org.eclipse.jpt.utility.internal.iterators.CloneListIterator;
import org.eclipse.jpt.utility.tests.internal.MultiThreadedTestCase;
import org.eclipse.jpt.utility.tests.internal.TestTools;

@SuppressWarnings("nls")
public class CloneListIteratorTests
	extends MultiThreadedTestCase
{
	List<String> originalList;

	private List<String> concurrentList;

	public CloneListIteratorTests(String name) {
		super(name);
	}

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		this.originalList = this.buildList();
	}

	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.concurrentList = new ArrayList<String>();
		Thread thread = this.buildThread(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");
		thread.join();
		List<String> expected = new ArrayList<String>();
		this.populateList(expected);
		assertEquals(expected, this.concurrentList);
	}

	private Runnable buildRunnable() {
		return new TestRunnable() {
			@Override
			protected void run_() throws Throwable {
				CloneListIteratorTests.this.loopWithCloneListIterator();
			}
		};
	}

	/**
	 * use a clone iterator to loop over the "slow" collection and copy its
	 * contents to the concurrent collection
	 */
	void loopWithCloneListIterator() {
		for (ListIterator<String> stream = this.buildCloneListIterator(); stream.hasNext();) {
			this.concurrentList.add(stream.next());
		}
	}

}

Back to the top