package org.opendaylight.controller.sal.connector.remoterpc.impl;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+import java.net.URI;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
import junit.framework.Assert;
+
import org.apache.felix.dm.Component;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
import org.opendaylight.controller.clustering.services.IClusterServices;
import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import java.net.URI;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * @author: syedbahm
- */
public class RoutingTableImplTest {
- private IClusterGlobalServices ics = mock(IClusterGlobalServices.class);
- private RoutingTableImpl rti = new RoutingTableImpl();
-
private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace,"global");
-
- ConcurrentMap concurrentMapMock = mock(ConcurrentMap.class);
+ private final QName QNAME = new QName(namespace, "global");
+
+ private IClusterGlobalServices clusterService;
+ private RoutingTableImpl<RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>, String> routingTable;
+ ConcurrentMap mockGlobalRpcCache;
+ ConcurrentMap mockRpcCache;
+
+ @Before
+ public void setUp() throws Exception{
+ clusterService = mock(IClusterGlobalServices.class);
+ routingTable = new RoutingTableImpl<RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>, String>();
+ mockGlobalRpcCache = new ConcurrentHashMap<>();
+ mockRpcCache = new ConcurrentHashMap<>();
+ createRoutingTableCache();
+ }
+ @After
+ public void tearDown(){
+ reset(clusterService);
+ mockGlobalRpcCache = null;
+ mockRpcCache = null;
+ }
@Test
- public void testAddGlobalRoute() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ public void addGlobalRoute_ValidArguments_ShouldAdd() throws Exception {
- Assert.assertNotNull(concurrentMap);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
- InstanceIdentifier identifier = mock(InstanceIdentifier.class);
- when(routeIdentifier.getType()).thenReturn(QNAME);
- when(routeIdentifier.getRoute()).thenReturn(identifier);
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+
+ final String expectedRoute = "172.27.12.1:5000";
+ routingTable.addGlobalRoute(routeIdentifier, expectedRoute);
+
+ ConcurrentMap latestCache = routingTable.getGlobalRpcCache();
+ Assert.assertEquals(mockGlobalRpcCache, latestCache);
+ Assert.assertEquals(expectedRoute, latestCache.get(routeIdentifier));
+ }
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ @Test (expected = RoutingTable.DuplicateRouteException.class)
+ public void addGlobalRoute_DuplicateRoute_ShouldThrow() throws Exception{
- Set<String> globalService = new HashSet<String>();
- globalService.add("172.27.12.1:5000");
+ Assert.assertNotNull(mockGlobalRpcCache);
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+ routingTable.addGlobalRoute(routeIdentifier, new String());
+ routingTable.addGlobalRoute(routeIdentifier, new String());
+ }
- Assert.assertEquals(concurrentMap,latestCache);
+ @Test
+ public void getGlobalRoute_ExistingRouteId_ShouldReturnRoute() throws Exception {
- Set<String> servicesGlobal = (Set<String>)latestCache.get(routeIdentifier);
- Assert.assertEquals(servicesGlobal.size(),1);
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+ String expectedRoute = "172.27.12.1:5000";
- Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+ routingTable.addGlobalRoute(routeIdentifier, expectedRoute);
+ String actualRoute = routingTable.getGlobalRoute(routeIdentifier);
+ Assert.assertEquals(expectedRoute, actualRoute);
}
@Test
- public void testGetRoutes() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ public void getGlobalRoute_NonExistentRouteId_ShouldReturnNull() throws Exception {
- Assert.assertNotNull(concurrentMap);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
- InstanceIdentifier identifier = mock(InstanceIdentifier.class);
- when(routeIdentifier.getContext()).thenReturn(QNAME);
- when(routeIdentifier.getRoute()).thenReturn(identifier);
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ String actualRoute = routingTable.getGlobalRoute(routeIdentifier);
+ Assert.assertNull(actualRoute);
+ }
- String globalService = "172.27.12.1:5000";
+ @Test
+ public void removeGlobalRoute_ExistingRouteId_ShouldRemove() throws Exception {
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- Assert.assertEquals(concurrentMap,latestCache);
+ ConcurrentMap cache = routingTable.getGlobalRpcCache();
+ Assert.assertTrue(cache.size() == 0);
+ routingTable.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ Assert.assertTrue(cache.size() == 1);
- Set<String> servicesGlobal = rti.getRoutes(routeIdentifier);
+ routingTable.removeGlobalRoute(routeIdentifier);
+ Assert.assertTrue(cache.size() == 0);
+ }
- Assert.assertEquals(servicesGlobal.size(),1);
- Iterator<String> iterator = servicesGlobal.iterator();
- while(iterator.hasNext()){
- Assert.assertEquals(iterator.next(),"172.27.12.1:5000");
- }
+ @Test
+ public void removeGlobalRoute_NonExistentRouteId_ShouldDoNothing() throws Exception {
+
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+
+ ConcurrentMap cache = routingTable.getGlobalRpcCache();
+ Assert.assertTrue(cache.size() == 0);
+ routingTable.removeGlobalRoute(routeIdentifier);
+ Assert.assertTrue(cache.size() == 0);
}
- @Test
- public void testRegisterRouteChangeListener() throws Exception {
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),0);
- rti.registerRouteChangeListener(new RouteChangeListenerImpl());
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),0); //old should not work
- //what about the new approach - using whiteboard pattern
- rti.setRouteChangeListener(new RouteChangeListenerImpl());
+ @Test
+ public void addRoute_ForNewRouteId_ShouldAddRoute() throws Exception {
+ Assert.assertTrue(mockRpcCache.size() == 0);
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),1); //should not work
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeId = getRouteIdentifier();
+ routingTable.addRoute(routeId, new String());
+ Assert.assertTrue(mockRpcCache.size() == 1);
+ Set<String> routes = routingTable.getRoutes(routeId);
+ Assert.assertEquals(1, routes.size());
}
+
@Test
- public void testRemoveGlobalRoute()throws Exception {
+ public void addRoute_ForExistingRouteId_ShouldAppendRoute() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ Assert.assertTrue(mockRpcCache.size() == 0);
- Assert.assertNotNull(concurrentMap);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
- InstanceIdentifier identifier = mock(InstanceIdentifier.class);
- when(routeIdentifier.getContext()).thenReturn(QNAME);
- when(routeIdentifier.getRoute()).thenReturn(identifier);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeId = getRouteIdentifier();
+
+ String route_1 = "10.0.0.1:5955";
+ String route_2 = "10.0.0.2:5955";
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
- String globalService = "172.27.12.1:5000";
+ Assert.assertTrue(mockRpcCache.size() == 1);
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ Set<String> routes = routingTable.getRoutes(routeId);
+ Assert.assertEquals(2, routes.size());
+ Assert.assertTrue(routes.contains(route_1));
+ Assert.assertTrue(routes.contains(route_2));
+ }
- Assert.assertEquals(concurrentMap,latestCache);
+ @Test
+ public void addRoute_UsingMultipleThreads_ShouldNotOverwrite(){
+ ExecutorService threadPool = Executors.newCachedThreadPool();
+
+ int numOfRoutesToAdd = 100;
+ String routePrefix_1 = "10.0.0.1:555";
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ threadPool.submit(addRoutes(numOfRoutesToAdd, routePrefix_1, routeId));
+ String routePrefix_2 = "10.0.0.1:556";
+ threadPool.submit(addRoutes(numOfRoutesToAdd, routePrefix_2, routeId));
+
+ // wait for all tasks to complete; timeout in 10 sec
+ threadPool.shutdown();
+ try {
+ threadPool.awaitTermination(10, TimeUnit.SECONDS); //
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
- Set<String> servicesGlobal = rti.getRoutes(routeIdentifier);
+ Assert.assertEquals(2*numOfRoutesToAdd, routingTable.getRoutes(routeId).size());
+ }
+ @Test(expected = NullPointerException.class)
+ public void addRoute_NullRouteId_shouldThrowNpe() throws Exception {
- Assert.assertEquals(servicesGlobal.size(),1);
+ routingTable.addRoute(null, new String());
+ }
- Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+ @Test(expected = NullPointerException.class)
+ public void addRoute_NullRoute_shouldThrowNpe() throws Exception{
- rti.removeGlobalRoute(routeIdentifier);
+ routingTable.addRoute(getRouteIdentifier(), null);
+ }
- Assert.assertNotNull(rti.getRoutes(routeIdentifier));
+ @Test (expected = UnsupportedOperationException.class)
+ public void getRoutes_Call_ShouldReturnImmutableCopy() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ routingTable.addRoute(routeId, new String());
+ Set<String> routes = routingTable.getRoutes(routeId); //returns Immutable Set
+ routes.add(new String()); //can not be modified; should throw
}
- private ConcurrentMap createRoutingTableCache() throws Exception {
+ @Test
+ public void getRoutes_With2RoutesFor1RouteId_ShouldReturnASetWithSize2() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ routingTable.addRoute(routeId, "10.0.0.1:5555");
+ routingTable.addRoute(routeId, "10.0.0.2:5555");
- //here init
- Component c = mock(Component.class);
+ Set<String> routes = routingTable.getRoutes(routeId); //returns Immutable Set
- when(ics.existCache(
- RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(false);
+ Assert.assertEquals(2, routes.size());
+ }
- when(ics.createCache(RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(concurrentMapMock);
- rti.setClusterGlobalServices(this.ics);
- rti.init(c);
+ @Test
+ public void getLastAddedRoute_WhenMultipleRoutesExists_ShouldReturnLatestRoute()
+ throws Exception {
- Assert.assertEquals(concurrentMapMock,rti.getRoutingTableCache() );
- return concurrentMapMock;
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
+ String route_2 = "10.0.0.2:5555";
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
+ Assert.assertEquals(route_2, routingTable.getLastAddedRoute(routeId));
}
+ @Test
+ public void removeRoute_WhenMultipleRoutesExist_RemovesGivenRoute() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
+ String route_2 = "10.0.0.2:5555";
+
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
+
+ Assert.assertEquals(2, routingTable.getRoutes(routeId).size());
+
+ routingTable.removeRoute(routeId, route_1);
+ Assert.assertEquals(1, routingTable.getRoutes(routeId).size());
+
+ }
@Test
- public void testCreateRoutingTableCacheReturnExistingCache() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ public void removeRoute_WhenOnlyOneRouteExists_RemovesRouteId() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
- //OK here we should try creating again the cache but this time it should return the existing one
- when(ics.existCache(
- RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(true);
+ routingTable.addRoute(routeId, route_1);
+ Assert.assertEquals(1, routingTable.getRoutes(routeId).size());
- when(ics.getCache(
- RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(concurrentMap);
+ routingTable.removeRoute(routeId, route_1);
+ ConcurrentMap cache = routingTable.getRpcCache();
+ Assert.assertFalse(cache.containsKey(routeId));
+ }
+
+ /*
+ * Private helper methods
+ */
+ private void createRoutingTableCache() throws Exception {
//here init
Component c = mock(Component.class);
- rti.init(c);
+ when(clusterService.existCache(
+ RoutingTableImpl.GLOBALRPC_CACHE)).thenReturn(false);
- Assert.assertEquals(concurrentMap,rti.getRoutingTableCache());
+ when(clusterService.createCache(RoutingTableImpl.GLOBALRPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).
+ thenReturn(mockGlobalRpcCache);
+ when(clusterService.existCache(
+ RoutingTableImpl.RPC_CACHE)).thenReturn(false);
+ when(clusterService.createCache(RoutingTableImpl.RPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).
+ thenReturn(mockRpcCache);
+ doNothing().when(clusterService).tbegin();
+ doNothing().when(clusterService).tcommit();
+ routingTable.setClusterGlobalServices(this.clusterService);
+ routingTable.init(c);
+ Assert.assertEquals(mockGlobalRpcCache, routingTable.getGlobalRpcCache());
+ Assert.assertEquals(mockRpcCache, routingTable.getRpcCache());
}
- private class RouteChangeListenerImpl<I,R> implements RouteChangeListener<I,R>{
-
- @Override
- public void onRouteUpdated(I key, R new_value) {
- //To change body of implemented methods use File | Settings | File Templates.
- }
+ private RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> getRouteIdentifier(){
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
+ InstanceIdentifier identifier = mock(InstanceIdentifier.class);
+ when(routeIdentifier.getType()).thenReturn(QNAME);
+ when(routeIdentifier.getRoute()).thenReturn(identifier);
- @Override
- public void onRouteDeleted(I key) {
- //To change body of implemented methods use File | Settings | File Templates.
- }
+ return routeIdentifier;
}
+ private Runnable addRoutes(final int numRoutes, final String routePrefix, final RpcRouter.RouteIdentifier routeId){
+ return new Runnable() {
+ @Override
+ public void run() {
+ for (int i=0;i<numRoutes;i++){
+ String route = routePrefix + i;
+ try {
+ routingTable.addRoute(routeId, route);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+ }
}