Unit tests for open flow controller message handling.

Change-Id: I9cffe4f4585eaf0df48d9fcb6a9f62ddcc0f9403
diff --git a/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java b/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
index fdcd2f0..b97c336 100644
--- a/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
+++ b/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
@@ -107,7 +107,7 @@
             label = "Number of controller worker threads; default is 16")
     private int workerThreads = DEFAULT_WORKER_THREADS;
 
-    private final ExecutorService executorMsgs =
+    protected ExecutorService executorMsgs =
         Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d"));
 
     private final ExecutorService executorBarrier =
@@ -611,10 +611,10 @@
         }
     }
 
-    private final class OFMessageHandler implements Runnable {
+    protected final class OFMessageHandler implements Runnable {
 
-        private final OFMessage msg;
-        private final Dpid dpid;
+        protected final OFMessage msg;
+        protected final Dpid dpid;
 
         public OFMessageHandler(Dpid dpid, OFMessage msg) {
             this.msg = msg;
diff --git a/openflow/ctl/src/test/java/org/onosproject/openflow/ExecutorServiceAdapter.java b/openflow/ctl/src/test/java/org/onosproject/openflow/ExecutorServiceAdapter.java
new file mode 100644
index 0000000..54c9c94
--- /dev/null
+++ b/openflow/ctl/src/test/java/org/onosproject/openflow/ExecutorServiceAdapter.java
@@ -0,0 +1,99 @@
+/*
+ * 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.openflow;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Test harness adapter for the ExecutorService.
+ */
+public class ExecutorServiceAdapter  implements ExecutorService {
+    @Override
+    public void shutdown() {
+
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        return null;
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return false;
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return false;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit)
+            throws InterruptedException {
+        return false;
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+        return null;
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+        return null;
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+        return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException {
+        return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws InterruptedException {
+        return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
+        return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        return null;
+    }
+
+    @Override
+    public void execute(Runnable command) {
+
+    }
+}
diff --git a/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfFeaturesReply.java b/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfFeaturesReply.java
new file mode 100644
index 0000000..e280d56
--- /dev/null
+++ b/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfFeaturesReply.java
@@ -0,0 +1,81 @@
+/*
+ * 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.openflow;
+
+import java.util.List;
+import java.util.Set;
+
+import org.projectfloodlight.openflow.protocol.OFActionType;
+import org.projectfloodlight.openflow.protocol.OFCapabilities;
+import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFAuxId;
+
+/**
+ * Mock of the Open FLow features reply message.
+ */
+public class MockOfFeaturesReply extends OfMessageAdapter implements OFFeaturesReply {
+    public MockOfFeaturesReply() {
+        super(OFType.FEATURES_REPLY);
+    }
+
+    @Override
+    public DatapathId getDatapathId() {
+        return null;
+    }
+
+    @Override
+    public long getNBuffers() {
+        return 0;
+    }
+
+    @Override
+    public short getNTables() {
+        return 0;
+    }
+
+    @Override
+    public Set<OFCapabilities> getCapabilities() {
+        return null;
+    }
+
+    @Override
+    public long getReserved() {
+        return 0;
+    }
+
+    @Override
+    public List<OFPortDesc> getPorts() {
+        return null;
+    }
+
+    @Override
+    public Set<OFActionType> getActions() {
+        return null;
+    }
+
+    @Override
+    public OFAuxId getAuxiliaryId() {
+        return null;
+    }
+
+    @Override
+    public OFFeaturesReply.Builder createBuilder() {
+        return null;
+    }
+}
diff --git a/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPacketIn.java b/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPacketIn.java
new file mode 100644
index 0000000..8e2069b
--- /dev/null
+++ b/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPacketIn.java
@@ -0,0 +1,84 @@
+/*
+ * 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.openflow;
+
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPacketInReason;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U64;
+
+/**
+ * Mock of the Open Flow packet in message.
+ */
+public class MockOfPacketIn extends OfMessageAdapter implements OFPacketIn {
+    public MockOfPacketIn() {
+        super(OFType.PACKET_IN);
+    }
+
+    @Override
+    public OFBufferId getBufferId() {
+        return null;
+    }
+
+    @Override
+    public int getTotalLen() {
+        return 0;
+    }
+
+    @Override
+    public OFPacketInReason getReason() {
+        return null;
+    }
+
+    @Override
+    public TableId getTableId() {
+        return null;
+    }
+
+    @Override
+    public Match getMatch() {
+        return null;
+    }
+
+    @Override
+    public byte[] getData() {
+        return new byte[0];
+    }
+
+    @Override
+    public OFPort getInPort() {
+        return null;
+    }
+
+    @Override
+    public OFPort getInPhyPort() {
+        return null;
+    }
+
+    @Override
+    public U64 getCookie() {
+        return null;
+    }
+
+    @Override
+    public OFPacketIn.Builder createBuilder() {
+        return null;
+    }
+}
diff --git a/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPortStatus.java b/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPortStatus.java
new file mode 100644
index 0000000..2e26542
--- /dev/null
+++ b/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPortStatus.java
@@ -0,0 +1,45 @@
+/*
+ * 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.openflow;
+
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortReason;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFType;
+
+/**
+ * Mocked open flow port status message.
+ */
+public class MockOfPortStatus extends OfMessageAdapter implements OFPortStatus {
+    public MockOfPortStatus() {
+        super(OFType.PORT_STATUS);
+    }
+
+    @Override
+    public OFPortReason getReason() {
+        return null;
+    }
+
+    @Override
+    public OFPortDesc getDesc() {
+        return null;
+    }
+
+    @Override
+    public OFPortStatus.Builder createBuilder() {
+        return null;
+    }
+}
diff --git a/openflow/ctl/src/test/java/org/onosproject/openflow/OfMessageAdapter.java b/openflow/ctl/src/test/java/org/onosproject/openflow/OfMessageAdapter.java
index e9b38e3..b7446eb 100644
--- a/openflow/ctl/src/test/java/org/onosproject/openflow/OfMessageAdapter.java
+++ b/openflow/ctl/src/test/java/org/onosproject/openflow/OfMessageAdapter.java
@@ -26,13 +26,21 @@
  * Adapter for testing against an OpenFlow message.
  */
 public class OfMessageAdapter implements OFMessage {
-    @Override
-    public OFVersion getVersion() {
-        return null;
+    OFType type;
+
+    private OfMessageAdapter() {}
+
+    public OfMessageAdapter(OFType type) {
+        this.type = type;
     }
 
     @Override
     public OFType getType() {
+        return type;
+    }
+
+    @Override
+    public OFVersion getVersion() {
         return null;
     }
 
diff --git a/openflow/ctl/src/test/java/org/onosproject/openflow/OpenFlowSwitchListenerAdapter.java b/openflow/ctl/src/test/java/org/onosproject/openflow/OpenFlowSwitchListenerAdapter.java
new file mode 100644
index 0000000..b018f42
--- /dev/null
+++ b/openflow/ctl/src/test/java/org/onosproject/openflow/OpenFlowSwitchListenerAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * 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.openflow;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+
+/**
+ * Test harness for a switch listener.
+ */
+public class OpenFlowSwitchListenerAdapter implements OpenFlowSwitchListener {
+    final List<Dpid> removedDpids = new ArrayList<>();
+    final List<Dpid> addedDpids = new ArrayList<>();
+    final List<Dpid> changedDpids = new ArrayList<>();
+    final Map<Dpid, OFPortStatus> portChangedDpids = new HashMap<>();
+
+    @Override
+    public void switchAdded(Dpid dpid) {
+        addedDpids.add(dpid);
+    }
+
+    @Override
+    public void switchRemoved(Dpid dpid) {
+        removedDpids.add(dpid);
+    }
+
+    @Override
+    public void switchChanged(Dpid dpid) {
+        changedDpids.add(dpid);
+    }
+
+    @Override
+    public void portChanged(Dpid dpid, OFPortStatus status) {
+        portChangedDpids.put(dpid, status);
+    }
+
+    @Override
+    public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
+        // Stub
+    }
+
+    public List<Dpid> removedDpids() {
+        return removedDpids;
+    }
+
+    public List<Dpid> addedDpids() {
+        return addedDpids;
+    }
+
+    public List<Dpid> changedDpids() {
+        return changedDpids;
+    }
+
+    public Map<Dpid, OFPortStatus> portChangedDpids() {
+        return portChangedDpids;
+    }
+}
diff --git a/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageEncoderTest.java b/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageEncoderTest.java
index 59685f1..d09e566 100644
--- a/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageEncoderTest.java
+++ b/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageEncoderTest.java
@@ -22,6 +22,7 @@
 import org.junit.Test;
 import org.onosproject.openflow.OfMessageAdapter;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFType;
 
 import com.google.common.collect.ImmutableList;
 
@@ -39,6 +40,7 @@
         final int id;
 
         MockOfMessage() {
+            super(OFType.ERROR);
             id = nextId++;
         }
 
diff --git a/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplPacketsTest.java b/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplPacketsTest.java
new file mode 100644
index 0000000..13086ca
--- /dev/null
+++ b/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplPacketsTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.openflow.controller.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.openflow.ExecutorServiceAdapter;
+import org.onosproject.openflow.MockOfFeaturesReply;
+import org.onosproject.openflow.MockOfPacketIn;
+import org.onosproject.openflow.MockOfPortStatus;
+import org.onosproject.openflow.OfMessageAdapter;
+import org.onosproject.openflow.OpenFlowSwitchListenerAdapter;
+import org.onosproject.openflow.OpenflowSwitchDriverAdapter;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowPacketContext;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.PacketListener;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFType;
+
+import static junit.framework.TestCase.fail;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Tests for packet processing in the open flow controller impl class.
+ */
+public class OpenFlowControllerImplPacketsTest {
+    OpenFlowControllerImpl controller;
+    OpenFlowControllerImpl.OpenFlowSwitchAgent agent;
+    Dpid dpid1;
+    OpenFlowSwitch switch1;
+    OpenFlowSwitchListenerAdapter switchListener;
+    TestPacketListener packetListener;
+    TestExecutorService executorService;
+
+    /**
+     * Mock packet listener that accumulates packets.
+     */
+    class TestPacketListener implements PacketListener {
+        List<OpenFlowPacketContext> contexts = new ArrayList<>();
+
+        @Override
+        public void handlePacket(OpenFlowPacketContext pktCtx) {
+            contexts.add(pktCtx);
+        }
+
+        List<OpenFlowPacketContext> contexts() {
+            return contexts;
+        }
+    }
+
+
+    /**
+     * Mock executor service that tracks submits.
+     */
+    static class TestExecutorService extends ExecutorServiceAdapter {
+        private List<OFMessage> submittedMessages = new ArrayList<>();
+
+        List<OFMessage> submittedMessages() {
+            return submittedMessages;
+        }
+
+        @Override
+        public Future<?> submit(Runnable task) {
+            OpenFlowControllerImpl.OFMessageHandler handler =
+                    (OpenFlowControllerImpl.OFMessageHandler) task;
+            submittedMessages.add(handler.msg);
+            return null;
+        }
+    }
+
+    /**
+     * Sets up switches to use as data, mocks and launches a controller instance.
+     */
+    @Before
+    public void setUp() {
+        try {
+            switch1 = new OpenflowSwitchDriverAdapter();
+            dpid1 = Dpid.dpid(new URI("of:0000000000000111"));
+        } catch (URISyntaxException ex) {
+            //  Does not happen
+            fail();
+        }
+
+        controller = new OpenFlowControllerImpl();
+        agent = controller.agent;
+        switchListener = new OpenFlowSwitchListenerAdapter();
+        controller.addListener(switchListener);
+
+        packetListener = new TestPacketListener();
+        controller.addPacketListener(100, packetListener);
+
+        executorService = new TestExecutorService();
+        controller.executorMsgs = executorService;
+    }
+
+    /**
+     * Tests a port status operation.
+     */
+    @Test
+    public void testPortStatus() {
+        OFMessage portStatusPacket = new MockOfPortStatus();
+        controller.processPacket(dpid1, portStatusPacket);
+        assertThat(switchListener.portChangedDpids().size(), is(1));
+        assertThat(switchListener.portChangedDpids().containsKey(dpid1),
+                   is(true));
+        assertThat(switchListener.portChangedDpids().get(dpid1),
+                   equalTo(portStatusPacket));
+    }
+
+    /**
+     * Tests a features reply operation.
+     */
+    @Test
+    public void testFeaturesReply() {
+        OFMessage ofFeaturesReplyPacket = new MockOfFeaturesReply();
+        controller.processPacket(dpid1, ofFeaturesReplyPacket);
+        assertThat(switchListener.changedDpids(), hasSize(1));
+        assertThat(switchListener.changedDpids().get(0),
+                   equalTo(dpid1));
+    }
+
+    /**
+     * Tests a packet in operation.
+     */
+    @Test
+    public void testPacketIn() {
+        agent.addConnectedSwitch(dpid1, switch1);
+        OFMessage packetInPacket = new MockOfPacketIn();
+        controller.processPacket(dpid1, packetInPacket);
+        assertThat(packetListener.contexts(), hasSize(1));
+    }
+
+    /**
+     * Tests an error operation.
+     */
+    @Test
+    public void testError() {
+        agent.addConnectedSwitch(dpid1, switch1);
+        OfMessageAdapter errorPacket = new OfMessageAdapter(OFType.ERROR);
+        controller.processPacket(dpid1, errorPacket);
+        assertThat(executorService.submittedMessages(), hasSize(1));
+        assertThat(executorService.submittedMessages().get(0), is(errorPacket));
+    }
+}