/*
* (c) Rob Gordon 2005
*/
package org.oddjob.structural;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.TestCase;
import org.apache.log4j.Logger;
import org.oddjob.MockStructural;
/**
*
*/
public class ChildHelperTest extends TestCase {
private static final Logger logger = Logger.getLogger(ChildHelperTest.class);
@Override
protected void setUp() throws Exception {
logger.debug("------------ " + getName() + " -------------");
}
public void testReplaceChild() {
class MyL implements StructuralListener {
int added;
int removed;
public void childAdded(StructuralEvent event) {
added = event.getIndex();
}
public void childRemoved(StructuralEvent event) {
removed = event.getIndex();
}
}
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
ChildHelper<Object> test = new ChildHelper<Object>(
new MockStructural());
MyL l = new MyL();
test.addStructuralListener(l);
test.insertChild(0, o1);
test.insertChild(1, o2);
test.removeChildAt(0);
test.insertChild(0, o3);
assertEquals("replace and in right place.", o3, test.getChildren()[0]);
assertEquals("notify correct removed index.", 0, l.removed);
assertEquals("notify correct added index.", 0, l.added);
}
public void testDeadlockOnNotify() throws InterruptedException {
final ExecutorService executor = Executors.newFixedThreadPool(5);
final ChildHelper<Object> test = new ChildHelper<Object>(new MockStructural());
test.insertChild(0, new Object());
test.insertChild(0, new Object());
test.insertChild(0, new Object());
test.insertChild(0, new Object());
test.insertChild(0, new Object());
class LoopbackListener implements StructuralListener {
ChildHelper<Object> copies = new ChildHelper<Object>(new MockStructural());
AtomicInteger events = new AtomicInteger();
CountDownLatch finished = new CountDownLatch(74);
public void childAdded(StructuralEvent event) {
copies.insertChild(event.getIndex(), event.getChild());
onEvent(event);
}
public void childRemoved(StructuralEvent event) {
copies.removeChildAt(event.getIndex());
onEvent(event);
}
private void onEvent(StructuralEvent event) {
int events = this.events.incrementAndGet();
logger.debug("" + events);
if (events < 50) {
executor.submit(new Runnable() {
public void run() {
logger.debug("Inserting.");
test.insertChild(0, new Object());
}
});
}
else if (events < 70) {
executor.submit(new Runnable() {
public void run() {
logger.debug("Removing.");
test.removeChildAt(0);
}
});
}
finished.countDown();
}
}
LoopbackListener listener = new LoopbackListener();
logger.debug("Starting");
test.addStructuralListener(listener);
logger.debug("Waiting");
listener.finished.await();
assertEquals(34, listener.copies.size());
logger.debug("Shutdown.");
executor.shutdown();
}
private class StructureAlteringListener implements StructuralListener {
ChildHelper<String> test;
List<String> children = new ArrayList<String>();
@Override
public void childAdded(StructuralEvent event) {
children.add(event.getIndex(), (String) event.getChild());
if (children.size() == 1) {
test.insertChild(1, "orange");
}
if (children.size() == 2) {
test.removeChildAt(0);
}
}
@Override
public void childRemoved(StructuralEvent event) {
children.remove(event.getIndex());
}
}
public void testNoMissedEvent() {
ChildHelper<String> test = new ChildHelper<String>(new MockStructural());
test.insertChild(0, "apple");
StructureAlteringListener listener = new StructureAlteringListener();
listener.test = test;
test.addStructuralListener(listener);
assertEquals(1, listener.children.size());
assertEquals("orange", listener.children.get(0));
}
private enum Type { ADDED, REMOVED }
private class SimpleListener implements StructuralListener {
private List<Type> types = new ArrayList<Type>();
private List<StructuralEvent> events = new ArrayList<StructuralEvent>();
@Override
public void childAdded(StructuralEvent event) {
types.add(Type.ADDED);
events.add(event);
}
@Override
public void childRemoved(StructuralEvent event) {
types.add(Type.REMOVED);
events.add(event);
}
}
public void testAddAndRemove() {
ChildHelper<Object> test = new ChildHelper<Object>(new MockStructural());
SimpleListener listener = new SimpleListener();
test.addStructuralListener(listener);
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
test.addChild(o1);
assertEquals(1, listener.types.size());
assertEquals(Type.ADDED, listener.types.get(0));
assertEquals(o1, listener.events.get(0).getChild());
assertEquals(0, listener.events.get(0).getIndex());
test.addChild(o2);
assertEquals(2, listener.types.size());
assertEquals(Type.ADDED, listener.types.get(1));
assertEquals(o2, listener.events.get(1).getChild());
assertEquals(1, listener.events.get(1).getIndex());
test.addChild(o3);
assertEquals(3, listener.types.size());
assertEquals(Type.ADDED, listener.types.get(2));
assertEquals(o3, listener.events.get(2).getChild());
assertEquals(2, listener.events.get(2).getIndex());
test.removeChild(o2);
assertEquals(4, listener.types.size());
assertEquals(Type.REMOVED, listener.types.get(3));
assertEquals(o2, listener.events.get(3).getChild());
assertEquals(1, listener.events.get(3).getIndex());
test.removeChild(o1);
assertEquals(5, listener.types.size());
assertEquals(Type.REMOVED, listener.types.get(4));
assertEquals(o1, listener.events.get(4).getChild());
assertEquals(0, listener.events.get(4).getIndex());
test.addChild(o4);
assertEquals(6, listener.types.size());
assertEquals(Type.ADDED, listener.types.get(5));
assertEquals(o4, listener.events.get(5).getChild());
assertEquals(1, listener.events.get(5).getIndex());
test.removeChild(o3);
assertEquals(7, listener.types.size());
assertEquals(Type.REMOVED, listener.types.get(6));
assertEquals(o3, listener.events.get(6).getChild());
assertEquals(0, listener.events.get(6).getIndex());
try {
test.removeChild(o1);
fail("Child doesn't exist");
}
catch (IllegalStateException e) {
// expected.
}
assertEquals(7, listener.types.size());
}
public void testIterable() {
ChildHelper<String> test = new ChildHelper<String>(new MockStructural());
test.insertChild(0, "apple");
test.insertChild(1, "orange");
test.insertChild(2, "pear");
Iterator<String> iterator = test.iterator();
assertEquals(true, iterator.hasNext());
assertEquals("apple", iterator.next());
assertEquals(true, iterator.hasNext());
assertEquals("orange", iterator.next());
test.removeChild("apple");
test.insertChild(2, "banana");
assertEquals(true, iterator.hasNext());
assertEquals("pear", iterator.next());
assertEquals(true, iterator.hasNext());
assertEquals("banana", iterator.next());
test.removeChild("banana");
test.insertChild(2, "kiwi");
assertEquals(true, iterator.hasNext());
assertEquals("kiwi", iterator.next());
assertEquals(false, iterator.hasNext());
}
public void testIterableInFor() {
ChildHelper<String> test = new ChildHelper<String>(new MockStructural());
test.insertChild(0, "apple");
test.insertChild(1, "orange");
test.insertChild(2, "pear");
List<String> results = new ArrayList<String>();
for (String next : test) {
results.add(next);
}
assertEquals("apple", results.get(0));
assertEquals("orange", results.get(1));
assertEquals("pear", results.get(2));
assertEquals(3, results.size());
}
}