VOL-2901 Unit tests and necessary updates on stats manager
in the scope of VOL-2796

Change-Id: I95d6fd0241d2c54a5a122d58917c6dc45e5abcf7
diff --git a/api/src/main/java/org/opencord/cordmcast/CordMcastStatistics.java b/api/src/main/java/org/opencord/cordmcast/CordMcastStatistics.java
index 6bdb702..10c05af 100644
--- a/api/src/main/java/org/opencord/cordmcast/CordMcastStatistics.java
+++ b/api/src/main/java/org/opencord/cordmcast/CordMcastStatistics.java
@@ -28,9 +28,11 @@
     private String sourceAddress;
     //vlan id used in mcast event.
     private VlanId vlanId;
+    private VlanId innerVlanId;
 
-    public CordMcastStatistics(IpAddress groupAddress, String sourceAddresss, VlanId vlanId) {
+    public CordMcastStatistics(IpAddress groupAddress, String sourceAddresss, VlanId vlanId, VlanId innerVlanId) {
         this.vlanId = vlanId;
+        this.innerVlanId = innerVlanId;
         this.sourceAddress = sourceAddresss;
         this.groupAddress = groupAddress;
     }
@@ -58,4 +60,12 @@
     public void setVlanId(VlanId vlanId) {
         this.vlanId = vlanId;
     }
+
+    public VlanId getInnerVlanId() {
+        return innerVlanId;
+    }
+
+    public void setInnerVlanId(VlanId innerVlanId) {
+        this.innerVlanId = innerVlanId;
+    }
 }
diff --git a/api/src/main/java/org/opencord/cordmcast/CordMcastStatisticsService.java b/api/src/main/java/org/opencord/cordmcast/CordMcastStatisticsService.java
index 3ab6f09..9b9ba71 100644
--- a/api/src/main/java/org/opencord/cordmcast/CordMcastStatisticsService.java
+++ b/api/src/main/java/org/opencord/cordmcast/CordMcastStatisticsService.java
@@ -27,5 +27,11 @@
      * To set current vlanValue in Statistics Service.
      * @param vlanValue current vlan value.
      */
-    public void setVlanValue(VlanId vlanValue);
+    void setVlanValue(VlanId vlanValue);
+
+    /**
+     * To set current innerVlanValue in Statistics Service.
+     * @param innerVlanValue current inner vlan value.
+     */
+    void setInnerVlanValue(VlanId innerVlanValue);
 }
diff --git a/app/src/main/java/org/opencord/cordmcast/impl/CordMcast.java b/app/src/main/java/org/opencord/cordmcast/impl/CordMcast.java
index f1b9429..d107f05 100644
--- a/app/src/main/java/org/opencord/cordmcast/impl/CordMcast.java
+++ b/app/src/main/java/org/opencord/cordmcast/impl/CordMcast.java
@@ -241,7 +241,7 @@
             log.error("Unable to parse configuration parameter for priority", ne);
             priority = DEFAULT_PRIORITY;
         }
-        cordMcastStatisticsService.setVlanValue(assignedVlan());
+        feedStatsServiceWithVlanConfigValues();
     }
 
     @Deactivate
@@ -256,6 +256,14 @@
         log.info("Stopped");
     }
 
