/* * 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.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.easymock.EasyMock; import org.junit.Ignore; import org.junit.Test; import de.rcenvironment.core.communication.channel.MessageChannelLifecycleListener; import de.rcenvironment.core.communication.channel.MessageChannelService; import de.rcenvironment.core.communication.common.CommunicationException; import de.rcenvironment.core.communication.common.InstanceNodeSessionId; import de.rcenvironment.core.communication.common.NetworkGraphLink; import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils; import de.rcenvironment.core.communication.model.NetworkContactPoint; import de.rcenvironment.core.communication.model.NetworkResponse; import de.rcenvironment.core.communication.testutils.AbstractVirtualInstanceTest; import de.rcenvironment.core.communication.testutils.NetworkContactPointGenerator; import de.rcenvironment.core.communication.testutils.TestConfiguration; import de.rcenvironment.core.communication.testutils.VirtualInstance; import de.rcenvironment.core.communication.testutils.VirtualInstanceGroup; import de.rcenvironment.core.communication.transport.spi.MessageChannel; import de.rcenvironment.core.communication.transport.spi.NetworkTransportProvider; import de.rcenvironment.core.communication.transport.virtual.VirtualTransportTestConfiguration; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.testing.CommonTestOptions; /** * Unit tests for {@link LinkStateRoutingProtocolManager}. * * @author Phillip Kroll * @author Robert Mischke */ // TODO rework to general topology tests? - misc_ro public class RoutingProtocolTest extends AbstractVirtualInstanceTest { 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 String CONNECTION_ID_1 = "#1"; private static final String FAILED_TO_CONNECT = "Failed to connect."; // private static final String NETWORK_NOT_FULLY_CONVERGED = "The network graph of every instance is supposed to be the same."; /** * Create a chain of instances that are not connected in a ring. * * @throws Exception on uncaught exceptions */ @Test public void testChainOfInstances() throws Exception { setupInstances(TEST_SIZE, false, true); prepareWaitForNextMessage(); instanceUtils.connectToChainTopology(allInstances); waitForNextMessage(); waitForNetworkSilence(); // TODO actually verify topology state } /** * Create instances that are connected in a ring. * * @throws Exception on uncaught exceptions */ @Test public void testRingOfInstances() throws Exception { setupInstances(TEST_SIZE, false, true); for (VirtualInstance vi : allInstances) { assertFalse("The network graph of any instance is not supposed to be the same.", instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances)); } prepareWaitForNextMessage(); instanceUtils.connectToRingTopology(allInstances); waitForNextMessage(); waitForNetworkSilence(); try { assertAllInstancesKnowSameTopology(); } catch (AssertionError e) { throw new RuntimeException(e); } // arbitrary number of iterations; using test size for (int i = 0; i < TEST_SIZE; i++) { VirtualInstance sender = instanceUtils.getRandomInstance(allInstances); VirtualInstance receiver = instanceUtils.getRandomInstance(allInstances, sender); // check a random route assertTrue(StringUtils.format(ERROR_MSG_A_NO_ROUTE_TO_B, sender, receiver), sender.getRouteTo(receiver) != null); } } /** * Test if stopping and restarting all virtual instances works smoothly. * * @throws Exception on uncaught exceptions */ @Test public void testStartAndStopp() throws Exception { VirtualInstance.setRememberRuntimePeersAfterRestarts(true); setupInstances(TEST_SIZE, false, true); VirtualInstanceGroup group = new VirtualInstanceGroup(allInstances); prepareWaitForNextMessage(); instanceUtils.connectToRingTopology(allInstances); waitForNextMessage(); waitForNetworkSilence(); assertAllInstancesKnowSameTopology(); prepareWaitForNextMessage(); group.shutDown(); waitForNextMessage(); waitForNetworkSilence(); prepareWaitForNextMessage(); group.start(); waitForNextMessage(); waitForNetworkSilence(); assertAllInstancesKnowSameTopology(); } /** * Add initial network peer afterwards and check if everything went ok. * * @throws Exception on uncaught exceptions */ @Test public void testClosingChainToRing() throws Exception { setupInstances(TEST_SIZE, false, true); VirtualInstance firstInstance = allInstances[0]; VirtualInstance lastInstance = allInstances[allInstances.length - 1]; prepareWaitForNextMessage(); instanceUtils.connectToChainTopology(allInstances); waitForNextMessage(); waitForNetworkSilence(); // connect last and first instance to create a ring prepareWaitForNextMessage(); firstInstance.connectAsync(lastInstance.getConfigurationService().getServerContactPoints().get(0)); waitForNextMessage(); waitForNetworkSilence(); // TODO This should not fail. assertTrue(instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances)); List<? extends NetworkGraphLink> route = firstInstance.getRouteTo(lastInstance); assertTrue(StringUtils.format(ERROR_MSG_A_NO_ROUTE_TO_B, firstInstance, lastInstance), route != null); assertEquals(1, route.size()); } /** * @throws Exception on uncaught exceptions */ @Test public void testCrashingNodeInSingleDirectionRing() throws Exception { VirtualInstance.setRememberRuntimePeersAfterRestarts(true); setupInstances(TEST_SIZE, false, true); prepareWaitForNextMessage(); testTopology.connectToRing(false); // single-direction ring waitForNextMessage(); waitForNetworkSilence(); VirtualInstance firstNode = allInstances[0]; VirtualInstance failingNodePredecessor = allInstances[allInstances.length / 2 - 1]; VirtualInstance failingNode = allInstances[allInstances.length / 2]; VirtualInstance lastNode = allInstances[allInstances.length - 1]; // verify initial message sending String dummyContent = instanceUtils.generateUniqueMessageToken(); assertTrue(firstNode.performRoutedRequest(dummyContent, lastNode.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT).isSuccess()); // failure failingNode.simulateCrash(); // there should still be a link in the topology map of the predecessor assertTrue(failingNodePredecessor.knownTopologyContainsLinkTo(failingNode)); // sender should falsely believe that there is a route. assertTrue(StringUtils.format(ERROR_MSG_A_NO_ROUTE_TO_B, firstNode, lastNode), firstNode.getRouteTo(lastNode) != null); dummyContent = instanceUtils.generateUniqueMessageToken(); prepareWaitForNextMessage(); // sending to the predecessor should still work normally assertTrue(firstNode.performRoutedRequest(dummyContent, failingNodePredecessor.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT) .isSuccess()); // sending across the whole chain should cause the predecessor to notice the crashed node // TODO verify location of routing failure assertFalse(firstNode.performRoutedRequest(dummyContent, lastNode.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT).isSuccess()); // sending a message to the failed node should fail as well assertFalse( firstNode.performRoutedRequest(dummyContent, failingNode.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT).isSuccess()); // sending to the predecessor should still work normally assertTrue(firstNode.performRoutedRequest(dummyContent, failingNodePredecessor.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT) .isSuccess()); waitForNextMessage(); waitForNetworkSilence(); // the link from the predecessor to the crashed node should now be gone // TODO necessary to ensure thread visibility here? assertFalse("Topology link should be gone after failed delivery", failingNodePredecessor.knownTopologyContainsLinkTo(failingNode)); prepareWaitForNextMessage(); // restart the crashed node failingNode.start(); // TODO reconnect actively until event-driven reconnect is available failingNodePredecessor.connectAsync(failingNode.getConfigurationService().getServerContactPoints().get(0)); waitForNextMessage(); waitForNetworkSilence(); assertAllInstancesKnowSameTopology(); // the channel should now be there again assertTrue(failingNodePredecessor.knownTopologyContainsLinkTo(failingNode)); assertTrue(StringUtils.format(ERROR_MSG_A_NO_ROUTE_TO_B, firstNode, lastNode), firstNode.getRouteTo(lastNode) != null); dummyContent = instanceUtils.generateUniqueMessageToken(); prepareWaitForNextMessage(); // this should not fail anymore assertTrue(firstNode.performRoutedRequest(dummyContent, failingNodePredecessor.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT) .isSuccess()); waitForNextMessage(); waitForNetworkSilence(); } /** * Send a simple routed message. * * @throws Exception on uncaught exceptions */ @Test public void testRoutedMessage() throws Exception { setupInstances(TEST_SIZE, false, true); String uniqueMessageToken = instanceUtils.generateUniqueMessageToken(); VirtualInstance sender = allInstances[0]; VirtualInstance receiver = allInstances[allInstances.length - 1]; prepareWaitForNextMessage(); instanceUtils.connectToRingTopology(allInstances); waitForNextMessage(); waitForNetworkSilence(); prepareWaitForNextMessage(); try { // log.debug(allInstances[0].getFormattedNetworkGraph()); NetworkResponse response = sender.performRoutedRequest(uniqueMessageToken, receiver.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT); assertTrue("Request failed: " + response.getResultCode(), response.isSuccess()); } catch (CommunicationException e) { fail(e.getMessage()); } waitForNextMessage(); waitForNetworkSilence(); } /** * Initiate LSAs randomly and wait for the network to converge. * * @throws Exception on uncaught exceptions */ @Test @Ignore @Deprecated // TODO old test; review and probably remove - misc_ro, Nov 2013 public void testDiscoveryByRandomLSAs() throws Exception { final int maxIterations = TEST_SIZE * 10; setupInstances(TEST_SIZE, false, true); String uniqueMessageToken = instanceUtils.generateUniqueMessageToken(); prepareWaitForNextMessage(); instanceUtils.connectToDoubleStarTopology(Arrays.copyOfRange(allInstances, 0, TEST_SIZE / 2 - 1)); instanceUtils.connectToDoubleStarTopology(Arrays.copyOfRange(allInstances, TEST_SIZE / 2, TEST_SIZE - 1)); instanceUtils.randomlyConcatenateTopologies( Arrays.copyOfRange(allInstances, 0, TEST_SIZE / 2 - 1), Arrays.copyOfRange(allInstances, TEST_SIZE / 2, TEST_SIZE - 1)); waitForNextMessage(); waitForNetworkSilence(); int j = 0; int max = maxIterations; boolean fullConvercenceForEveryone = false; // send LSAs randomly until every instance reports that the network knowledge is the same // everywhere while (!fullConvercenceForEveryone && j++ < max) { VirtualInstance randomInstance = instanceUtils.getRandomInstance(allInstances); prepareWaitForNextMessage(); // NOTE: commented out as the target method has been removed // randomInstance.getRoutingService().getProtocolManager().broadcastLsa(); // FIXME there should always be traffic; check waitForNextMessage(); waitForNetworkSilence(); fullConvercenceForEveryone = true; for (VirtualInstance vi : allInstances) { fullConvercenceForEveryone &= vi.hasSameTopologyHashesForAllNodes(); } // log.info("Iteration " + j + ": " + randomInstance.getFormattedNetworkGraph()); } assertTrue("Networks did not converge after the maximum of " + max + " iterations", j < max); // TODO perform several iterations VirtualInstance sender = instanceUtils.getRandomInstance(allInstances); VirtualInstance receiver = instanceUtils.getRandomInstance(allInstances, sender); prepareWaitForNextMessage(); // log.info(sender.getFormattedNetworkGraph()); assertTrue( sender.performRoutedRequest(uniqueMessageToken, receiver.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT).isSuccess()); waitForNextMessage(); waitForNetworkSilence(); } /** * Assert that instance ids, instance session ids, and logical node ids are not registered as separate nodes. * * @throws InterruptedException not expected */ @Test public void testSingleClientRoutingTopology() throws InterruptedException { VirtualInstance client1 = new VirtualInstance("Client1"); final int shortWaitTime = 100; Thread.sleep(shortWaitTime); // give async registration tasks time to complete assertEquals(1, client1.getRawNetworkGraph().getNodeIds().size()); } /** * @throws Exception on uncaught exceptions */ @Test public void testTwoClientsTalkingToEachOther() throws Exception { VirtualTransportTestConfiguration testConfiguration = new VirtualTransportTestConfiguration(false); NetworkTransportProvider transportProvider = testConfiguration.getTransportProvider(); NetworkContactPointGenerator contactPointGenerator = testConfiguration.getContactPointGenerator(); // create two virtual instances. VirtualInstance client1 = new VirtualInstance("Client1"); VirtualInstance client2 = new VirtualInstance("Client2"); addGlobalTrafficListener(new VirtualInstanceGroup(client1)); addGlobalTrafficListener(new VirtualInstanceGroup(client2)); // add the "virtual" transport method to the connection service client1.registerNetworkTransportProvider(transportProvider); client2.registerNetworkTransportProvider(transportProvider); // create a network contact point and make client 1 available under this contact NetworkContactPoint client1ContactPoint = contactPointGenerator.createContactPoint(); client1.addServerConfigurationEntry(client1ContactPoint); // create a network contact point and make client 2 available under this contact NetworkContactPoint client2ContactPoint = contactPointGenerator.createContactPoint(); client2.addServerConfigurationEntry(client2ContactPoint); MessageChannelLifecycleListener connectionListener = EasyMock.createMock(MessageChannelLifecycleListener.class); // FIXME why does this not work? // connectionListener.onOutgoingConnectionEstablished(EasyMock.createMock(MessageChannel.class)); // connectionListener.onOutgoingConnectionEstablished(EasyMock.createMock(MessageChannel.class)); EasyMock.replay(connectionListener); client1.getMessageChannelService().addChannelLifecycleListener(connectionListener); // network graphs should not be the same assertFalse(instancesHaveSameUnfilteredNetworkGraph(client1, client2)); try { // startup instances client1.start(); client2.start(); } catch (InterruptedException e) { fail("Failed to start virtual instances."); } // network graphs should not be the same assertFalse(instancesHaveSameUnfilteredNetworkGraph(client1, client2)); prepareWaitForNextMessage(); client1.connectAsync(client2ContactPoint); client2.connectAsync(client1ContactPoint); waitForNextMessage(); waitForNetworkSilence(); // network graphs should now be the same assertTrue(instancesHaveSameUnfilteredNetworkGraph(client1, client2)); EasyMock.verify(connectionListener); } /** * TODO This test might not be needed any more. * * @throws Exception on uncaught exceptions */ @SuppressWarnings("unused") // Currently not used code below remains commented in to be sensitive to refactoring. // Dead code warning should be suppressed anyways. @Test public void testAClientTalkingToAServer() throws Exception { // TODO recheck test; hangs after reworking startup mechanism; adapt comment at annotation "SuppressWarnings" above if (true) { // deactivating in JUnit 3 return; } // currently not used code VirtualTransportTestConfiguration testConfiguration = new VirtualTransportTestConfiguration(true); NetworkTransportProvider transportProvider = testConfiguration.getTransportProvider(); NetworkContactPointGenerator contactPointGenerator = testConfiguration.getContactPointGenerator(); // create two virtual instances. VirtualInstance client = new VirtualInstance("The Client"); VirtualInstance server = new VirtualInstance("The Server"); // add the "virtual" transport method to the connection service client.registerNetworkTransportProvider(transportProvider); server.registerNetworkTransportProvider(transportProvider); // create a network contact point and make client 1 available under this contact NetworkContactPoint client1ContactPoint = contactPointGenerator.createContactPoint(); client.addServerConfigurationEntry(client1ContactPoint); // create a network contact point and make client 2 available under this contact NetworkContactPoint serverContactPoint = contactPointGenerator.createContactPoint(); server.addServerConfigurationEntry(serverContactPoint); // client 1 should know client 2 from the beginning // client1.addInitialNetworkPeer(client2ContactPoint); // client 2 should know client 1 from the beginning server.addInitialNetworkPeer(client1ContactPoint); // server.getConnectionService().addConnectionListener((NetworkConnectionListener) new // NetworkRoutingServiceImpl()); // start client.start(); server.start(); try { // client connecting to server MessageChannelService messageChannelService = client.getMessageChannelService(); Future<MessageChannel> connection = messageChannelService.connect(serverContactPoint, true); MessageChannel clientToServer = connection.get(); messageChannelService.registerNewOutgoingChannel(clientToServer); assertEquals( server.getConfigurationService().getInitialNodeInformation().getInstanceNodeSessionId(), clientToServer.getRemoteNodeInformation().getInstanceNodeSessionId()); // send messages client.performRoutedRequest("hello", server.getInstanceNodeSessionId(), DEFAULT_REQUEST_TIMEOUT); } catch (CommunicationException e) { fail(FAILED_TO_CONNECT); } catch (InterruptedException e) { fail(FAILED_TO_CONNECT); } catch (ExecutionException e) { fail(FAILED_TO_CONNECT); } client.shutDown(); server.shutDown(); } /** * TODO This test might not be needed any more. * * Test graph updates with different {@link LinkStateAdvertisement}s. */ @Test @Ignore @Deprecated // TODO old test; review and probably delete - misc_ro, Nov 2013 public void testLsaIntegration1() { VirtualInstance instance1 = new VirtualInstance("The Instance 1", true); VirtualInstance instance2 = new VirtualInstance("The Instance 2", true); final InstanceNodeSessionId instanceSessionId1 = instance1.getInstanceNodeSessionId(); final InstanceNodeSessionId instanceSessionId2 = instance2.getInstanceNodeSessionId(); final InstanceNodeSessionId instanceSessionId3 = NodeIdentifierTestUtils .createTestInstanceNodeSessionIdWithDisplayName("testnode3"); // TODO Not such a nice workaround to get a connection service instance. LinkStateRoutingProtocolManager protocolManager1 = instance1.getRoutingService().getProtocolManager(); // new LinkStateRoutingProtocolManager(new InitialNodeInformationImpl(NODE_1), // instance1.getConnectionService()); LinkStateRoutingProtocolManager protocolManager2 = instance2.getRoutingService().getProtocolManager(); // new LinkStateRoutingProtocolManager(new InitialNodeInformationImpl(NODE_2), // instance2.getConnectionService()); protocolManager1.getTopologyMap().addLink(instanceSessionId1, instanceSessionId3, CONNECTION_ID_1); protocolManager2.getTopologyMap().addLink(instanceSessionId3, instanceSessionId1, CONNECTION_ID_1); assertFalse(protocolManager1.getTopologyMap().equals(protocolManager2.getTopologyMap())); // TODO Write more tests here without using an actual virtual connection. // protocol1.scanNetworkContacts(); // protocol2.scanNetworkContacts(); // protocol1.sendLinkStateAdvertisement(); // protocol2.sendLinkStateAdvertisement(); // assertEquals(protocol1.getNetworkGraph(), protocol2.getNetworkGraph()); } @Override protected TestConfiguration defineTestConfiguration() { return new VirtualTransportTestConfiguration(false); } }