/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.core.position.impl;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
import org.testng.annotations.Test;
import com.opengamma.core.position.PortfolioNode;
import com.opengamma.core.position.Position;
import com.opengamma.id.UniqueId;
import com.opengamma.id.UniqueIdentifiable;
import com.opengamma.util.PoolExecutor;
import com.opengamma.util.test.TestGroup;
/**
* Test {@link PortfolioNodeTraverser}.
*/
public class PortfolioNodeTraverserTest {
/* Test tree =
N0
N1 N10 P19 P20
N2 N5 P8 P9 N11 N14 P17 P18
P3 P4 P6 P7 P12 P13 P15 P16
*/
private Position createTestPosition(final AtomicInteger nextId) {
final SimplePosition position = new SimplePosition();
position.setUniqueId(UniqueId.of("Test", Integer.toString(nextId.getAndIncrement())));
return position;
}
private PortfolioNode createTestPortfolioNode(final AtomicInteger nextId, final int depth) {
final SimplePortfolioNode node = new SimplePortfolioNode();
node.setUniqueId(UniqueId.of("Test", Integer.toString(nextId.getAndIncrement())));
if (depth > 0) {
node.addChildNode(createTestPortfolioNode(nextId, depth - 1));
node.addChildNode(createTestPortfolioNode(nextId, depth - 1));
}
node.addPosition(createTestPosition(nextId));
node.addPosition(createTestPosition(nextId));
return node;
}
private static final int NODE_PRE = 0;
private static final int NODE_POST = 1;
private static final int POSITION_PRE = 2;
private static final int POSITION_POST = 3;
private static class Callback implements PortfolioNodeTraversalCallback {
private final Queue<Integer> _visited = new LinkedList<Integer>();
protected void visit(final int type, final UniqueIdentifiable uniqueId) {
_visited.add(type);
_visited.add(Integer.parseInt(uniqueId.getUniqueId().getValue()));
}
@Override
public void postOrderOperation(final PortfolioNode portfolioNode) {
visit(NODE_POST, portfolioNode);
}
@Override
public void postOrderOperation(final PortfolioNode parentNode, final Position position) {
visit(POSITION_POST, position);
}
@Override
public void preOrderOperation(final PortfolioNode portfolioNode) {
visit(NODE_PRE, portfolioNode);
}
@Override
public void preOrderOperation(final PortfolioNode parentNode, final Position position) {
visit(POSITION_PRE, position);
}
private void assertVisit(final int type, final int... identifiers) {
for (int identifier : identifiers) {
assertEquals(_visited.remove().intValue(), type);
assertEquals(_visited.remove().intValue(), identifier);
}
}
private void assertVisitBefore(final int t1, final int i1, final int t2, final int i2) {
final Iterator<Integer> itr = _visited.iterator();
while (itr.hasNext()) {
int t = itr.next();
int i = itr.next();
if ((t == t1) && (i == i1)) {
while (itr.hasNext()) {
t = itr.next();
i = itr.next();
if ((t == t2) && (i == i2)) {
return;
}
}
fail("Expected " + t1 + "/" + i1 + " before " + t2 + "/" + i2);
}
}
}
private void assertVisitAfter(final int t1, final int i1, final int t2, final int i2) {
assertVisitBefore(t2, i2, t1, i1);
}
private void assertVisitCount() {
// four values for each of the 21 node/position entries in the graph
assertEquals(_visited.size(), 84);
}
}
//-------------------------------------------------------------------------
@Test(groups = TestGroup.UNIT)
public void testDepthFirst() {
final Callback cb = new Callback();
PortfolioNodeTraverser.depthFirst(cb).traverse(createTestPortfolioNode(new AtomicInteger(), 2));
cb.assertVisitCount();
cb.assertVisit(NODE_PRE, 0);
cb.assertVisit(POSITION_PRE, 19, 20);
cb.assertVisit(NODE_PRE, 1);
cb.assertVisit(POSITION_PRE, 8, 9);
cb.assertVisit(NODE_PRE, 2);
cb.assertVisit(POSITION_PRE, 3, 4);
cb.assertVisit(POSITION_POST, 3, 4);
cb.assertVisit(NODE_POST, 2);
cb.assertVisit(NODE_PRE, 5);
cb.assertVisit(POSITION_PRE, 6, 7);
cb.assertVisit(POSITION_POST, 6, 7);
cb.assertVisit(NODE_POST, 5);
cb.assertVisit(POSITION_POST, 8, 9);
cb.assertVisit(NODE_POST, 1);
cb.assertVisit(NODE_PRE, 10);
cb.assertVisit(POSITION_PRE, 17, 18);
cb.assertVisit(NODE_PRE, 11);
cb.assertVisit(POSITION_PRE, 12, 13);
cb.assertVisit(POSITION_POST, 12, 13);
cb.assertVisit(NODE_POST, 11);
cb.assertVisit(NODE_PRE, 14);
cb.assertVisit(POSITION_PRE, 15, 16);
cb.assertVisit(POSITION_POST, 15, 16);
cb.assertVisit(NODE_POST, 14);
cb.assertVisit(POSITION_POST, 17, 18);
cb.assertVisit(NODE_POST, 10);
cb.assertVisit(POSITION_POST, 19, 20);
cb.assertVisit(NODE_POST, 0);
}
@Test(groups = TestGroup.UNIT, enabled = false)
public void testBreadthFirst() {
final Callback cb = new Callback();
PortfolioNodeTraverser.breadthFirst(cb).traverse(createTestPortfolioNode(new AtomicInteger(), 2));
cb.assertVisitCount();
// Is this the desired order for a "breadth-first" search??
cb.assertVisit(NODE_PRE, 0);
cb.assertVisit(POSITION_PRE, 19, 20);
cb.assertVisit(NODE_PRE, 1, 10);
cb.assertVisit(POSITION_PRE, 8, 9, 17, 18);
cb.assertVisit(NODE_PRE, 2, 5, 11, 14);
cb.assertVisit(POSITION_PRE, 3, 4, 6, 7, 12, 13, 15, 16);
cb.assertVisit(POSITION_POST, 3, 4, 6, 7, 12, 13, 15, 16);
cb.assertVisit(NODE_POST, 2, 5, 11, 14);
cb.assertVisit(POSITION_POST, 8, 9, 17, 18);
cb.assertVisit(NODE_POST, 1, 10);
cb.assertVisit(POSITION_POST, 19, 20);
cb.assertVisit(NODE_POST, 0);
}
@Test(groups = TestGroup.UNIT, expectedExceptions = UnsupportedOperationException.class)
public void testBreadthFirstBroken() {
final Callback cb = new Callback();
PortfolioNodeTraverser.breadthFirst(cb).traverse(createTestPortfolioNode(new AtomicInteger(), 2));
}
private void assertParallelOrder(final Callback cb) {
cb.assertVisitCount();
// Exact ordering can't be predicted but make sure some thing happened before or after others
cb.assertVisitBefore(NODE_PRE, 0, NODE_PRE, 1);
cb.assertVisitBefore(NODE_PRE, 0, NODE_PRE, 10);
cb.assertVisitBefore(NODE_PRE, 0, POSITION_PRE, 19);
cb.assertVisitBefore(NODE_PRE, 0, POSITION_PRE, 20);
cb.assertVisitAfter(NODE_POST, 0, NODE_POST, 1);
cb.assertVisitAfter(NODE_POST, 0, NODE_POST, 10);
cb.assertVisitAfter(NODE_POST, 0, POSITION_POST, 19);
cb.assertVisitAfter(NODE_POST, 0, POSITION_POST, 20);
cb.assertVisitBefore(NODE_PRE, 1, NODE_PRE, 2);
cb.assertVisitBefore(NODE_PRE, 1, NODE_PRE, 5);
cb.assertVisitBefore(NODE_PRE, 1, POSITION_PRE, 8);
cb.assertVisitBefore(NODE_PRE, 1, POSITION_PRE, 9);
cb.assertVisitBefore(NODE_PRE, 2, POSITION_PRE, 3);
cb.assertVisitBefore(NODE_PRE, 2, POSITION_PRE, 4);
cb.assertVisitBefore(POSITION_PRE, 3, POSITION_POST, 4);
cb.assertVisitBefore(POSITION_PRE, 4, POSITION_POST, 4);
cb.assertVisitAfter(NODE_POST, 2, POSITION_POST, 3);
cb.assertVisitAfter(NODE_POST, 2, POSITION_POST, 4);
cb.assertVisitAfter(POSITION_POST, 8, POSITION_POST, 3);
cb.assertVisitAfter(POSITION_POST, 8, POSITION_POST, 4);
cb.assertVisitAfter(POSITION_POST, 9, POSITION_POST, 3);
cb.assertVisitAfter(POSITION_POST, 9, POSITION_POST, 4);
}
@Test(groups = TestGroup.UNIT)
public void testParallelNoSlaveThreads() {
final Callback cb = new Callback();
PortfolioNodeTraverser.parallel(cb, new PoolExecutor(0, getClass().getSimpleName())).traverse(createTestPortfolioNode(new AtomicInteger(), 2));
// With a single thread (the caller) should be depth first
assertParallelOrder(cb);
}
@Test(groups = TestGroup.UNIT_SLOW)
public void testParallelSlaveThreads() {
final Callback cb = new Callback() {
@Override
protected synchronized void visit(final int type, final UniqueIdentifiable uniqueId) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
super.visit(type, uniqueId);
}
};
PortfolioNodeTraverser.parallel(cb, new PoolExecutor(8, getClass().getSimpleName())).traverse(createTestPortfolioNode(new AtomicInteger(), 2));
assertParallelOrder(cb);
}
}