Introducing the Modification classses
[controller.git] / opendaylight / md-sal / remoterpc-routingtable / implementation / src / test / java / org / opendaylight / controller / sal / connector / remoterpc / impl / RoutingTableImplTest.java
index 50460d4e5ec897892a9a507a1b48244098887c86..1ff49c1543feb3c59b81c027eac81e47f19b3a02 100644 (file)
 
 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();
+                    }
+                }
+            }
+        };
+    }
 }