+    /**
+     * Updates the stats service with current VLAN config values.
+     */
+    private void feedStatsServiceWithVlanConfigValues() {
+        cordMcastStatisticsService.setVlanValue(assignedVlan());
+        cordMcastStatisticsService.setInnerVlanValue(assignedInnerVlan());
+    }
+
     public void clearGroups() {
         mcastLock();
         try {
@@ -574,6 +582,7 @@
         if (config.egressInnerVlan() != null) {
             mcastInnerVlan = config.egressInnerVlan();
         }
+        feedStatsServiceWithVlanConfigValues();
     }
 
     private class NextKey {
diff --git a/app/src/main/java/org/opencord/cordmcast/impl/CordMcastStatisticsManager.java b/app/src/main/java/org/opencord/cordmcast/impl/CordMcastStatisticsManager.java
index a5715f4..1044adc 100644
--- a/app/src/main/java/org/opencord/cordmcast/impl/CordMcastStatisticsManager.java
+++ b/app/src/main/java/org/opencord/cordmcast/impl/CordMcastStatisticsManager.java
@@ -78,6 +78,7 @@
     private ScheduledExecutorService executor;
 
     private VlanId vlanId;
+    private VlanId innerVlanId;
 
     @Activate
     public void activate(ComponentContext context) {
@@ -119,7 +120,7 @@
         routes.forEach(route -> {
             mcastData.add(new CordMcastStatistics(route.group(),
                     route.source().isEmpty() ? "*" : route.source().get().toString(),
-                    vlanId));
+                    vlanId, innerVlanId));
         });
         return mcastData;
     }
@@ -129,6 +130,11 @@
         vlanId = vlanValue;
     }
 
+    @Override
+    public void setInnerVlanValue(VlanId innerVlanValue) {
+        innerVlanId = innerVlanValue;
+    }
+
     /**
      * pushing mcast stat data as event.
      */
@@ -141,7 +147,9 @@
                     " | Source: " +
                     (mcastStats.getSourceAddress() != null ? mcastStats.getSourceAddress().toString() : "null") +
                     " | Vlan: " +
-                    (mcastStats.getVlanId() != null ? mcastStats.getVlanId().toString() : "null"));
+                    (mcastStats.getVlanId() != null ? mcastStats.getVlanId().toString() : "null") +
+                    " | InnerVlan: " +
+                    (mcastStats.getInnerVlanId() != null ? mcastStats.getInnerVlanId().toString() : "null"));
         });
         post(new CordMcastStatisticsEvent(CordMcastStatisticsEvent.Type.STATUS_UPDATE, routeList));
     }
diff --git a/app/src/test/java/org/opencord/cordmcast/impl/McastTest.java b/app/src/test/java/org/opencord/cordmcast/impl/McastTest.java
index a622e6d..9e64dca 100644
--- a/app/src/test/java/org/opencord/cordmcast/impl/McastTest.java
+++ b/app/src/test/java/org/opencord/cordmcast/impl/McastTest.java
@@ -36,7 +36,9 @@
 import org.onosproject.net.HostId;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.flowobjective.Objective;
 import org.onosproject.store.service.StorageServiceAdapter;
@@ -71,51 +73,7 @@
 
   @Before
   public void setUp() {
-      cordMcast = new CordMcast();
-      cordMcastStatisticsManager = new CordMcastStatisticsManager();
-
-      cordMcast.coreService = new MockCoreService();
-      cordMcast.networkConfig = new TestNetworkConfigRegistry();
-      cordMcast.flowObjectiveService = new MockFlowObjectiveService();
-      cordMcast.mastershipService = new TestMastershipService();
-      cordMcast.deviceService = new MockDeviceService();
-      cordMcast.componentConfigService = new ComponentConfigAdapter();
-      cordMcastStatisticsManager.componentConfigService = new ComponentConfigAdapter();
-      cordMcastStatisticsManager.addListener(mockListener);
-      cordMcast.sadisService = new MockSadisService();
-      cordMcast.cordMcastStatisticsService = cordMcastStatisticsManager;
-
-      cordMcast.storageService = EasyMock.createMock(StorageServiceAdapter.class);
-      expect(cordMcast.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>());
-      replay(cordMcast.storageService);
-
-      Dictionary<String, Object> cfgDict = new Hashtable<String, Object>();
-      cfgDict.put("vlanEnabled", false);
-      cfgDict.put("eventGenerationPeriodInSeconds", EVENT_GENERATION_PERIOD);
-
-      cordMcast.componentConfigService = EasyMock.createNiceMock(ComponentConfigService.class);
-      replay(cordMcast.componentConfigService);
-
-      Set<McastRoute> route1Set = new HashSet<McastRoute>();
-      route1Set.add(route1);
-
-      cordMcast.mcastService = EasyMock.createNiceMock(MulticastRouteService.class);
-      expect(cordMcast.mcastService.getRoutes()).andReturn(Sets.newHashSet());
-      replay(cordMcast.mcastService);
-
-      cordMcastStatisticsManager.mcastService = EasyMock.createNiceMock(MulticastRouteService.class);
-      expect(cordMcastStatisticsManager.mcastService.getRoutes()).andReturn(route1Set).times(2);
-      replay(cordMcastStatisticsManager.mcastService);
-
-      TestUtils.setField(cordMcastStatisticsManager, "eventDispatcher", new TestEventDispatcher());
-
-      ComponentContext componentContext = EasyMock.createMock(ComponentContext.class);
-      expect(componentContext.getProperties()).andReturn(cfgDict).times(2);
-      replay(componentContext);
-      cordMcast.cordMcastStatisticsService = cordMcastStatisticsManager;
-      cordMcastStatisticsManager.activate(componentContext);
-
-      cordMcast.activate(componentContext);
+        //setup operation is handled by init() method in each test
    }
 
     @After
