Unit tets for instruction JSON codecs

Fixed several bugs in codecs turned up by unit tests

Change-Id: Icddb31aa3d2cb58612a0578772d24ff5f113d073
diff --git a/web/api/pom.xml b/web/api/pom.xml
index 093edcf..f1691be 100644
--- a/web/api/pom.xml
+++ b/web/api/pom.xml
@@ -43,6 +43,13 @@
             <artifactId>easymock</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <classifier>tests</classifier>
+        </dependency>
     </dependencies>
 
     <properties>
diff --git a/web/api/src/main/java/org/onosproject/codec/impl/ConnectivityIntentCodec.java b/web/api/src/main/java/org/onosproject/codec/impl/ConnectivityIntentCodec.java
index f460913..3a7aa15 100644
--- a/web/api/src/main/java/org/onosproject/codec/impl/ConnectivityIntentCodec.java
+++ b/web/api/src/main/java/org/onosproject/codec/impl/ConnectivityIntentCodec.java
@@ -20,8 +20,10 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.Constraint;
 import org.onosproject.net.intent.Intent;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -50,6 +52,17 @@
             result.set("treatment", treatmentCodec.encode(intent.treatment(), context));
         }
 
+        if (intent.constraints() != null) {
+            final ArrayNode jsonConstraints = result.putArray("constraints");
+
+            if (intent.constraints() != null) {
+                for (final Constraint constraint : intent.constraints()) {
+                    // TODO: constraint should have its own codec
+                    jsonConstraints.add(constraint.toString());
+                }
+            }
+        }
+
         return result;
     }
 }
diff --git a/web/api/src/main/java/org/onosproject/codec/impl/CriterionCodec.java b/web/api/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
index 81033f2..e6fbea8 100644
--- a/web/api/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
+++ b/web/api/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
@@ -50,6 +50,7 @@
             case ETH_SRC:
             case ETH_DST:
                 final Criteria.EthCriterion ethCriterion = (Criteria.EthCriterion) criterion;
+                result.put("mac", ethCriterion.mac().toString());
                 break;
 
             case ETH_TYPE:
@@ -60,6 +61,8 @@
 
             case IPV4_SRC:
             case IPV6_SRC:
