ONOS-985: Sample integration test application for group subsystem

Change-Id: I68352f922e5c7a0800fcc4fa839955769bf925a6
diff --git a/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java b/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
index 4cb21e5..1490b0b 100644
--- a/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
+++ b/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
@@ -32,6 +32,9 @@
 import org.onosproject.event.AbstractListenerRegistry;
 import org.onosproject.event.EventDeliveryService;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.group.Group;
 import org.onosproject.net.group.GroupBuckets;
 import org.onosproject.net.group.GroupDescription;
@@ -67,17 +70,22 @@
     private final AbstractListenerRegistry<GroupEvent, GroupListener>
                 listenerRegistry = new AbstractListenerRegistry<>();
     private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate();
+    private final DeviceListener deviceListener = new InternalDeviceListener();
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected GroupStore store;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
 
     @Activate
     public void activate() {
         store.setDelegate(delegate);
         eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
+        deviceService.addListener(deviceListener);
         log.info("Started");
     }
 
@@ -232,6 +240,8 @@
             GroupOperations groupOps = null;
             switch (event.type()) {
             case GROUP_ADD_REQUESTED:
+                log.debug("GROUP_ADD_REQUESTED for Group {} on device {}",
+                          group.id(), group.deviceId());
                 GroupOperation groupAddOp = GroupOperation.
                         createAddGroupOperation(group.id(),
                                                 group.type(),
@@ -242,6 +252,8 @@
                 break;
 
             case GROUP_UPDATE_REQUESTED:
+                log.debug("GROUP_UPDATE_REQUESTED for Group {} on device {}",
+                          group.id(), group.deviceId());
                 GroupOperation groupModifyOp = GroupOperation.
                         createModifyGroupOperation(group.id(),
                                                 group.type(),
@@ -252,6 +264,8 @@
                 break;
 
             case GROUP_REMOVE_REQUESTED:
+                log.debug("GROUP_REMOVE_REQUESTED for Group {} on device {}",
+                          group.id(), group.deviceId());
                 GroupOperation groupDeleteOp = GroupOperation.
                         createDeleteGroupOperation(group.id(),
                                                 group.type());
@@ -294,10 +308,14 @@
             GroupProvider gp = getProvider(group.deviceId());
             switch (group.state()) {
                 case PENDING_DELETE:
+                    log.debug("Group {} delete confirmation from device {}",
+                              group, group.deviceId());
                     store.removeGroupEntry(group);
                     break;
                 case ADDED:
                 case PENDING_ADD:
+                    log.debug("Group {} is in store but not on device {}",
+                              group, group.deviceId());
                     GroupOperation groupAddOp = GroupOperation.
                                     createAddGroupOperation(group.id(),
                                                             group.type(),
@@ -314,7 +332,8 @@
 
 
         private void extraneousGroup(Group group) {
-            log.debug("Group {} is on switch but not in store.", group);
+            log.debug("Group {} is on device {} but not in store.",
+                      group, group.deviceId());
             checkValidity();
             store.addOrUpdateExtraneousGroupEntry(group);
         }
@@ -322,13 +341,16 @@
         private void groupAdded(Group group) {
             checkValidity();
 
-            log.trace("Group {}", group);
+            log.trace("Group {} Added or Updated in device {}",
+                      group, group.deviceId());
             store.addOrUpdateGroupEntry(group);
         }
 
         @Override
         public void pushGroupMetrics(DeviceId deviceId,
                                      Collection<Group> groupEntries) {
+            log.trace("Received group metrics from device {}",
+                    deviceId);
             boolean deviceInitialAuditStatus =
                     store.deviceInitialAuditStatus(deviceId);
             Set<Group> southboundGroupEntries =
@@ -338,31 +360,75 @@
             Set<Group> extraneousStoredEntries =
                     Sets.newHashSet(store.getExtraneousGroups(deviceId));
 
+            log.trace("Displaying all southboundGroupEntries for device {}", deviceId);
+            for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
+                Group group = it.next();
+                log.trace("Group {} in device {}", group, deviceId);
+            }
+
+            log.trace("Displaying all stored group entries for device {}", deviceId);
+            for (Iterator<Group> it = storedGroupEntries.iterator(); it.hasNext();) {
+                Group group = it.next();
+                log.trace("Stored Group {} for device {}", group, deviceId);
+            }
+
             for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
                 Group group = it.next();
                 if (storedGroupEntries.remove(group)) {
                     // we both have the group, let's update some info then.
+                    log.trace("Group AUDIT: group {} exists "
+                            + "in both planes for device {}",
+                            group.id(), deviceId);
                     groupAdded(group);
                     it.remove();
                 }
             }
             for (Group group : southboundGroupEntries) {
                 // there are groups in the switch that aren't in the store
+                log.trace("Group AUDIT: extraneous group {} exists "
+                        + "in data plane for device {}",
+                        group.id(), deviceId);
                 extraneousStoredEntries.remove(group);
                 extraneousGroup(group);
             }
             for (Group group : storedGroupEntries) {
                 // there are groups in the store that aren't in the switch
+                log.trace("Group AUDIT: group {} missing "
+                        + "in data plane for device {}",
+                        group.id(), deviceId);
                 groupMissing(group);
             }
             for (Group group : extraneousStoredEntries) {
                 // there are groups in the extraneous store that
                 // aren't in the switch
+                log.trace("Group AUDIT: clearing extransoeus group {} "
+                        + "from store for device {}",
+                        group.id(), deviceId);
                 store.removeExtraneousGroupEntry(group);
             }
 
             if (!deviceInitialAuditStatus) {
-                store.deviceInitialAuditCompleted(deviceId);
+                log.debug("Group AUDIT: Setting device {} initial "
+                        + "AUDIT completed", deviceId);
+                store.deviceInitialAuditCompleted(deviceId, true);
+            }
+        }
+    }
+
+    private class InternalDeviceListener implements DeviceListener {
+
+        @Override
+        public void event(DeviceEvent event) {
+            switch (event.type()) {
+            case DEVICE_REMOVED:
+                log.debug("Clearing device {} initial "
+                        + "AUDIT completed status as device is going down",
+                        event.subject().id());
+                store.deviceInitialAuditCompleted(event.subject().id(), false);
+                break;
+
+            default:
+                break;
             }
         }
     }
diff --git a/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java b/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
index 7099464..2fe5e4b 100644
--- a/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
@@ -32,6 +32,7 @@
 import org.onosproject.event.impl.TestEventDispatcher;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.impl.DeviceManager;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.group.DefaultGroup;
@@ -81,6 +82,7 @@
     public void setUp() {
         mgr = new GroupManager();
         groupService = mgr;
+        mgr.deviceService = new DeviceManager();
         mgr.store = new SimpleGroupStore();
         mgr.eventDispatcher = new TestEventDispatcher();
         providerRegistry = mgr;
@@ -147,11 +149,34 @@
      */
     @Test
     public void testGroupService() {
+        // Test Group creation before AUDIT process
+        testGroupCreationBeforeAudit();
+
+        // Test initial group audit process
+        testInitialAuditWithPendingGroupRequests();
+
+        // Test audit with extraneous and missing groups
+        testAuditWithExtraneousMissingGroups();
+
+        // Test audit with confirmed groups
+        testAuditWithConfirmedGroups();
+
+        // Test group add bucket operations
+        testAddBuckets();
+
+        // Test group remove bucket operations
+        testRemoveBuckets();
+
+        // Test group remove operations
+        testRemoveGroup();
+    }
+
+    // Test Group creation before AUDIT process
+    private void testGroupCreationBeforeAudit() {
         PortNumber[] ports1 = {PortNumber.portNumber(31),
                                PortNumber.portNumber(32)};
         PortNumber[] ports2 = {PortNumber.portNumber(41),
                                PortNumber.portNumber(42)};
-        // Test Group creation before AUDIT process
         TestGroupKey key = new TestGroupKey("group1BeforeAudit");
         List<GroupBucket> buckets = new ArrayList<GroupBucket>();
         List<PortNumber> outPorts = new ArrayList<PortNumber>();
@@ -177,8 +202,14 @@
         internalProvider.validate(DID, null);
         assertEquals(null, groupService.getGroup(DID, key));
         assertEquals(0, Iterables.size(groupService.getGroups(DID, appId)));
+    }
 
-        // Test initial group audit process
+    // Test initial AUDIT process with pending group requests
+    private void testInitialAuditWithPendingGroupRequests() {
+        PortNumber[] ports1 = {PortNumber.portNumber(31),
+                               PortNumber.portNumber(32)};
+        PortNumber[] ports2 = {PortNumber.portNumber(41),
+                               PortNumber.portNumber(42)};
         GroupId gId1 = new DefaultGroupId(1);
         Group group1 = createSouthboundGroupEntry(gId1,
                                                   Arrays.asList(ports1),
@@ -193,50 +224,76 @@
         providerService.pushGroupMetrics(DID, groupEntries);
         // First group metrics would trigger the device audit completion
         // post which all pending group requests are also executed.
+        TestGroupKey key = new TestGroupKey("group1BeforeAudit");
         Group createdGroup = groupService.getGroup(DID, key);
         int createdGroupId = createdGroup.id().id();
         assertNotEquals(gId1.id(), createdGroupId);
         assertNotEquals(gId2.id(), createdGroupId);
+
         List<GroupOperation> expectedGroupOps = Arrays.asList(
                             GroupOperation.createDeleteGroupOperation(gId1,
                                                           Group.Type.SELECT),
                             GroupOperation.createAddGroupOperation(
                                            createdGroup.id(),
                                            Group.Type.SELECT,
-                                           groupBuckets));
+                                           createdGroup.buckets()));
         internalProvider.validate(DID, expectedGroupOps);
+    }
 
-        group1 = createSouthboundGroupEntry(gId1,
+    // Test AUDIT process with extraneous groups and missing groups
+    private void testAuditWithExtraneousMissingGroups() {
+        PortNumber[] ports1 = {PortNumber.portNumber(31),
+                               PortNumber.portNumber(32)};
+        PortNumber[] ports2 = {PortNumber.portNumber(41),
+                               PortNumber.portNumber(42)};
+        GroupId gId1 = new DefaultGroupId(1);
+        Group group1 = createSouthboundGroupEntry(gId1,
                                             Arrays.asList(ports1),
                                             0);
-        group2 = createSouthboundGroupEntry(gId2,
+        GroupId gId2 = new DefaultGroupId(2);
+        Group group2 = createSouthboundGroupEntry(gId2,
                                             Arrays.asList(ports2),
                                             0);
-        groupEntries = Arrays.asList(group1, group2);
+        List<Group> groupEntries = Arrays.asList(group1, group2);
         providerService.pushGroupMetrics(DID, groupEntries);
-        expectedGroupOps = Arrays.asList(
+        TestGroupKey key = new TestGroupKey("group1BeforeAudit");
+        Group createdGroup = groupService.getGroup(DID, key);
+        List<GroupOperation> expectedGroupOps = Arrays.asList(
                 GroupOperation.createDeleteGroupOperation(gId1,
                                                           Group.Type.SELECT),
                 GroupOperation.createDeleteGroupOperation(gId2,
                                                           Group.Type.SELECT),
                 GroupOperation.createAddGroupOperation(createdGroup.id(),
                                                        Group.Type.SELECT,
-                                                       groupBuckets));
+                                                       createdGroup.buckets()));
         internalProvider.validate(DID, expectedGroupOps);
+    }
 
+    // Test AUDIT with confirmed groups
+    private void testAuditWithConfirmedGroups() {
+        TestGroupKey key = new TestGroupKey("group1BeforeAudit");
+        Group createdGroup = groupService.getGroup(DID, key);
         createdGroup = new DefaultGroup(createdGroup.id(),
                                         DID,
                                         Group.Type.SELECT,
-                                        groupBuckets);
-        groupEntries = Arrays.asList(createdGroup);
+                                        createdGroup.buckets());
+        List<Group> groupEntries = Arrays.asList(createdGroup);
         providerService.pushGroupMetrics(DID, groupEntries);
         internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_ADDED));
+    }
 
