/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.model.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.util.List;
import java.util.concurrent.Callable;
import org.junit.Before;
import org.junit.Test;
import de.rcenvironment.core.communication.common.NetworkGraphLink;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils;
import de.rcenvironment.core.communication.model.NetworkRoutingInformation;
import de.rcenvironment.core.communication.routing.internal.v2.NoRouteToNodeException;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
import de.rcenvironment.toolkit.modules.concurrency.api.CallablesGroup;
/**
* {@link NetworkGraphImpl} unit test.
*
* @author Robert Mischke
*/
public class NetworkGraphImplTest {
private InstanceNodeSessionId node1;
private InstanceNodeSessionId node2;
private InstanceNodeSessionId node3;
private InstanceNodeSessionId node4;
private InstanceNodeSessionId node5;
private NetworkGraphLinkImpl link12;
private NetworkGraphLinkImpl link23;
private NetworkGraphLinkImpl link34;
private NetworkGraphLinkImpl link45;
/**
* Common test setup.
*/
@Before
public void setUp() {
node1 = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("1");
node2 = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("2");
node3 = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("3");
node4 = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("4");
node5 = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("5");
link12 = new NetworkGraphLinkImpl("12s-x", node1, node2);
link23 = new NetworkGraphLinkImpl("23s-x", node2, node3);
link34 = new NetworkGraphLinkImpl("34s-x", node3, node4);
link45 = new NetworkGraphLinkImpl("45s-x", node4, node5);
}
/**
* Tests the computation of the reachable graph from various source graphs.
*/
@Test
public void testReductionToReachable() {
int n = 5;
InstanceNodeSessionId[] nodes = new InstanceNodeSessionId[n];
for (int i = 0; i < n; i++) {
nodes[i] = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("Node" + i);
}
// test with linear chains
for (int i = 0; i < n; i++) {
NetworkGraphImpl rawGraph = new NetworkGraphImpl(nodes[i]);
for (int j = 0; j < n - 1; j++) {
rawGraph.addLink(j + "-", nodes[j], nodes[j + 1]);
}
assertEquals(n, rawGraph.getNodeCount());
assertEquals(n - 1, rawGraph.getLinkCount());
NetworkGraphImpl reachableGraph = rawGraph.reduceToReachableGraph();
assertEquals(n - i, reachableGraph.getNodeCount());
assertEquals((n - i) - 1, reachableGraph.getLinkCount());
// raw graph should be unchanged
assertEquals(n, rawGraph.getNodeCount());
assertEquals(n - 1, rawGraph.getLinkCount());
}
// test with closed loops
for (int i = 0; i < n; i++) {
NetworkGraphImpl rawGraph = new NetworkGraphImpl(nodes[i]);
for (int j = 0; j < n - 1; j++) {
rawGraph.addLink(j + "-", nodes[j], nodes[j + 1]);
}
rawGraph.addLink("back-", nodes[n - 1], nodes[0]);
assertEquals(n, rawGraph.getNodeCount());
assertEquals(n, rawGraph.getLinkCount());
NetworkGraphImpl reachableGraph = rawGraph.reduceToReachableGraph();
// reachable graph should be full graph
assertEquals(n, reachableGraph.getNodeCount());
assertEquals(n, reachableGraph.getLinkCount());
// raw graph should be unchanged
assertEquals(n, rawGraph.getNodeCount());
assertEquals(n, rawGraph.getLinkCount());
}
NetworkGraphImpl rawGraph;
NetworkGraphImpl reachableGraph;
// test detached nodes
rawGraph = new NetworkGraphImpl(node1);
rawGraph.addNode(node2);
rawGraph.addNode(node3);
rawGraph.addLink("x-", node1, node2);
assertEquals(3, rawGraph.getNodeCount());
assertEquals(1, rawGraph.getLinkCount());
reachableGraph = rawGraph.reduceToReachableGraph();
assertEquals(2, reachableGraph.getNodeCount());
assertEquals(1, reachableGraph.getLinkCount());
// test attached, but unreachable nodes
rawGraph = new NetworkGraphImpl(node1);
rawGraph.addNode(node2);
rawGraph.addNode(node3);
rawGraph.addLink("x-", node1, node2);
rawGraph.addLink("y-", node3, node1);
assertEquals(3, rawGraph.getNodeCount());
assertEquals(2, rawGraph.getLinkCount());
reachableGraph = rawGraph.reduceToReachableGraph();
assertEquals(2, reachableGraph.getNodeCount());
assertEquals(1, reachableGraph.getLinkCount());
}
/**
* Test of routing map generation and caching.
*
* @throws NoRouteToNodeException on unexpected routing failures
*/
@Test
public void testChainWithRootInMiddle() throws NoRouteToNodeException {
InstanceNodeSessionId rootNode = node3;
NetworkGraphImpl rawGraph = createFiveNodeChainWithRootAt(rootNode);
NetworkRoutingInformation routingInformation = rawGraph.generateRoutingInformation();
assertRoutingToLocalNodeFails(routingInformation, rootNode);
// check adjacent node
assertEquals(link34, routingInformation.getNextLinkTowards(node4));
assertEquals(1, getRoutingCacheMisses(routingInformation));
// repeat -> should not cause another cache miss
assertEquals(link34, routingInformation.getNextLinkTowards(node4));
assertEquals(1, getRoutingCacheMisses(routingInformation));
// check adjacent, but unreachble node (only inbound link)
try {
routingInformation.getNextLinkTowards(node2);
fail("Routing should have failed");
} catch (NoRouteToNodeException e) {
assertEquals(node2, e.getTargetNodeId());
}
// make no assumptions about caching of routing failures, as they should be the exception
// reset cache miss counter
resetCacheMisses(routingInformation);
// check reachable, non-adjacent node
assertEquals(link34, routingInformation.getNextLinkTowards(node5));
assertEquals(1, getRoutingCacheMisses(routingInformation));
// repeat -> should not cause another cache miss
assertEquals(link34, routingInformation.getNextLinkTowards(node5));
assertEquals(1, getRoutingCacheMisses(routingInformation));
// check unreachable, non-adjacent node
try {
routingInformation.getNextLinkTowards(node1);
fail("Routing should have failed");
} catch (NoRouteToNodeException e) {
assertEquals(node1, e.getTargetNodeId());
}
}
/**
* Test 5-node chain, with node 1 being root, going forward (2..5) with routing requests.
*
* @throws NoRouteToNodeException on unexpected routing failures
*/
@Test
public void testChainWithRootAtStartGoingForward() throws NoRouteToNodeException {
InstanceNodeSessionId rootNode = node1;
NetworkGraphImpl rawGraph = createFiveNodeChainWithRootAt(rootNode);
NetworkGraphImpl processedGraph = rawGraph.reduceToReachableGraph();
NetworkRoutingInformation routingInformation = processedGraph.getRoutingInformation();
assertRoutingToLocalNodeFails(routingInformation, rootNode);
assertEquals(link12, routingInformation.getNextLinkTowards(node2));
assertEquals(1, getRoutingCacheMisses(routingInformation));
assertEquals(link12, routingInformation.getNextLinkTowards(node3));
assertEquals(2, getRoutingCacheMisses(routingInformation));
assertEquals(link12, routingInformation.getNextLinkTowards(node4));
assertEquals(3, getRoutingCacheMisses(routingInformation));
assertEquals(link12, routingInformation.getNextLinkTowards(node5));
assertEquals(4, getRoutingCacheMisses(routingInformation));
// repeat arbitrary node -> should not cause another cache miss
resetCacheMisses(routingInformation);
assertEquals(link12, routingInformation.getNextLinkTowards(node4));
assertEquals(0, getRoutingCacheMisses(routingInformation));
}
/**
* Test 5-node chain, with node 1 being root, going backward (5..2) with routing requests.
*
* @throws NoRouteToNodeException on unexpected routing failures
*/
@Test
public void testChainWithRootAtStartGoingBackward() throws NoRouteToNodeException {
InstanceNodeSessionId rootNode = node1;
NetworkGraphImpl rawGraph = createFiveNodeChainWithRootAt(rootNode);
NetworkGraphImpl processedGraph = rawGraph.reduceToReachableGraph();
NetworkRoutingInformation routingInformation = processedGraph.getRoutingInformation();
assertRoutingToLocalNodeFails(routingInformation, rootNode);
assertEquals(link12, routingInformation.getNextLinkTowards(node5));
// cache misses should immediately jump to 4, then stay there
assertEquals(4, getRoutingCacheMisses(routingInformation));
assertEquals(link12, routingInformation.getNextLinkTowards(node4));
assertEquals(4, getRoutingCacheMisses(routingInformation));
assertEquals(link12, routingInformation.getNextLinkTowards(node3));
assertEquals(4, getRoutingCacheMisses(routingInformation));
assertEquals(link12, routingInformation.getNextLinkTowards(node2));
assertEquals(4, getRoutingCacheMisses(routingInformation));
// repeat arbitrary node -> should not cause another cache miss
resetCacheMisses(routingInformation);
assertEquals(link12, routingInformation.getNextLinkTowards(node4));
assertEquals(0, getRoutingCacheMisses(routingInformation));
}
/**
* Tests basic route calculation.
*/
@Test
public final void testBasicRouteComputation() {
NetworkGraphImpl networkGraph = new NetworkGraphImpl(node1);
networkGraph.addNode(node2);
networkGraph.addNode(node3);
networkGraph.addLink("link12", node1, node2);
networkGraph.addLink("link23", node2, node3);
networkGraph.addLink("link31", node3, node1);
networkGraph.addLink("link41", node4, node1);
NetworkRoutingInformation routingInformation = networkGraph.generateRoutingInformation();
List<? extends NetworkGraphLink> route = routingInformation.getRouteTo(node3);
assertEquals(2, route.size());
assertEquals(node2, route.get(0).getTargetNodeId());
assertEquals(node3, route.get(1).getTargetNodeId());
route = routingInformation.getRouteTo(node2);
assertEquals(1, route.size());
assertEquals(node2, route.get(0).getTargetNodeId());
// test failing route (unreachable node)
route = routingInformation.getRouteTo(node4);
assertNull("Route should be null", route);
// test exception when passing the local node
try {
route = routingInformation.getRouteTo(node1);
fail("Exception expected");
} catch (RuntimeException e) {
assertEquals(IllegalArgumentException.class, e.getClass());
}
}
/**
* Tests implicit addition of nodes from added edges, additionally, the edges are added from different threads.
*/
public void testMultiThreadedGraphCreationFromAddedEdges() {
final NetworkGraphImpl rawGraph = new NetworkGraphImpl(node1);
int n = 10 * 10; // yay for Checkstyle :)
CallablesGroup<Void> callablesGroup = ConcurrencyUtils.getFactory().createCallablesGroup(Void.class);
for (int i = 0; i < n; i++) {
final int i2 = i;
callablesGroup.add(new Callable<Void>() {
@Override
public Void call() throws Exception {
InstanceNodeSessionId tempNode =
NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("tempNode" + i2);
if (i2 % 2 == 0) {
rawGraph.addLink("link" + i2, node1, tempNode);
} else {
rawGraph.addLink("link" + i2, tempNode, node1);
}
return null;
}
});
callablesGroup.executeParallel(null);
}
assertEquals(n + 1, rawGraph.getNodeCount());
assertEquals(n + 1, rawGraph.getNodeIds().size());
assertEquals(n * 2, rawGraph.getLinkCount());
}
private NetworkGraphImpl createFiveNodeChainWithRootAt(InstanceNodeSessionId rootNodeId) {
NetworkGraphImpl rawGraph = new NetworkGraphImpl(rootNodeId);
rawGraph.addNode(node1);
rawGraph.addNode(node2);
rawGraph.addNode(node3);
rawGraph.addNode(node4);
rawGraph.addNode(node5);
rawGraph.addLink(link12);
rawGraph.addLink(link23);
rawGraph.addLink(link34);
rawGraph.addLink(link45);
return rawGraph;
}
/**
* Common test method to verify that trying to route to the local node fails.
*/
private void assertRoutingToLocalNodeFails(NetworkRoutingInformation routingInformation, InstanceNodeSessionId rootNode) {
try {
routingInformation.getNextLinkTowards(rootNode);
fail("Routing to local node should have failed");
} catch (NoRouteToNodeException e) {
assertEquals(rootNode, e.getTargetNodeId());
}
// cache should be untouched
assertEquals(0, getRoutingCacheMisses(routingInformation));
}
private int getRoutingCacheMisses(NetworkRoutingInformation routingInformation) {
return ((NetworkRoutingInformationImpl) routingInformation).getRoutingCacheMisses();
}
private void resetCacheMisses(NetworkRoutingInformation routingInformation) {
((NetworkRoutingInformationImpl) routingInformation).resetCacheMisses();
}
}