Unit tests for the distributed group store.

Change-Id: Ie8f00b9bbc1ba46a6f80e70f63d1fd853d64154b
diff --git a/core/store/dist/src/test/java/org/onosproject/store/group/impl/DistributedGroupStoreTest.java b/core/store/dist/src/test/java/org/onosproject/store/group/impl/DistributedGroupStoreTest.java
new file mode 100644
index 0000000..560fdb3
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onosproject/store/group/impl/DistributedGroupStoreTest.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.group.impl;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.mastership.MastershipServiceAdapter;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupStore;
+import org.onosproject.net.group.GroupStoreDelegate;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationServiceAdapter;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.TestStorageService;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.testing.EqualsTester;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.onosproject.net.NetTestTools.APP_ID;
+import static org.onosproject.net.NetTestTools.did;
+
+/**
+ * Distributed group store test.
+ */
+public class DistributedGroupStoreTest {
+
+    DeviceId deviceId1 = did("dev1");
+    DeviceId deviceId2 = did("dev2");
+    GroupId groupId1 = new DefaultGroupId(1);
+    GroupId groupId2 = new DefaultGroupId(2);
+    GroupKey groupKey1 = new DefaultGroupKey("abc".getBytes());
+    GroupKey groupKey2 = new DefaultGroupKey("def".getBytes());
+
+    TrafficTreatment treatment =
+            DefaultTrafficTreatment.emptyTreatment();
+    GroupBucket selectGroupBucket =
+            DefaultGroupBucket.createSelectGroupBucket(treatment);
+    GroupBucket failoverGroupBucket =
+            DefaultGroupBucket.createFailoverGroupBucket(treatment,
+                    PortNumber.IN_PORT, groupId1);
+
+    GroupBuckets buckets = new GroupBuckets(ImmutableList.of(selectGroupBucket));
+    GroupDescription groupDescription1 = new DefaultGroupDescription(
+            deviceId1,
+            GroupDescription.Type.INDIRECT,
+            buckets,
+            groupKey1,
+            groupId1.id(),
+            APP_ID);
+    GroupDescription groupDescription2 = new DefaultGroupDescription(
+            deviceId2,
+            GroupDescription.Type.INDIRECT,
+            buckets,
+            groupKey2,
+            groupId2.id(),
+            APP_ID);
+
+    DistributedGroupStore groupStoreImpl;
+    GroupStore groupStore;
+    EventuallyConsistentMap auditPendingReqQueue;
+
+    static class MasterOfAll extends MastershipServiceAdapter {
+        @Override
+        public MastershipRole getLocalRole(DeviceId deviceId) {
+            return MastershipRole.MASTER;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        groupStoreImpl = new DistributedGroupStore();
+        groupStoreImpl.storageService = new TestStorageService();
+        groupStoreImpl.clusterCommunicator = new ClusterCommunicationServiceAdapter();
+        groupStoreImpl.mastershipService = new MasterOfAll();
+        groupStoreImpl.activate();
+        groupStore = groupStoreImpl;
+        auditPendingReqQueue =
+                TestUtils.getField(groupStoreImpl, "auditPendingReqQueue");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        groupStoreImpl.deactivate();
+    }
+
+    /**
+     * Tests the initial state of the store.
+     */
+    @Test
+    public void testEmptyStore() {
+        assertThat(groupStore.getGroupCount(deviceId1), is(0));
+        assertThat(groupStore.getGroup(deviceId1, groupId1), nullValue());
+        assertThat(groupStore.getGroup(deviceId1, groupKey1), nullValue());
+    }
+
+    /**
+     * Tests adding a pending group.
+     */
+    @Test
+    public void testAddPendingGroup() throws Exception {
+        // Make sure the pending list starts out empty
+        assertThat(auditPendingReqQueue.size(), is(0));
+
+        // Add a new pending group. Make sure that the store remains empty
+        groupStore.storeGroupDescription(groupDescription1);
+        assertThat(groupStore.getGroupCount(deviceId1), is(0));
+        assertThat(groupStore.getGroup(deviceId1, groupId1), nullValue());
+        assertThat(groupStore.getGroup(deviceId1, groupKey1), nullValue());
+
+        // Make sure the group is pending
+        assertThat(auditPendingReqQueue.size(), is(1));
+
+        groupStore.deviceInitialAuditCompleted(deviceId1, true);
+
+        // Make sure the group isn't pending anymore
+        assertThat(auditPendingReqQueue.size(), is(0));
+    }
+
+
+    /**
+     * Tests adding and removing a group.
+     */
+    @Test
+    public void testAddRemoveGroup() throws Exception {
+        groupStore.deviceInitialAuditCompleted(deviceId1, true);
+        assertThat(groupStore.deviceInitialAuditStatus(deviceId1), is(true));
+
+        // Make sure the pending list starts out empty
+        assertThat(auditPendingReqQueue.size(), is(0));
+
+        groupStore.storeGroupDescription(groupDescription1);
+        assertThat(groupStore.getGroupCount(deviceId1), is(1));
+        assertThat(groupStore.getGroup(deviceId1, groupId1), notNullValue());
+        assertThat(groupStore.getGroup(deviceId1, groupKey1), notNullValue());
+
+        // Make sure that nothing is pending
+        assertThat(auditPendingReqQueue.size(), is(0));
+
+        Group groupById = groupStore.getGroup(deviceId1, groupId1);
+        Group groupByKey = groupStore.getGroup(deviceId1, groupKey1);
+        assertThat(groupById, notNullValue());
+        assertThat(groupByKey, notNullValue());
+        assertThat(groupById, is(groupByKey));
+        assertThat(groupById.deviceId(), is(did("dev1")));
+
+        groupStore.removeGroupEntry(groupById);
+
+        assertThat(groupStore.getGroupCount(deviceId1), is(0));
+        assertThat(groupStore.getGroup(deviceId1, groupId1), nullValue());
+        assertThat(groupStore.getGroup(deviceId1, groupKey1), nullValue());
+
+        // Make sure that nothing is pending
+        assertThat(auditPendingReqQueue.size(), is(0));
+    }
+
+    /**
+     * Tests adding and removing a group.
+     */
+    @Test
+    public void testRemoveGroupDescription() throws Exception {
+        groupStore.deviceInitialAuditCompleted(deviceId1, true);
+
+        groupStore.storeGroupDescription(groupDescription1);
+
+        groupStore.deleteGroupDescription(deviceId1, groupKey1);
+
+        // Group should still be there, marked for removal
+        assertThat(groupStore.getGroupCount(deviceId1), is(1));
+        Group queriedGroup = groupStore.getGroup(deviceId1, groupId1);
+        assertThat(queriedGroup.state(), is(Group.GroupState.PENDING_DELETE));
+
+    }
+
+    /**
+     * Tests pushing group metrics.
+     */
+    @Test
+    public void testPushGroupMetrics() {
+        groupStore.deviceInitialAuditCompleted(deviceId1, true);
+        groupStore.deviceInitialAuditCompleted(deviceId2, true);
+
+        GroupDescription groupDescription3 = new DefaultGroupDescription(
+                deviceId1,
+                GroupDescription.Type.SELECT,
+                buckets,
+                new DefaultGroupKey("aaa".getBytes()),
+                null,
+                APP_ID);
+
+        groupStore.storeGroupDescription(groupDescription1);
+        groupStore.storeGroupDescription(groupDescription2);
+        groupStore.storeGroupDescription(groupDescription3);
+        Group group1 = groupStore.getGroup(deviceId1, groupId1);
+
+        assertThat(group1, instanceOf(DefaultGroup.class));
+        DefaultGroup defaultGroup1 = (DefaultGroup) group1;
+        defaultGroup1.setPackets(55L);
+        defaultGroup1.setBytes(66L);
+        groupStore.pushGroupMetrics(deviceId1, ImmutableList.of(group1));
+
+        // Make sure the group was updated.
+
+        Group requeryGroup1 = groupStore.getGroup(deviceId1, groupId1);
+        assertThat(requeryGroup1.packets(), is(55L));
+        assertThat(requeryGroup1.bytes(), is(66L));
+
+    }
+
+    class TestDelegate implements GroupStoreDelegate {
+        private List<GroupEvent> eventsSeen = new LinkedList<>();
+        @Override
+        public void notify(GroupEvent event) {
+            eventsSeen.add(event);
+        }
+
+        public List<GroupEvent> eventsSeen() {
+            return eventsSeen;
+        }
+
+        public void resetEvents() {
+            eventsSeen.clear();
+        }
+    }
+
+    /**
+     * Tests group operation failed interface.
+     */
+    @Test
+    public void testGroupOperationFailed() {
+        TestDelegate delegate = new TestDelegate();
+        groupStore.setDelegate(delegate);
+        groupStore.deviceInitialAuditCompleted(deviceId1, true);
+        groupStore.deviceInitialAuditCompleted(deviceId2, true);
+
+        groupStore.storeGroupDescription(groupDescription1);
+        groupStore.storeGroupDescription(groupDescription2);
+
+        List<GroupEvent> eventsAfterAdds = delegate.eventsSeen();
+        assertThat(eventsAfterAdds, hasSize(2));
+        eventsAfterAdds.stream().forEach(event -> assertThat(event.type(), is(GroupEvent.Type.GROUP_ADD_REQUESTED)));
+        delegate.resetEvents();
+
+        GroupOperation opAdd =
+                GroupOperation.createAddGroupOperation(groupId1,
+                        GroupDescription.Type.INDIRECT,
+                        buckets);
+        groupStore.groupOperationFailed(deviceId1, opAdd);
+
+        List<GroupEvent> eventsAfterAddFailed = delegate.eventsSeen();
+        assertThat(eventsAfterAddFailed, hasSize(2));
+        assertThat(eventsAfterAddFailed.get(0).type(),
+                is(GroupEvent.Type.GROUP_ADD_FAILED));
+        assertThat(eventsAfterAddFailed.get(1).type(),
+                is(GroupEvent.Type.GROUP_REMOVED));
+        delegate.resetEvents();
+
+        GroupOperation opModify =
+                GroupOperation.createModifyGroupOperation(groupId2,
+                        GroupDescription.Type.INDIRECT,
+                        buckets);
+        groupStore.groupOperationFailed(deviceId2, opModify);
+        List<GroupEvent> eventsAfterModifyFailed = delegate.eventsSeen();
+        assertThat(eventsAfterModifyFailed, hasSize(1));
+        assertThat(eventsAfterModifyFailed.get(0).type(),
+                is(GroupEvent.Type.GROUP_UPDATE_FAILED));
+        delegate.resetEvents();
+
+        GroupOperation opDelete =
+                GroupOperation.createDeleteGroupOperation(groupId2,
+                        GroupDescription.Type.INDIRECT);
+        groupStore.groupOperationFailed(deviceId2, opDelete);
+        List<GroupEvent> eventsAfterDeleteFailed = delegate.eventsSeen();
+        assertThat(eventsAfterDeleteFailed, hasSize(1));
+        assertThat(eventsAfterDeleteFailed.get(0).type(),
+                is(GroupEvent.Type.GROUP_REMOVE_FAILED));
+        delegate.resetEvents();
+    }
+
+    /**
+     * Tests extraneous group operations.
+     */
+    @Test
+    public void testExtraneousOperations() {
+        ArrayList<Group> extraneous;
+        groupStore.deviceInitialAuditCompleted(deviceId1, true);
+
+        groupStore.storeGroupDescription(groupDescription1);
+        Group group1 = groupStore.getGroup(deviceId1, groupId1);
+
+        extraneous = Lists.newArrayList(groupStore.getExtraneousGroups(deviceId1));
+        assertThat(extraneous, hasSize(0));
+
+        groupStore.addOrUpdateExtraneousGroupEntry(group1);
+        extraneous = Lists.newArrayList(groupStore.getExtraneousGroups(deviceId1));
+        assertThat(extraneous, hasSize(1));
+
+        groupStore.removeExtraneousGroupEntry(group1);
+        extraneous = Lists.newArrayList(groupStore.getExtraneousGroups(deviceId1));
+        assertThat(extraneous, hasSize(0));
+    }
+
+    /**
+     * Tests updating of group descriptions.
+     */
+    @Test
+    public void testUpdateGroupDescription() {
+
+        GroupBuckets buckets =
+                new GroupBuckets(ImmutableList.of(failoverGroupBucket));
+
+        groupStore.deviceInitialAuditCompleted(deviceId1, true);
+        groupStore.storeGroupDescription(groupDescription1);
+
+        GroupKey newKey = new DefaultGroupKey("123".getBytes());
+        groupStore.updateGroupDescription(deviceId1,
+                groupKey1,
+                GroupStore.UpdateType.ADD,
+                buckets,
+                newKey);
+        Group group1 = groupStore.getGroup(deviceId1, groupId1);
+        assertThat(group1.appCookie(), is(newKey));
+        assertThat(group1.buckets().buckets(), hasSize(2));
+    }
+
+    @Test
+    public void testEqualsGroupStoreIdMapKey() {
+        DistributedGroupStore.GroupStoreIdMapKey key1 =
+            new DistributedGroupStore.GroupStoreIdMapKey(deviceId1, groupId1);
+        DistributedGroupStore.GroupStoreIdMapKey sameAsKey1 =
+                new DistributedGroupStore.GroupStoreIdMapKey(deviceId1, groupId1);
+        DistributedGroupStore.GroupStoreIdMapKey key2 =
+                new DistributedGroupStore.GroupStoreIdMapKey(deviceId2, groupId1);
+        DistributedGroupStore.GroupStoreIdMapKey key3 =
+                new DistributedGroupStore.GroupStoreIdMapKey(deviceId1, groupId2);
+
+        new EqualsTester()
+                .addEqualityGroup(key1, sameAsKey1)
+                .addEqualityGroup(key2)
+                .addEqualityGroup(key3)
+                .testEquals();
+    }
+
+    @Test
+    public void testEqualsGroupStoreKeyMapKey() {
+        DistributedGroupStore.GroupStoreKeyMapKey key1 =
+                new DistributedGroupStore.GroupStoreKeyMapKey(deviceId1, groupKey1);
+        DistributedGroupStore.GroupStoreKeyMapKey sameAsKey1 =
+                new DistributedGroupStore.GroupStoreKeyMapKey(deviceId1, groupKey1);
+        DistributedGroupStore.GroupStoreKeyMapKey key2 =
+                new DistributedGroupStore.GroupStoreKeyMapKey(deviceId2, groupKey1);
+        DistributedGroupStore.GroupStoreKeyMapKey key3 =
+                new DistributedGroupStore.GroupStoreKeyMapKey(deviceId1, groupKey2);
+
+        new EqualsTester()
+                .addEqualityGroup(key1, sameAsKey1)
+                .addEqualityGroup(key2)
+                .addEqualityGroup(key3)
+                .testEquals();
+    }
+
+    @Test
+    public void testEqualsGroupStoreMapKey() {
+        DistributedGroupStore.GroupStoreMapKey key1 =
+                new DistributedGroupStore.GroupStoreMapKey(deviceId1);
+        DistributedGroupStore.GroupStoreMapKey sameAsKey1 =
+                new DistributedGroupStore.GroupStoreMapKey(deviceId1);
+        DistributedGroupStore.GroupStoreMapKey key2 =
+                new DistributedGroupStore.GroupStoreMapKey(deviceId2);
+        DistributedGroupStore.GroupStoreMapKey key3 =
+                new DistributedGroupStore.GroupStoreMapKey(did("dev3"));
+
+        new EqualsTester()
+                .addEqualityGroup(key1, sameAsKey1)
+                .addEqualityGroup(key2)
+                .addEqualityGroup(key3)
+                .testEquals();
+    }
+}