+            case IPV4_DST:
+            case IPV6_DST:
                 final Criteria.IPCriterion iPCriterion = (Criteria.IPCriterion) criterion;
                 result.put("ip", iPCriterion.ip().toString());
                 break;
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/ConnectPointJsonMatcher.java b/web/api/src/test/java/org/onosproject/codec/impl/ConnectPointJsonMatcher.java
new file mode 100644
index 0000000..8d85751
--- /dev/null
+++ b/web/api/src/test/java/org/onosproject/codec/impl/ConnectPointJsonMatcher.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2015 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 org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.net.ConnectPoint;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Hamcrest matcher for connect points.
+ */
+
+public final class ConnectPointJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final ConnectPoint connectPoint;
+
+    private ConnectPointJsonMatcher(ConnectPoint connectPointValue) {
+        connectPoint = connectPointValue;
+    }
+
+    @Override
+    public boolean matchesSafely(JsonNode jsonConnectPoint, Description description) {
+        // check device
+        final String jsonDevice = jsonConnectPoint.get("device").asText();
+        final String device = connectPoint.deviceId().toString();
+        if (!jsonDevice.equals(device)) {
+            description.appendText("device was " + jsonDevice);
+            return false;
+        }
+
+        // check port
+        final String jsonPort = jsonConnectPoint.get("port").asText();
+        final String port = connectPoint.port().toString();
+        if (!jsonPort.equals(port)) {
+            description.appendText("port was " + jsonPort);
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(connectPoint.toString());
+    }
+
+    /**
+     * Factory to allocate an connect point matcher.
+     *
+     * @param connectPoint connect point object we are looking for
+     * @return matcher
+     */
+    public static ConnectPointJsonMatcher matchesConnectPoint(ConnectPoint connectPoint) {
+        return new ConnectPointJsonMatcher(connectPoint);
+    }
+}
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java b/web/api/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java
new file mode 100644
index 0000000..e0baf87
--- /dev/null
+++ b/web/api/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2015 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 org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Hamcrest matcher for criterion objects.
+ */
+public final class CriterionJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    final Criterion criterion;
+
+    private CriterionJsonMatcher(Criterion criterionValue) {
+        criterion = criterionValue;
+    }
+
+    @Override
+    public boolean matchesSafely(JsonNode jsonCriterion, Description description) {
+        final String type = criterion.type().name();
+        final String jsonType = jsonCriterion.get("type").asText();
+        if (!type.equals(jsonType)) {
+            description.appendText("type was " + type);
+            return false;
+        }
+
+        switch (criterion.type()) {
+
+            case IN_PORT:
+                final Criteria.PortCriterion portCriterion = (Criteria.PortCriterion) criterion;
+                final long port = portCriterion.port().toLong();
+                final long jsonPort = jsonCriterion.get("port").asLong();
+                if (port != jsonPort) {
+                    description.appendText("port was " + Long.toString(jsonPort));
+                    return false;
+                }
+                break;
+
+            case ETH_SRC:
+            case ETH_DST:
+                final Criteria.EthCriterion ethCriterion = (Criteria.EthCriterion) criterion;
+                final String mac = ethCriterion.mac().toString();
+                final String jsonMac = jsonCriterion.get("mac").textValue();
+                if (!mac.equals(jsonMac)) {
+                    description.appendText("mac was " + jsonMac);
+                    return false;
+                }
+                break;
+
+            case ETH_TYPE:
+                final Criteria.EthTypeCriterion ethTypeCriterion =
+                        (Criteria.EthTypeCriterion) criterion;
+                final String ethType = ethTypeCriterion.ethType().toString();
+                final String jsonEthType = jsonCriterion.get("ethType").textValue();
+                if (!ethType.equals(jsonEthType)) {
+                    description.appendText("ethType was " + jsonEthType);
+                    return false;
+                }
+                break;
+
+            case IPV4_SRC:
+            case IPV6_SRC:
+            case IPV4_DST:
+            case IPV6_DST:
+                final Criteria.IPCriterion ipCriterion = (Criteria.IPCriterion) criterion;
+                final String ip = ipCriterion.ip().toString();
+                final String jsonIp = jsonCriterion.get("ip").textValue();
+                if (!ip.equals(jsonIp)) {
+                    description.appendText("ip was " + jsonIp);
+                    return false;
+                }
+                break;
+
+            case IP_PROTO:
+                final Criteria.IPProtocolCriterion iPProtocolCriterion =
+                        (Criteria.IPProtocolCriterion) criterion;
+                final byte protocol = iPProtocolCriterion.protocol();
+                final byte jsonProtocol = (byte) jsonCriterion.get("protocol").shortValue();
+                if (protocol != jsonProtocol) {
+                    description.appendText("protocol was " + Byte.toString(jsonProtocol));
+                    return false;
+                }
+                break;
+
+            case VLAN_PCP:
+                final Criteria.VlanPcpCriterion vlanPcpCriterion =
+                        (Criteria.VlanPcpCriterion) criterion;
+                final byte priority = vlanPcpCriterion.priority();
+                final byte jsonPriority = (byte) jsonCriterion.get("protocol").shortValue();
+                if (priority != jsonPriority) {
+                    description.appendText("priority was " + Byte.toString(jsonPriority));
+                    return false;
+                }
+                break;
+
+            case VLAN_VID:
+                final Criteria.VlanIdCriterion vlanIdCriterion =
+                        (Criteria.VlanIdCriterion) criterion;
+                final short vlanId = vlanIdCriterion.vlanId().toShort();
+                final short jsonvlanId = jsonCriterion.get("vlanId").shortValue();
+                if (vlanId != jsonvlanId) {
+                    description.appendText("vlanId was " + Short.toString(jsonvlanId));
+                    return false;
+                }
+                break;
+
+            case TCP_SRC:
+            case TCP_DST:
+                final Criteria.TcpPortCriterion tcpPortCriterion =
+                        (Criteria.TcpPortCriterion) criterion;
+                final byte tcpPort = tcpPortCriterion.tcpPort().byteValue();
+                final byte jsonTcpPort = (byte) jsonCriterion.get("tcpPort").shortValue();
+                if (tcpPort != jsonTcpPort) {
+                    description.appendText("tcp port was " + Byte.toString(jsonTcpPort));
+                    return false;
+                }
+                break;
+
+            case MPLS_LABEL:
+                final Criteria.MplsCriterion mplsCriterion =
+                        (Criteria.MplsCriterion) criterion;
+                final int label = mplsCriterion.label();
+                final int jsonLabel = jsonCriterion.get("label").intValue();
+                if (label != jsonLabel) {
+                    description.appendText("label was " + Integer.toString(jsonLabel));
+                    return false;
+                }
+                break;
+
+            case OCH_SIGID:
+                final Criteria.LambdaCriterion lambdaCriterion =
+                        (Criteria.LambdaCriterion) criterion;
+                final short lambda = lambdaCriterion.lambda();
+                final short jsonLambda = jsonCriterion.get("lambda").shortValue();
+                if (lambda != jsonLambda) {
+                    description.appendText("lambda was " + Short.toString(lambda));
+                    return false;
+                }
+                break;
+
+            case OCH_SIGTYPE:
+                final Criteria.OpticalSignalTypeCriterion opticalSignalTypeCriterion =
+                        (Criteria.OpticalSignalTypeCriterion) criterion;
+                final short signalType = opticalSignalTypeCriterion.signalType();
+                final short jsonSignalType = jsonCriterion.get("signalType").shortValue();
+                if (signalType != jsonSignalType) {
+                    description.appendText("signal type was " + Short.toString(signalType));
+                    return false;
+                }
+                break;
+
+            default:
+                // Don't know how to format this type
+                description.appendText("unknown criterion type " +
+                        criterion.type());
+                return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(criterion.toString());
+    }
+
+    /**
+     * Factory to allocate an criterion matcher.
+     *
+     * @param criterion criterion object we are looking for
+     * @return matcher
+     */
+    public static CriterionJsonMatcher matchesCriterion(Criterion criterion) {
+        return new CriterionJsonMatcher(criterion);
+    }
+}
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/EthernetJsonMatcher.java b/web/api/src/test/java/org/onosproject/codec/impl/EthernetJsonMatcher.java
index fd4190e..c5827b9 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/EthernetJsonMatcher.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/EthernetJsonMatcher.java
@@ -24,12 +24,12 @@
 /**
  * Hamcrest matcher for ethernet objects.
  */
