/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.routing.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import de.rcenvironment.core.communication.common.CommunicationException;
import de.rcenvironment.core.communication.common.NetworkGraphLink;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.model.NetworkResponse;
import de.rcenvironment.core.communication.testutils.TestConfiguration;
import de.rcenvironment.core.communication.testutils.TestNetworkRequestHandler;
import de.rcenvironment.core.communication.testutils.VirtualInstance;
import de.rcenvironment.core.communication.testutils.VirtualInstanceState;
import de.rcenvironment.core.communication.transport.virtual.VirtualTransportTestConfiguration;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.testing.CommonTestOptions;
/**
* @author Robert Mischke
* @author Phillip Kroll
*/
public class LargeScaleScenarioDuplexTest extends AbstractLargeScaleTest {
private static final String DUMMY_REQUEST_CONTENT = "hello";
@Deprecated
private static final int TEMP_ARBITRARY_WAIT = 50;
private static final int DEFAULT_REQUEST_TIMEOUT = 10000;
// test size 5 for fast/standard testing, 10 for extended testing
private static final int TEST_SIZE = CommonTestOptions.selectStandardOrExtendedValue(5, 10);
private static final int EPOCHS = 3;
/**
* @throws Exception on uncaught exceptions
*/
@BeforeClass
public static void setTestParameters() throws Exception {
testSize = TEST_SIZE;
epochs = EPOCHS;
}
@Override
protected TestConfiguration defineTestConfiguration() {
return new VirtualTransportTestConfiguration(true);
}
/**
* @throws Exception on uncaught exceptions
*/
@Test
@Deprecated
public void testIterativelyGrowingLinearNetwork() throws Exception {
for (int i = 0; i < allInstances.length - 1; i++) {
logIteration(i);
int newInstanceIndex = i + 1;
prepareWaitForNextMessage();
instanceUtils.concatenateInstances(allInstances, i, newInstanceIndex);
waitForNextMessage();
waitForNetworkSilence();
// note: the third parameter in Arrays.copyOfRange() is exclusive and must be "last + 1"
assertTrue("Instances did not converge at i=" + i,
instanceUtils.allInstancesHaveSameRawNetworkGraph(Arrays.copyOfRange(allInstances, 0, newInstanceIndex + 1)));
}
}
/**
* @throws Exception on uncaught exceptions
*/
@Test
@Deprecated
public void testConcurrentlyGrowingLinearNetwork() throws Exception {
prepareWaitForNextMessage();
for (int i = 0; i < allInstances.length - 1; i++) {
logIteration(i);
instanceUtils.concatenateInstances(allInstances, i, i + 1);
}
waitForNextMessage();
waitForNetworkSilence();
assertTrue(instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances));
}
/**
* @throws Exception on uncaught exceptions
*/
@Test
@Deprecated
public void testIterativelyGrowingRandomNetwork() throws Exception {
Random random = new Random();
for (int i = 0; i < allInstances.length - 1; i++) {
logIteration(i);
int newInstanceIndex = i + 1;
prepareWaitForNextMessage();
int connectedInstanceIndex = random.nextInt(newInstanceIndex); // 0..newInstanceIndex-1
instanceUtils.concatenateInstances(allInstances, connectedInstanceIndex, newInstanceIndex);
waitForNextMessage();
waitForNetworkSilence();
// note: the third parameter in Arrays.copyOfRange() is exclusive and must be "last + 1"
assertTrue("Instances did not converge at i=" + i,
instanceUtils.allInstancesHaveSameRawNetworkGraph(Arrays.copyOfRange(allInstances, 0, newInstanceIndex + 1)));
}
}
/**
* @throws Exception on uncaught exceptions
*/
@Test
@Deprecated
public void testConcurrentlyGrowingRandomNetwork() throws Exception {
prepareWaitForNextMessage();
Random random = new Random();
for (int i = 0; i < allInstances.length - 1; i++) {
logIteration(i);
int connectedInstanceIndex = random.nextInt(i + 1); // 0..i
instanceUtils.concatenateInstances(allInstances, connectedInstanceIndex, i + 1);
}
waitForNextMessage();
waitForNetworkSilence();
assertTrue(ERROR_MSG_INSTANCES_DID_NOT_CONVERGE, instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances));
}
/**
* Test a simple ring topology. In contrast to the non-duplex test, a double-connected ring should result, with N*2 channels.
*
* @throws Exception on uncaught exceptions
*/
@Test
public void testLargeDuplexRingTopology() throws Exception {
log.info("Topology: ring");
prepareWaitForNextMessage();
instanceUtils.connectToRingTopology(allInstances);
waitForNextMessage();
waitForNetworkSilence();
for (VirtualInstance vi : allInstances) {
assertNodeAndLinkCount(vi, testSize, testSize * 2);
}
}
/**
* Test a simple chain topology. Although connections are only initiated in one direction, a double-connected chain should result.
*
* @throws Exception on uncaught exceptions
*/
@Test
public void testLargeDuplexChainTopology() throws Exception {
prepareWaitForNextMessage();
instanceUtils.connectToChainTopology(allInstances);
waitForNextMessage();
waitForNetworkSilence();
for (VirtualInstance vi : allInstances) {
assertNodeAndLinkCount(vi, testSize, testSize * 2 - 2);
}
}
/**
* Handle large network.
*
* @throws Exception on uncaught exceptions
*/
@Test
public void testInwardDuplexStarTopology() throws Exception {
prepareWaitForNextMessage();
instanceUtils.connectToInwardStarTopology(allInstances);
waitForNextMessage();
waitForNetworkSilence();
for (VirtualInstance vi : allInstances) {
// assertTrue(NETWORK_NOT_FULLY_CONVERGED, vi.isFullyConverged());
assertNodeAndLinkCount(vi, testSize, (testSize - 1) * 2);
}
assertTrue(ERROR_MSG_INSTANCES_DID_NOT_CONVERGE, instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances));
}
/**
* Compute many routes.
*
* @throws Exception on uncaught exceptions
*/
@Test
@Ignore
// FIXME test does not succeed; review -- misc_ro, Dec 2012
public void testComputeManyRoutes() throws Exception {
prepareWaitForNextMessage();
instanceUtils.connectToDoubleRingTopology(allInstances);
waitForNextMessage();
waitForNetworkSilence();
for (VirtualInstance vi : allInstances) {
// assertTrue(NETWORK_NOT_FULLY_CONVERGED, vi.isFullyConverged());
assertNodeAndLinkCount(vi, testSize, testSize * 2);
}
for (int i = 0; i < testSize; i++) {
// compute a random route
VirtualInstance vi1 = instanceUtils.getRandomInstance(allInstances);
VirtualInstance vi2 = instanceUtils.getRandomInstance(allInstances, vi1);
List<? extends NetworkGraphLink> route = vi1.getRouteTo(vi2);
// Route should exist
assertTrue(StringUtils.format("Could not find a valid route from %s to %s",
vi1.getInstanceNodeSessionId(),
vi2.getInstanceNodeSessionId()),
route != null);
// Route should not be larger than the number of nodes in the network
assertTrue(route.size() < instanceUtils.getRandomInstance(allInstances).getKnownNodeCount());
}
}
/**
* Tests shutdown/restart recovery in a duplex ring topology.
*
* @throws Exception on uncaught exceptions
*/
@Test
@Ignore
// FIXME test does not succeed; review -- misc_ro, Dec 2012
public void testDuplexRingFailureRecovery() throws Exception {
// this test needs instances to keep their peer config after a stop/start cycle -- misc_ro
VirtualInstance.setRememberRuntimePeersAfterRestarts(true);
prepareWaitForNextMessage();
instanceUtils.connectToRingTopology(allInstances);
waitForNextMessage();
waitForNetworkSilence();
// iterate over epochs randomly generated scenarios
for (int i = 0; i < epochs; i++) {
String message = instanceUtils.generateUniqueMessageToken();
VirtualInstance failingInstance = instanceUtils.getRandomInstance(allInstances);
VirtualInstance sender = instanceUtils.getRandomInstance(allInstances, failingInstance);
VirtualInstance receiver = instanceUtils.getRandomInstance(allInstances, sender, failingInstance);
log.info("Stating i=" + i + "; instance to restart is " + failingInstance.getInstanceNodeSessionId());
// check that everything is normal (e.g. ring topology)
for (VirtualInstance vi : allInstances) {
// TODO check removal of passive connections on shutdown
assertNodeAndLinkCount(vi, testSize, testSize * 2);
}
prepareWaitForNextMessage();
failingInstance.shutDown();
waitForNextMessage();
waitForNetworkSilence();
// now every instance is expected to know that a node is missing
for (VirtualInstance vi : allInstances) {
// assertTrue(NETWORK_NOT_FULLY_CONVERGED, vi.isFullyConverged());
if (!vi.equals(failingInstance)) {
assertNodeAndLinkCount(vi, testSize - 1, testSize * 2 - 4);
}
}
log.debug(StringUtils.format("%s attempts to communicate with %s.",
instanceUtils.getFormattedName(sender),
instanceUtils.getFormattedName(receiver)));
NetworkResponse response = sender.performRoutedRequest(message, receiver.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT);
assertTrue("Could not send a message", response.isSuccess());
assertTrue(
StringUtils.format("%s failed to send routed message to %s. \n\n %s",
instanceUtils.getFormattedName(sender),
instanceUtils.getFormattedName(receiver),
sender.getFormattedLegacyNetworkGraph()),
receiver.checkMessageReceivedByContent(message));
// waitSomeTime(instances.length);
log.debug("starting up: " + instanceUtils.getFormattedName(failingInstance));
prepareWaitForNextMessage();
failingInstance.start();
waitForNextMessage();
waitForNetworkSilence();
log.info(failingInstance.getFormattedNetworkStats());
}
}
/**
* @throws Exception on uncaught exceptions
*/
@Test
public void testDuplexChainFailureRecovery() throws Exception {
prepareWaitForNextMessage();
instanceUtils.connectToChainTopology(allInstances);
waitForNextMessage();
waitForNetworkSilence();
// iterate over epochs randomly generated scenarios
for (int i = 0; i < epochs; i++) {
log.debug("Epoche: " + i);
String messageContent = instanceUtils.generateUniqueMessageToken();
int failingIndex = randomGenerator.nextInt(testSize);
VirtualInstance failingNode = allInstances[failingIndex];
VirtualInstance firstNode = allInstances[0];
VirtualInstance lastNode = allInstances[testSize - 1];
NetworkResponse response;
response = firstNode.performRoutedRequest(messageContent, lastNode.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT);
assertTrue("Request failed: " + response.getResultCode(), response.isSuccess());
// shutdown
prepareWaitForNextMessage();
failingNode.shutDown();
waitForNextMessage();
waitForNetworkSilence();
response = firstNode.performRoutedRequest(messageContent, lastNode.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT);
assertFalse(response.toString(), response.isSuccess());
// startup
prepareWaitForNextMessage();
failingNode.start();
// FIXME this should not be necessary; investigate
Thread.sleep(TEMP_ARBITRARY_WAIT);
assertEquals(VirtualInstanceState.STARTED, failingNode.getCurrentState());
// connect outwards into both directions; this differs from the original setup, but
// should result in the same topology due to duplex connections being used -- misc_ro
if (failingIndex > 0) {
testTopology.connect(failingIndex, failingIndex - 1);
}
if (failingIndex < testSize - 1) {
testTopology.connect(failingIndex, failingIndex + 1);
}
waitForNextMessage();
waitForNetworkSilence();
// send a message
response = firstNode.performRoutedRequest(messageContent, lastNode.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT);
assertTrue(response.toString(), response.isSuccess());
// LOGGER.info(failingInstance.getFormattedNetworkStats());
log.info(failingNode.getFormattedLegacyNetworkGraph());
}
}
/**
*
* @throws Exception on uncaught exceptions
*/
@Test
public void testMessageConfirmation() throws Exception {
log.info("Topology: double ring");
prepareWaitForNextMessage();
instanceUtils.connectToDoubleRingTopology(allInstances);
waitForNextMessage();
waitForNetworkSilence();
log.info(allInstances[0].getFormattedLegacyNetworkGraph());
// note: added n repetitions to this test; arbitrary value
for (int i = 0; i < testSize; i++) {
long trafficCountBefore = getGlobalRequestCount();
VirtualInstance sender = instanceUtils.getRandomInstance(allInstances);
VirtualInstance receiver = instanceUtils.getRandomInstance(allInstances, sender);
// old:
// String id = sender.sendRoutedAsyncMessage("hello", receiver);
// sender.getRoutingService().getProtocol().waitForMessageConfirmation(id);
String requestBody = DUMMY_REQUEST_CONTENT + 1;
InstanceNodeSessionId receiverId = receiver.getInstanceNodeSessionId();
// Systemx.out.println(receiverId);
NetworkResponse response = sender.performRoutedRequest(requestBody, receiverId, DEFAULT_REQUEST_TIMEOUT);
assertNotNull("Unexpected null response", response);
assertTrue("Received failure response; code=" + response.getResultCode(), response.isSuccess());
assertEquals(TestNetworkRequestHandler.getTestResponse(requestBody, receiverId),
response.getDeserializedContent());
log.debug("Routed message delivered and confirmed; registered " + (getGlobalRequestCount() - trafficCountBefore)
+ " new network requests");
}
}
/**
* @throws Exception on uncaught exceptions
*/
@Test
public void testMessageFailure() throws Exception {
log.info("Topology: double ring");
VirtualInstance.setRememberRuntimePeersAfterRestarts(true);
// create topology
prepareWaitForNextMessage();
instanceUtils.connectToDoubleRingTopology(allInstances);
waitForNextMessage();
waitForNetworkSilence();
VirtualInstance failingInstance = instanceUtils.getRandomInstance(allInstances);
VirtualInstance sender = instanceUtils.getRandomInstance(allInstances, failingInstance);
// this should work
NetworkResponse response =
sender.performRoutedRequest(DUMMY_REQUEST_CONTENT, failingInstance.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT);
assertTrue("Initial routed request failed", response.isSuccess());
prepareWaitForNextMessage();
failingInstance.shutDown();
waitForNextMessage();
waitForNetworkSilence();
try {
response =
sender.performRoutedRequest(DUMMY_REQUEST_CONTENT, failingInstance.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT);
// this should fail, because instance is shut down.
assertFalse(NetworkFormatter.networkResponseToString(response), response.isSuccess());
} catch (CommunicationException e) {
assertTrue(e.getMessage(), e.getMessage().contains("could not find"));
}
prepareWaitForNextMessage();
failingInstance.start();
waitForNextMessage();
waitForNetworkSilence();
}
/**
* @throws Exception on uncaught exceptions
*/
@Test
@Ignore
// FIXME test does not succeed; review -- misc_ro, Dec 2012
public void testDisconnectedNetworkParts() throws Exception {
final int chainSize = testSize / 4;
log.info("Topology: two rings connected with a chain.");
prepareWaitForNextMessage();
// TODO review: check topology here; not obvious how this relates to the description
instanceUtils.connectToDoubleRingTopology(allInstances);
instanceUtils.connectToDoubleRingTopology(allInstances, 0, testSize / 2 - chainSize / 2);
instanceUtils.connectToDoubleChainTopology(allInstances, testSize / 2 - (chainSize / 2
- 1), testSize / 2 + (chainSize / 2 - 1));
instanceUtils.connectToDoubleRingTopology(allInstances, testSize / 2 + chainSize / 2,
testSize - 1);
instanceUtils.doubleConcatenateInstances(allInstances, testSize / 2 - chainSize / 2,
testSize / 2 - (chainSize / 2 - 1));
instanceUtils.doubleConcatenateInstances(allInstances, testSize / 2 + chainSize / 2,
testSize / 2 + (chainSize / 2 - 1));
waitForNextMessage();
waitForNetworkSilence();
// for (VirtualInstance vi : allInstances) {
// TODO Include this test.
// assertEquals(NUMBER_OF_NODES, testSize, vi.getTopologyMap().getNodeCount());
// assertEquals(NUMBER_OF_CHANNELS, testSize * 2 + 2,
// vi.getTopologyMap().getChannelCount());
// }
VirtualInstance failingInstance = allInstances[testSize / 2];
prepareWaitForNextMessage();
failingInstance.shutDown();
waitForNextMessage();
waitForNetworkSilence();
NetworkResponse response;
// TODO clarify failure response vs. exception behaviour for routed request method;
// checking both execution paths with assertions here
try {
response =
allInstances[0].performRoutedRequest(DUMMY_REQUEST_CONTENT,
allInstances[allInstances.length - 1].getInstanceNodeSessionId(),
DEFAULT_REQUEST_TIMEOUT);
assertFalse("Routed message sending should have failed; response=" + response.getDeserializedContent(), response.isSuccess());
} catch (CommunicationException e) {
assertTrue(e.getMessage().contains("could not find a route"));
}
// TODO when available, query response metadata for failure location (node id / hop count)
for (VirtualInstance vi : allInstances) {
if (!vi.equals(failingInstance)) {
assertNodeAndLinkCount(vi, testSize - 1, testSize * 2 - 2);
}
}
prepareWaitForNextMessage();
failingInstance.start();
waitForNextMessage();
waitForNetworkSilence();
}
/**
* @throws Exception on uncaught exceptions
*/
@Test
@Ignore
// FIXME test does not succeed; review -- misc_ro, Dec 2012
public void testMergingNetworks() throws Exception {
log.info("Topology: two rings unconnected rings");
prepareWaitForNextMessage();
// create two disconnected networks
// TODO no non-double variants available
instanceUtils.connectToDoubleRingTopology(allInstances, 0, testSize / 2 - 1);
instanceUtils.connectToDoubleRingTopology(allInstances, testSize / 2, testSize - 1);
waitForNextMessage();
waitForNetworkSilence();
for (VirtualInstance vi : allInstances) {
assertNodeAndLinkCount(vi, testSize / 2, testSize);
}
// connect networks
prepareWaitForNextMessage();
instanceUtils.doubleConcatenateInstances(allInstances, testSize / 2 - 1, testSize / 2);
waitForNextMessage();
waitForNetworkSilence();
for (VirtualInstance vi : allInstances) {
assertNodeAndLinkCount(vi, testSize, testSize * 2 + 2);
}
}
private void logIteration(int i) {
log.debug("i: " + i);
}
}