package org.marketcetera.util.collections; import static org.junit.Assert.*; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.marketcetera.util.test.CollectionAssert; /* $License$ */ /** * Tests {@link UnmodifiableDeque}. * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: UnmodifiableDequeTest.java 16154 2012-07-14 16:34:05Z colin $ * @since 2.1.4 */ public class UnmodifiableDequeTest { /** * Tests the use of the constructor. * * @throws Exception if an unexpected error occurs */ @Test public void constructorTest() throws Exception { try { new UnmodifiableDeque<Object>(null); fail(); } catch (NullPointerException e) { // expected failure } Deque<String> testCollection = new LinkedList<String>(); Deque<String> q = new UnmodifiableDeque<String>(testCollection); assertTrue(q.isEmpty()); assertEquals(0, q.size()); testCollection.add("value"); assertEquals(1, q.size()); } /** * Tests the set of functions that are not supported. * * @throws Exception if an unexpected error occurs */ @Test public void unsupportedOperations() throws Exception { Deque<String> testCollection = new LinkedList<String>(); Deque<String> q = new UnmodifiableDeque<String>(testCollection); try { q.addAll(testCollection); fail(); } catch (UnsupportedOperationException e) {} try { q.clear(); fail(); } catch (UnsupportedOperationException e) {} try { q.removeAll(testCollection); fail(); } catch (UnsupportedOperationException e) {} try { q.retainAll(testCollection); fail(); } catch (UnsupportedOperationException e) {} try { q.add("new value"); fail(); } catch (UnsupportedOperationException e) {} try { q.addFirst("new value"); fail(); } catch (UnsupportedOperationException e) {} try { q.addLast("new value"); fail(); } catch (UnsupportedOperationException e) {} try { q.offer("new value"); fail(); } catch (UnsupportedOperationException e) {} try { q.offerFirst("new value"); fail(); } catch (UnsupportedOperationException e) {} try { q.offerLast("new value"); fail(); } catch (UnsupportedOperationException e) {} try { q.poll(); fail(); } catch (UnsupportedOperationException e) {} try { q.pollFirst(); fail(); } catch (UnsupportedOperationException e) {} try { q.pollLast(); fail(); } catch (UnsupportedOperationException e) {} try { q.pop(); fail(); } catch (UnsupportedOperationException e) {} try { q.push("new value again"); fail(); } catch (UnsupportedOperationException e) {} try { q.remove(); fail(); } catch (UnsupportedOperationException e) {} try { q.remove("new value again"); fail(); } catch (UnsupportedOperationException e) {} try { q.removeFirst(); fail(); } catch (UnsupportedOperationException e) {} try { q.removeFirstOccurrence("new value again"); fail(); } catch (UnsupportedOperationException e) {} try { q.removeLast(); fail(); } catch (UnsupportedOperationException e) {} try { q.removeLastOccurrence("new value again"); fail(); } catch (UnsupportedOperationException e) {} } /** * Tests the set of supported operations. * * @throws Exception if an unexpected error occurs */ @Test public void supportedOperations() throws Exception { Deque<String> testCollection = new LinkedList<String>(); Deque<String> q = new UnmodifiableDeque<String>(testCollection); Deque<String> expectedCollection = new LinkedList<String>(); verify(expectedCollection, q); String value1 = "value-" + System.nanoTime(); testCollection.add(value1); expectedCollection.add(value1); verify(expectedCollection, q); String value2 = "value-" + System.nanoTime(); assertFalse(value1.equals(value2)); testCollection.addFirst(value2); expectedCollection.addFirst(value2); verify(expectedCollection, q); testCollection.addLast(value2); expectedCollection.addLast(value2); verify(expectedCollection, q); } /** * Verifies that the returned collection is not thread-safe. * * @throws Exception if an unexpected error occurs */ @Test public void concurrenceTest() throws Exception { Deque<String> testCollection = new LinkedList<String>(); final Deque<String> q = new UnmodifiableDeque<String>(testCollection); // add a few values to the testCollection (reflected in q) testCollection.add("value1"); testCollection.add("value2"); testCollection.add("value3"); // set up a flag to pause iteration final AtomicBoolean keepWaiting = new AtomicBoolean(true); final AtomicBoolean clientComplete = new AtomicBoolean(false); final AtomicBoolean clientReady = new AtomicBoolean(false); // track any exceptions our iterator collects final List<Exception> exceptions = new ArrayList<Exception>(); // set up an object that iterates using a q iterator Thread client1 = new Thread(new Runnable() { @Override public void run() { Iterator<String> iterator = q.iterator(); clientReady.set(true); synchronized(clientReady) { clientReady.notifyAll(); } try { while(iterator.hasNext()) { iterator.next(); while(keepWaiting.get()) { synchronized(keepWaiting) { keepWaiting.wait(); } } } } catch (Exception e) { exceptions.add(e); } finally { clientComplete.set(true); } } }); client1.start(); // client1 should now be waiting after retrieving value1 using an active iterator assertFalse(clientComplete.get()); while(!clientReady.get()) { synchronized(clientReady) { clientReady.wait(); } } // remove value2 from the underlying collection and see if the iterator breaks assertEquals(3, testCollection.size()); testCollection.remove("value2"); assertEquals(2, testCollection.size()); // free up client1 to complete keepWaiting.set(false); synchronized(keepWaiting) { keepWaiting.notifyAll(); } client1.join(); assertTrue(clientComplete.get()); assertEquals(1, exceptions.size()); assertTrue(exceptions.get(0) instanceof ConcurrentModificationException); } /** * Verifies that the two collections contain the same elements and * have the same behavior. * * @param inExpectedCollection a <code>Deque<String></code> value * @param inActualCollection a <code>Deque<String></code> value * @throws Exception if an unexpected error occurs */ private void verify(Deque<String> inExpectedCollection, Deque<String> inActualCollection) throws Exception { assertNotNull(inActualCollection.toString()); assertEquals(inExpectedCollection.isEmpty(), inActualCollection.isEmpty()); assertEquals(inExpectedCollection.size(), inActualCollection.size()); assertEquals(inExpectedCollection.peek(), inActualCollection.peek()); assertEquals(inExpectedCollection.peekFirst(), inActualCollection.peekFirst()); assertEquals(inExpectedCollection.peekLast(), inActualCollection.peekLast()); if(inExpectedCollection.isEmpty()) { try { inExpectedCollection.getFirst(); fail(); } catch (NoSuchElementException e) {} try { inActualCollection.getFirst(); fail(); } catch (NoSuchElementException e) {} try { inExpectedCollection.getLast(); fail(); } catch (NoSuchElementException e) {} try { inActualCollection.getLast(); fail(); } catch (NoSuchElementException e) {} try { inExpectedCollection.element(); fail(); } catch (NoSuchElementException e) {} try { inActualCollection.element(); fail(); } catch (NoSuchElementException e) {} } else { assertEquals(inExpectedCollection.getFirst(), inActualCollection.getFirst()); assertEquals(inExpectedCollection.getLast(), inActualCollection.getLast()); assertEquals(inExpectedCollection.element(), inActualCollection.element()); } assertTrue(inActualCollection.containsAll(inExpectedCollection)); CollectionAssert.assertArrayPermutation(inExpectedCollection.toArray(), inActualCollection.toArray()); CollectionAssert.assertArrayPermutation(inExpectedCollection.toArray(new String[0]), inActualCollection.toArray(new String[0])); Iterator<String> expectedIterator = inExpectedCollection.iterator(); Iterator<String> actualIterator = inActualCollection.iterator(); while(expectedIterator.hasNext()) { assertTrue(actualIterator.hasNext()); String expectedElement = expectedIterator.next(); String actualElement = actualIterator.next(); assertEquals(expectedElement, actualElement); assertTrue(inActualCollection.contains(actualElement)); try { actualIterator.remove(); } catch (UnsupportedOperationException e) {} } assertFalse(actualIterator.hasNext()); expectedIterator = inExpectedCollection.descendingIterator(); actualIterator = inActualCollection.descendingIterator(); while(expectedIterator.hasNext()) { assertTrue(actualIterator.hasNext()); String expectedElement = expectedIterator.next(); String actualElement = actualIterator.next(); assertEquals(expectedElement, actualElement); assertTrue(inActualCollection.contains(actualElement)); try { actualIterator.remove(); } catch (UnsupportedOperationException e) {} } assertFalse(actualIterator.hasNext()); } }