Unit tests for flow objective manager

Change-Id: I2f4e3084493174fcff7d64d2da1806b7b811b41f
diff --git a/core/api/src/test/java/org/onosproject/net/behaviour/PipelinerAdapter.java b/core/api/src/test/java/org/onosproject/net/behaviour/PipelinerAdapter.java
new file mode 100644
index 0000000..73e83cd
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/behaviour/PipelinerAdapter.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 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.net.behaviour;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+
+/**
+ * Testing adapter for pipeliner class.
+ */
+public class PipelinerAdapter implements Pipeliner {
+    @Override
+    public void init(DeviceId deviceId, PipelinerContext context) {
+
+    }
+
+    @Override
+    public void filter(FilteringObjective filterObjective) {
+
+    }
+
+    @Override
+    public void forward(ForwardingObjective forwardObjective) {
+
+    }
+
+    @Override
+    public void next(NextObjective nextObjective) {
+
+    }
+
+    @Override
+    public DriverHandler handler() {
+        return null;
+    }
+
+    @Override
+    public void setHandler(DriverHandler handler) {
+
+    }
+
+    @Override
+    public DriverData data() {
+        return null;
+    }
+
+    @Override
+    public void setData(DriverData data) {
+
+    }
+}
diff --git a/core/net/pom.xml b/core/net/pom.xml
index 9426e85..3ac84e0 100644
--- a/core/net/pom.xml
+++ b/core/net/pom.xml
@@ -80,6 +80,14 @@
         </dependency>
 
         <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-of-ctl</artifactId>
+            <scope>test</scope>
+            <classifier>tests</classifier>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.easymock</groupId>
             <artifactId>easymock</artifactId>
             <scope>test</scope>
