/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.Map;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import de.rcenvironment.core.communication.api.CommunicationService;
import de.rcenvironment.core.communication.api.PlatformService;
import de.rcenvironment.core.communication.common.IdentifierException;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.common.LogicalNodeSessionId;
import de.rcenvironment.core.communication.common.NetworkGraph;
import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils;
import de.rcenvironment.core.communication.common.NodeIdentifierUtils;
import de.rcenvironment.core.communication.common.ResolvableNodeId;
import de.rcenvironment.core.communication.management.CommunicationManagementService;
import de.rcenvironment.core.communication.model.internal.NetworkGraphImpl;
import de.rcenvironment.core.communication.routing.NetworkRoutingService;
import de.rcenvironment.core.communication.routing.internal.LinkStateRoutingProtocolManager;
import de.rcenvironment.core.communication.rpc.api.RemotableCallbackService;
import de.rcenvironment.core.communication.rpc.internal.OSGiLocalServiceResolver;
import de.rcenvironment.core.communication.rpc.spi.ServiceProxyFactory;
import de.rcenvironment.core.communication.testutils.CommunicationServiceDefaultStub;
import de.rcenvironment.core.communication.testutils.PlatformServiceDefaultStub;
import de.rcenvironment.core.utils.common.ServiceUtils;
/**
* Test cases for the {@link CommunicationServiceImpl}.
*
* @author Doreen Seider
* @author Robert Mischke (adaptations)
*/
public class CommunicationServiceImplTest {
private static final int SAFETY_NET_TEST_TIMEOUT = 10000;
private CommunicationServiceImpl communicationService;
private BundleContext contextMock;
private final InstanceNodeSessionId instanceSessionIdLocal =
NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("local");
private final InstanceNodeSessionId instanceSessionIdRemote =
NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("remote");
private final InstanceNodeSessionId instanceSessionIdNotReachable =
NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("notReachable");
// arbitrary test service interface; must be annotated with @RemotableService to pass the service publication tests
private final Class<?> iface = RemotableCallbackService.class;
private final RemotableCallbackService serviceInstance = EasyMock.createNiceMock(RemotableCallbackService.class);
private final Map<String, String> serviceProperties = new HashMap<String, String>();
private LiveNetworkIdResolutionServiceImpl idResolutionService;
/**
* Setup.
*
* @throws InvalidSyntaxException on uncaught exceptions
*/
@Before
// suppress warnings from generics mocking; TODO check if this can be solved better
@SuppressWarnings({ "rawtypes", "unchecked" })
public void setUp() throws InvalidSyntaxException {
serviceProperties.put("piti", "platsch");
communicationService = new CommunicationServiceImpl();
communicationService.bindServiceProxyFactory(new CustomServiceProxyFactoryMock());
DummyPlatformServiceLocal platformServiceMock = new DummyPlatformServiceLocal();
communicationService.bindPlatformService(platformServiceMock);
communicationService.bindCommunicationManagementService(EasyMock.createMock(CommunicationManagementService.class));
NetworkRoutingService routingServiceMock = new CustomNetworkRoutingServiceMock(platformServiceMock.getLocalInstanceNodeSessionId());
communicationService.bindNetworkRoutingService(routingServiceMock);
idResolutionService = new LiveNetworkIdResolutionServiceImpl();
communicationService.bindLiveNetworkIdResolutionService(idResolutionService);
contextMock = EasyMock.createNiceMock(BundleContext.class);
ServiceReference ifaceReferenceMock = EasyMock.createNiceMock(ServiceReference.class);
EasyMock.expect(contextMock.getServiceReference(iface.getName())).andReturn(ifaceReferenceMock).anyTimes();
// for OSGiLocalServiceResolver compatibility, which uses this call pattern
EasyMock.expect(contextMock.getServiceReferences(iface.getName(), null)).andReturn(new ServiceReference[] { ifaceReferenceMock })
.anyTimes();
ServiceReference[] referencesDummy = new ServiceReference[2];
referencesDummy[0] = ifaceReferenceMock;
EasyMock.expect(contextMock.getServiceReferences(iface.getName(),
ServiceUtils.constructFilter(serviceProperties))).andReturn(referencesDummy).anyTimes();
EasyMock.expect(contextMock.getService(ifaceReferenceMock)).andReturn(serviceInstance).anyTimes();
ServiceReference platformReferenceMock = EasyMock.createNiceMock(ServiceReference.class);
EasyMock.expect(contextMock.getServiceReference(PlatformService.class.getName())).andReturn(platformReferenceMock).anyTimes();
EasyMock.expect(contextMock.getService(platformReferenceMock)).andReturn(new DummyPlatformServiceLocal()).anyTimes();
EasyMock.replay(contextMock, serviceInstance);
OSGiLocalServiceResolver localServiceResolverAdapter = new OSGiLocalServiceResolver(2); // reduce retry count to 2
localServiceResolverAdapter.activate(contextMock);
communicationService.bindLocalServiceResolver(localServiceResolverAdapter);
LiveNetworkIdResolutionServiceImpl liveNetworkIdResolver = new LiveNetworkIdResolutionServiceImpl();
communicationService.bindLiveNetworkIdResolutionService(liveNetworkIdResolver);
communicationService.activate();
// register the ids "observed" in the network; this must be done after activate() as the local id is reserved there
liveNetworkIdResolver.registerInstanceNodeSessionId(instanceSessionIdLocal);
liveNetworkIdResolver.registerInstanceNodeSessionId(instanceSessionIdRemote);
}
/** Test. */
@Test(timeout = SAFETY_NET_TEST_TIMEOUT)
public void testGetPlatforms() {
// TODO >=3.0.0: add new test for communicationService.getAvailableNodes()? - misc_ro
}
/**
* Test.
*
* @throws Exception if an error occur.
**/
@Test
public void testGetService() throws Exception {
Object service = communicationService.getRemotableService(iface, instanceSessionIdLocal);
// TODO disabled for now, as local services are proxied now as well, but the proxy is created internally - misc_ro
// assertEquals(serviceInstance, service);
// register the remote session id as known/seen on the routing level
idResolutionService.registerInstanceNodeSessionId(instanceSessionIdRemote);
service = communicationService.getRemotableService(iface, instanceSessionIdRemote);
assertEquals(serviceInstance, service);
try {
communicationService.getRemotableService(iface, null);
fail("Exception expected on null node id");
} catch (RuntimeException e) {
assertTrue(e instanceof IllegalArgumentException);
}
// TODO review: does it make sense to run this twice?
service = communicationService.getRemotableService(iface, instanceSessionIdLocal);
// see above
// assertEquals(serviceInstance, service);
service = communicationService.getRemotableService(iface, instanceSessionIdRemote);
assertEquals(serviceInstance, service);
try {
communicationService.getRemotableService(iface, null);
fail("Exception expected on null node id");
} catch (RuntimeException e) {
assertTrue(e instanceof IllegalArgumentException);
}
}
/**
* Test.
*
* @throws IdentifierException not expected
*/
@Test(expected = IllegalStateException.class)
public void testGetServiceIfServiceRefArrayIsEmpty() throws IdentifierException {
EasyMock.reset(contextMock);
ServiceReference[] referencesDummy = new ServiceReference[0];
try {
EasyMock.expect(contextMock.getServiceReferences(iface.getName(),
ServiceUtils.constructFilter(serviceProperties))).andReturn(referencesDummy).anyTimes();
} catch (InvalidSyntaxException e) {
fail();
}
EasyMock.replay(contextMock);
Object service = communicationService.getRemotableService(iface, instanceSessionIdLocal);
assertEquals(serviceInstance, service);
}
/**
* Test.
*
* @throws IdentifierException not expected
*/
@Test(expected = IllegalStateException.class)
public void testGetServiceIfNoServiceAvailable() throws IdentifierException {
EasyMock.reset(contextMock);
try {
EasyMock.expect(contextMock.getServiceReferences(iface.getName(),
ServiceUtils.constructFilter(serviceProperties))).andReturn(null).anyTimes();
} catch (InvalidSyntaxException e) {
fail();
}
EasyMock.replay(contextMock);
Object service = communicationService.getRemotableService(iface, instanceSessionIdLocal);
assertEquals(serviceInstance, service);
}
/**
* Test.
*
* @throws IdentifierException not expected
*/
@Test(expected = IllegalStateException.class)
public void testGetServiceIfServiceRefIsNull() throws IdentifierException {
EasyMock.reset(contextMock);
EasyMock.expect(contextMock.getServiceReference(iface.getName())).andReturn(null).anyTimes();
EasyMock.replay(contextMock);
Object service = communicationService.getRemotableService(iface, instanceSessionIdLocal);
assertEquals(serviceInstance, service);
}
/**
* Test.
*
* @throws IdentifierException not expected
*/
@Test(expected = IllegalStateException.class)
public void testGetServiceIfServiceIsNull() throws IdentifierException {
EasyMock.reset(contextMock);
ServiceReference referenceMock = EasyMock.createNiceMock(ServiceReference.class);
EasyMock.expect(contextMock.getServiceReference(iface.getName())).andReturn(referenceMock).anyTimes();
EasyMock.replay(contextMock);
Object service = communicationService.getRemotableService(iface, instanceSessionIdLocal);
assertEquals(serviceInstance, service);
}
/**
* Dummy Remote Service Handler.
*
* @author Doreen Seider
*/
@SuppressWarnings("serial")
private class CustomServiceProxyFactoryMock implements ServiceProxyFactory {
@SuppressWarnings("unchecked")
private <T> T createNullService(final Class<T> clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] parameters) throws Throwable {
throw new UndeclaredThrowableException(new RuntimeException("Service not available"));
}
});
}
@Override
public Object createServiceProxy(ResolvableNodeId rawNodeId, Class<?> serviceIface, Class<?>[] ifaces) {
Object service = null;
LogicalNodeSessionId nodeId;
try {
nodeId = idResolutionService.resolveToLogicalNodeSessionId(rawNodeId);
} catch (IdentifierException e) {
throw NodeIdentifierUtils.wrapIdentifierException(e); // should not happen in test environment
}
if (nodeId.isSameInstanceNodeSessionAs(instanceSessionIdLocal) && serviceIface == PlatformService.class) {
service = new DummyPlatformServiceLocal();
} else if (nodeId.isSameInstanceNodeSessionAs(instanceSessionIdRemote) && serviceIface == PlatformService.class) {
service = new DummyPlatformServiceRemote();
} else if (nodeId.isSameInstanceNodeSessionAs(instanceSessionIdNotReachable) && serviceIface == PlatformService.class) {
service = createNullService(PlatformService.class);
} else if (nodeId.isSameInstanceNodeSessionAs(instanceSessionIdLocal) && serviceIface == CommunicationService.class) {
service = new DummyCommunicationService();
} else if (nodeId.isSameInstanceNodeSessionAs(instanceSessionIdRemote) && serviceIface == CommunicationService.class) {
service = new DummyBrokenCommunicationService();
} else if (nodeId.isSameInstanceNodeSessionAs(instanceSessionIdNotReachable) && serviceIface == CommunicationService.class) {
service = createNullService(CommunicationService.class);
} else if (nodeId.isSameInstanceNodeSessionAs(instanceSessionIdRemote) && serviceIface == iface) {
service = serviceInstance;
}
return service;
}
}
/**
* Custom mock implementation of {@link NetworkRoutingService}.
*
* TODO add default stub and subclass it
*
* @author Robert Mischke
*/
private class CustomNetworkRoutingServiceMock implements NetworkRoutingService {
private NetworkGraphImpl networkGraph;
CustomNetworkRoutingServiceMock(InstanceNodeSessionId ownNodeId) {
networkGraph = new NetworkGraphImpl(ownNodeId);
}
@Override
public NetworkGraph getRawNetworkGraph() {
return networkGraph;
}
@Override
public NetworkGraph getReachableNetworkGraph() {
return networkGraph;
}
@Override
public String getFormattedNetworkInformation(String type) {
return null;
}
@Override
public LinkStateRoutingProtocolManager getProtocolManager() {
return null;
}
}
/**
* Dummy local platform service.
*
* @author Doreen Seider
*/
private class DummyPlatformServiceLocal extends PlatformServiceDefaultStub {
@Override
public InstanceNodeSessionId getLocalInstanceNodeSessionId() {
return instanceSessionIdLocal;
}
@Override
public boolean matchesLocalInstance(ResolvableNodeId nodeId) {
if (nodeId == instanceSessionIdLocal) {
return true;
}
return false;
}
}
/**
* Dummy local platform service.
*
* @author Doreen Seider
*/
private class DummyPlatformServiceLocal2 extends PlatformServiceDefaultStub {
@Override
public InstanceNodeSessionId getLocalInstanceNodeSessionId() {
return instanceSessionIdRemote;
}
@Override
public boolean matchesLocalInstance(ResolvableNodeId nodeId) {
if (nodeId == instanceSessionIdRemote) {
return true;
}
return false;
}
}
/**
* Dummy remote platform service.
*
* @author Doreen Seider
*/
private class DummyPlatformServiceRemote extends PlatformServiceDefaultStub {
@Override
public InstanceNodeSessionId getLocalInstanceNodeSessionId() {
return instanceSessionIdRemote;
}
@Override
public boolean matchesLocalInstance(ResolvableNodeId nodeId) {
return false;
}
}
/**
* Dummy implementation of {@link CommunicationService}.
*
* @author Doreen Seider
*/
private class DummyCommunicationService extends CommunicationServiceDefaultStub {
}
/**
* Dummy implementation of {@link CommunicationService}.
*
* @author Doreen Seider
*/
private class DummyBrokenCommunicationService extends CommunicationServiceDefaultStub {
}
}