ONOS-4326: TopoRegions: Implement basic structure of response to 'topo2Start' event.
- this is WIP: still need to extract data from model cache.
Change-Id: I5ab843a1c352275a8da89964c886b660e3b8b616
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiNode.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiNode.java
index 79fdf5a..bc9fbf5 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiNode.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiNode.java
@@ -19,7 +19,7 @@
/**
* Represents a node drawn on the topology view (region, device, host).
*/
-abstract class UiNode extends UiElement {
+public abstract class UiNode extends UiElement {
/**
* Default "layer" tag.
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
index be19389..df6dba9 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -37,12 +37,19 @@
import org.onosproject.ui.model.topo.UiDevice;
import org.onosproject.ui.model.topo.UiHost;
import org.onosproject.ui.model.topo.UiLink;
+import org.onosproject.ui.model.topo.UiNode;
import org.onosproject.ui.model.topo.UiRegion;
import org.onosproject.ui.model.topo.UiTopoLayout;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
/**
* Facility for creating JSON messages to send to the topology view in the
@@ -50,6 +57,11 @@
*/
class Topo2Jsonifier {
+ private static final String E_DEF_NOT_LAST =
+ "UiNode.LAYER_DEFAULT not last in layer list";
+ private static final String E_UNKNOWN_UI_NODE =
+ "Unknown subclass of UiNode: ";
+
private final ObjectMapper mapper = new ObjectMapper();
private ServiceDirectory directory;
@@ -87,7 +99,10 @@
portStatsService = directory.get(PortStatisticsService.class);
topologyService = directory.get(TopologyService.class);
tunnelService = directory.get(TunnelService.class);
+ }
+ // for unit testing
+ Topo2Jsonifier() {
}
private ObjectNode objectNode() {
@@ -155,47 +170,104 @@
* Returns a JSON representation of the region to display in the topology
* view.
*
- * @param region the region to transform to JSON
+ * @param region the region to transform to JSON
+ * @param subRegions the subregions within this region
* @return a JSON representation of the data
*/
- ObjectNode region(UiRegion region) {
+ ObjectNode region(UiRegion region, Set<UiRegion> subRegions) {
ObjectNode payload = objectNode();
-
if (region == null) {
payload.put("note", "no-region");
return payload;
}
+ payload.put("id", region.idAsString());
+ payload.set("subregions", jsonSubRegions(subRegions));
- payload.put("id", region.id().toString());
+ List<String> layerTags = region.layerOrder();
+ List<Set<UiNode>> splitDevices = splitByLayer(layerTags, region.devices());
+ List<Set<UiNode>> splitHosts = splitByLayer(layerTags, region.hosts());
+ Set<UiLink> links = region.links();
- ArrayNode layerOrder = arrayNode();
- payload.set("layerOrder", layerOrder);
- region.layerOrder().forEach(layerOrder::add);
-
- ArrayNode devices = arrayNode();
- payload.set("devices", devices);
- for (UiDevice device : region.devices()) {
- devices.add(json(device));
- }
-
- ArrayNode hosts = arrayNode();
- payload.set("hosts", hosts);
- for (UiHost host : region.hosts()) {
- hosts.add(json(host));
- }
-
- ArrayNode links = arrayNode();
- payload.set("links", links);
- for (UiLink link : region.links()) {
- links.add(json(link));
- }
+ payload.set("devices", jsonGrouped(splitDevices));
+ payload.set("hosts", jsonGrouped(splitHosts));
+ payload.set("links", jsonLinks(links));
+ payload.set("layerOrder", jsonStrings(layerTags));
return payload;
}
+ private ArrayNode jsonSubRegions(Set<UiRegion> subregions) {
+ ArrayNode kids = arrayNode();
+ if (subregions != null) {
+ subregions.forEach(s -> kids.add(jsonClosedRegion(s)));
+ }
+ return kids;
+ }
+
+ private ArrayNode jsonStrings(List<String> strings) {
+ ArrayNode array = arrayNode();
+ strings.forEach(array::add);
+ return array;
+ }
+
+ private ArrayNode jsonLinks(Set<UiLink> links) {
+ ArrayNode result = arrayNode();
+ links.forEach(lnk -> result.add(json(lnk)));
+ return result;
+ }
+
+ private ArrayNode jsonGrouped(List<Set<UiNode>> groupedNodes) {
+ ArrayNode result = arrayNode();
+ groupedNodes.forEach(g -> {
+ ArrayNode subset = arrayNode();
+ g.forEach(n -> subset.add(json(n)));
+ result.add(subset);
+ });
+ return result;
+ }
+
+ /**
+ * Returns a JSON payload that encapsulates the devices, hosts, links that
+ * do not belong to any region.
+ *
+ * @param oDevices orphan devices
+ * @param oHosts orphan hosts
+ * @param oLinks orphan links
+ * @param layerTags layer tags
+ * @return a JSON representation of the data
+ */
+ ObjectNode orphans(Set<UiDevice> oDevices, Set<UiHost> oHosts,
+ Set<UiLink> oLinks, List<String> layerTags) {
+
+ ObjectNode payload = objectNode();
+
+ List<Set<UiNode>> splitDevices = splitByLayer(layerTags, oDevices);
+ List<Set<UiNode>> splitHosts = splitByLayer(layerTags, oHosts);
+
+ payload.set("devices", jsonGrouped(splitDevices));
+ payload.set("hosts", jsonGrouped(splitHosts));
+ payload.set("links", jsonLinks(oLinks));
+ payload.set("layerOrder", jsonStrings(layerTags));
+
+ return payload;
+ }
+
+ private ObjectNode json(UiNode node) {
+ if (node instanceof UiRegion) {
+ return jsonClosedRegion((UiRegion) node);
+ }
+ if (node instanceof UiDevice) {
+ return json((UiDevice) node);
+ }
+ if (node instanceof UiHost) {
+ return json((UiHost) node);
+ }
+ throw new IllegalStateException(E_UNKNOWN_UI_NODE + node.getClass());
+ }
+
private ObjectNode json(UiDevice device) {
ObjectNode node = objectNode()
- .put("id", device.id().toString())
+ .put("id", device.idAsString())
.put("type", device.type())
.put("online", device.isOnline())
.put("master", device.master().toString())
@@ -216,7 +288,7 @@
private ObjectNode json(UiHost host) {
return objectNode()
- .put("id", host.id().toString())
+ .put("id", host.idAsString())
.put("layer", host.layer());
// TODO: complete host details
}
@@ -224,9 +296,101 @@
private ObjectNode json(UiLink link) {
return objectNode()
- .put("id", link.id().toString());
+ .put("id", link.idAsString());
// TODO: complete link details
}
+ private ObjectNode jsonClosedRegion(UiRegion region) {
+ return objectNode()
+ .put("id", region.idAsString());
+ // TODO: complete closed-region details
+ }
+
+
+ /**
+ * Returns a JSON array representation of a list of regions. Note that the
+ * information about each region is limited to what needs to be used to
+ * show the regions as nodes on the view.
+ *
+ * @param regions the regions
+ * @return a JSON representation of the minimal region information
+ */
+ public ArrayNode closedRegions(Set<UiRegion> regions) {
+ ArrayNode array = arrayNode();
+ for (UiRegion r : regions) {
+ array.add(jsonClosedRegion(r));
+ }
+ return array;
+ }
+
+ /**
+ * Returns a JSON array representation of a list of devices.
+ *
+ * @param devices the devices
+ * @return a JSON representation of the devices
+ */
+ public ArrayNode devices(Set<UiDevice> devices) {
+ ArrayNode array = arrayNode();
+ for (UiDevice device : devices) {
+ array.add(json(device));
+ }
+ return array;
+ }
+
+ /**
+ * Returns a JSON array representation of a list of hosts.
+ *
+ * @param hosts the hosts
+ * @return a JSON representation of the hosts
+ */
+ public ArrayNode hosts(Set<UiHost> hosts) {
+ ArrayNode array = arrayNode();
+ for (UiHost host : hosts) {
+ array.add(json(host));
+ }
+ return array;
+ }
+
+ /**
+ * Returns a JSON array representation of a list of links.
+ *
+ * @param links the links
+ * @return a JSON representation of the links
+ */
+ public ArrayNode links(Set<UiLink> links) {
+ ArrayNode array = arrayNode();
+ for (UiLink link : links) {
+ array.add(json(link));
+ }
+ return array;
+ }
+
+ // package-private for unit testing
+ List<Set<UiNode>> splitByLayer(List<String> layerTags,
+ Set<? extends UiNode> nodes) {
+ final int nLayers = layerTags.size();
+ if (!layerTags.get(nLayers - 1).equals(LAYER_DEFAULT)) {
+ throw new IllegalArgumentException(E_DEF_NOT_LAST);
+ }
+
+ List<Set<UiNode>> splitList = new ArrayList<>(layerTags.size());
+ Map<String, Set<UiNode>> byLayer = new HashMap<>(layerTags.size());
+
+ for (String tag : layerTags) {
+ Set<UiNode> set = new HashSet<>();
+ byLayer.put(tag, set);
+ splitList.add(set);
+ }
+
+ for (UiNode n : nodes) {
+ String which = n.layer();
+ if (!layerTags.contains(which)) {
+ which = LAYER_DEFAULT;
+ }
+ byLayer.get(which).add(n);
+ }
+
+ return splitList;
+ }
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
index 21023fa..7f44244 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
@@ -17,6 +17,7 @@
package org.onosproject.ui.impl.topo;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.ui.RequestHandler;
@@ -24,6 +25,9 @@
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.impl.UiWebSocket;
import org.onosproject.ui.model.topo.UiClusterMember;
+import org.onosproject.ui.model.topo.UiDevice;
+import org.onosproject.ui.model.topo.UiHost;
+import org.onosproject.ui.model.topo.UiLink;
import org.onosproject.ui.model.topo.UiRegion;
import org.onosproject.ui.model.topo.UiTopoLayout;
import org.slf4j.Logger;
@@ -31,6 +35,9 @@
import java.util.Collection;
import java.util.List;
+import java.util.Set;
+
+import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
/*
NOTES:
@@ -58,11 +65,14 @@
private static final String TOPO2_STOP = "topo2Stop";
// === Outbound event identifiers
+ private static final String ALL_INSTANCES = "topo2AllInstances";
private static final String CURRENT_LAYOUT = "topo2CurrentLayout";
private static final String CURRENT_REGION = "topo2CurrentRegion";
- private static final String ALL_INSTANCES = "topo2AllInstances";
+ private static final String PEER_REGIONS = "topo2PeerRegions";
+ private static final String ORPHANS = "topo2Orphans";
private static final String TOPO_START_DONE = "topo2StartDone";
+
private UiTopoSession topoSession;
private Topo2Jsonifier t2json;
@@ -99,18 +109,36 @@
log.debug("topo2Start: {}", payload);
+ // this is the list of ONOS cluster members
List<UiClusterMember> instances = topoSession.getAllInstances();
sendMessage(ALL_INSTANCES, t2json.instances(instances));
+ // this is the layout that the user has chosen to display
UiTopoLayout currentLayout = topoSession.currentLayout();
sendMessage(CURRENT_LAYOUT, t2json.layout(currentLayout));
+ // this is the region that is associated with the current layout
+ // this message includes details of the sub-regions, devices,
+ // hosts, and links within the region
+ // (as well as layer-order hints)
UiRegion region = topoSession.getRegion(currentLayout);
- sendMessage(CURRENT_REGION, t2json.region(region));
+ Set<UiRegion> kids = topoSession.getSubRegions(currentLayout);
+ sendMessage(CURRENT_REGION, t2json.region(region, kids));
- // TODO: send information about devices/hosts/links in non-region
- // TODO: send information about "linked, peer" regions
+ // these are the regions that are siblings to this one
+ Set<UiRegion> peers = topoSession.getPeerRegions(currentLayout);
+ ObjectNode peersPayload = objectNode();
+ peersPayload.set("peers", t2json.closedRegions(peers));
+ sendMessage(PEER_REGIONS, peersPayload);
+ // return devices, hosts, links belonging to no region
+ Set<UiDevice> oDevices = topoSession.getOrphanDevices();
+ Set<UiHost> oHosts = topoSession.getOrphanHosts();
+ Set<UiLink> oLinks = topoSession.getOrphanLinks();
+ List<String> oLayers = getOrphanLayerOrder();
+ sendMessage(ORPHANS, t2json.orphans(oDevices, oHosts, oLinks, oLayers));
+
+ // finally, tell the UI that we are done
sendMessage(TOPO_START_DONE, null);
@@ -122,6 +150,16 @@
// sendAllHosts();
// sendTopoStartDone();
}
+
+
+ }
+
+ // TODO: we need to decide on how this should really get populated.
+ // For example, to be "backward compatible", this should really be
+ // [ LAYER_OPTICAL, LAYER_PACKET, LAYER_DEFAULT ]
+ private List<String> getOrphanLayerOrder() {
+ // NOTE that LAYER_DEFAULT must always be last in the array
+ return ImmutableList.of(LAYER_DEFAULT);
}
private final class Topo2Stop extends RequestHandler {
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
index 1c9fc9d..611a22d 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
@@ -22,12 +22,17 @@
import org.onosproject.ui.impl.topo.model.UiModelListener;
import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
import org.onosproject.ui.model.topo.UiClusterMember;
+import org.onosproject.ui.model.topo.UiDevice;
+import org.onosproject.ui.model.topo.UiHost;
+import org.onosproject.ui.model.topo.UiLink;
import org.onosproject.ui.model.topo.UiRegion;
import org.onosproject.ui.model.topo.UiTopoLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Coordinates with the {@link UiTopoLayoutService} to access
@@ -44,6 +49,7 @@
* interact with it when topo-related events come in from the client.
*/
public class UiTopoSession implements UiModelListener {
+
private final Logger log = LoggerFactory.getLogger(getClass());
private final UiWebSocket webSocket;
@@ -73,6 +79,13 @@
this.layoutService = layoutService;
}
+ // constructs a neutered instance, for unit testing
+ UiTopoSession() {
+ webSocket = null;
+ username = null;
+ sharedModel = null;
+ }
+
/**
* Initializes the session; registering with the shared model.
*/
@@ -154,6 +167,68 @@
* @return region that the layout is based upon
*/
public UiRegion getRegion(UiTopoLayout layout) {
- return sharedModel.getRegion(layout);
+ return sharedModel.getRegion(layout.regionId());
}
+
+ /**
+ * Returns the regions that are "peers" to this region. That is, based on
+ * the layout the user is viewing, all the regions that are associated with
+ * layouts that are children of the parent layout to this layout.
+ *
+ * @param layout the layout being viewed
+ * @return all regions that are "siblings" to this layout's region
+ */
+ public Set<UiRegion> getPeerRegions(UiTopoLayout layout) {
+ UiRegion currentRegion = getRegion(layout);
+
+ // TODO: consult topo layout service to get hierarchy info...
+ // TODO: then consult shared model to get regions
+ return Collections.emptySet();
+ }
+
+ /**
+ * Returns the subregions of the region in the specified layout.
+ *
+ * @param layout the layout being viewed
+ * @return all regions that are "contained within" this layout's region
+ */
+ public Set<UiRegion> getSubRegions(UiTopoLayout layout) {
+ UiRegion currentRegion = getRegion(layout);
+
+ // TODO: consult topo layout service to get child layouts...
+ // TODO: then consult shared model to get regions
+ return Collections.emptySet();
+ }
+
+
+ /**
+ * Returns all devices that are not in a region.
+ *
+ * @return all devices not in a region
+ */
+ public Set<UiDevice> getOrphanDevices() {
+ // TODO: get devices with no region
+ return Collections.emptySet();
+ }
+
+ /**
+ * Returns all hosts that are not in a region.
+ *
+ * @return all hosts not in a region
+ */
+ public Set<UiHost> getOrphanHosts() {
+ // TODO: get hosts with no region
+ return Collections.emptySet();
+ }
+
+ /**
+ * Returns all links that are not in a region.
+ *
+ * @return all links not in a region
+ */
+ public Set<UiLink> getOrphanLinks() {
+ // TODO: get links with no region
+ return Collections.emptySet();
+ }
+
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
index 54c92f6..debdde9 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
@@ -55,6 +55,7 @@
import org.onosproject.net.link.LinkService;
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionEvent;
+import org.onosproject.net.region.RegionId;
import org.onosproject.net.region.RegionListener;
import org.onosproject.net.region.RegionService;
import org.onosproject.net.statistic.StatisticService;
@@ -62,15 +63,11 @@
import org.onosproject.ui.impl.topo.UiTopoSession;
import org.onosproject.ui.model.ServiceBundle;
import org.onosproject.ui.model.topo.UiClusterMember;
-import org.onosproject.ui.model.topo.UiElement;
import org.onosproject.ui.model.topo.UiRegion;
-import org.onosproject.ui.model.topo.UiTopoLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -210,23 +207,14 @@
return cache.getAllClusterMembers();
}
- public Set<UiElement> getElements(UiTopoLayout layout) {
- Set<UiElement> results = new HashSet<>();
-
- // TODO: figure out how to extract the appropriate nodes
- // from the cache, for the given layout.
-
- return results;
- }
-
/**
- * Returns the region for the given layout.
+ * Returns the region for the given identifier.
*
- * @param layout layout filter
- * @return the region the layout is based upon
+ * @param id region identifier
+ * @return the region
*/
- public UiRegion getRegion(UiTopoLayout layout) {
- return cache.accessRegion(layout.regionId());
+ public UiRegion getRegion(RegionId id) {
+ return cache.accessRegion(id);
}
// =====================================================================
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/Topo2JsonifierTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/Topo2JsonifierTest.java
new file mode 100644
index 0000000..1a06c62
--- /dev/null
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/Topo2JsonifierTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.junit.Test;
+import org.onosproject.ui.impl.AbstractUiImplTest;
+import org.onosproject.ui.model.topo.UiNode;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
+import static org.onosproject.ui.model.topo.UiNode.LAYER_OPTICAL;
+import static org.onosproject.ui.model.topo.UiNode.LAYER_PACKET;
+
+/**
+ * Unit tests for {@link Topo2ViewMessageHandler}.
+ */
+public class Topo2JsonifierTest extends AbstractUiImplTest {
+
+ // mock node class for testing
+ private static class MockNode extends UiNode {
+ private final String id;
+
+ MockNode(String id, String layer) {
+ this.id = id;
+ setLayer(layer);
+ }
+
+ @Override
+ public String idAsString() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+ }
+
+ private static final List<String> ALL_TAGS = ImmutableList.of(
+ LAYER_OPTICAL, LAYER_PACKET, LAYER_DEFAULT
+ );
+
+ private static final List<String> PKT_DEF_TAGS = ImmutableList.of(
+ LAYER_PACKET, LAYER_DEFAULT
+ );
+
+ private static final List<String> DEF_TAG_ONLY = ImmutableList.of(
+ LAYER_DEFAULT
+ );
+
+ private static final MockNode NODE_A = new MockNode("A-O", LAYER_OPTICAL);
+ private static final MockNode NODE_B = new MockNode("B-P", LAYER_PACKET);
+ private static final MockNode NODE_C = new MockNode("C-O", LAYER_OPTICAL);
+ private static final MockNode NODE_D = new MockNode("D-D", LAYER_DEFAULT);
+ private static final MockNode NODE_E = new MockNode("E-P", LAYER_PACKET);
+ private static final MockNode NODE_F = new MockNode("F-r", "random");
+
+ private static final Set<MockNode> NODES = ImmutableSet.of(
+ NODE_A, NODE_B, NODE_C, NODE_D, NODE_E, NODE_F
+ );
+
+ private Topo2Jsonifier t2 = new Topo2Jsonifier();
+
+ @Test
+ public void threeLayers() {
+ print("threeLayers()");
+
+ List<Set<UiNode>> result = t2.splitByLayer(ALL_TAGS, NODES);
+ print(result);
+
+ assertEquals("wrong split size", 3, result.size());
+ Set<UiNode> opt = result.get(0);
+ Set<UiNode> pkt = result.get(1);
+ Set<UiNode> def = result.get(2);
+
+ assertEquals("opt bad size", 2, opt.size());
+ assertEquals("missing node A", true, opt.contains(NODE_A));
+ assertEquals("missing node C", true, opt.contains(NODE_C));
+
+ assertEquals("pkt bad size", 2, pkt.size());
+ assertEquals("missing node B", true, pkt.contains(NODE_B));
+ assertEquals("missing node E", true, pkt.contains(NODE_E));
+
+ assertEquals("def bad size", 2, def.size());
+ assertEquals("missing node D", true, def.contains(NODE_D));
+ assertEquals("missing node F", true, def.contains(NODE_F));
+ }
+
+ @Test
+ public void twoLayers() {
+ print("twoLayers()");
+
+ List<Set<UiNode>> result = t2.splitByLayer(PKT_DEF_TAGS, NODES);
+ print(result);
+
+ assertEquals("wrong split size", 2, result.size());
+ Set<UiNode> pkt = result.get(0);
+ Set<UiNode> def = result.get(1);
+
+ assertEquals("pkt bad size", 2, pkt.size());
+ assertEquals("missing node B", true, pkt.contains(NODE_B));
+ assertEquals("missing node E", true, pkt.contains(NODE_E));
+
+ assertEquals("def bad size", 4, def.size());
+ assertEquals("missing node D", true, def.contains(NODE_D));
+ assertEquals("missing node F", true, def.contains(NODE_F));
+ assertEquals("missing node A", true, def.contains(NODE_A));
+ assertEquals("missing node C", true, def.contains(NODE_C));
+ }
+
+ @Test
+ public void oneLayer() {
+ print("oneLayer()");
+
+ List<Set<UiNode>> result = t2.splitByLayer(DEF_TAG_ONLY, NODES);
+ print(result);
+
+ assertEquals("wrong split size", 1, result.size());
+ Set<UiNode> def = result.get(0);
+
+ assertEquals("def bad size", 6, def.size());
+ assertEquals("missing node D", true, def.contains(NODE_D));
+ assertEquals("missing node F", true, def.contains(NODE_F));
+ assertEquals("missing node A", true, def.contains(NODE_A));
+ assertEquals("missing node C", true, def.contains(NODE_C));
+ assertEquals("missing node B", true, def.contains(NODE_B));
+ assertEquals("missing node E", true, def.contains(NODE_E));
+ }
+}