@@ -125,8 +83,61 @@
       nextMap.clear();
     }
 
+    private void init(boolean vlanEnabled, VlanId egressVlan, VlanId egressInnerVlan) {
+        cordMcast = new CordMcast();
+        cordMcastStatisticsManager = new CordMcastStatisticsManager();
+        cordMcast.coreService = new MockCoreService();
+
+        TestNetworkConfigRegistry testNetworkConfigRegistry = new TestNetworkConfigRegistry();
+        testNetworkConfigRegistry.setEgressVlan(egressVlan);
+        testNetworkConfigRegistry.setEgressInnerVlan(egressInnerVlan);
+        cordMcast.networkConfig = testNetworkConfigRegistry;
+
+        cordMcast.flowObjectiveService = new MockFlowObjectiveService();
+        cordMcast.mastershipService = new TestMastershipService();
+        cordMcast.deviceService = new MockDeviceService();
+        cordMcast.componentConfigService = new ComponentConfigAdapter();
+        cordMcastStatisticsManager.componentConfigService = new ComponentConfigAdapter();
+        cordMcastStatisticsManager.addListener(mockListener);
+        cordMcast.sadisService = new MockSadisService();
+        cordMcast.cordMcastStatisticsService = cordMcastStatisticsManager;
+
+        cordMcast.storageService = EasyMock.createMock(StorageServiceAdapter.class);
+        expect(cordMcast.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>());
+        replay(cordMcast.storageService);
+
+        Dictionary<String, Object> cfgDict = new Hashtable<String, Object>();
+        cfgDict.put("vlanEnabled", vlanEnabled);
+        cfgDict.put("eventGenerationPeriodInSeconds", EVENT_GENERATION_PERIOD);
+
+        cordMcast.componentConfigService = EasyMock.createNiceMock(ComponentConfigService.class);
+        replay(cordMcast.componentConfigService);
+
+        Set<McastRoute> route1Set = new HashSet<McastRoute>();
+        route1Set.add(route1);
+
+        cordMcast.mcastService = EasyMock.createNiceMock(MulticastRouteService.class);
+        expect(cordMcast.mcastService.getRoutes()).andReturn(Sets.newHashSet());
+        replay(cordMcast.mcastService);
+
+        cordMcastStatisticsManager.mcastService = EasyMock.createNiceMock(MulticastRouteService.class);
+        expect(cordMcastStatisticsManager.mcastService.getRoutes()).andReturn(route1Set).times(2);
+        replay(cordMcastStatisticsManager.mcastService);
+
+        TestUtils.setField(cordMcastStatisticsManager, "eventDispatcher", new TestEventDispatcher());
+
+        ComponentContext componentContext = EasyMock.createMock(ComponentContext.class);
+        expect(componentContext.getProperties()).andReturn(cfgDict).times(2);
+        replay(componentContext);
+        cordMcast.cordMcastStatisticsService = cordMcastStatisticsManager;
+        cordMcastStatisticsManager.activate(componentContext);
+
+        cordMcast.activate(componentContext);
+    }
+
     @Test
     public void testAddingSinkEvent() throws InterruptedException {
+      init(false, null, null);
 
       Set<ConnectPoint> sinks2Cp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_B));
       Map<HostId, Set<ConnectPoint>> sinks2 = ImmutableMap.of(HOST_ID_NONE, sinks2Cp);
