/*
* 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.assertTrue;
import java.util.Arrays;
import java.util.List;
import org.easymock.Capture;
import org.easymock.CaptureType;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import de.rcenvironment.core.communication.channel.MessageChannelState;
import de.rcenvironment.core.communication.channel.ServerContactPoint;
import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils;
import de.rcenvironment.core.communication.configuration.IPWhitelistConnectionFilter;
import de.rcenvironment.core.communication.model.InitialNodeInformation;
import de.rcenvironment.core.communication.model.NetworkContactPoint;
import de.rcenvironment.core.communication.model.NetworkRequest;
import de.rcenvironment.core.communication.model.NetworkResponse;
import de.rcenvironment.core.communication.model.impl.InitialNodeInformationImpl;
import de.rcenvironment.core.communication.model.impl.NetworkResponseImpl;
import de.rcenvironment.core.communication.protocol.MessageMetaData;
import de.rcenvironment.core.communication.protocol.NetworkRequestFactory;
import de.rcenvironment.core.communication.protocol.ProtocolConstants;
import de.rcenvironment.core.communication.testutils.AbstractTransportBasedTest;
import de.rcenvironment.core.communication.transport.spi.BrokenMessageChannelListener;
import de.rcenvironment.core.communication.transport.spi.MessageChannel;
import de.rcenvironment.core.communication.transport.spi.MessageChannelEndpointHandler;
import de.rcenvironment.core.communication.transport.spi.MessageChannelResponseHandler;
import de.rcenvironment.core.communication.utils.MessageUtils;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
/**
* A common base class that defines common tests to verify proper transport operation. Subclasses implement
* {@link #defineTestConfiguration()} to create a transport-specific test.
*
* @author Robert Mischke
*/
public abstract class AbstractTransportLowLevelTest extends AbstractTransportBasedTest {
private static final int DEFAULT_REQUEST_TIMEOUT = 1000;
private static final long WAIT_FOR_INBOUND_CHANNEL_CLOSING_EVENT_MSEC = 1000;
/**
* This sets a conservative timeout for all derived tests; individual test may set stricter timeouts. - misc_ro
*/
@Rule
public Timeout globalTimeout = new Timeout(DEFAULT_SAFEGUARD_TEST_TIMEOUT);
/**
* This test verifies the basic functions of a network transport. It covers {@link ServerContactPoint} startup, connection initiation,
* initial node information handshake, and the basic request/response loop.
*
* @author Robert Mischke
* @throws Exception on uncaught exceptions
*/
@Test
public void basicTransportOperation() throws Exception {
int messageRepetitions = 10;
// create mock server config
InitialNodeInformationImpl mockServerNodeInformation =
new InitialNodeInformationImpl(NodeIdentifierTestUtils.createTestInstanceNodeSessionId());
mockServerNodeInformation.setDisplayName("Mock Server");
// configure mock endpoint handler
MessageChannelEndpointHandler serverEndpointHandler = EasyMock.createMock(MessageChannelEndpointHandler.class);
// configure handshake response
EasyMock.expect(serverEndpointHandler.exchangeNodeInformation(EasyMock.anyObject(InitialNodeInformation.class))).andReturn(
mockServerNodeInformation);
// expect passive connection event (if applicable)
if (transportProvider.supportsRemoteInitiatedConnections()) {
serverEndpointHandler.onRemoteInitiatedChannelEstablished(EasyMock.anyObject(MessageChannel.class),
EasyMock.anyObject(ServerContactPoint.class));
}
EasyMock.replay(serverEndpointHandler);
BrokenMessageChannelListener brokenConnectionListener = EasyMock.createMock(BrokenMessageChannelListener.class);
EasyMock.replay(brokenConnectionListener);
// create server contact point
NetworkContactPoint ncp = contactPointGenerator.createContactPoint();
IPWhitelistConnectionFilter ipFilter = new IPWhitelistConnectionFilter();
// allow test connections from IPv4 localhost; adapt if necessary
ipFilter.configure(Arrays.asList(new String[] { "127.0.0.1" }));
ServerContactPoint scp =
new ServerContactPoint(transportProvider, ncp, ProtocolConstants.PROTOCOL_COMPATIBILITY_VERSION, serverEndpointHandler,
ipFilter);
// start it
assertFalse(scp.isAcceptingMessages());
scp.start();
assertTrue(scp.isAcceptingMessages());
// create mock client config
InitialNodeInformationImpl clientNodeInformation =
new InitialNodeInformationImpl(NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("clientNode"));
// connect
// (allows duplex connections, but omits client endpoint handler as it should not be used)
MessageChannel channel =
transportProvider.connect(ncp, clientNodeInformation, ProtocolConstants.PROTOCOL_COMPATIBILITY_VERSION, true, null,
brokenConnectionListener);
assertEquals(MessageChannelState.ESTABLISHED, channel.getState());
assertTrue(channel.isReadyToUse());
// verify server side of handshake
EasyMock.verify(serverEndpointHandler);
// verify client side of handshake
assertNotNull(channel.getChannelId());
assertNotNull(channel.getRemoteNodeInformation());
assertEquals(mockServerNodeInformation, channel.getRemoteNodeInformation());
logThreadPoolState("after connecting");
// define server response behavior
final String requestString = "Hi world";
final String responseSuffix = "#response"; // arbitrary
EasyMock.reset(serverEndpointHandler);
serverEndpointHandler.onRawRequestReceived(EasyMock.isA(NetworkRequest.class), EasyMock.isA(String.class));
EasyMock.expectLastCall().andAnswer(new IAnswer<NetworkResponse>() {
@Override
public NetworkResponse answer() throws Throwable {
try {
NetworkRequest request = (NetworkRequest) EasyMock.getCurrentArguments()[0];
String responseString = request.getDeserializedContent().toString() + responseSuffix;
byte[] responseBytes = MessageUtils.serializeSafeObject(responseString);
return new NetworkResponseImpl(responseBytes, MessageMetaData.create().getInnerMap());
} catch (RuntimeException e) {
log.warn("RTE in mock", e);
return null;
}
}
}).times(messageRepetitions);
// set up mock client response handler
MessageChannelResponseHandler responseHandler = EasyMock.createMock(MessageChannelResponseHandler.class);
// define expected callback
Capture<NetworkResponse> responseCapture = new Capture<NetworkResponse>(CaptureType.ALL);
responseHandler.onResponseAvailable(EasyMock.capture(responseCapture));
EasyMock.expectLastCall().times(messageRepetitions);
// enter test mode
EasyMock.replay(serverEndpointHandler, responseHandler);
// send request(s)
for (int i = 0; i < messageRepetitions; i++) {
byte[] contentBytes = MessageUtils.serializeSafeObject(requestString);
// note: message type is arbitrary, but must be valid
NetworkRequest request =
NetworkRequestFactory.createNetworkRequest(contentBytes, ProtocolConstants.VALUE_MESSAGE_TYPE_HEALTH_CHECK,
clientNodeInformation.getInstanceNodeSessionId(),
mockServerNodeInformation.getInstanceNodeSessionId());
channel.sendRequest(request, responseHandler, DEFAULT_REQUEST_TIMEOUT);
}
// TODO improve; quick&dirty hack to test larger message repetition counts
Thread.sleep(testConfiguration.getDefaultTrafficWaitTimeout() + messageRepetitions * 10);
// first, verify that the endpoint handler was called
EasyMock.verify(serverEndpointHandler);
// then, verify that the response handler was called
EasyMock.verify(responseHandler);
// verify response content
List<NetworkResponse> responses = responseCapture.getValues();
assertEquals(messageRepetitions, responses.size());
for (NetworkResponse response : responses) {
assertEquals(requestString + responseSuffix, response.getDeserializedContent());
}
// close channel and verify that the remote endpoint handler is notified
EasyMock.reset(serverEndpointHandler);
serverEndpointHandler.onInboundChannelClosing(channel.getChannelId());
EasyMock.replay(serverEndpointHandler);
channel.close();
Thread.sleep(WAIT_FOR_INBOUND_CHANNEL_CLOSING_EVENT_MSEC);
EasyMock.verify(serverEndpointHandler);
scp.shutDown();
assertFalse(scp.isAcceptingMessages());
logThreadPoolState("after server shutdown");
}
private void logThreadPoolState(String timeDescription) {
log.debug("Thread pool state " + timeDescription + ":\n"
+ ConcurrencyUtils.getThreadPoolManagement().getFormattedStatistics(true));
}
}