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));
+ }
+}