@@ -160,12 +171,109 @@
       IPCriterion ipCriterion = ipAddress(trafficSelector);
       assertNotNull(ipCriterion);
       assertTrue(MULTICAST_IP.equals(ipCriterion.ip().address()));
+      //checking the vlan criterion
+      TrafficSelector meta = forwardMap.get(DEVICE_ID_OF_A).meta();
+      VlanIdCriterion vlanId = vlanId(meta, Criterion.Type.VLAN_VID);
+      assertNull(vlanId); //since vlanEnabled flag is false
+      VlanIdCriterion innerVlanIdCriterion = vlanId(meta, Criterion.Type.INNER_VLAN_VID);
+      assertNull(innerVlanIdCriterion);
+    }
 
+    @Test
+    public void testAddingSinkEventVlanEnabled() throws InterruptedException {
+        // vlanEnabled is set to true and just egressVlan is set
+        init(true, VlanId.vlanId("4000"), null);
+
+        Set<ConnectPoint> sinks2Cp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_B));
+        Map<HostId, Set<ConnectPoint>> sinks2 = ImmutableMap.of(HOST_ID_NONE, sinks2Cp);
+
+        //Adding the details to create different routes
+        previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
+        currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks2);
+        // Creating new mcast event for adding sink
+        McastEvent event = new McastEvent(McastEvent.Type.SINKS_ADDED, previousSubject, currentSubject);
+        cordMcast.listener.event(event);
+        synchronized (forwardMap) {
+            forwardMap.wait(WAIT_TIMEOUT);
+        }
+        // ForwardMap will contain the operation "Add" in the flowObjective. None -> CP_B
+        assertNotNull(forwardMap.get(DEVICE_ID_OF_A));
+        assertTrue(forwardMap.get(DEVICE_ID_OF_A).op() == Objective.Operation.ADD);
+
+        // Output port number will be PORT_B i.e. 16
+        Collection<TrafficTreatment> traffictreatMentCollection =
+                nextMap.get(DEVICE_ID_OF_A).next();
+        assertTrue(1 == traffictreatMentCollection.size());
+        OutputInstruction output = null;
+        for (TrafficTreatment trafficTreatment : traffictreatMentCollection) {
+            output = outputPort(trafficTreatment);
+        }
+        assertNotNull(output);
+        assertTrue(PORT_B == output.port());
+        // Checking the group ip address
+        TrafficSelector trafficSelector = forwardMap.get(DEVICE_ID_OF_A).selector();
+        IPCriterion ipCriterion = ipAddress(trafficSelector);
+        assertNotNull(ipCriterion);
+        assertTrue(MULTICAST_IP.equals(ipCriterion.ip().address()));
+        //checking the vlan criteria
+        TrafficSelector meta = forwardMap.get(DEVICE_ID_OF_A).meta();
+        VlanIdCriterion vlanIdCriterion = vlanId(meta, Criterion.Type.VLAN_VID);
+        assertNotNull(vlanIdCriterion); //since vlanEnabled flag is true
+        assertEquals(cordMcast.assignedVlan(), vlanIdCriterion.vlanId());
+        VlanIdCriterion innerVlanIdCriterion = vlanId(meta, Criterion.Type.INNER_VLAN_VID);
+        assertNull(innerVlanIdCriterion);
+    }
+
+    @Test
+    public void testAddingSinkEventInnerVlanEnabled() throws InterruptedException {
+        // vlanEnabled is set to true and egressVlan & egressInnerVlan are set
+        init(true, VlanId.vlanId("4000"), VlanId.vlanId("1000"));
+
+        Set<ConnectPoint> sinks2Cp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_B));
+        Map<HostId, Set<ConnectPoint>> sinks2 = ImmutableMap.of(HOST_ID_NONE, sinks2Cp);
+
+        //Adding the details to create different routes
+        previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
+        currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks2);
+        // Creating new mcast event for adding sink
+        McastEvent event = new McastEvent(McastEvent.Type.SINKS_ADDED, previousSubject, currentSubject);
+        cordMcast.listener.event(event);
+        synchronized (forwardMap) {
+            forwardMap.wait(WAIT_TIMEOUT);
+        }
+
+        // ForwardMap will contain the operation "Add" in the flowObjective. None -> CP_B
+        assertNotNull(forwardMap.get(DEVICE_ID_OF_A));
+        assertTrue(forwardMap.get(DEVICE_ID_OF_A).op() == Objective.Operation.ADD);
+
+        // Output port number will be PORT_B i.e. 16
+        Collection<TrafficTreatment> traffictreatMentCollection =
+                nextMap.get(DEVICE_ID_OF_A).next();
+        assertTrue(1 == traffictreatMentCollection.size());
+        OutputInstruction output = null;
+        for (TrafficTreatment trafficTreatment : traffictreatMentCollection) {
+            output = outputPort(trafficTreatment);
+        }
+        assertNotNull(output);
+        assertTrue(PORT_B == output.port());
+        // Checking the group ip address
+        TrafficSelector trafficSelector = forwardMap.get(DEVICE_ID_OF_A).selector();
+        IPCriterion ipCriterion = ipAddress(trafficSelector);
+        assertNotNull(ipCriterion);
+        assertTrue(MULTICAST_IP.equals(ipCriterion.ip().address()));
+        //checking the vlan criteria
+        TrafficSelector meta = forwardMap.get(DEVICE_ID_OF_A).meta();
+        VlanIdCriterion vlanIdCriterion = vlanId(meta, Criterion.Type.VLAN_VID);
+        assertNotNull(vlanIdCriterion); //since vlanEnabled flag is true
+        assertEquals(cordMcast.assignedVlan(), vlanIdCriterion.vlanId());
+        VlanIdCriterion innerVlanIdCriterion = vlanId(meta, Criterion.Type.INNER_VLAN_VID);
+        assertNotNull(innerVlanIdCriterion);
+        assertEquals(cordMcast.assignedInnerVlan(), innerVlanIdCriterion.vlanId());
     }
 
     @Test
     public void testAddToExistingSinkEvent() throws InterruptedException {
-
+      init(false, null, null);
        // Adding first sink (none --> CP_B)
        testAddingSinkEvent();
 
@@ -195,7 +303,7 @@
 
     @Test
     public void testRemoveSinkEvent() throws InterruptedException {
-
+       init(false, null, null);
        testAddToExistingSinkEvent();
        // Handling the mcast event for removing sink.
        Set<ConnectPoint> sinksCp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_B, CONNECT_POINT_C));