-public class EthernetJsonMatcher extends TypeSafeMatcher<JsonNode> {
+public final class EthernetJsonMatcher extends TypeSafeMatcher<JsonNode> {
 
     private final Ethernet ethernet;
     private String reason = "";
 
-    public EthernetJsonMatcher(Ethernet ethernetValue) {
+    private EthernetJsonMatcher(Ethernet ethernetValue) {
         ethernet = ethernetValue;
     }
 
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java b/web/api/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java
index 781f133..8bf4439 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java
@@ -44,7 +44,7 @@
     JsonCodec<Instruction> instructionCodec;
 
     /**
-     * Sets up for each tests.  Creates a context and fetches the instruction
+     * Sets up for each test.  Creates a context and fetches the instruction
      * codec.
      */
     @Before
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java b/web/api/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
index d5b4ae0..001a865 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
@@ -31,11 +31,11 @@
 /**
  * Hamcrest matcher for instructions.
  */
-public class InstructionJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+public final class InstructionJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
 
     private final Instruction instruction;
 
-    public InstructionJsonMatcher(Instruction instructionValue) {
+    private InstructionJsonMatcher(Instruction instructionValue) {
         instruction = instructionValue;
     }
 
@@ -134,7 +134,7 @@
     }
 
     /**
-     * Matches the contents of a mod lambda instruction.
+     * Matches the contents of a mod Ethernet instruction.
      *
      * @param instructionJson JSON instruction to match
      * @return true if contents match, false otherwise
@@ -262,7 +262,7 @@
     }
 
     /**
-     * Matches the contents of a mod ip instruction.
+     * Matches the contents of a mod MPLS label instruction.
      *
      * @param instructionJson JSON instruction to match
      * @return true if contents match, false otherwise
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/IntentCodecTest.java b/web/api/src/test/java/org/onosproject/codec/impl/IntentCodecTest.java
new file mode 100644
index 0000000..fd84f17
--- /dev/null
+++ b/web/api/src/test/java/org/onosproject/codec/impl/IntentCodecTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2015 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 java.util.List;
+
+import org.junit.Test;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.HostId;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.HostToHostIntent;
+import org.onosproject.net.intent.AbstractIntentTest;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.constraint.BandwidthConstraint;
+import org.onosproject.net.intent.constraint.LambdaConstraint;
+import org.onosproject.net.resource.Bandwidth;
+import org.onosproject.net.resource.Lambda;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableList;
+
+import static org.onosproject.codec.impl.IntentJsonMatcher.matchesIntent;
+import static org.onosproject.net.NetTestTools.hid;
+
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Unit tests for the host to host intent class codec.
+ */
+public class IntentCodecTest extends AbstractIntentTest {
+
+    private final HostId id1 = hid("12:34:56:78:91:ab/1");
+    private final HostId id2 = hid("12:34:56:78:92:ab/1");
+    private final ApplicationId appId =
+            new DefaultApplicationId((short) 3, "test");
+    final TrafficSelector emptySelector =
+            DefaultTrafficSelector.builder().build();
+    final TrafficTreatment emptyTreatment =
+            DefaultTrafficTreatment.builder().build();
+    private final CodecContext context = new MockCodecContext();
+
+    /**
+     * Tests the encoding of a host to host intent.
+     */
+    @Test
+    public void hostToHostIntent() {
+        final HostToHostIntent intent =
+                new HostToHostIntent(appId, id1, id2);
+
+        final JsonCodec<HostToHostIntent> intentCodec =
+                context.codec(HostToHostIntent.class);
+        assertThat(intentCodec, notNullValue());
+
+        final ObjectNode intentJson = intentCodec.encode(intent, context);
+        assertThat(intentJson, matchesIntent(intent));
+    }
+
+    /**
+     * Tests the encoding of a point to point intent.
+     */
+    @Test
+    public void pointToPointIntent() {
+        ConnectPoint ingress = NetTestTools.connectPoint("ingress", 1);
+        ConnectPoint egress = NetTestTools.connectPoint("egress", 2);
+
+        final PointToPointIntent intent =
+                new PointToPointIntent(appId, emptySelector,
+                        emptyTreatment, ingress, egress);
+
+        final CodecContext context = new MockCodecContext();
+        final JsonCodec<PointToPointIntent> intentCodec =
+                context.codec(PointToPointIntent.class);
+        assertThat(intentCodec, notNullValue());
+
+        final ObjectNode intentJson = intentCodec.encode(intent, context);
+        assertThat(intentJson, matchesIntent(intent));
+    }
+
+    /**
+     * Tests the encoding of an intent with treatment, selector and constraints
+     * specified.
+     */
+    @Test
+    public void intentWithTreatmentSelectorAndConstraints() {
+        ConnectPoint ingress = NetTestTools.connectPoint("ingress", 1);
+        ConnectPoint egress = NetTestTools.connectPoint("egress", 2);
+        final TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchIPProtocol((byte) 3)
+                .matchMplsLabel(4)
+                .matchOpticalSignalType((short) 5)
+                .matchLambda((short) 6)
+                .matchEthDst(MacAddress.BROADCAST)
+                .matchIPDst(IpPrefix.valueOf("1.2.3.4/24"))
+                .build();
+        final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setLambda((short) 33)
+                .setMpls(44)
+                .setOutput(PortNumber.CONTROLLER)
+                .setEthDst(MacAddress.BROADCAST)
+                .build();
+        final Constraint constraint1 = new BandwidthConstraint(Bandwidth.valueOf(1.0));
+        final Constraint constraint2 = new LambdaConstraint(Lambda.valueOf(3));
+        final List<Constraint> constraints = ImmutableList.of(constraint1, constraint2);
+
+        final PointToPointIntent intent =
+                new PointToPointIntent(appId, selector, treatment,
+                                       ingress, egress, constraints);
+
+        final CodecContext context = new MockCodecContext();
+        final JsonCodec<PointToPointIntent> intentCodec =
+                context.codec(PointToPointIntent.class);
+        assertThat(intentCodec, notNullValue());
+
+        final ObjectNode intentJson = intentCodec.encode(intent, context);
+        assertThat(intentJson, matchesIntent(intent));
+
+    }
+}
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java b/web/api/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java
new file mode 100644
index 0000000..a49307b
--- /dev/null
+++ b/web/api/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2015 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 java.util.List;
+import java.util.Set;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.HostToHostIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.PointToPointIntent;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Hamcrest matcher to check that an intent representation in JSON matches
+ * the actual intent.
+ */
+public final class IntentJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final Intent intent;
+
+    /**
+     * Constructor is private, use factory method.
+     *
+     * @param intentValue the intent object to compare against
+     */
+    private IntentJsonMatcher(Intent intentValue) {
+        intent = intentValue;
+    }
+
+    /**
+     * Matches the JSON representation of a host to host intent.
+     *
+     * @param jsonIntent JSON representation of the intent
+     * @param description Description object used for recording errors
+     * @return true if the JSON matches the intent, false otherwise
+     */
+    private boolean matchHostToHostIntent(JsonNode jsonIntent, Description description) {
+        final HostToHostIntent hostToHostIntent = (HostToHostIntent) intent;
+
+        // check host one
+        final String host1 = hostToHostIntent.one().toString();
+        final String jsonHost1 = jsonIntent.get("one").asText();
+        if (!host1.equals(jsonHost1)) {
+            description.appendText("host one was " + jsonHost1);
+            return false;
+        }
+
+        // check host 2
+        final String host2 = hostToHostIntent.two().toString();
+        final String jsonHost2 = jsonIntent.get("two").asText();
+        if (!host2.equals(jsonHost2)) {
+            description.appendText("host two was " + jsonHost2);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches the JSON representation of a point to point intent.
+     *
+     * @param jsonIntent JSON representation of the intent
+     * @param description Description object used for recording errors
+     * @return true if the JSON matches the intent, false otherwise
+     */
+    private boolean matchPointToPointIntent(JsonNode jsonIntent, Description description) {
+        final PointToPointIntent pointToPointIntent = (PointToPointIntent) intent;
+
+        // check ingress connection
+        final ConnectPoint ingress = pointToPointIntent.ingressPoint();
+        final ConnectPointJsonMatcher ingressMatcher =
+                ConnectPointJsonMatcher.matchesConnectPoint(ingress);
+        final JsonNode jsonIngress = jsonIntent.get("ingressPoint");
+        final boolean ingressMatches =
+                ingressMatcher.matchesSafely(jsonIngress, description);
+
+        if (!ingressMatches) {
+            description.appendText("ingress was " + jsonIngress);
+            return false;
+        }
+
+        // check egress connection
+        final ConnectPoint egress = pointToPointIntent.egressPoint();
+        final ConnectPointJsonMatcher egressMatcher =
+                ConnectPointJsonMatcher.matchesConnectPoint(egress);
+        final JsonNode jsonEgress = jsonIntent.get("egressPoint");
+        final boolean egressMatches =
+                egressMatcher.matchesSafely(jsonEgress, description);
+
+        if (!egressMatches) {
+            description.appendText("egress was " + jsonEgress);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Matches the JSON representation of a connectivity intent. Calls the
+     * matcher for the connectivity intent subtype.
+     *
+     * @param jsonIntent JSON representation of the intent
+     * @param description Description object used for recording errors
+     * @return true if the JSON matches the intent, false otherwise
+     */
+    private boolean matchConnectivityIntent(JsonNode jsonIntent, Description description) {
+        final ConnectivityIntent connectivityIntent = (ConnectivityIntent) intent;
+
+        // check selector
+        final JsonNode jsonSelector = jsonIntent.get("selector");
+        final TrafficSelector selector = connectivityIntent.selector();
+        final Set<Criterion> criteria = selector.criteria();
+        final JsonNode jsonCriteria = jsonSelector.get("criteria");
+        if (jsonCriteria.size() != criteria.size()) {
+            description.appendText("size of criteria array is "
+                    + Integer.toString(jsonCriteria.size()));
+            return false;
+        }
+
+        for (Criterion criterion : criteria) {
+            boolean criterionFound = false;
+            for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
+                final CriterionJsonMatcher criterionMatcher =
+                        CriterionJsonMatcher.matchesCriterion(criterion);
+                if (criterionMatcher.matches(jsonCriteria.get(criterionIndex))) {
+                    criterionFound = true;
+                    break;
+                }
+            }
+            if (!criterionFound) {
+                description.appendText("criterion not found " + criterion.toString());
+                return false;
+            }
+        }
+
+        // check treatment
+        final JsonNode jsonTreatment = jsonIntent.get("treatment");
+        final TrafficTreatment treatment = connectivityIntent.treatment();
+        final List<Instruction> instructions = treatment.instructions();
+        final JsonNode jsonInstructions = jsonTreatment.get("instructions");
+        if (jsonInstructions.size() != instructions.size()) {
+            description.appendText("size of instructions array is "
+                    + Integer.toString(jsonInstructions.size()));
+            return false;
+        }
+
+        for (Instruction instruction : instructions) {
+            boolean instructionFound = false;
+            for (int instructionIndex = 0; instructionIndex < jsonCriteria.size(); instructionIndex++) {
+                final InstructionJsonMatcher instructionMatcher =
+                        InstructionJsonMatcher.matchesInstruction(instruction);
+                if (instructionMatcher.matches(jsonInstructions.get(instructionIndex))) {
+                    instructionFound = true;
+                    break;
+                }
+            }
+            if (!instructionFound) {
+                description.appendText("instruction not found " + instruction.toString());
+                return false;
+            }
+        }
+
+        // Check constraints
+        final JsonNode jsonConstraints = jsonIntent.get("constraints");
+        if (connectivityIntent.constraints() != null) {
+            if (connectivityIntent.constraints().size() != jsonConstraints.size()) {
+                description.appendText("constraints array size was "
+                        + Integer.toString(jsonConstraints.size()));
+                return false;
+            }
+            for (final Constraint constraint : connectivityIntent.constraints()) {
+                boolean constraintFound = false;
+                final String constraintString = constraint.toString();
+                for (int constraintIndex = 0; constraintIndex < jsonConstraints.size();
+                     constraintIndex++) {
+                    final JsonNode value = jsonConstraints.get(constraintIndex);
+                    if (value.asText().equals(constraintString)) {
+                        constraintFound = true;
+                    }
+                }
+                if (!constraintFound) {
+                    description.appendText("resource missing " + constraintString);
+                    return false;
+                }
+            }
+        } else if (jsonConstraints.size() != 0) {
+            description.appendText("constraint array not empty");
+            return false;
+        }
+
+        if (connectivityIntent instanceof HostToHostIntent) {
+            return matchHostToHostIntent(jsonIntent, description);
+        } else if (connectivityIntent instanceof PointToPointIntent) {
+            return matchPointToPointIntent(jsonIntent, description);
+        } else {
+            description.appendText("class of connectivity intent is unknown");
+            return false;
+        }
+    }
+
+    @Override
+    public boolean matchesSafely(JsonNode jsonIntent, Description description) {
+        // check id
+        final String jsonId = jsonIntent.get("id").asText();
+        final String id = intent.id().toString();
+        if (!jsonId.equals(id)) {
+            description.appendText("id was " + jsonId);
+            return false;
+        }
+
+        // check application id
+        final String jsonAppId = jsonIntent.get("appId").asText();
+        final String appId = intent.appId().toString();
+        if (!jsonAppId.equals(appId)) {
+            description.appendText("appId was " + jsonAppId);
+            return false;
+        }
+
+        // check intent type
+        final String jsonType = jsonIntent.get("type").asText();
+        final String type = intent.getClass().getSimpleName();
+        if (!jsonType.equals(type)) {
+            description.appendText("type was " + jsonType);
+            return false;
+        }
+
+        // check details field
+        final String jsonDetails = jsonIntent.get("details").asText();
+        final String details = intent.toString();
+        if (!jsonDetails.equals(details)) {
+            description.appendText("details were " + jsonDetails);
+            return false;
+        }
+
+        // check resources array
+        final JsonNode jsonResources = jsonIntent.get("resources");
+        if (intent.resources() != null) {
+            if (intent.resources().size() != jsonResources.size()) {
+                description.appendText("resources array size was "
+                                       + Integer.toString(jsonResources.size()));
+                return false;
+            }
+            for (final NetworkResource resource : intent.resources()) {
+                boolean resourceFound = false;
+                final String resourceString = resource.toString();
+                for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
+                    final JsonNode value = jsonResources.get(resourceIndex);
+                    if (value.asText().equals(resourceString)) {
+                        resourceFound = true;
+                    }
+                }
+                if (!resourceFound) {
+                    description.appendText("resource missing " + resourceString);
+                    return false;
+                }
+            }
+        } else if (jsonResources.size() != 0) {
+            description.appendText("resources array empty");
+            return false;
+        }
+
+        if (intent instanceof ConnectivityIntent) {
+            return matchConnectivityIntent(jsonIntent, description);
+        } else {
+            description.appendText("class of intent is unknown");
+            return false;
+        }
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(intent.toString());
+    }
+
+    /**
+     * Factory to allocate an intent matcher.
+     *
+     * @param intent intent object we are looking for
+     * @return matcher
+     */
+    public static IntentJsonMatcher matchesIntent(Intent intent) {
+        return new IntentJsonMatcher(intent);
+    }
+}
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/MockCodecContext.java b/web/api/src/test/java/org/onosproject/codec/impl/MockCodecContext.java
index f54f523..f49e829 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/MockCodecContext.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/MockCodecContext.java
@@ -28,6 +28,9 @@
     private ObjectMapper mapper = new ObjectMapper();
     private CodecManager manager = new CodecManager();
 
+    /**
+     * Constructs a new mock codec context.
+     */
     public MockCodecContext() {
         manager.activate();
     }