Added host-related unit tests for model cache.

Change-Id: Iac27f62daed2c8e9114ce924c3014f35eb0ae5ce
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiHost.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiHost.java
index e80836a..8c5107e 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiHost.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiHost.java
@@ -106,12 +106,30 @@
     }
 
     /**
-     * Identifier for the edge link between this host and the device to which
-     * it is connected.
+     * Returns the identifier for the edge link between this host and
+     * the device to which it is connected.
      *
      * @return edge link identifier
      */
     public UiLinkId edgeLinkId() {
-        return null;
+        return edgeLinkId;
+    }
+
+    /**
+     * Returns the identifier of the device to which the host is connected.
+     *
+     * @return device identifier
+     */
+    public DeviceId locationDevice() {
+        return locDevice;
+    }
+
+    /**
+     * Returns the port number of the device to which the host is connected.
+     *
+     * @return port number
+     */
+    public PortNumber locationPort() {
+        return locPort;
     }
 }
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
index 9fad51c..75a405e 100644
--- a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
@@ -525,49 +525,60 @@
     }
 
 
+    /**
+     * Creates a default host connected at the given edge device and port. Note
+     * that an identifying hex character ("a" - "f") should be supplied. This
+     * will be included in the MAC address of the host (and equivalent value
+     * as last byte in IP address).
+     *
+     * @param device  edge device
+     * @param port    port number
+     * @param hexChar identifying hex character
+     * @return host connected at that location
+     */
+    protected static Host createHost(Device device, int port, String hexChar) {
+        DeviceId deviceId = device.id();
+        String devNum = deviceId.toString().substring(1);
+
+        MacAddress mac = MacAddress.valueOf(HOST_MAC_PREFIX + devNum + hexChar);
+        HostId hostId = hostId(String.format("%s/-1", mac));
+
+        int ipByte = Integer.valueOf(hexChar, 16);
+        if (ipByte < 10 || ipByte > 15) {
+            throw new IllegalArgumentException("hexChar must be a-f");
+        }
+        HostLocation loc = new HostLocation(deviceId, portNumber(port), 0);
+
+        IpAddress ip = ip("10." + devNum + ".0." + ipByte);
+
+        return new DefaultHost(ProviderId.NONE, hostId, mac, VlanId.NONE,
+                loc, ImmutableSet.of(ip));
+    }
+
+    /**
+     * Creates a pair of hosts connected to the specified device.
+     *
+     * @param d edge device
+     * @return pair of hosts
+     */
+    protected static List<Host> createHostPair(Device d) {
+        List<Host> hosts = new ArrayList<>();
+        hosts.add(createHost(d, 101, "a"));
+        hosts.add(createHost(d, 102, "b"));
+        return hosts;
+    }
+
     private static class MockHostService extends HostServiceAdapter {
         private final Map<HostId, Host> hosts = new HashMap<>();
 
         MockHostService() {
             for (Device d : ALL_DEVS) {
-                // two hosts per device
-                createHosts(hosts, d);
+                for (Host h : createHostPair(d)) {
+                    hosts.put(h.id(), h);
+                }
             }
         }
 
-        private void createHosts(Map<HostId, Host> hosts, Device d) {
-            DeviceId deviceId = d.id();
-            String devNum = deviceId.toString().substring(1);
-
-            String ha = devNum + "a";
-            String hb = devNum + "b";
-
-            MacAddress macA = MacAddress.valueOf(HOST_MAC_PREFIX + ha);
-            MacAddress macB = MacAddress.valueOf(HOST_MAC_PREFIX + hb);
-
-            HostId hostA = hostId(String.format("%s/-1", macA));
-            HostId hostB = hostId(String.format("%s/-1", macB));
-
-            PortNumber portA = portNumber(101);
-            PortNumber portB = portNumber(102);
-
-            HostLocation locA = new HostLocation(deviceId, portA, 0);
-            HostLocation locB = new HostLocation(deviceId, portB, 0);
-
-            IpAddress ipA = ip("10." + devNum + ".0.1");
-            IpAddress ipB = ip("10." + devNum + ".0.2");
-
-            Host host = new DefaultHost(ProviderId.NONE,
-                    hostA, macA, VlanId.NONE, locA,
-                    ImmutableSet.of(ipA));
-            hosts.put(hostA, host);
-
-            host = new DefaultHost(ProviderId.NONE,
-                    hostB, macB, VlanId.NONE, locB,
-                    ImmutableSet.of(ipB));
-            hosts.put(hostB, host);
-        }
-
         @Override
         public int getHostCount() {
             return hosts.size();
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
index 5ab7343..4d409dc 100644
--- a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
@@ -22,12 +22,15 @@
 import org.onosproject.event.EventDispatcher;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
 import org.onosproject.net.Link;
 import org.onosproject.net.region.Region;
 import org.onosproject.ui.impl.topo.model.UiModelEvent.Type;
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiDevice;
 import org.onosproject.ui.model.topo.UiElement;
+import org.onosproject.ui.model.topo.UiHost;
 import org.onosproject.ui.model.topo.UiLink;
 import org.onosproject.ui.model.topo.UiLinkId;
 import org.onosproject.ui.model.topo.UiRegion;
@@ -38,8 +41,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.onosproject.cluster.NodeId.nodeId;
+import static org.onosproject.net.PortNumber.portNumber;
 import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
 
 /**
@@ -305,6 +310,88 @@
         assertEquals("unex # links", 0, cache.linkCount());
     }
 
+    private void assertHostLinkCounts(int nHosts, int nLinks) {
+        assertEquals("unex # hosts", nHosts, cache.hostCount());
+        assertEquals("unex # links", nLinks, cache.linkCount());
+    }
+
+    private void assertLocation(HostId hid, DeviceId expDev, int expPort) {
+        UiHost h = cache.accessHost(hid);
+        assertEquals("unex device", expDev, h.locationDevice());
+        assertEquals("unex port", portNumber(expPort), h.locationPort());
+    }
+
+    @Test
+    public void addHosts() {
+        title("addHosts");
+
+        assertHostLinkCounts(0, 0);
+        Host hostA = createHost(DEV_1, 101, "a");
+        Host hostB = createHost(DEV_1, 102, "b");
+
+        // add a host
+        cache.addOrUpdateHost(hostA);
+        dispatcher.assertLast(Type.HOST_ADDED_OR_UPDATED, hostA.id().toString());
+        dispatcher.assertEventCount(1);
+        assertHostLinkCounts(1, 1);
+        assertLocation(hostA.id(), DEVID_1, 101);
+
+        // add a second host
+        cache.addOrUpdateHost(hostB);
+        dispatcher.assertLast(Type.HOST_ADDED_OR_UPDATED, hostB.id().toString());
+        dispatcher.assertEventCount(2);
+        assertHostLinkCounts(2, 2);
+        assertLocation(hostB.id(), DEVID_1, 102);
+
+        // update the first host
+        cache.addOrUpdateHost(hostA);
+        dispatcher.assertLast(Type.HOST_ADDED_OR_UPDATED, hostA.id().toString());
+        dispatcher.assertEventCount(3);
+        assertHostLinkCounts(2, 2);
+        assertLocation(hostA.id(), DEVID_1, 101);
+
+        print(cache.dumpString());
+
+        // remove the second host
+        cache.removeHost(hostB);
+        dispatcher.assertLast(Type.HOST_REMOVED, hostB.id().toString());
+        dispatcher.assertEventCount(4);
+        assertHostLinkCounts(1, 1);
+        assertNull("still host B?", cache.accessHost(hostB.id()));
+
+        print(cache.dumpString());
+
+        // first, verify where host A is currently residing
+        assertLocation(hostA.id(), DEVID_1, 101);
+
+        // now let's move hostA to a different port
+        Host movedHost = createHost(DEV_1, 200, "a");
+        print(hostA);
+        print(movedHost);
+
+        cache.moveHost(movedHost, hostA);
+        dispatcher.assertLast(Type.HOST_MOVED, hostA.id().toString());
+        dispatcher.assertEventCount(5);
+        assertHostLinkCounts(1, 1);
+
+        assertLocation(hostA.id(), DEVID_1, 200);
+
+        print(cache.dumpString());
+
+        // finally, let's move the host to a different device and port
+        Host movedAgain = createHost(DEV_8, 800, "a");
+
+        cache.moveHost(movedAgain, movedHost);
+        dispatcher.assertLast(Type.HOST_MOVED, hostA.id().toString());
+        dispatcher.assertEventCount(6);
+        assertHostLinkCounts(1, 1);
+
+        assertLocation(hostA.id(), DEVID_8, 800);
+
+        print(cache.dumpString());
+    }
+
+
     @Test
     public void load() {
         title("load");