@@ -226,7 +334,7 @@
 
     @Test
     public void testRemoveLastSinkEvent() throws InterruptedException {
-
+        init(false, null, null);
        testRemoveSinkEvent();
        // Handling the mcast event for removing sink.
        Set<ConnectPoint> sinksCp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_C));
@@ -255,7 +363,7 @@
 
   @Test
   public void testUnkownOltDevice() throws InterruptedException {
-
+       init(false, null, null);
        // Configuration of mcast event for unknown olt device
        final DeviceId deviceIdOfB = DeviceId.deviceId("of:1");
 
@@ -283,7 +391,7 @@
 
   @Test
   public void testRouteAddedEvent() throws InterruptedException {
-
+      init(false, null, null);
       //Adding the details to create different routes
       previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, emptySource, sinks);
       currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, emptySource, sinks);
@@ -299,7 +407,7 @@
 
   @Test
   public void testRouteRemovedEvent() throws InterruptedException {
-
+      init(false, null, null);
       testRouteAddedEvent();
 
       //Adding the details to create different routes
@@ -316,7 +424,7 @@
 
   @Test
   public void testSourceAddedEvent() throws InterruptedException {
-
+      init(false, null, null);
       // Adding route before adding source.
       testRouteAddedEvent();
 
@@ -334,7 +442,7 @@
 
   @Test
   public void testSourcesRemovedEvent() throws InterruptedException {
-
+      init(false, null, null);
       testSourceAddedEvent();
 
       //Adding the details to create different routes
@@ -350,6 +458,7 @@
 
     @Test
     public void mcastTestEventGeneration() throws InterruptedException {
+      init(false, VlanId.vlanId("4000"), VlanId.NONE);
       //fetching route details used to push CordMcastStatisticsEvent.
       IpAddress testGroup = route1.group();
       String testSource = route1.source().isEmpty() ? "*" : route1.source().get().toString();
diff --git a/app/src/test/java/org/opencord/cordmcast/impl/McastTestBase.java b/app/src/test/java/org/opencord/cordmcast/impl/McastTestBase.java
index 986c175..4ae2825 100644
--- a/app/src/test/java/org/opencord/cordmcast/impl/McastTestBase.java
+++ b/app/src/test/java/org/opencord/cordmcast/impl/McastTestBase.java
@@ -54,6 +54,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.flowobjective.FlowObjectiveServiceAdapter;
@@ -164,14 +165,22 @@
      * Mocks the McastConfig class to return vlan id value.
      */
     static class MockMcastConfig extends McastConfig {
+        private VlanId egressVlan;
+        private VlanId egressInnerVlan;
+
+        public MockMcastConfig(VlanId egressVlan, VlanId egressInnerVlan) {
+            this.egressVlan = egressVlan;
+            this.egressInnerVlan = egressInnerVlan;
+        }
+
         @Override
         public VlanId egressVlan() {
-            return VlanId.vlanId("4000");
+            return egressVlan;
         }
 
         @Override
         public VlanId egressInnerVlan() {
-            return VlanId.NONE;
+            return egressInnerVlan;
         }
     }
 
@@ -181,9 +190,21 @@
     @SuppressWarnings("unchecked")
     static final class TestNetworkConfigRegistry
             extends NetworkConfigRegistryAdapter {
+
+        private VlanId egressVlan = VlanId.vlanId("4000");
+        private VlanId egressInnerVlan = VlanId.NONE;
+
+        public void setEgressVlan(VlanId egressVlan) {
+            this.egressVlan = egressVlan;
+        }
+
+        public void setEgressInnerVlan(VlanId egressInnerVlan) {
+            this.egressInnerVlan = egressInnerVlan;
+        }
+
         @Override
         public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
-            McastConfig mcastConfig = new MockMcastConfig();
+            McastConfig mcastConfig = new MockMcastConfig(egressVlan, egressInnerVlan);
             return (C) mcastConfig;
         }
     }
@@ -293,4 +314,16 @@
        return (IPCriterion) ipCriterion;
      }
 
+    public VlanIdCriterion vlanId(TrafficSelector trafficSelector, Criterion.Type type) {
+        Set<Criterion> criterionSet = trafficSelector.criteria();
+        Iterator<Criterion> it = criterionSet.iterator();
+        VlanIdCriterion criterion = null;
+        while (it.hasNext()) {
+            Criterion criteria = it.next();
+            if (type == criteria.type()) {
+                criterion = (VlanIdCriterion) criteria;
+            }
+        }
+        return criterion;
+    }
 }