-        // Test group add bucket operations
+    // Test group add bucket operations
+    private void testAddBuckets() {
         TestGroupKey addKey = new TestGroupKey("group1AddBuckets");
+
+        TestGroupKey prevKey = new TestGroupKey("group1BeforeAudit");
+        Group createdGroup = groupService.getGroup(DID, prevKey);
+        List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+        buckets.addAll(createdGroup.buckets().buckets());
+
         PortNumber[] addPorts = {PortNumber.portNumber(51),
                                  PortNumber.portNumber(52)};
-        outPorts.clear();
+        List<PortNumber> outPorts = new ArrayList<PortNumber>();
         outPorts.addAll(Arrays.asList(addPorts));
         List<GroupBucket> addBuckets = new ArrayList<GroupBucket>();
         for (PortNumber portNumber: outPorts) {
@@ -253,26 +310,34 @@
         }
         GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
         groupService.addBucketsToGroup(DID,
-                                       key,
+                                       prevKey,
                                        groupAddBuckets,
                                        addKey,
                                        appId);
         GroupBuckets updatedBuckets = new GroupBuckets(buckets);
-        expectedGroupOps = Arrays.asList(
+        List<GroupOperation> expectedGroupOps = Arrays.asList(
                GroupOperation.createModifyGroupOperation(createdGroup.id(),
                                                          Group.Type.SELECT,
                                                          updatedBuckets));
         internalProvider.validate(DID, expectedGroupOps);
         Group existingGroup = groupService.getGroup(DID, addKey);
-        groupEntries = Arrays.asList(existingGroup);
+        List<Group> groupEntries = Arrays.asList(existingGroup);
         providerService.pushGroupMetrics(DID, groupEntries);
         internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
+    }
 
-        // Test group remove bucket operations
+    // Test group remove bucket operations
+    private void testRemoveBuckets() {
         TestGroupKey removeKey = new TestGroupKey("group1RemoveBuckets");
+
+        TestGroupKey prevKey = new TestGroupKey("group1AddBuckets");
+        Group createdGroup = groupService.getGroup(DID, prevKey);
+        List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+        buckets.addAll(createdGroup.buckets().buckets());
+
         PortNumber[] removePorts = {PortNumber.portNumber(31),
                                  PortNumber.portNumber(32)};
-        outPorts.clear();
+        List<PortNumber> outPorts = new ArrayList<PortNumber>();
         outPorts.addAll(Arrays.asList(removePorts));
         List<GroupBucket> removeBuckets = new ArrayList<GroupBucket>();
         for (PortNumber portNumber: outPorts) {
@@ -289,28 +354,32 @@
         }
         GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets);
         groupService.removeBucketsFromGroup(DID,
-                                            addKey,
+                                            prevKey,
                                             groupRemoveBuckets,
                                             removeKey,
                                             appId);
-        updatedBuckets = new GroupBuckets(buckets);
-        expectedGroupOps = Arrays.asList(
+        GroupBuckets updatedBuckets = new GroupBuckets(buckets);
+        List<GroupOperation> expectedGroupOps = Arrays.asList(
                GroupOperation.createModifyGroupOperation(createdGroup.id(),
                                                          Group.Type.SELECT,
                                                          updatedBuckets));
         internalProvider.validate(DID, expectedGroupOps);
-        existingGroup = groupService.getGroup(DID, removeKey);
-        groupEntries = Arrays.asList(existingGroup);
+        Group existingGroup = groupService.getGroup(DID, removeKey);
+        List<Group> groupEntries = Arrays.asList(existingGroup);
         providerService.pushGroupMetrics(DID, groupEntries);
         internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_UPDATED));
+    }
 
-        // Test group remove operations
-        groupService.removeGroup(DID, removeKey, appId);
-        expectedGroupOps = Arrays.asList(
-             GroupOperation.createDeleteGroupOperation(createdGroup.id(),
+    // Test group remove operations
+    private void testRemoveGroup() {
+        TestGroupKey currKey = new TestGroupKey("group1RemoveBuckets");
+        Group existingGroup = groupService.getGroup(DID, currKey);
+        groupService.removeGroup(DID, currKey, appId);
+        List<GroupOperation> expectedGroupOps = Arrays.asList(
+             GroupOperation.createDeleteGroupOperation(existingGroup.id(),
                                                        Group.Type.SELECT));
         internalProvider.validate(DID, expectedGroupOps);
-        groupEntries = Collections.emptyList();
+        List<Group> groupEntries = Collections.emptyList();
         providerService.pushGroupMetrics(DID, groupEntries);
         internalListener.validateEvent(Arrays.asList(GroupEvent.Type.GROUP_REMOVED));
     }