diff --git a/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManagerTest.java b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManagerTest.java
new file mode 100644
index 0000000..5360e10
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManagerTest.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2016 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.net.flowobjective.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.packet.ChassisId;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipServiceAdapter;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.behaviour.DefaultNextGroup;
+import org.onosproject.net.behaviour.NextGroup;
+import org.onosproject.net.behaviour.PipelinerAdapter;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.driver.AbstractDriverLoader;
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.DefaultDriverHandler;
+import org.onosproject.net.driver.DefaultDriverProviderService;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.ObjectiveEvent;
+import org.onosproject.net.intent.TestTools;
+import org.onosproject.openflow.DriverAdapter;
+import org.onosproject.openflow.DriverServiceAdapter;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.TestUtils.TestUtilsException;
+
+/**
+ * Tests for the flow objective manager.
+ */
+public class FlowObjectiveManagerTest {
+
+    private static final int RETRY_MS = 250;
+    private FlowObjectiveManager manager;
+    DeviceId id1 = NetTestTools.did("d1");
+    DefaultDevice d1 = new DefaultDevice(NetTestTools.PID, id1, Device.Type.SWITCH,
+                                         "test", "1.0", "1.0",
+                                         "abacab", new ChassisId("c"),
+                                         DefaultAnnotations.EMPTY);
+
+    DeviceId id2 = NetTestTools.did("d2");
+    DefaultDevice d2 = new DefaultDevice(NetTestTools.PID, id2, Device.Type.SWITCH,
+                                         "test", "1.0", "1.0",
+                                         "abacab", new ChassisId("c"),
+                                         DefaultAnnotations.EMPTY);
+
+    List<String> filteringObjectives;
+    List<String> forwardingObjectives;
+    List<String> nextObjectives;
+
+    private class TestDeviceService extends DeviceServiceAdapter {
+
+        List<Device> deviceList;
+
+        TestDeviceService() {
+            deviceList = new ArrayList<>();
+
+            deviceList.add(d1);
+        }
+
+        @Override
+        public Iterable<Device> getDevices() {
+            return deviceList;
+        }
+
+        @Override
+        public boolean isAvailable(DeviceId deviceId) {
+            return true;
+        }
+    }
+
+    private class TestFlowObjectiveStore extends FlowObjectiveStoreAdapter {
+        @Override
+        public NextGroup getNextGroup(Integer nextId) {
+            if (nextId != 4) {
+                byte[] data = new byte[1];
+                data[0] = 5;
+                return new DefaultNextGroup(data);
+            } else {
+                return null;
+            }
+        }
+
+    }
+
+    private class TestDriversLoader extends AbstractDriverLoader implements DefaultDriverProviderService {
+        public TestDriversLoader() {
+            super("/onos-drivers.xml");
+        }
+    }
+
+    private class TestDriver extends DriverAdapter {
+
+        @Override
+        public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
+            return true;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T extends Behaviour> T createBehaviour(DriverData data, Class<T> behaviourClass) {
+            return (T) new TestPipeliner();
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T extends Behaviour> T createBehaviour(DriverHandler handler, Class<T> behaviourClass) {
+            return (T) new TestPipeliner();
+        }
+
+    }
+
+    private class TestPipeliner extends PipelinerAdapter {
+        DeviceId deviceId;
+
+        @Override
+        public void init(DeviceId deviceId, PipelinerContext context) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public void filter(FilteringObjective filterObjective) {
+            filteringObjectives.add(deviceId.toString());
+        }
+
+        @Override
+        public void forward(ForwardingObjective forwardObjective) {
+            forwardingObjectives.add(deviceId.toString());
+        }
+
+        @Override
+        public void next(NextObjective nextObjective) {
+            nextObjectives.add(deviceId.toString());
+        }
+    }
+
+    private class TestDriverService extends DriverServiceAdapter {
+        @Override
+        public DriverHandler createHandler(DeviceId deviceId, String... credentials) {
+            Driver driver = new TestDriver();
+            return new DefaultDriverHandler(new DefaultDriverData(driver, id1));
+        }
+    }
+
+    @Before
+    public void initializeTest() {
+        manager = new FlowObjectiveManager();
+        manager.flowObjectiveStore = new TestFlowObjectiveStore();
+        manager.mastershipService = new MastershipServiceAdapter();
+        manager.deviceService = new TestDeviceService();
+        manager.defaultDriverService = new TestDriversLoader();
+        manager.driverService = new TestDriverService();
+
+        filteringObjectives = new ArrayList<>();
+        forwardingObjectives = new ArrayList<>();
+        nextObjectives = new ArrayList<>();
+        manager.activate();
+    }
+
+    @After
+    public void tearDownTest() {
+        manager.deactivate();
+        manager = null;
+        filteringObjectives.clear();
+        forwardingObjectives.clear();
+        nextObjectives.clear();
+    }
+
+    /**
+     * Tests adding a forwarding objective.
+     */
+    @Test
+    public void forwardingObjective() {
+        TrafficSelector selector = DefaultTrafficSelector.emptySelector();
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+        ForwardingObjective forward =
+                DefaultForwardingObjective.builder()
+                        .fromApp(NetTestTools.APP_ID)
+                        .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                        .withSelector(selector)
+                        .withTreatment(treatment)
+                        .makePermanent()
+                        .add();
+
+        manager.forward(id1, forward);
+
+        TestTools.assertAfter(RETRY_MS, () ->
+            assertThat(forwardingObjectives, hasSize(1)));
+
+        assertThat(forwardingObjectives, hasItem("of:d1"));
+        assertThat(filteringObjectives, hasSize(0));
+        assertThat(nextObjectives, hasSize(0));
+    }
+
+    /**
+     * Tests adding a filtering objective.
+     */
+    @Test
+    public void filteringObjective() {
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+        FilteringObjective filter =
+                DefaultFilteringObjective.builder()
+                        .fromApp(NetTestTools.APP_ID)
+                        .withMeta(treatment)
+                        .makePermanent()
+                        .deny()
+                        .addCondition(Criteria.matchEthType(12))
+                        .add();
+
+        manager.activate();
+        manager.filter(id1, filter);
+
+        TestTools.assertAfter(RETRY_MS, () ->
+                assertThat(filteringObjectives, hasSize(1)));
+
+        assertThat(forwardingObjectives, hasSize(0));
+        assertThat(filteringObjectives, hasItem("of:d1"));
+        assertThat(nextObjectives, hasSize(0));
+    }
+
+    /**
+     * Tests adding a next objective.
+     */
+    @Test
+    public void nextObjective() {
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+        NextObjective next =
+                DefaultNextObjective.builder()
+                        .withId(manager.allocateNextId())
+                        .addTreatment(treatment)
+                        .withType(NextObjective.Type.BROADCAST)
+                        .fromApp(NetTestTools.APP_ID)
+                        .makePermanent()
+                        .add();
+
+        manager.next(id1, next);
+
+        TestTools.assertAfter(RETRY_MS, () ->
+                assertThat(nextObjectives, hasSize(1)));
+
+        assertThat(forwardingObjectives, hasSize(0));
+        assertThat(filteringObjectives, hasSize(0));
+        assertThat(nextObjectives, hasItem("of:d1"));
+    }
+
+    /**
+     * Tests adding a pending forwarding objective.
+     *
+     * @throws TestUtilsException if lookup of a field fails
+     */
+    @Test
+    public void pendingForwardingObjective() throws TestUtilsException {
+        TrafficSelector selector = DefaultTrafficSelector.emptySelector();
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+        ForwardingObjective forward4 =
+                DefaultForwardingObjective.builder()
+                        .fromApp(NetTestTools.APP_ID)
+                        .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                        .withSelector(selector)
+                        .withTreatment(treatment)
+                        .makePermanent()
+                        .nextStep(4)
+                        .add();
+        ForwardingObjective forward5 =
+                DefaultForwardingObjective.builder()
+                        .fromApp(NetTestTools.APP_ID)
+                        .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                        .withSelector(selector)
+                        .withTreatment(treatment)
+                        .makePermanent()
+                        .nextStep(5)
+                        .add();
+
+        //  multiple pending forwards should be combined
+        manager.forward(id1, forward4);
+        manager.forward(id1, forward4);
+        manager.forward(id1, forward5);
+
+
+        //  1 should be complete, 1 pending
+        TestTools.assertAfter(RETRY_MS, () ->
+                assertThat(forwardingObjectives, hasSize(1)));
+
+        assertThat(forwardingObjectives, hasItem("of:d1"));
+        assertThat(filteringObjectives, hasSize(0));
+        assertThat(nextObjectives, hasSize(0));
+
+        // Now send events to trigger the objective still in the queue
+        ObjectiveEvent event1 = new ObjectiveEvent(ObjectiveEvent.Type.ADD, 4);
+        FlowObjectiveStoreDelegate delegate = TestUtils.getField(manager, "delegate");
+        delegate.notify(event1);
+
+        // all should be processed now
+        TestTools.assertAfter(RETRY_MS, () ->
+                assertThat(forwardingObjectives, hasSize(2)));
+        assertThat(forwardingObjectives, hasItem("of:d1"));
+        assertThat(filteringObjectives, hasSize(0));
+        assertThat(nextObjectives, hasSize(0));
+    }
+
+    /**
+     * Tests receipt of a device up event.
+     *
+     * @throws TestUtilsException if lookup of a field fails
+     */
+    @Test
+    public void deviceUpEvent() throws TestUtilsException {
+        TrafficSelector selector = DefaultTrafficSelector.emptySelector();
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+        DeviceEvent event = new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, d2);
+        DeviceListener listener = TestUtils.getField(manager, "deviceListener");
+        assertThat(listener, notNullValue());
+
+        listener.event(event);
+
+        ForwardingObjective forward =
+                DefaultForwardingObjective.builder()
+                        .fromApp(NetTestTools.APP_ID)
+                        .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                        .withSelector(selector)
+                        .withTreatment(treatment)
+                        .makePermanent()
+                        .add();
+        manager.forward(id2, forward);
+
+        // new device should have an objective now
+        TestTools.assertAfter(RETRY_MS, () ->
+                assertThat(forwardingObjectives, hasSize(1)));
+
+        assertThat(forwardingObjectives, hasItem("of:d2"));
+        assertThat(filteringObjectives, hasSize(0));
+        assertThat(nextObjectives, hasSize(0));
+    }
+
+    /**
+     * Tests recepit of a device mastership event.
+     *
+     * @throws TestUtilsException if lookup of a field fails
+     */
+    @Test
+    public void deviceMastershipEvent() throws TestUtilsException {
+        TrafficSelector selector = DefaultTrafficSelector.emptySelector();
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+        MastershipEvent event =
+                new MastershipEvent(MastershipEvent.Type.MASTER_CHANGED, id2, null);
+        MastershipListener listener = TestUtils.getField(manager, "mastershipListener");
+        assertThat(listener, notNullValue());
+
+        listener.event(event);
+
+        ForwardingObjective forward =
+                DefaultForwardingObjective.builder()
+                        .fromApp(NetTestTools.APP_ID)
+                        .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                        .withSelector(selector)
+                        .withTreatment(treatment)
+                        .makePermanent()
+                        .add();
+        manager.forward(id2, forward);
+
+        // new device should have an objective now
+        TestTools.assertAfter(RETRY_MS, () ->
+                assertThat(forwardingObjectives, hasSize(1)));
+
+        assertThat(forwardingObjectives, hasItem("of:d2"));
+        assertThat(filteringObjectives, hasSize(0));
+        assertThat(nextObjectives, hasSize(0));
+    }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveStoreAdapter.java b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveStoreAdapter.java
new file mode 100644
index 0000000..378ea73
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveStoreAdapter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 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.net.flowobjective.impl;
+
+import org.onosproject.net.behaviour.NextGroup;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+
+/**
+ * Test adapter for the flow objective store API.
+ */
+public class FlowObjectiveStoreAdapter implements FlowObjectiveStore {
+    @Override
+    public void putNextGroup(Integer nextId, NextGroup group) {
+
+    }
+
+    @Override
+    public NextGroup getNextGroup(Integer nextId) {
+        return null;
+    }
+
+    @Override
+    public NextGroup removeNextGroup(Integer nextId) {
+        return null;
+    }
+
+    @Override
+    public int allocateNextId() {
+        return 0;
+    }
+
+    @Override
+    public void setDelegate(FlowObjectiveStoreDelegate delegate) {
+
+    }
+
+    @Override
+    public void unsetDelegate(FlowObjectiveStoreDelegate delegate) {
+
+    }
+
+    @Override
+    public boolean hasDelegate() {
+        return false;
+    }
+}