/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.communication.testutils.templates; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeoutException; import org.junit.BeforeClass; import org.junit.Test; import de.rcenvironment.core.communication.common.InstanceNodeSessionId; import de.rcenvironment.core.communication.configuration.NodeConfigurationService; import de.rcenvironment.core.communication.connection.api.ConnectionSetup; import de.rcenvironment.core.communication.connection.api.ConnectionSetupService; import de.rcenvironment.core.communication.connection.api.ConnectionSetupState; import de.rcenvironment.core.communication.connection.api.DisconnectReason; import de.rcenvironment.core.communication.connection.impl.ConnectionSetupServiceImpl; import de.rcenvironment.core.communication.model.NetworkResponse; import de.rcenvironment.core.communication.nodeproperties.NodePropertyConstants; import de.rcenvironment.core.communication.protocol.ProtocolConstants; import de.rcenvironment.core.communication.routing.internal.NetworkFormatter; import de.rcenvironment.core.communication.testutils.AbstractVirtualInstanceTest; import de.rcenvironment.core.communication.testutils.ConnectionSetupStateTracker; import de.rcenvironment.core.communication.testutils.TestNetworkRequestHandler; import de.rcenvironment.core.communication.testutils.VirtualInstance; import de.rcenvironment.core.communication.testutils.VirtualInstanceGroup; import de.rcenvironment.core.communication.transport.virtual.testutils.VirtualTopology; import de.rcenvironment.core.configuration.ConfigurationService; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.utils.testing.CommonTestOptions; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncTaskService; /** * Base class providing tests that can operate using duplex as well as non-duplex transports. A common use case is setting up a topology * where are topology links are logially bidirectional. In this case, the test code must adapt as this bidirectional linkage is * automatically achieved when using a duplex transport, but must be explicitly wired when using a non-duplex transport. * * @author Robert Mischke * @author Phillip Kroll */ public abstract class AbstractSwitchableVirtualInstanceTest extends AbstractVirtualInstanceTest { private static final String DEFAULT_SERVER_NODE_ID = "server"; /** * A short wait time for connect/disconnect actions to complete; fairly long to support real transports in stress-test setups. */ private static final int CONNECTION_OPERATION_WAIT_MSEC = 5000; // simulated number of clients for concurrent connection setups testing; 5 for standard, 20 for extended testing private static final int CONCURRENT_CS_TEST_NUM_CLIENTS = CommonTestOptions.selectStandardOrExtendedValue(5, 20); private static final int CONCURRENT_CS_TEST_TIMEOUT_MSEC = 10000; private static final String MESSAGE_INSTANCES_DID_NOT_CONVERGE_AT_ITERATION = "Instances did not converge at iteration index "; // test size 5 for fast/standard testing, 10 for extended testing private static final int TEST_SIZE = CommonTestOptions.selectStandardOrExtendedValue(5, 10); protected final Random randomGenerator = new Random(); /** * @throws Exception on uncaught exceptions */ @BeforeClass // TODO transitional; rework this public static void setTestParameters() throws Exception { testSize = TEST_SIZE; } @Override protected void setupInstances(int numNodes, boolean useDuplexTransport, boolean startInstances) throws InterruptedException { super.setupInstances(numNodes, useDuplexTransport, startInstances); // this causes all connect() calls to explicitly create a connection for each direction by default testTopology.setConnectBothDirectionsByDefaultFlag(!usingDuplexTransport); } /** * @throws Exception on uncaught exceptions */ @Test public void testClientServerBidirectionalMessaging() throws Exception { setupInstances(2, usingDuplexTransport, true); VirtualInstance client = testTopology.getInstance(0); VirtualInstance server = testTopology.getInstance(1); prepareWaitForNextMessage(); testTopology.connect(0, 1); waitForNextMessage(); waitForNetworkSilence(); NetworkResponse serverResponse = client.performRoutedRequest("c2s", ProtocolConstants.VALUE_MESSAGE_TYPE_TEST, server.getInstanceNodeSessionId()); assertNotNull("C2S communication failed", serverResponse); assertTrue("C2S communication failed: " + NetworkFormatter.networkResponseToString(serverResponse), serverResponse.isSuccess()); assertEquals(TestNetworkRequestHandler.getTestResponse("c2s", server.getInstanceNodeSessionId()), serverResponse.getDeserializedContent()); NetworkResponse clientResponse = server.performRoutedRequest("s2c", ProtocolConstants.VALUE_MESSAGE_TYPE_TEST, client.getInstanceNodeSessionId()); assertNotNull("S2C communication failed", clientResponse); assertTrue("S2C communication failed", clientResponse.isSuccess()); assertEquals(TestNetworkRequestHandler.getTestResponse("s2c", client.getInstanceNodeSessionId()), clientResponse.getDeserializedContent()); // Systemx.out.println(NetworkFormatter.summary(client.getTopologyMap())); // Systemx.out.println(NetworkFormatter.summary(server.getTopologyMap())); prepareWaitForNextMessage(); testTopology.getAsGroup().shutDown(); waitForNextMessage(); waitForNetworkSilence(); } /** * @throws Exception on uncaught exceptions */ @Test public void testIterativelyGrowingLinearNetwork() throws Exception { setupInstances(TEST_SIZE, usingDuplexTransport, true); for (int i = 0; i < allInstances.length - 1; i++) { logIteration(i); int newInstanceIndex = i + 1; prepareWaitForNextMessage(); testTopology.connect(i, newInstanceIndex); waitForNextMessage(); waitForNetworkSilence(); // note: the third parameter in Arrays.copyOfRange() is exclusive and must be "last + 1" assertTrue(MESSAGE_INSTANCES_DID_NOT_CONVERGE_AT_ITERATION + i, instanceUtils.allInstancesHaveSameRawNetworkGraph(Arrays.copyOfRange(allInstances, 0, newInstanceIndex + 1))); } } /** * @throws Exception on uncaught exceptions */ @Test public void testConcurrentlyGrowingLinearNetwork() throws Exception { setupInstances(TEST_SIZE, usingDuplexTransport, true); prepareWaitForNextMessage(); for (int i = 0; i < allInstances.length - 1; i++) { testTopology.connect(i, i + 1); } waitForNextMessage(); waitForNetworkSilence(); assertTrue(testTopology.allInstancesConverged()); } /** * @throws Exception on uncaught exceptions */ @Test public void testIterativelyGrowingRandomAcyclicNetwork() throws Exception { setupInstances(TEST_SIZE, usingDuplexTransport, true); Random random = new Random(); for (int i = 0; i < allInstances.length - 1; i++) { logIteration(i); int newInstanceIndex = i + 1; int connectedInstanceIndex = random.nextInt(newInstanceIndex); // 0..newInstanceIndex-1 prepareWaitForNextMessage(); testTopology.connect(connectedInstanceIndex, newInstanceIndex); waitForNextMessage(); waitForNetworkSilence(); // note: the third parameter in Arrays.copyOfRange() is exclusive and must be "last + 1" assertTrue(MESSAGE_INSTANCES_DID_NOT_CONVERGE_AT_ITERATION + i, instanceUtils.allInstancesHaveSameRawNetworkGraph(Arrays.copyOfRange(allInstances, 0, newInstanceIndex + 1))); } } /** * @throws Exception on uncaught exceptions */ @Test public void testIterativelyGrowingRandomCyclicNetwork() throws Exception { setupInstances(TEST_SIZE, usingDuplexTransport, true); Random random = new Random(); for (int i = 0; i < allInstances.length - 1; i++) { logIteration(i); int newInstanceIndex = i + 1; int connectedInstanceIndex1 = random.nextInt(newInstanceIndex); // 0..newInstanceIndex-1 prepareWaitForNextMessage(); testTopology.connect(connectedInstanceIndex1, newInstanceIndex); // connect to two existing nodes per iteration to create cycles if (i >= 3) { // do not start at i=2, otherwise the starting graph will always be the same int connectedInstanceIndex2; do { connectedInstanceIndex2 = random.nextInt(newInstanceIndex); // 0..i } while (connectedInstanceIndex2 == connectedInstanceIndex1); testTopology.connect(connectedInstanceIndex2, newInstanceIndex); } waitForNextMessage(); waitForNetworkSilence(); // note: the third parameter in Arrays.copyOfRange() is exclusive and must be "last + 1" assertTrue(MESSAGE_INSTANCES_DID_NOT_CONVERGE_AT_ITERATION + i, instanceUtils.allInstancesHaveSameRawNetworkGraph(Arrays.copyOfRange(allInstances, 0, newInstanceIndex + 1))); } } /** * @throws Exception on uncaught exceptions */ @Test public void testConcurrentlyGrowingRandomAcyclicNetwork() throws Exception { setupInstances(TEST_SIZE, usingDuplexTransport, true); prepareWaitForNextMessage(); Random random = new Random(); for (int i = 0; i < allInstances.length - 1; i++) { logIteration(i); int newInstanceIndex = i + 1; int connectedInstanceIndex = random.nextInt(newInstanceIndex); // 0..i testTopology.connect(connectedInstanceIndex, newInstanceIndex); } waitForNextMessage(); waitForNetworkSilence(); assertTrue(ERROR_MSG_INSTANCES_DID_NOT_CONVERGE, instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances)); } /** * @throws Exception on uncaught exceptions */ @Test public void testConcurrentlyGrowingRandomCyclicNetwork() throws Exception { setupInstances(TEST_SIZE, usingDuplexTransport, true); prepareWaitForNextMessage(); Random random = new Random(); for (int i = 0; i < allInstances.length - 1; i++) { logIteration(i); int newInstanceIndex = i + 1; int connectedInstanceIndex1 = random.nextInt(newInstanceIndex); // 0..i testTopology.connect(connectedInstanceIndex1, newInstanceIndex); // connect to two existing nodes per iteration to create cycles if (i >= 3) { // do not start at i=2, otherwise the starting graph will always be the same int connectedInstanceIndex2; do { connectedInstanceIndex2 = random.nextInt(newInstanceIndex); // 0..i } while (connectedInstanceIndex2 == connectedInstanceIndex1); testTopology.connect(connectedInstanceIndex2, newInstanceIndex); } } waitForNextMessage(); waitForNetworkSilence(); assertTrue(ERROR_MSG_INSTANCES_DID_NOT_CONVERGE, instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances)); } /** * @throws Exception on uncaught exceptions */ @Test public void testNodePropertiesInConcurrentlyGrowingRandomNetwork() throws Exception { setupInstances(TEST_SIZE, usingDuplexTransport, true); prepareWaitForNextMessage(); Random random = new Random(); for (int i = 0; i < allInstances.length - 1; i++) { logIteration(i); int newInstanceIndex = i + 1; int connectedInstanceIndex = random.nextInt(newInstanceIndex); // 0..i testTopology.connect(connectedInstanceIndex, newInstanceIndex); } waitForNextMessage(); waitForNetworkSilence(); assertTrue(ERROR_MSG_INSTANCES_DID_NOT_CONVERGE, instanceUtils.allInstancesHaveSameRawNetworkGraph(allInstances)); // check for consistently converged properties on all nodes for (VirtualInstance vi : allInstances) { Map<InstanceNodeSessionId, Map<String, String>> allNodeProperties = vi.getNodePropertiesService().getAllNodeProperties(); assertEquals(allInstances.length, allNodeProperties.size()); // each node should see proper "nodeId" properties fields for each node for (VirtualInstance vi2 : allInstances) { InstanceNodeSessionId nodeId = vi2.getInstanceNodeSessionId(); Map<String, String> nodeProperties = allNodeProperties.get(nodeId); assertNotNull("No metadata for node " + nodeId + " at " + vi.getInstanceNodeSessionId(), nodeProperties); assertEquals(nodeId.getInstanceNodeSessionIdString(), nodeProperties.get(NodePropertyConstants.KEY_NODE_ID)); } } // inject a metadata value at each node String insertionTestMetadataKey = "insertionTest"; String insertionTestDataSuffix = ".inserted"; prepareWaitForNextMessage(); for (VirtualInstance vi : allInstances) { vi.getNodePropertiesService().addOrUpdateLocalNodeProperty(insertionTestMetadataKey, vi.getInstanceNodeSessionId().getInstanceNodeSessionIdString() + insertionTestDataSuffix); } waitForNextMessage(); waitForNetworkSilence(); // check for consistent *injected* properties on all nodes for (VirtualInstance vi : allInstances) { Map<InstanceNodeSessionId, Map<String, String>> allNodeProperties = vi.getNodePropertiesService().getAllNodeProperties(); assertEquals(allInstances.length, allNodeProperties.size()); // each node should see proper "nodeId" property fields for each node for (VirtualInstance vi2 : allInstances) { InstanceNodeSessionId nodeId = vi2.getInstanceNodeSessionId(); assertEquals(nodeId.getInstanceNodeSessionIdString() + insertionTestDataSuffix, allNodeProperties.get(nodeId).get(insertionTestMetadataKey)); } } // print debug output // Map<NodeIdentifier, Map<String, String>> allNodeProperties = // allInstances[0].getNodePropertiesService().getAllNodeProperties(); // Thread.sleep(500); // for (VirtualInstance vi : allInstances) { // NodeIdentifier nodeId = vi.getNodeId(); // log.info("Metadata knowledge about " + nodeId + ": " + allNodeProperties.get(nodeId)); // } // Thread.sleep(500); } /** * Tests for proper {@link ConnectionSetupService} behaviour. * * @throws Exception on uncaught Exceptions */ @Test(timeout = CONCURRENT_CS_TEST_TIMEOUT_MSEC) public void testConcurrentConnectionSetupBehaviour() throws Exception { final VirtualInstance server = new VirtualInstance(DEFAULT_SERVER_NODE_ID, false); // no relay server.registerNetworkTransportProvider(transportProvider); server.addServerConfigurationEntry(contactPointGenerator.createContactPoint()); server.start(); ConnectionSetupService serverConnectionSetupService = server.getConnectionSetupService(); assertEquals(0, serverConnectionSetupService.getAllConnectionSetups().size()); final List<RuntimeException> exceptions = Collections.synchronizedList(new ArrayList<RuntimeException>()); final CountDownLatch phase1Cdl = new CountDownLatch(CONCURRENT_CS_TEST_NUM_CLIENTS); final CountDownLatch serverShutdownCdl = new CountDownLatch(1); final CountDownLatch phase2Cdl = new CountDownLatch(CONCURRENT_CS_TEST_NUM_CLIENTS); AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); for (int i = 1; i <= CONCURRENT_CS_TEST_NUM_CLIENTS; i++) { final int i2 = i; threadPool.execute(new Runnable() { @Override public void run() { log.debug("Starting thread " + i2); try { VirtualInstance client = new VirtualInstance("client-" + i2); client.registerNetworkTransportProvider(transportProvider); client.start(); log.debug("Created client " + client.getInstanceNodeSessionId()); ConnectionSetupServiceImpl clientConnectionSetupService = (ConnectionSetupServiceImpl) client.getConnectionSetupService(); assertEquals(0, clientConnectionSetupService.getAllConnectionSetups().size()); ConnectionSetup setupC2S = clientConnectionSetupService.createConnectionSetup(server.getDefaultContactPoint(), "test-cs-" + i2, true); assertEquals(1, clientConnectionSetupService.getAllConnectionSetups().size()); ConnectionSetupStateTracker tracker = new ConnectionSetupStateTracker(setupC2S); clientConnectionSetupService.addConnectionSetupListener(tracker); testConnectionSetupBehaviourBeforePotentialServerShutdown(client, server, clientConnectionSetupService, setupC2S, tracker); phase1Cdl.countDown(); // if this is not a duplex test, verify direct connection shutdown if (usingDuplexTransport) { log.debug("Waiting for server shutdown..."); serverShutdownCdl.await(); } else { setupC2S.signalStopIntent(); tracker.awaitAndExpect(ConnectionSetupState.DISCONNECTING, CONNECTION_OPERATION_WAIT_MSEC); } testConnectionSetupBehaviourAfterPotentialServerShutdown(client, server, clientConnectionSetupService, setupC2S, tracker); client.shutDown(); phase2Cdl.countDown(); } catch (InterruptedException | AssertionError | TimeoutException e) { onException(e); } } private void onException(Throwable e) { exceptions.add(new RuntimeException(e)); // let it terminate quickly; it will fail anyway phase1Cdl.countDown(); phase2Cdl.countDown(); } }); } phase1Cdl.await(); assertEquals(0, serverConnectionSetupService.getAllConnectionSetups().size()); if (usingDuplexTransport) { // if this is a duplex test, verify indirect/remote connection shutdown server.shutDown(); serverShutdownCdl.countDown(); } phase2Cdl.await(); if (!usingDuplexTransport) { server.shutDown(); } assertEquals(0, serverConnectionSetupService.getAllConnectionSetups().size()); if (!exceptions.isEmpty()) { for (Throwable e : exceptions) { log.error("Async test exception", e); } fail("Async exceptions have occurred (see log)"); } } private void testConnectionSetupBehaviourBeforePotentialServerShutdown(VirtualInstance client, VirtualInstance server, ConnectionSetupServiceImpl clientConnectionSetupService, ConnectionSetup setupC2S, ConnectionSetupStateTracker tracker) throws InterruptedException, AssertionError, TimeoutException { // check initial, unconnected state assertEquals(1, clientConnectionSetupService.getAllConnectionSetups().size()); assertEquals(setupC2S, clientConnectionSetupService.getAllConnectionSetups().iterator().next()); assertEquals(ConnectionSetupState.DISCONNECTED, setupC2S.getState()); assertNull(setupC2S.getCurrentChannelId()); assertNull(setupC2S.getLastChannelId()); setupC2S.signalStartIntent(); tracker.awaitAndExpect(ConnectionSetupState.CONNECTING, CONNECTION_OPERATION_WAIT_MSEC); tracker.awaitAndExpect(ConnectionSetupState.CONNECTED, CONNECTION_OPERATION_WAIT_MSEC); assertEquals(1, clientConnectionSetupService.getAllConnectionSetups().size()); assertEquals(ConnectionSetupState.CONNECTED, setupC2S.getState()); assertNotNull(setupC2S.getCurrentChannelId()); assertNotNull(setupC2S.getLastChannelId()); String firstChannelId = setupC2S.getCurrentChannelId(); setupC2S.signalStopIntent(); tracker.awaitAndExpect(ConnectionSetupState.DISCONNECTING, CONNECTION_OPERATION_WAIT_MSEC); tracker.awaitAndExpect(ConnectionSetupState.DISCONNECTED, CONNECTION_OPERATION_WAIT_MSEC); assertEquals(ConnectionSetupState.DISCONNECTED, setupC2S.getState()); assertEquals(DisconnectReason.ACTIVE_SHUTDOWN, setupC2S.getDisconnectReason()); assertNull(setupC2S.getCurrentChannelId()); // last channel id should still be present, e.g. for debugging assertNotNull(setupC2S.getLastChannelId()); assertTrue(setupC2S.getLastChannelId().equals(firstChannelId)); // reconnect setupC2S.signalStartIntent(); tracker.awaitAndExpect(ConnectionSetupState.CONNECTING, CONNECTION_OPERATION_WAIT_MSEC); tracker.awaitAndExpect(ConnectionSetupState.CONNECTED, CONNECTION_OPERATION_WAIT_MSEC); assertEquals(ConnectionSetupState.CONNECTED, setupC2S.getState()); assertNotNull(setupC2S.getCurrentChannelId()); assertNotNull(setupC2S.getLastChannelId()); // ensure the connection setup received a new channel id after reconnect assertFalse(setupC2S.getLastChannelId().equals(firstChannelId)); } private void testConnectionSetupBehaviourAfterPotentialServerShutdown(VirtualInstance client, VirtualInstance server, ConnectionSetupServiceImpl clientConnectionSetupService, ConnectionSetup setupC2S, ConnectionSetupStateTracker tracker) throws InterruptedException, AssertionError, TimeoutException { tracker.awaitAndExpect(ConnectionSetupState.DISCONNECTED, CONNECTION_OPERATION_WAIT_MSEC); assertEquals(ConnectionSetupState.DISCONNECTED, setupC2S.getState()); assertNull(setupC2S.getCurrentChannelId()); assertNotNull(setupC2S.getLastChannelId()); if (usingDuplexTransport) { assertEquals(DisconnectReason.REMOTE_SHUTDOWN, setupC2S.getDisconnectReason()); } else { assertEquals(DisconnectReason.ACTIVE_SHUTDOWN, setupC2S.getDisconnectReason()); } } /** * Tests the effect of the {@link NodeConfigurationService#isRelay()} setting (which is read from * {@link ConfigurationService#getIsRelay()} in live instances). * * @throws Exception on uncaught exceptions */ @Test public void testRelayBehaviour() throws Exception { // create instances manually to control the "is relay" settings VirtualInstance c1 = new VirtualInstance("c1", false); // actually irrelevant VirtualInstance c2 = new VirtualInstance("c2", false); VirtualInstance c3 = new VirtualInstance("c3", false); // actually irrelevant VirtualInstance s1 = new VirtualInstance("s1", true); VirtualInstance s2 = new VirtualInstance("s2", true); VirtualInstanceGroup allNodes = new VirtualInstanceGroup(c1, c2, c3, s1, s2); allInstances = allNodes.toArray(); for (VirtualInstance instance : allInstances) { instance.addServerConfigurationEntry(contactPointGenerator.createContactPoint()); } allNodes.registerNetworkTransportProvider(transportProvider); allNodes.addNetworkTrafficListener(getGlobalTrafficListener()); testTopology = new VirtualTopology(allInstances); testTopology.setConnectBothDirectionsByDefaultFlag(!usingDuplexTransport); allNodes.start(); // connect to an "M" shape prepareWaitForNextMessage(); testTopology.connect(0, 3); // c1-s1 testTopology.connect(1, 3); // c2-s1 testTopology.connect(1, 4); // c2-s2 testTopology.connect(2, 4); // c3-s2 waitForNextMessage(); waitForNetworkSilence(); for (VirtualInstance instance : allInstances) { log.debug(NetworkFormatter.networkGraphToGraphviz(instance.getRawNetworkGraph(), true)); } VirtualInstance[][] expectedNonTrivialVisibilities = new VirtualInstance[][] { { c1, s1 }, { c1, c2 }, { c2, c1 }, { c2, s1 }, { c2, s2 }, { c2, c3 }, { c3, c2 }, { c3, s2 }, { s1, c1 }, { s1, c2 }, { s2, c2 }, { s2, c3 } }; // create visibility matrix Map<VirtualInstance, Map<VirtualInstance, Boolean>> visibilityMatrix = new HashMap<VirtualInstance, Map<VirtualInstance, Boolean>>(); for (VirtualInstance i1 : allInstances) { Map<VirtualInstance, Boolean> singleInstanceVisMap = new HashMap<VirtualInstance, Boolean>(); visibilityMatrix.put(i1, singleInstanceVisMap); for (VirtualInstance i2 : allInstances) { // initialize with trivial visibility singleInstanceVisMap.put(i2, (i1 == i2)); } } // add non-trivial visibilities for (VirtualInstance[] entry : expectedNonTrivialVisibilities) { visibilityMatrix.get(entry[0]).put(entry[1], Boolean.TRUE); } // test network graph visibility for (VirtualInstance i1 : allInstances) { for (VirtualInstance i2 : allInstances) { boolean expected = visibilityMatrix.get(i1).get(i2); assertEquals("Raw network graph visibility: " + i1.getInstanceNodeSessionId() + " ->" + i2.getInstanceNodeSessionId(), expected, i1.getRawNetworkGraph().getNodeIds().contains(i2.getInstanceNodeSessionId())); } } // verify expected visible node counts (as a cross-check) Set<InstanceNodeSessionId> c1VisibleNodes = c1.getRawNetworkGraph().getNodeIds(); Set<InstanceNodeSessionId> c2VisibleNodes = c2.getRawNetworkGraph().getNodeIds(); Set<InstanceNodeSessionId> c3VisibleNodes = c3.getRawNetworkGraph().getNodeIds(); Set<InstanceNodeSessionId> s1VisibleNodes = s1.getRawNetworkGraph().getNodeIds(); Set<InstanceNodeSessionId> s2VisibleNodes = s2.getRawNetworkGraph().getNodeIds(); assertEquals(3, c1VisibleNodes.size()); assertEquals(3, s1VisibleNodes.size()); assertEquals(5, c2VisibleNodes.size()); assertEquals(3, c3VisibleNodes.size()); assertEquals(3, s2VisibleNodes.size()); // test messaging for (VirtualInstance i1 : allInstances) { for (VirtualInstance i2 : allInstances) { // local-to-local routing is not allowed, so do not test it if (i1 == i2) { continue; } boolean expected = visibilityMatrix.get(i1).get(i2); // perform a standard routed request; while this is a good test of the "positive" allowed routing paths, // the "negative" (forbidden) paths are not really tested as the network graph should simply not contain // the target node, which was already tested above - misc_ro // TODO add test that non-relays actually refuse to forward requests NetworkResponse response = i1.performRoutedRequest("test", ProtocolConstants.VALUE_MESSAGE_TYPE_TEST, i2.getInstanceNodeSessionId()); boolean success = response.isSuccess(); assertEquals("Messaging result: " + i1.getInstanceNodeSessionId() + " ->" + i2.getInstanceNodeSessionId(), expected, success); } } // test node property visibilty; for (VirtualInstance i1 : allInstances) { Map<InstanceNodeSessionId, Map<String, String>> allNodeProperties = i1.getNodePropertiesService().getAllNodeProperties(); assertEquals("Node " + i1 + " knows a different number of nodes with published properties " + "than its total number known network nodes", i1.getRawNetworkGraph().getNodeCount(), allNodeProperties.size()); // check that all node id properties (simplest differing one to test) are actually visible for (Entry<InstanceNodeSessionId, Map<String, String>> e : allNodeProperties.entrySet()) { InstanceNodeSessionId targetNodeId = e.getKey(); Map<String, String> targetProperties = e.getValue(); assertEquals(targetNodeId.getInstanceNodeSessionIdString(), targetProperties.get(NodePropertyConstants.KEY_NODE_ID)); } } prepareWaitForNextMessage(); allNodes.shutDown(); waitForNextMessage(); waitForNetworkSilence(); } private void logIteration(int i) { log.debug("Starting iteration " + i); } }