/* * 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.TestNetworkRequestHandler; import de.rcenvironment.core.communication.testutils.VirtualInstance; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.testing.CommonTestOptions; /** * @author Phillip Kroll * @author Robert Mischke * */ public class LargeScaleScenarioTest extends AbstractLargeScaleTest { private static final String DUMMY_REQUEST_CONTENT = "hello"; private static final int DEFAULT_REQUEST_TIMEOUT = 10000; // FIXME the tests testTwoRingsAndOneChainTopology() and testMergingNetworks() fail with any test size // except 10; review and fix (or remove) them - misc_ro private static final int TEST_SIZE = CommonTestOptions.selectStandardOrExtendedValue(10, 10); private static final int EPOCHS = 3; /** * @throws Exception on uncaught exceptions */ @BeforeClass public static void setTestParameters() throws Exception { testSize = TEST_SIZE; epochs = EPOCHS; } /** * @throws Exception on uncaught exceptions */ @Test public void testTwoRingsAndOneChainTopology() throws Exception { log.info("Topology: two rings connected with a chain."); prepareWaitForNextMessage(); int chainSize = testSize / 4; 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); testTopology.connect(testSize / 2 - chainSize / 2, testSize / 2 - (chainSize / 2 - 1), true); testTopology.connect(testSize / 2 + chainSize / 2, testSize / 2 + (chainSize / 2 - 1), true); waitForNextMessage(); waitForNetworkSilence(); // TODO review: check for topology hashcode, too? for (VirtualInstance vi : allInstances) { assertNodeAndLinkCount(vi, testSize, testSize * 2 + 2); } } /** * @throws Exception on uncaught exceptions */ @Test public void testLargeDoubleRingTopology() throws Exception { prepareWaitForNextMessage(); testTopology.connectToRing(true); waitForNextMessage(); waitForNetworkSilence(); for (VirtualInstance vi : allInstances) { // assertTrue(NETWORK_NOT_FULLY_CONVERGED, vi.isFullyConverged()); assertNodeAndLinkCount(vi, testSize, testSize * 2); } } /** * @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(); testTopology.connect(i, newInstanceIndex, true); 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); testTopology.connect(i, i + 1, true); } waitForNextMessage(); waitForNetworkSilence(); assertTrue(testTopology.allInstancesConverged()); for (int i = 0; i < allInstances.length - 1; i++) { assertEquals(allInstances.length, allInstances[i].getReachableNetworkGraph().getNodeCount()); } } /** * @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 testTopology.connect(connectedInstanceIndex, newInstanceIndex, true); 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 = 1; i <= allInstances.length - 1; i++) { logIteration(i); int connectedInstanceIndex = random.nextInt(i); // 0..(i-1) testTopology.connect(connectedInstanceIndex, i, true); } waitForNextMessage(); waitForNetworkSilence(); assertTrue("Instances did not converge", instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances)); } /** * Test a simple ring topology. * * @throws Exception on uncaught exceptions */ @Test public void testLargeRingTopology() throws Exception { log.info("Topology: ring"); // single direction; rename? prepareWaitForNextMessage(); testTopology.connectToRing(false); waitForNextMessage(); waitForNetworkSilence(); for (VirtualInstance vi : allInstances) { assertNodeAndLinkCount(vi, testSize, testSize); } } /** * Handle large network. * * @throws Exception on uncaught exceptions */ @Test public void testLargeDoubleChainTopology() throws Exception { log.info("Topology: double chain"); prepareWaitForNextMessage(); testTopology.connectToChain(true); waitForNextMessage(); waitForNetworkSilence(); for (VirtualInstance vi : allInstances) { assertNodeAndLinkCount(vi, testSize, testSize * 2 - 2); } } /** * Handle large network. * * @throws Exception on uncaught exceptions */ @Test public void testLargeDoubleStarTopology() throws Exception { log.info("Topology: duplex star"); prepareWaitForNextMessage(); instanceUtils.connectToDoubleStarTopology(allInstances); waitForNextMessage(); waitForNetworkSilence(); for (VirtualInstance vi : allInstances) { // assertTrue(NETWORK_NOT_FULLY_CONVERGED, vi.isFullyConverged()); assertNodeAndLinkCount(vi, testSize, (testSize - 1) * 2); } } /** * Compute many routes. * * @throws Exception on uncaught exceptions */ @Test public void testComputeManyRoutes() throws Exception { prepareWaitForNextMessage(); testTopology.connectToRing(true); 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 = testTopology.getRandomInstance(); VirtualInstance vi2 = testTopology.getRandomInstanceExcept(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()); } } /** * Handle large network. * * @throws Exception on uncaught exceptions */ @Test @Ignore // TODO (p2) (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(); testTopology.connectToRing(true); waitForNextMessage(); waitForNetworkSilence(); // iterate over epochs randomly generated scenarios for (int i = 0; i < epochs; i++) { log.info("Epoch: " + i); // waitSomeTime(instances.length); String message = instanceUtils.generateUniqueMessageToken(); VirtualInstance failingInstance = testTopology.getRandomInstance(); VirtualInstance sender = testTopology.getRandomInstanceExcept(failingInstance); VirtualInstance receiver = testTopology.getRandomInstanceExcept(sender, failingInstance); // check that everything is normal (e.g. ring topology) for (VirtualInstance vi : allInstances) { // TODO (p2) (part of disabled test) check: this assertion fails frequently 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 @Ignore // FIXME test does not succeed; review -- misc_ro, Dec 2012 public void testDuplexChainFailureRecovery() throws Exception { // this test needs instances to keep their peer config after a stop/start cycle -- misc_ro VirtualInstance.setRememberRuntimePeersAfterRestarts(true); prepareWaitForNextMessage(); testTopology.connectToRing(true); waitForNextMessage(); waitForNetworkSilence(); // iterate over epochs randomly generated scenarios for (int i = 0; i < epochs; i++) { log.debug("Epoche: " + i); String messageContent = instanceUtils.generateUniqueMessageToken(); VirtualInstance failingInstance = testTopology.getRandomInstance(); VirtualInstance sender = testTopology.getRandomInstanceExcept(failingInstance); VirtualInstance receiver = testTopology.getRandomInstanceExcept(sender, failingInstance); // shutdown prepareWaitForNextMessage(); failingInstance.shutDown(); waitForNextMessage(); waitForNetworkSilence(); log.debug(StringUtils.format("%s attempts to communicate with %s.", instanceUtils.getFormattedName(sender), instanceUtils.getFormattedName(receiver))); try { // this can fail sometimes NetworkResponse response = sender.performRoutedRequest(messageContent, receiver.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT); assertTrue(response.getDeserializedContent().toString(), 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.getRoutingService().getProtocolManager().messageReivedByContent(messageContent)); assertTrue(receiver.checkMessageReceivedByContent(messageContent)); // waitSomeTime(instances.length); } catch (CommunicationException e) { log.warn(e.getMessage()); } // startup prepareWaitForNextMessage(); failingInstance.start(); waitForNextMessage(); waitForNetworkSilence(); // send a message NetworkResponse response = sender.performRoutedRequest(messageContent, receiver.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT); assertTrue(response.toString(), response.isSuccess()); log.info(failingInstance.getFormattedNetworkStats()); } } /** * @throws Exception on uncaught exceptions */ @Test public void testMessageConfirmation() throws Exception { log.info("Topology: double ring"); prepareWaitForNextMessage(); testTopology.connectToRing(true); 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 = testTopology.getRandomInstance(); VirtualInstance receiver = testTopology.getRandomInstanceExcept(sender); // old: // String id = sender.sendRoutedAsyncMessage("hello", receiver); // sender.getRoutingService().getProtocol().waitForMessageConfirmation(id); String requestBody = DUMMY_REQUEST_CONTENT + i; 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 @Ignore // TODO (p3) (test) temporarily disabled; review public void testMessageFailure() throws Exception { log.info("Topology: double ring"); VirtualInstance.setRememberRuntimePeersAfterRestarts(true); // create topology prepareWaitForNextMessage(); testTopology.connectToRing(true); waitForNextMessage(); waitForNetworkSilence(); VirtualInstance failingInstance = testTopology.getRandomInstance(); VirtualInstance sender = testTopology.getRandomInstanceExcept(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 document/clarify created topology 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); testTopology.connect(testSize / 2 - chainSize / 2, testSize / 2 - (chainSize / 2 - 1), true); testTopology.connect(testSize / 2 + chainSize / 2, testSize / 2 + (chainSize / 2 - 1), true); 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; try { // TODO clarify failure response vs. exception behaviour for routed request method; // checking both execution paths with assertions here response = allInstances[0].performRoutedRequest(DUMMY_REQUEST_CONTENT, allInstances[allInstances.length - 1].getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT); // TODO when available, query response metadata for failure location (node id / hop // count) assertFalse("Routed message sending should have failed; response=" + response.getDeserializedContent(), response.isSuccess()); } catch (CommunicationException e) { assertTrue(e.getMessage().contains("could not find a route")); } 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 public void testMergingNetworks() throws Exception { log.info("Topology: two rings unconnected rings"); prepareWaitForNextMessage(); // create two disconnected networks 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(); testTopology.connect(testSize / 2 - 1, testSize / 2, true); waitForNextMessage(); waitForNetworkSilence(); for (VirtualInstance vi : allInstances) { assertNodeAndLinkCount(vi, testSize, testSize * 2 + 2); } } private void logIteration(int i) { log.debug("Starting test iteration " + i); } }