From b68b9a8177f95fd1169da9beee60f090b018b8f4 Mon Sep 17 00:00:00 2001 From: Jian Li Date: Tue, 23 Feb 2016 10:25:54 +0900 Subject: [PATCH] [ONOS-4016] Add Region codec with unit test for Region REST API Change-Id: Ib7d6daa3adf8b23bea681e7bd3ef64839be65d13 --- .../onosproject/codec/impl/CodecManager.java | 2 + .../onosproject/codec/impl/RegionCodec.java | 145 ++++++++++++++++++ .../codec/impl/RegionCodecTest.java | 122 +++++++++++++++ .../codec/impl/RegionJsonMatcher.java | 86 +++++++++++ .../org/onosproject/codec/impl/Region.json | 13 ++ 5 files changed, 368 insertions(+) create mode 100644 core/common/src/main/java/org/onosproject/codec/impl/RegionCodec.java create mode 100644 core/common/src/test/java/org/onosproject/codec/impl/RegionCodecTest.java create mode 100644 core/common/src/test/java/org/onosproject/codec/impl/RegionJsonMatcher.java create mode 100644 core/common/src/test/resources/org/onosproject/codec/impl/Region.json diff --git a/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java b/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java index de74801a88..5bea2d3ee5 100644 --- a/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java +++ b/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java @@ -58,6 +58,7 @@ import org.onosproject.net.mcast.McastRoute; import org.onosproject.net.meter.Band; import org.onosproject.net.meter.Meter; import org.onosproject.net.meter.MeterRequest; +import org.onosproject.net.region.Region; import org.onosproject.net.statistic.Load; import org.onosproject.net.topology.Topology; import org.onosproject.net.topology.TopologyCluster; @@ -121,6 +122,7 @@ public class CodecManager implements CodecService { registerCodec(NextObjective.class, new NextObjectiveCodec()); registerCodec(McastRoute.class, new McastRouteCodec()); registerCodec(DeviceKey.class, new DeviceKeyCodec()); + registerCodec(Region.class, new RegionCodec()); log.info("Started"); } diff --git a/core/common/src/main/java/org/onosproject/codec/impl/RegionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/RegionCodec.java new file mode 100644 index 0000000000..fd0610199e --- /dev/null +++ b/core/common/src/main/java/org/onosproject/codec/impl/RegionCodec.java @@ -0,0 +1,145 @@ +/* + * Copyright 2016 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.codec.impl; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Sets; +import org.onosproject.cluster.NodeId; +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.net.region.DefaultRegion; +import org.onosproject.net.region.Region; +import org.onosproject.net.region.RegionId; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.IntStream; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.onlab.util.Tools.nullIsIllegal; + +/** + * Codec for the Region class. + */ +public class RegionCodec extends JsonCodec { + + // JSON field names + private static final String REGION_ID = "id"; + private static final String NAME = "name"; + private static final String TYPE = "type"; + private static final String MASTERS = "masters"; + private static final String NODE_ID = "nodeId"; + private static final String REGION_NOT_NULL_MSG = "Region cannot be null"; + private static final String MISSING_MEMBER_MESSAGE = " member is required in Region"; + + private static final BiMap REGION_TYPE_MAP = HashBiMap.create(); + + static { + // key is String representation of Region.Type + // value is Region.Type + REGION_TYPE_MAP.put("CONTINENT", Region.Type.CONTINENT); + REGION_TYPE_MAP.put("COUNTRY", Region.Type.COUNTRY); + REGION_TYPE_MAP.put("METRO", Region.Type.METRO); + REGION_TYPE_MAP.put("CAMPUS", Region.Type.CAMPUS); + REGION_TYPE_MAP.put("BUILDING", Region.Type.BUILDING); + REGION_TYPE_MAP.put("FLOOR", Region.Type.FLOOR); + REGION_TYPE_MAP.put("ROOM", Region.Type.ROOM); + REGION_TYPE_MAP.put("RACK", Region.Type.RACK); + REGION_TYPE_MAP.put("LOGICAL_GROUP", Region.Type.LOGICAL_GROUP); + } + + @Override + public ObjectNode encode(Region region, CodecContext context) { + checkNotNull(region, REGION_NOT_NULL_MSG); + + ObjectNode result = context.mapper().createObjectNode() + .put(REGION_ID, region.id().toString()) + .put(NAME, region.name()) + .put(TYPE, region.type().toString()); + + ArrayNode masters = context.mapper().createArrayNode(); + + region.masters().forEach(sets -> { + ArrayNode setsJson = context.mapper().createArrayNode(); + sets.forEach(nodeId -> setsJson.add(nodeId.toString())); + masters.add(setsJson); + }); + result.set(MASTERS, masters); + return result; + } + + @Override + public Region decode(ObjectNode json, CodecContext context) { + if (json == null || !json.isObject()) { + return null; + } + + // parse masters + List> masters = new ArrayList<>(); + JsonNode mastersJson = json.get(MASTERS); + checkNotNull(mastersJson); + + if (mastersJson != null) { + IntStream.range(0, mastersJson.size()).forEach(i -> { + ObjectNode setsJson = get(mastersJson, i); + final Set nodeIds = Sets.newHashSet(); + if (setsJson != null && setsJson.isArray()) { + Set localNodeIds = Sets.newHashSet(); + IntStream.range(0, mastersJson.size()).forEach(j -> { + ObjectNode nodeIdJson = get(setsJson, j); + localNodeIds.add(decodeNodeId(nodeIdJson)); + }); + nodeIds.addAll(localNodeIds); + } + masters.add(nodeIds); + }); + } + + // parse region id + RegionId regionId = RegionId.regionId(nullIsIllegal(json.get(REGION_ID), + REGION_ID + MISSING_MEMBER_MESSAGE).asText()); + + // parse region name + String name = nullIsIllegal(json.get(NAME), NAME + + MISSING_MEMBER_MESSAGE).asText(); + + // parse region type + String typeText = nullIsIllegal(json.get(TYPE), TYPE + + MISSING_MEMBER_MESSAGE).asText(); + + Region.Type type = REGION_TYPE_MAP.get(typeText); + + return new DefaultRegion(regionId, name, type, masters); + } + + /** + * Decodes node id json to node id object. + * + * @param json json object + * @return decoded node id object + */ + private NodeId decodeNodeId(ObjectNode json) { + NodeId nodeId = NodeId.nodeId(nullIsIllegal(json, NODE_ID + + MISSING_MEMBER_MESSAGE).asText()); + + return nodeId; + } +} diff --git a/core/common/src/test/java/org/onosproject/codec/impl/RegionCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/RegionCodecTest.java new file mode 100644 index 0000000000..8b8d2761d2 --- /dev/null +++ b/core/common/src/test/java/org/onosproject/codec/impl/RegionCodecTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2016 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.codec.impl; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.hamcrest.MatcherAssert; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.cluster.NodeId; +import org.onosproject.codec.JsonCodec; +import org.onosproject.core.CoreService; +import org.onosproject.net.region.DefaultRegion; +import org.onosproject.net.region.Region; +import org.onosproject.net.region.RegionId; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Set; + +import static org.easymock.EasyMock.createMock; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.onosproject.codec.impl.RegionJsonMatcher.matchesRegion; + +/** + * Unit tests for region codec. + */ +public class RegionCodecTest { + + MockCodecContext context; + JsonCodec regionCodec; + final CoreService mockCoreService = createMock(CoreService.class); + + @Before + public void setUp() { + context = new MockCodecContext(); + regionCodec = context.codec(Region.class); + assertThat(regionCodec, notNullValue()); + } + + /** + * Tests encoding of a Region object. + */ + @Test + public void testRegionEncode() { + NodeId nodeId1 = NodeId.nodeId("1"); + NodeId nodeId2 = NodeId.nodeId("2"); + NodeId nodeId3 = NodeId.nodeId("3"); + NodeId nodeId4 = NodeId.nodeId("4"); + + Set set1 = ImmutableSet.of(nodeId1); + Set set2 = ImmutableSet.of(nodeId1, nodeId2); + Set set3 = ImmutableSet.of(nodeId1, nodeId2, nodeId3); + Set set4 = ImmutableSet.of(nodeId1, nodeId2, nodeId3, nodeId4); + List> masters = ImmutableList.of(set1, set2, set3, set4); + + RegionId regionId = RegionId.regionId("1"); + String name = "foo"; + Region.Type type = Region.Type.ROOM; + + Region region = new DefaultRegion(regionId, name, type, masters); + + ObjectNode regionJson = regionCodec.encode(region, context); + assertThat(regionJson, matchesRegion(region)); + } + + /** + * Tests decoding of a json object. + */ + @Test + public void testRegionDecode() throws IOException { + Region region = getRegion("Region.json"); + checkCommonData(region); + + assertThat(region.masters().size(), is(2)); + } + + /** + * Checks that the data shared by all the resource is correct for a given region. + * + * @param region region to check + */ + private void checkCommonData(Region region) { + assertThat(region.id().toString(), is("1")); + assertThat(region.type().toString(), is("ROOM")); + assertThat(region.name(), is("foo")); + } + + /** + * Reads in a region from the given resource and decodes it. + * + * @param resourceName resource to use to read the JSON for the rule + * @return decoded region + * @throws IOException if processing the resource fails + */ + private Region getRegion(String resourceName) throws IOException { + InputStream jsonStream = RegionCodecTest.class.getResourceAsStream(resourceName); + JsonNode json = context.mapper().readTree(jsonStream); + MatcherAssert.assertThat(json, notNullValue()); + Region region = regionCodec.decode((ObjectNode) json, context); + assertThat(region, notNullValue()); + return region; + } +} diff --git a/core/common/src/test/java/org/onosproject/codec/impl/RegionJsonMatcher.java b/core/common/src/test/java/org/onosproject/codec/impl/RegionJsonMatcher.java new file mode 100644 index 0000000000..b87162b330 --- /dev/null +++ b/core/common/src/test/java/org/onosproject/codec/impl/RegionJsonMatcher.java @@ -0,0 +1,86 @@ +/* + * Copyright 2016 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.codec.impl; + +import com.fasterxml.jackson.databind.JsonNode; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.onosproject.net.region.Region; + +/** + * Hamcrest matcher for region. + */ +public final class RegionJsonMatcher extends TypeSafeDiagnosingMatcher { + + private final Region region; + + private RegionJsonMatcher(Region region) { + this.region = region; + } + + @Override + protected boolean matchesSafely(JsonNode jsonRegion, Description description) { + // check id + String jsonRegionId = jsonRegion.get("id").asText(); + String regionId = region.id().toString(); + if (!jsonRegionId.equals(regionId)) { + description.appendText("region id was " + jsonRegionId); + return false; + } + + // check type + String jsonType = jsonRegion.get("type").asText(); + String type = region.type().toString(); + if (!jsonType.equals(type)) { + description.appendText("type was " + jsonType); + return false; + } + + // check name + String jsonName = jsonRegion.get("name").asText(); + String name = region.name(); + if (!jsonName.equals(name)) { + description.appendText("name was " + jsonName); + return false; + } + + // check size of master array + JsonNode jsonMasters = jsonRegion.get("masters"); + if (jsonMasters.size() != region.masters().size()) { + description.appendText("masters size was " + jsonMasters.size()); + return false; + } + + // TODO: check the content inside masters + + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText(region.toString()); + } + + /** + * Factory to allocate a region matcher. + * + * @param region region object we are looking for + * @return matcher + */ + public static RegionJsonMatcher matchesRegion(Region region) { + return new RegionJsonMatcher(region); + } +} diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/Region.json b/core/common/src/test/resources/org/onosproject/codec/impl/Region.json new file mode 100644 index 0000000000..087f66eda2 --- /dev/null +++ b/core/common/src/test/resources/org/onosproject/codec/impl/Region.json @@ -0,0 +1,13 @@ +{ + "id": 1, + "type": "ROOM", + "name": "foo", + "masters": [ + [ + "1" + ], + [ + "1", "2" + ] + ] +} \ No newline at end of file