Completed testing of EdgeManager

Change-Id: I2d2f5ba62951951f29780904acfa5eec07657129
diff --git a/core/net/src/test/java/org/onosproject/net/edgeservice/impl/EdgeManagerTest.java b/core/net/src/test/java/org/onosproject/net/edgeservice/impl/EdgeManagerTest.java
index 65504f2..1c6349e 100644
--- a/core/net/src/test/java/org/onosproject/net/edgeservice/impl/EdgeManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/edgeservice/impl/EdgeManagerTest.java
@@ -15,47 +15,80 @@
  */
 package org.onosproject.net.edgeservice.impl;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.event.Event;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPort;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetTestTools;
 import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceServiceAdapter;
 import org.onosproject.net.edge.EdgePortEvent;
 import org.onosproject.net.edge.EdgePortListener;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketServiceAdapter;
 import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyEvent;
 import org.onosproject.net.topology.TopologyListener;
 import org.onosproject.net.topology.TopologyServiceAdapter;
 
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.*;
+import static org.onosproject.net.device.DeviceEvent.Type.*;
+import static org.onosproject.net.edge.EdgePortEvent.Type.EDGE_PORT_ADDED;
+import static org.onosproject.net.edge.EdgePortEvent.Type.EDGE_PORT_REMOVED;
+import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
+import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
+import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
 
 /**
- * Test of the edge port manager.
+ * Test of the edge port manager. Each device has ports '0' through 'numPorts - 1'
+ * as specified by the variable 'numPorts'.
  */
 public class EdgeManagerTest {
 
     private EdgeManager mgr;
-    private final EdgePortListener testListener = new TestListener();
-
+    private int totalPorts = 10;
+    private boolean alwaysReturnPorts = false;
+    private final Set<ConnectPoint> infrastructurePorts = Sets.newConcurrentHashSet();
+    private List<EdgePortEvent> events = Lists.newArrayList();
+    private final Map<DeviceId, Device> devices = Maps.newConcurrentMap();
+    private Set<OutboundPacket> packets = Sets.newConcurrentHashSet();
+    private final EdgePortListener testListener = new TestListener(events);
+    private TestTopologyManager testTopologyManager;
 
     @Before
     public void setUp() {
         mgr = new EdgeManager();
         mgr.eventDispatcher = new TestEventDispatcher();
-        mgr.topologyService = new TestTopologyManager();
-        mgr.deviceService = new TestDeviceManager();
+        testTopologyManager = new TestTopologyManager(infrastructurePorts);
+        mgr.topologyService = testTopologyManager;
+        mgr.deviceService = new TestDeviceManager(devices);
         mgr.packetService = new TestPacketManager();
         mgr.activate();
         mgr.addListener(testListener);
+
     }
 
+
     @After
     public void tearDown() {
         mgr.removeListener(testListener);
@@ -63,14 +96,347 @@
     }
 
     @Test
-    public void basics() {
+    public void testBasics() {
+        //Setup
+        int numDevices = 20;
+        int numPorts = 4;
+        defaultPopulator(numDevices, numPorts);
+
+        assertEquals("Unexpected number of ports", numDevices * numPorts, infrastructurePorts.size());
+
         assertFalse("no ports expected", mgr.getEdgePoints().iterator().hasNext());
+
+        assertFalse("Expected isEdge to return false",
+                mgr.isEdgePoint(NetTestTools.connectPoint(Integer.toString(1), 1)));
+
+        removeInfraPort(NetTestTools.connectPoint(Integer.toString(1), 1));
+        assertTrue("Expected isEdge to return false",
+                mgr.isEdgePoint(NetTestTools.connectPoint(Integer.toString(1), 1)));
+    }
+
+    @Test
+    public void testLinkUpdates() {
+        //Setup
+        ConnectPoint testPoint, referencePoint;
+
+        //Testing link removal
+        List<Event> eventsToAdd = Lists.newArrayList();
+        eventsToAdd.add(new LinkEvent(LINK_REMOVED, NetTestTools.link("a", 1, "b", 2)));
+        TopologyEvent event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd);
+        testTopologyManager.listener.event(event);
+
+        assertTrue("The list contained an unexpected number of events", events.size() == 2);
+        assertTrue("The first element is of the wrong type.",
+                events.get(0).type() == EDGE_PORT_ADDED);
+        assertTrue("The second element is of the wrong type.",
+                events.get(1).type() == EDGE_PORT_ADDED);
+
+        testPoint = events.get(0).subject();
+        referencePoint = NetTestTools.connectPoint("a", 1);
+        assertTrue("The port numbers of the first element are incorrect",
+                testPoint.port().toLong() == referencePoint.port().toLong());
+        assertTrue("The device id of the first element is incorrect.",
+                testPoint.deviceId().equals(referencePoint.deviceId()));
+
+        testPoint = events.get(1).subject();
+        referencePoint = NetTestTools.connectPoint("b", 2);
+        assertTrue("The port numbers of the second element are incorrect",
+                testPoint.port().toLong() == referencePoint.port().toLong());
+        assertTrue("The device id of the second element is incorrect.",
+                testPoint.deviceId().equals(referencePoint.deviceId()));
+
+        //Rebroadcast event to ensure it results in no additional events
+        testTopologyManager.listener.event(event);
+        assertTrue("The list contained an unexpected number of events", events.size() == 2);
+
+        //Testing link adding when links to remove exist
+        eventsToAdd.clear();
+        events.clear();
+        eventsToAdd.add(new LinkEvent(LINK_ADDED, NetTestTools.link("a", 1, "b", 2)));
+        event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd);
+        testTopologyManager.listener.event(event);
+
+        assertTrue("The list contained an unexpected number of events", events.size() == 2);
+        assertTrue("The first element is of the wrong type.",
+                events.get(0).type() == EDGE_PORT_REMOVED);
+        assertTrue("The second element is of the wrong type.",
+                events.get(1).type() == EDGE_PORT_REMOVED);
+
+        testPoint = events.get(0).subject();
+        referencePoint = NetTestTools.connectPoint("a", 1);
+        assertTrue("The port numbers of the first element are incorrect",
+                testPoint.port().toLong() == referencePoint.port().toLong());
+        assertTrue("The device id of the first element is incorrect.",
+                testPoint.deviceId().equals(referencePoint.deviceId()));
+
+        testPoint = events.get(1).subject();
+        referencePoint = NetTestTools.connectPoint("b", 2);
+        assertTrue("The port numbers of the second element are incorrect",
+                testPoint.port().toLong() == referencePoint.port().toLong());
+        assertTrue("The device id of the second element is incorrect.",
+                testPoint.deviceId().equals(referencePoint.deviceId()));
+
+        //Apparent duplicate of previous method tests removal when the elements have already been removed
+        eventsToAdd.clear();
+        events.clear();
+        eventsToAdd.add(new LinkEvent(LINK_ADDED, NetTestTools.link("a", 1, "b", 2)));
+        event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd);
+        testTopologyManager.listener.event(event);
+
+        assertTrue("The list should contain no events, the removed elements don't exist.", events.size() == 0);
+    }
+
+    @Test
+    public void testDeviceUpdates() {
+        //Setup
+
+        Device referenceDevice;
+        TopologyEvent event;
+        List<Event> eventsToAdd = Lists.newArrayList();
+        int numDevices = 10;
+        int numInfraPorts = 5;
+        totalPorts = 10;
+        defaultPopulator(numDevices, numInfraPorts);
+
+        //Test response to device added events
+        referenceDevice = NetTestTools.device("1");
+        eventsToAdd.add(new DeviceEvent(DEVICE_ADDED, referenceDevice,
+                new DefaultPort(referenceDevice, PortNumber.portNumber(1), true)));
+        event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd);
+        testTopologyManager.listener.event(event);
+
+        //Check that ports were populated correctly
+        assertTrue("Unexpected number of new ports added",
+                mgr.deviceService.getPorts(NetTestTools.did("1")).size() == 10);
+
+        //Check that of the ten ports the half that are infrastructure ports aren't added
+        assertEquals("Unexpected number of new edge ports added", (totalPorts - numInfraPorts), events.size());
+
+        for (int index = 0; index < numInfraPorts; index++) {
+            assertTrue("Unexpected type of event", events.get(index).type() == EDGE_PORT_ADDED);
+        }
+        //Names here are irrelevant, the first 5 ports are populated as infrastructure, 6-10 are edge
+        for (int index = 0; index < events.size(); index++) {
+            assertEquals("Port added had unexpected port number.",
+                    events.get(index).subject().port(),
+                    NetTestTools.connectPoint("a", index + numInfraPorts + 1).port());
+        }
+        events.clear();
+
+        //Repost the event to test repeated posts
+        testTopologyManager.listener.event(event);
+        assertEquals("The redundant notification should not have created additional notifications.",
+                0, events.size());
+        //Calculate the size of the returned iterable of edge points.
+        Iterable<ConnectPoint> pts = mgr.getEdgePoints();
+        Iterator pointIterator = pts.iterator();
+        int count = 0;
+        for (; pointIterator.hasNext(); count++) {
+            pointIterator.next();
+        }
+        assertEquals("Unexpected number of edge points", totalPorts - numInfraPorts, count);
+        //Testing device removal
+        events.clear();
+        eventsToAdd.clear();
+        eventsToAdd.add(new DeviceEvent(DEVICE_REMOVED, referenceDevice,
+                new DefaultPort(referenceDevice, PortNumber.portNumber(1), true)));
+        event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd);
+        testTopologyManager.listener.event(event);
+
+        assertEquals("There should be five new events from removal of edge points",
+                totalPorts - numInfraPorts, events.size());
+        for (int index = 0; index < events.size(); index++) {
+            //Assert that the correct port numbers were removed in the correct order
+            assertEquals("Port removed had unexpected port number.",
+                    events.get(index).subject().port(),
+                    (NetTestTools.connectPoint("a", index + numInfraPorts + 1).port()));
+            //Assert that the events are of the correct type
+            assertEquals("Unexpected type of event", events.get(index).type(), EDGE_PORT_REMOVED);
+        }
+        events.clear();
+        //Rebroadcast event to check that it triggers no new behavior
+        testTopologyManager.listener.event(event);
+        assertEquals("Rebroadcast of removal event should not produce additional events",
+                0, events.size());
+
+        //Testing device status change, changed from unavailable to available
+        events.clear();
+        eventsToAdd.clear();
+        //Make sure that the devicemanager shows the device as available.
+        addDevice(referenceDevice, "1", 5);
+        devices.put(referenceDevice.id(), referenceDevice);
+
+        eventsToAdd.add(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, referenceDevice));
+        event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd);
+        testTopologyManager.listener.event(event);
+        //An earlier setup set half of the reference device ports to infrastructure
+        assertEquals("An unexpected number of events were generated.", totalPorts - numInfraPorts, events.size());
+        for (int i = 0; i < 5; i++) {
+            assertEquals("The event was not of the right type", events.get(i).type(), EDGE_PORT_ADDED);
+        }
+        events.clear();
+        testTopologyManager.listener.event(event);
+        assertEquals("No events should have been generated for a set of existing ports.", 0, events.size());
+
+        //Test removal when state changes when the device becomes unavailable
+
+        //Ensure that the deviceManager shows the device as unavailable
+        removeDevice(referenceDevice);
+        /*This variable copies the behavior of the topology by returning ports attached to an unavailable device
+        //this behavior is necessary for the following event to execute properly, if these statements are removed
+        no events will be generated since no ports will be provided in getPorts() to EdgeManager.
+        */
+        alwaysReturnPorts = true;
+        testTopologyManager.listener.event(event);
+        alwaysReturnPorts = false;
+        assertEquals("An unexpected number of events were created.", totalPorts - numInfraPorts, events.size());
+        for (int i = 0; i < 5; i++) {
+            EdgePortEvent edgeEvent = events.get(i);
+            assertEquals("The event is of an unexpected type.",
+                    EdgePortEvent.Type.EDGE_PORT_REMOVED, edgeEvent.type());
+            assertEquals("The event pertains to an unexpected port", PortNumber.portNumber(i + numInfraPorts + 1),
+                    edgeEvent.subject().port());
+        }
+    }
+
+    @Test
+    public void testInternalCache() {
+        List<Event> eventsToAdd = Lists.newArrayList();
+        int numDevices = 10;
+        //Number of infrastructure ports per device
+        int numPorts = 5;
+        //Total ports per device when requesting all devices
+        totalPorts = 10;
+        defaultPopulator(numDevices, numPorts);
+        for (int i = 0; i < numDevices; i++) {
+            Device newDevice = NetTestTools.device(Integer.toString(i));
+            devices.put(newDevice.id(), newDevice);
+            eventsToAdd.add(new DeviceEvent(DEVICE_ADDED, newDevice));
+        }
+        TopologyEvent event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd);
+        testTopologyManager.listener.event(event);
+
+        //Check all ports have correct designations
+        ConnectPoint testPoint;
+        for (int deviceNum = 0; deviceNum < numDevices; deviceNum++) {
+            for (int portNum = 1; portNum <= totalPorts; portNum++) {
+                testPoint = NetTestTools.connectPoint(Integer.toString(deviceNum), portNum);
+                if (portNum <= numPorts) {
+                    assertFalse("This should not be an edge point", mgr.isEdgePoint(testPoint));
+                } else {
+                    assertTrue("This should be an edge point", mgr.isEdgePoint(testPoint));
+                }
+            }
+        }
+        int count = 0;
+        for (ConnectPoint ignored : mgr.getEdgePoints()) {
+            count++;
+        }
+        assertEquals("There are an unexpeceted number of edge points returned.",
+                (totalPorts - numPorts) * numDevices, count);
+        for (int deviceNumber = 0; deviceNumber < numDevices; deviceNumber++) {
+            count = 0;
+            for (ConnectPoint ignored : mgr.getEdgePoints(NetTestTools.did("1"))) {
+                count++;
+            }
+            assertEquals("This element has an unexpected number of edge points.", (totalPorts - numPorts), count);
+        }
+    }
+
+
+    @Test
+    public void testEmit() {
+        byte[] arr = new byte[10];
+        Device referenceDevice;
+        TopologyEvent event;
+        List<Event> eventsToAdd = Lists.newArrayList();
+        int numDevices = 10;
+        int numInfraPorts = 5;
+        totalPorts = 10;
+        defaultPopulator(numDevices, numInfraPorts);
+        for (byte byteIndex = 0; byteIndex < arr.length; byteIndex++) {
+            arr[byteIndex] = byteIndex;
+        }
+        for (int i = 0; i < numDevices; i++) {
+            referenceDevice = NetTestTools.device(Integer.toString(i));
+            eventsToAdd.add(new DeviceEvent(DEVICE_ADDED, referenceDevice,
+                    new DefaultPort(referenceDevice, PortNumber.portNumber(1), true)));
+        }
+        event = new TopologyEvent(TOPOLOGY_CHANGED, null, eventsToAdd);
+        testTopologyManager.listener.event(event);
+
+        mgr.emitPacket(ByteBuffer.wrap(arr), Optional.<TrafficTreatment>empty());
+
+        assertEquals("There were an unexpected number of emitted packets",
+                (totalPorts - numInfraPorts) * numDevices, packets.size());
+        Iterator<OutboundPacket> packetIter = packets.iterator();
+        OutboundPacket packet;
+        while (packetIter.hasNext()) {
+            packet = packetIter.next();
+            assertEquals("The packet had an incorrect payload.", arr, packet.data().array());
+        }
+        //Start testing emission to a specific device
+        packets.clear();
+        mgr.emitPacket(NetTestTools.did(Integer.toString(1)), ByteBuffer.wrap(arr), Optional.<TrafficTreatment>empty());
+
+        assertEquals("Unexpected number of outbound packets were emitted.",
+                totalPorts - numInfraPorts, packets.size());
+        packetIter = packets.iterator();
+        while (packetIter.hasNext()) {
+            packet = packetIter.next();
+            assertEquals("The packet had an incorrect payload", arr, packet.data().array());
+        }
+    }
+
+
+    /**
+     * @param numDevices    the number of devices to populate.
+     * @param numInfraPorts the number of ports to be set as infrastructure on each device, numbered base 0, ports 0
+     *                      through numInfraPorts - 1
+     */
+    private void defaultPopulator(int numDevices, int numInfraPorts) {
+        for (int device = 0; device < numDevices; device++) {
+            String str = Integer.toString(device);
+            Device deviceToAdd = NetTestTools.device(str);
+            devices.put(deviceToAdd.id(), deviceToAdd);
+            for (int port = 1; port <= numInfraPorts; port++) {
+                infrastructurePorts.add(NetTestTools.connectPoint(str, port));
+            }
+        }
+    }
+
+    /**
+     * Adds the specified device with the specified number of edge ports so long as it is less than the total ports.
+     *
+     * @param device        The device to be added
+     * @param deviceName    The name given to generate the devices DID
+     * @param numInfraPorts The number of ports to be added numbered 1 ... numInfraPorts
+     */
+    private void addDevice(Device device, String deviceName, int numInfraPorts) {
+        if (!devices.keySet().contains(device.id())) {
+            devices.put(device.id(), device);
+            for (int i = 1; i <= numInfraPorts && i <= totalPorts; i++) {
+                infrastructurePorts.add(NetTestTools.connectPoint(deviceName, i));
+            }
+        }
+    }
+
+    private void removeDevice(Device device) {
+        devices.remove(device.id());
+    }
+
+    private void removeInfraPort(ConnectPoint port) {
+        infrastructurePorts.remove(port);
     }
 
     private class TestTopologyManager extends TopologyServiceAdapter {
         private TopologyListener listener;
         private Set<ConnectPoint> infrastructurePorts;
 
+        public TestTopologyManager(Set<ConnectPoint> infrastructurePorts) {
+            this.infrastructurePorts = infrastructurePorts;
+        }
+
         @Override
         public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
             return infrastructurePorts.contains(connectPoint);
@@ -89,12 +455,16 @@
 
     private class TestDeviceManager extends DeviceServiceAdapter {
 
-        private Set<Device> devices;
+        private Map<DeviceId, Device> devices;
+
+        public TestDeviceManager(Map<DeviceId, Device> devices) {
+            this.devices = devices;
+        }
 
         @Override
         public boolean isAvailable(DeviceId deviceId) {
-            for (Device device : devices) {
-                if (device.id().equals(deviceId)) {
+            for (DeviceId id : devices.keySet()) {
+                if (id.equals(deviceId)) {
                     return true;
                 }
             }
@@ -103,22 +473,38 @@
 
         @Override
         public List<Port> getPorts(DeviceId deviceId) {
-            return super.getPorts(deviceId);
+            List<Port> ports = new ArrayList<>();
+            Device device = devices.get(deviceId);
+            if (device == null && !alwaysReturnPorts) {
+                return ports;
+            }
+            for (int portNum = 1; portNum <= totalPorts; portNum++) {
+                //String is generated using 'of:' + the passed name, this creates a
+                ports.add(new DefaultPort(device, PortNumber.portNumber(portNum), true));
+            }
+            return ports;
         }
 
         @Override
         public Iterable<Device> getAvailableDevices() {
-            return devices;
+            return devices.values();
         }
     }
 
     private class TestPacketManager extends PacketServiceAdapter {
+        @Override
+        public void emit(OutboundPacket packet) {
+            packets.add(packet);
+        }
     }
 
-
     private class TestListener implements EdgePortListener {
         private List<EdgePortEvent> events;
 
+        public TestListener(List<EdgePortEvent> events) {
+            this.events = events;
+        }
+
         @Override
         public void event(EdgePortEvent event) {
             events.add(event);