/* * Copyright 2016-present 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.ui.impl.topo.model; import org.junit.Before; import org.junit.Test; import org.onosproject.event.Event; 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.ui.model.topo.UiClusterMember; import org.onosproject.ui.model.topo.UiDevice; import org.onosproject.ui.model.topo.UiDeviceLink; import org.onosproject.ui.model.topo.UiElement; import org.onosproject.ui.model.topo.UiHost; import org.onosproject.ui.model.topo.UiLinkId; import org.onosproject.ui.model.topo.UiModelEvent.Type; import org.onosproject.ui.model.topo.UiRegion; import java.util.Collection; import java.util.Iterator; import static org.junit.Assert.*; import static org.onosproject.cluster.NodeId.nodeId; import static org.onosproject.net.PortNumber.portNumber; import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId; /** * Unit tests for {@link ModelCache}. */ public class ModelCacheTest extends AbstractTopoModelTest { private class TestEvDisp implements EventDispatcher { private Event<Type, UiElement> lastEvent = null; private int eventCount = 0; @Override public void post(Event event) { lastEvent = event; eventCount++; // print("Event dispatched: %s", event); } private void assertEventCount(int exp) { assertEquals("unex event count", exp, eventCount); } private void assertLast(Type expEventType, String expId) { assertNotNull("no last event", lastEvent); assertEquals("unex event type", expEventType, lastEvent.type()); assertEquals("unex element ID", expId, lastEvent.subject().idAsString()); } } private final TestEvDisp dispatcher = new TestEvDisp(); private ModelCache cache; private void assertContains(String msg, Collection<?> coll, Object... things) { for (Object o : things) { assertTrue(msg, coll.contains(o)); } } @Before public void setUp() { cache = new ModelCache(MOCK_SERVICES, dispatcher); } @Test public void basic() { title("basic"); print(cache); assertEquals("unex # members", 0, cache.clusterMemberCount()); assertEquals("unex # regions", 0, cache.regionCount()); } @Test public void addAndRemoveClusterMember() { title("addAndRemoveClusterMember"); print(cache); assertEquals("unex # members", 0, cache.clusterMemberCount()); dispatcher.assertEventCount(0); cache.addOrUpdateClusterMember(CNODE_1); print(cache); assertEquals("unex # members", 1, cache.clusterMemberCount()); dispatcher.assertEventCount(1); dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1); cache.removeClusterMember(CNODE_1); print(cache); assertEquals("unex # members", 0, cache.clusterMemberCount()); dispatcher.assertEventCount(2); dispatcher.assertLast(Type.CLUSTER_MEMBER_REMOVED, C1); } @Test public void nonExistentClusterMember() { title("nonExistentClusterMember"); cache.addOrUpdateClusterMember(CNODE_1); print(cache); assertEquals("unex # members", 1, cache.clusterMemberCount()); dispatcher.assertEventCount(1); dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1); cache.removeClusterMember(CNODE_2); assertEquals("unex # members", 1, cache.clusterMemberCount()); dispatcher.assertEventCount(1); } @Test public void createThreeNodeCluster() { title("createThreeNodeCluster"); cache.addOrUpdateClusterMember(CNODE_1); dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1); cache.addOrUpdateClusterMember(CNODE_2); dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C2); cache.addOrUpdateClusterMember(CNODE_3); dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C3); dispatcher.assertEventCount(3); print(cache); } @Test public void addNodeThenExamineIt() { title("addNodeThenExamineIt"); cache.addOrUpdateClusterMember(CNODE_1); dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1); UiClusterMember member = cache.accessClusterMember(nodeId(C1)); print(member); // see AbstractUiImplTest Mock Environment for expected values... assertEquals("wrong id str", C1, member.idAsString()); assertEquals("wrong id", nodeId(C1), member.id()); assertEquals("not online", true, member.isOnline()); assertEquals("not ready", true, member.isReady()); } @Test public void addNodeAndDevices() { title("addNodeAndDevices"); cache.addOrUpdateClusterMember(CNODE_1); dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1); cache.addOrUpdateDevice(DEV_1); dispatcher.assertLast(Type.DEVICE_ADDED_OR_UPDATED, D1); cache.addOrUpdateDevice(DEV_2); dispatcher.assertLast(Type.DEVICE_ADDED_OR_UPDATED, D2); cache.addOrUpdateDevice(DEV_3); dispatcher.assertLast(Type.DEVICE_ADDED_OR_UPDATED, D3); dispatcher.assertEventCount(4); print(cache); assertEquals("unex # nodes", 1, cache.clusterMemberCount()); assertEquals("unex # devices", 3, cache.deviceCount()); cache.removeDevice(DEV_4); assertEquals("unex # devices", 3, cache.deviceCount()); dispatcher.assertEventCount(4); cache.removeDevice(DEV_2); dispatcher.assertLast(Type.DEVICE_REMOVED, D2); dispatcher.assertEventCount(5); // check out details of device UiDevice dev = cache.accessDevice(DEVID_1); assertEquals("wrong id", D1, dev.idAsString()); assertEquals("wrong region", R1, dev.regionId().toString()); Device d = dev.backingDevice(); assertEquals("wrong serial", SERIAL, d.serialNumber()); } @Test public void addRegions() { title("addRegions"); cache.addOrUpdateRegion(REGION_1); dispatcher.assertLast(Type.REGION_ADDED_OR_UPDATED, R1); dispatcher.assertEventCount(1); assertEquals("unex # regions", 1, cache.regionCount()); cache.addOrUpdateRegion(REGION_2); dispatcher.assertLast(Type.REGION_ADDED_OR_UPDATED, R2); dispatcher.assertEventCount(2); assertEquals("unex # regions", 2, cache.regionCount()); print(cache); cache.removeRegion(REGION_3); dispatcher.assertEventCount(2); assertEquals("unex # regions", 2, cache.regionCount()); cache.removeRegion(REGION_1); dispatcher.assertLast(Type.REGION_REMOVED, R1); dispatcher.assertEventCount(3); assertEquals("unex # regions", 1, cache.regionCount()); print(cache); UiRegion region = cache.accessRegion(REGION_2.id()); assertEquals("wrong id", REGION_2.id(), region.id()); assertEquals("unex # device IDs", 3, region.deviceIds().size()); assertContains("missing ID", region.deviceIds(), DEVID_4, DEVID_5, DEVID_6); } private static final String[] LINKS_2_7 = {D2, "27", D7, "72"}; @Test public void addLinks() { title("addLinks"); Iterator<Link> iter = makeLinkPair(LINKS_2_7).iterator(); Link link1 = iter.next(); Link link2 = iter.next(); print(link1); print(link2); UiLinkId idA2B = uiLinkId(link1); UiLinkId idB2A = uiLinkId(link2); // remember, link IDs are canonicalized assertEquals("not same link ID", idA2B, idB2A); // we've established that the ID is the same for both UiLinkId linkId = idA2B; cache.addOrUpdateDeviceLink(link1); dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString()); dispatcher.assertEventCount(1); assertEquals("unex # links", 1, cache.deviceLinkCount()); UiDeviceLink link = cache.accessDeviceLink(linkId); assertEquals("dev A not d2", DEVID_2, link.deviceA()); assertEquals("dev B not d7", DEVID_7, link.deviceB()); assertEquals("wrong backing link A-B", link1, link.linkAtoB()); assertEquals("backing link B-A?", null, link.linkBtoA()); cache.addOrUpdateDeviceLink(link2); dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString()); dispatcher.assertEventCount(2); // NOTE: yes! expect 1 UiLink assertEquals("unex # links", 1, cache.deviceLinkCount()); link = cache.accessDeviceLink(linkId); assertEquals("dev A not d2", DEVID_2, link.deviceA()); assertEquals("dev B not d7", DEVID_7, link.deviceB()); assertEquals("wrong backing link A-B", link1, link.linkAtoB()); assertEquals("wrong backing link B-A", link2, link.linkBtoA()); // now remove links one at a time cache.removeDeviceLink(link1); // NOTE: yes! ADD_OR_UPDATE, since the link was updated dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString()); dispatcher.assertEventCount(3); // NOTE: yes! expect 1 UiLink (still) assertEquals("unex # links", 1, cache.deviceLinkCount()); link = cache.accessDeviceLink(linkId); assertEquals("dev A not d2", DEVID_2, link.deviceA()); assertEquals("dev B not d7", DEVID_7, link.deviceB()); assertEquals("backing link A-B?", null, link.linkAtoB()); assertEquals("wrong backing link B-A", link2, link.linkBtoA()); // remove final link cache.removeDeviceLink(link2); dispatcher.assertLast(Type.LINK_REMOVED, linkId.toString()); dispatcher.assertEventCount(4); // NOTE: finally link should be removed from cache assertEquals("unex # links", 0, cache.deviceLinkCount()); } private void assertHostLinkCounts(int nHosts, int nLinks) { assertEquals("unex # hosts", nHosts, cache.hostCount()); assertEquals("unex # links", nLinks, cache.edgeLinkCount()); } 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"); cache.load(); print(cache.dumpString()); // See mock service bundle for expected values (AbstractTopoModelTest) assertEquals("unex # cnodes", 3, cache.clusterMemberCount()); assertEquals("unex # regions", 3, cache.regionCount()); assertEquals("unex # devices", 9, cache.deviceCount()); assertEquals("unex # hosts", 18, cache.hostCount()); assertEquals("unex # device-links", 8, cache.deviceLinkCount()); assertEquals("unex # edge-links", 18, cache.edgeLinkCount()); assertEquals("unex # synth-links", 0, cache.synthLinkCount()); } }