[ONOS-6624]Unit tests for Pi* core classes

Change-Id: Ia0a20712f8108549b3ac6434be8aee1bc6025bb2
diff --git a/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java b/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
index 38c2405..7a89895 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
@@ -136,7 +136,6 @@
     Criterion sameAsMatchUdpPort1 = Criteria.matchUdpSrc(tpPort1);
     Criterion matchUdpPort2 = Criteria.matchUdpDst(tpPort2);
 
-
     int tcpFlags1 =
         Criterion.TcpFlags.NS.getValue() |
         Criterion.TcpFlags.CWR.getValue() |
@@ -340,6 +339,7 @@
         assertThatClassIsImmutable(OduSignalIdCriterion.class);
         assertThatClassIsImmutable(OduSignalTypeCriterion.class);
         assertThatClassIsImmutable(PbbIsidCriterion.class);
+        assertThatClassIsImmutable(PiCriterion.class);
     }
 
     // PortCriterion class
diff --git a/core/api/src/test/java/org/onosproject/net/flow/criteria/PiCriteriaTest.java b/core/api/src/test/java/org/onosproject/net/flow/criteria/PiCriteriaTest.java
new file mode 100644
index 0000000..d1f30e1
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/flow/criteria/PiCriteriaTest.java
@@ -0,0 +1,599 @@
+/*
+ * Copyright 2017-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.net.flow.criteria;
+
+import com.google.common.collect.Maps;
+import com.google.common.testing.EqualsTester;
+import org.apache.commons.collections.CollectionUtils;
+import org.junit.Test;
+import org.onosproject.net.pi.runtime.PiExactFieldMatch;
+import org.onosproject.net.pi.runtime.PiFieldMatch;
+import org.onosproject.net.pi.runtime.PiHeaderFieldId;
+import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
+import org.onosproject.net.pi.runtime.PiRangeFieldMatch;
+import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
+import org.onosproject.net.pi.runtime.PiValidFieldMatch;
+
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+
+/**
+ * Unit tests for the PiCriteria class.
+ */
+public class PiCriteriaTest {
+
+    PiHeaderFieldId piEthHeaderFieldId = PiHeaderFieldId.of("ethernet_t", "etherType");
+    byte[] matchExactBytes1 = {0x08, 0x00};
+    byte[] matchExactBytes2 = {0x08, 0x06};
+    Criterion matchPiExactByte1 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactBytes1).build();
+    Criterion sameAsMatchPiExactByte1 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactBytes1).build();
+    Criterion matchPiExactByte2 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactBytes2).build();
+
+    short matchExactShort1 = 0x800;
+    short matchExactShort2 = 0x806;
+    Criterion matchPiExactShort1 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactShort1).build();
+    Criterion sameAsMatchPiExactShort1 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactShort1).build();
+    Criterion matchPiExactShort2 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactShort2).build();
+
+    int matchExactInt1 = 0x800;
+    int matchExactInt2 = 0x806;
+    Criterion matchPiExactInt1 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactInt1).build();
+    Criterion sameAsMatchPiExactInt1 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactInt1).build();
+    Criterion matchPiExactInt2 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactInt2).build();
+
+    long matchExactLong1 = 0x800;
+    long matchExactLong2 = 0x806;
+    Criterion matchPiExactLong1 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactLong1).build();
+    Criterion sameAsMatchPiExactLong1 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactLong1).build();
+    Criterion matchPiExactLong2 = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactLong2).build();
+
+    PiHeaderFieldId piIpv4HeaderFieldId = PiHeaderFieldId.of("ipv4_t", "dstAddr");
+    int mask = 0x00ffffff;
+    byte[] matchLpmBytes1 = {0x0a, 0x01, 0x01, 0x01};
+    byte[] matchLpmBytes2 = {0x0a, 0x01, 0x01, 0x02};
+    Criterion matchPiLpmByte1 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmBytes1, mask).build();
+    Criterion sameAsMatchPiLpmByte1 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmBytes1, mask).build();
+    Criterion matchPiLpmByte2 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmBytes2, mask).build();
+
+    short matchLpmShort1 = 0x0a0a;
+    short matchLpmShort2 = 0x0a0b;
+    Criterion matchPiLpmShort1 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmShort1, mask).build();
+    Criterion sameAsMatchPiLpmShort1 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId,
+            matchLpmShort1, mask)
+            .build();
+    Criterion matchPiLpmShort2 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmShort2, mask).build();
+
+    int matchLpmInt1 = 0x0a010101;
+    int matchLpmInt2 = 0x0a010102;
+    Criterion matchPiLpmInt1 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmInt1, mask).build();
+    Criterion sameAsMatchPiLpmInt1 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmInt1, mask).build();
+    Criterion matchPiLpmInt2 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmInt2, mask).build();
+
+    long matchLpmLong1 = 0x0a010101;
+    long matchLpmLong2 = 0x0a010102;
+    Criterion matchPiLpmLong1 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmLong1, mask).build();
+    Criterion sameAsMatchPiLpmLong1 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmLong1, mask).build();
+    Criterion matchPiLpmLong2 = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmLong2, mask).build();
+
+
+    byte[] matchTernaryBytes1 = {0x0a, 0x01, 0x01, 0x01};
+    byte[] matchTernaryBytes2 = {0x0a, 0x01, 0x01, 0x02};
+    byte[] matchTernaryMaskBytes = {0x7f, 0x7f, 0x7f, 0x00};
+    Criterion matchPiTernaryByte1 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryBytes1,
+            matchTernaryMaskBytes)
+            .build();
+    Criterion sameAsMatchPiTernaryByte1 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryBytes1,
+            matchTernaryMaskBytes)
+            .build();
+    Criterion matchPiTernaryByte2 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryBytes2,
+            matchTernaryMaskBytes)
+            .build();
+
+    short matchTernaryShort1 = 0x0a0a;
+    short matchTernaryShort2 = 0x0a0b;
+    short matchTernaryMaskShort = 0xff0;
+    Criterion matchPiTernaryShort1 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryShort1,
+            matchTernaryMaskShort)
+            .build();
+    Criterion sameAsMatchPiTernaryShort1 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryShort1,
+            matchTernaryMaskShort)
+            .build();
+    Criterion matchPiTernaryShort2 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryShort2,
+            matchTernaryMaskShort)
+            .build();
+
+    int matchTernaryInt1 = 0x0a010101;
+    int matchTernaryInt2 = 0x0a010102;
+    int matchTernaryMaskInt = 0xffff;
+    Criterion matchPiTernaryInt1 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryInt1,
+            matchTernaryMaskInt)
+            .build();
+    Criterion sameAsMatchPiTernaryInt1 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryInt1,
+            matchTernaryMaskInt)
+            .build();
+    Criterion matchPiTernaryInt2 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryInt2,
+            matchTernaryMaskInt)
+            .build();
+
+    long matchTernaryLong1 = 0x0a010101;
+    long matchTernaryLong2 = 0x0a010102;
+    long matchTernaryMaskLong = 0xffff;
+    Criterion matchPiTernaryLong1 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryLong1,
+            matchTernaryMaskLong)
+            .build();
+    Criterion sameAsMatchPiTernaryLong1 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryLong1,
+            matchTernaryMaskLong)
+            .build();
+    Criterion matchPiTernaryLong2 = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId,
+            matchTernaryLong2,
+            matchTernaryMaskLong)
+            .build();
+
+    Criterion matchPiValid1 = PiCriterion.builder().matchValid(piIpv4HeaderFieldId, false).build();
+    Criterion sameAsMatchPiValid1 = PiCriterion.builder().matchValid(piIpv4HeaderFieldId, false).build();
+    Criterion matchPiValid2 = PiCriterion.builder().matchValid(piIpv4HeaderFieldId, true).build();
+
+    byte[] matchRangeBytes1 = {0x10};
+    byte[] matchRangeBytes2 = {0x20};
+    byte[] matchRangeHighBytes = {0x30};
+    Criterion matchPiRangeByte1 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeBytes1,
+            matchRangeHighBytes)
+            .build();
+    Criterion sameAsMatchPiRangeByte1 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeBytes1,
+            matchRangeHighBytes)
+            .build();
+    Criterion matchPiRangeByte2 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeBytes2,
+            matchRangeHighBytes)
+            .build();
+
+    short matchRangeShort1 = 0x100;
+    short matchRangeShort2 = 0x200;
+    short matchRangeHighShort = 0x300;
+    Criterion matchPiRangeShort1 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeShort1,
+            matchRangeHighShort)
+            .build();
+    Criterion sameAsMatchPiRangeShort1 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeShort1,
+            matchRangeHighShort)
+            .build();
+    Criterion matchPiRangeShort2 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeShort2,
+            matchRangeHighShort)
+            .build();
+
+    int matchRangeInt1 = 0x100;
+    int matchRangeInt2 = 0x200;
+    int matchRangeHighInt = 0x300;
+    Criterion matchPiRangeInt1 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeInt1,
+            matchRangeHighInt)
+            .build();
+    Criterion sameAsMatchPiRangeInt1 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeInt1,
+            matchRangeHighInt)
+            .build();
+    Criterion matchPiRangeInt2 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeInt2,
+            matchRangeHighInt)
+            .build();
+
+    long matchRangeLong1 = 0x100;
+    long matchRangeLong2 = 0x200;
+    long matchRangeHighLong = 0x300;
+    Criterion matchPiRangeLong1 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeLong1,
+            matchRangeHighLong)
+            .build();
+    Criterion sameAsMatchPiRangeLong1 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeLong1,
+            matchRangeHighLong)
+            .build();
+    Criterion matchPiRangeLong2 = PiCriterion.builder().matchRange(piIpv4HeaderFieldId,
+            matchRangeLong2,
+            matchRangeHighLong)
+            .build();
+
+    /**
+     * Checks that a Criterion object has the proper type, and then converts
+     * it to the proper type.
+     *
+     * @param criterion Criterion object to convert
+     * @param type      Enumerated type value for the Criterion class
+     * @param clazz     Desired Criterion class
+     * @param <T>       The type the caller wants returned
+     * @return converted object
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T checkAndConvert(Criterion criterion, Criterion.Type type, Class clazz) {
+        assertThat(criterion, is(notNullValue()));
+        assertThat(criterion.type(), is(equalTo(type)));
+        assertThat(criterion, instanceOf(clazz));
+        return (T) criterion;
+    }
+
+    /**
+     * Test the ExactMatchPi method.
+     */
+    @Test
+    public void testExactMatchPiMethod() {
+
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesExactBytes = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesExactShort = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesExactInt = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesExactLong = Maps.newHashMap();
+
+        Criterion matchPiBytes = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactBytes1).build();
+        PiCriterion piCriterionBytes =
+                checkAndConvert(matchPiBytes, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesExactBytes.put(piEthHeaderFieldId, new PiExactFieldMatch(piEthHeaderFieldId,
+                copyFrom(matchExactBytes1)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionBytes.fieldMatches(), fieldMatchesExactBytes.values()));
+        assertThat(piCriterionBytes.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+
+        Criterion matchPiShort = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactShort1).build();
+        PiCriterion piCriterionShort =
+                checkAndConvert(matchPiShort, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesExactShort.put(piEthHeaderFieldId, new PiExactFieldMatch(piEthHeaderFieldId,
+                copyFrom(matchExactShort1)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionShort.fieldMatches(), fieldMatchesExactShort.values()));
+        assertThat(piCriterionShort.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+
+        Criterion matchPiInt = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactInt1).build();
+        PiCriterion piCriterionInt =
+                checkAndConvert(matchPiInt, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesExactInt.put(piEthHeaderFieldId, new PiExactFieldMatch(piEthHeaderFieldId,
+                copyFrom(matchExactInt1)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionInt.fieldMatches(), fieldMatchesExactInt.values()));
+        assertThat(piCriterionInt.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+
+        Criterion matchPiLong = PiCriterion.builder().matchExact(piEthHeaderFieldId, matchExactLong1).build();
+        PiCriterion piCriterionLong =
+                checkAndConvert(matchPiLong, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesExactLong.put(piEthHeaderFieldId, new PiExactFieldMatch(piEthHeaderFieldId,
+                copyFrom(matchExactLong1)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionLong.fieldMatches(), fieldMatchesExactLong.values()));
+        assertThat(piCriterionLong.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+    }
+
+    /**
+     * Test the LpmMatchPi method.
+     */
+    @Test
+    public void testLpmMatchPiMethod() {
+
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesLpmBytes = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesLpmShort = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesLpmInt = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesLpmLong = Maps.newHashMap();
+
+        Criterion matchPiBytes = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmBytes1, mask).build();
+        PiCriterion piCriterionBytes =
+                checkAndConvert(matchPiBytes, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesLpmBytes.put(piIpv4HeaderFieldId, new PiLpmFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchLpmBytes1), mask));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionBytes.fieldMatches(), fieldMatchesLpmBytes.values()));
+        assertThat(piCriterionBytes.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+
+        Criterion matchPiShort = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmShort1, mask).build();
+        PiCriterion piCriterionShort =
+                checkAndConvert(matchPiShort, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesLpmShort.put(piIpv4HeaderFieldId, new PiLpmFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchLpmShort1), mask));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionShort.fieldMatches(), fieldMatchesLpmShort.values()));
+        assertThat(piCriterionShort.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+
+        Criterion matchPiInt = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmInt1, mask).build();
+        PiCriterion piCriterionInt =
+                checkAndConvert(matchPiInt, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesLpmInt.put(piIpv4HeaderFieldId, new PiLpmFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchLpmInt1), mask));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionInt.fieldMatches(), fieldMatchesLpmInt.values()));
+        assertThat(piCriterionInt.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+
+        Criterion matchPiLong = PiCriterion.builder().matchLpm(piIpv4HeaderFieldId, matchLpmLong1, mask).build();
+        PiCriterion piCriterionLong =
+                checkAndConvert(matchPiLong, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesLpmLong.put(piIpv4HeaderFieldId, new PiLpmFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchLpmLong1), mask));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionLong.fieldMatches(), fieldMatchesLpmLong.values()));
+        assertThat(piCriterionLong.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+    }
+
+    /**
+     * Test the TernaryMatchPi method.
+     */
+    @Test
+    public void testTernaryMatchPiMethod() {
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesTernaryBytes = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesTernaryShort = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesTernaryInt = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesTernaryLong = Maps.newHashMap();
+
+        Criterion matchPiBytes = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId, matchTernaryBytes1,
+                matchTernaryMaskBytes)
+                .build();
+        PiCriterion piCriterionBytes =
+                checkAndConvert(matchPiBytes, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesTernaryBytes.put(piIpv4HeaderFieldId, new PiTernaryFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchTernaryBytes1),
+                copyFrom(matchTernaryMaskBytes)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionBytes.fieldMatches(), fieldMatchesTernaryBytes.values()));
+        assertThat(piCriterionBytes.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+        Criterion matchPiShort = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId, matchTernaryShort1,
+                matchTernaryMaskShort)
+                .build();
+        PiCriterion piCriterionShort =
+                checkAndConvert(matchPiShort, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesTernaryShort.put(piIpv4HeaderFieldId, new PiTernaryFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchTernaryShort1),
+                copyFrom(matchTernaryMaskShort)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionShort.fieldMatches(), fieldMatchesTernaryShort.values()));
+        assertThat(piCriterionShort.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+        Criterion matchPiInt = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId, matchTernaryInt1,
+                matchTernaryMaskInt)
+                .build();
+        PiCriterion piCriterionInt =
+                checkAndConvert(matchPiInt, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesTernaryInt.put(piIpv4HeaderFieldId, new PiTernaryFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchTernaryInt1),
+                copyFrom(matchTernaryMaskInt)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionInt.fieldMatches(), fieldMatchesTernaryInt.values()));
+        assertThat(piCriterionInt.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+        Criterion matchPiLong = PiCriterion.builder().matchTernary(piIpv4HeaderFieldId, matchTernaryLong1,
+                matchTernaryMaskLong)
+                .build();
+        PiCriterion piCriterionLong =
+                checkAndConvert(matchPiLong, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesTernaryLong.put(piIpv4HeaderFieldId, new PiTernaryFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchTernaryLong1),
+                copyFrom(matchTernaryMaskLong)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionLong.fieldMatches(), fieldMatchesTernaryLong.values()));
+        assertThat(piCriterionLong.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+    }
+
+    /**
+     * Test the ValidMatchPi method.
+     */
+    @Test
+    public void testValidMatchPiMethod() {
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesValid = Maps.newHashMap();
+        Criterion matchPiBytes = PiCriterion.builder().matchValid(piIpv4HeaderFieldId, true).build();
+        PiCriterion piCriterionBytes = checkAndConvert(matchPiBytes, Criterion.Type.PROTOCOL_INDEPENDENT,
+                PiCriterion.class);
+        fieldMatchesValid.put(piIpv4HeaderFieldId, new PiValidFieldMatch(piIpv4HeaderFieldId, true));
+
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionBytes.fieldMatches(), fieldMatchesValid.values()));
+        assertThat(piCriterionBytes.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+    }
+
+    /**
+     * Test the RangeMatchPi method.
+     */
+    @Test
+    public void testRangeMatchPiMethod() {
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesRangeBytes = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesRangeShort = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesRangeInt = Maps.newHashMap();
+        Map<PiHeaderFieldId, PiFieldMatch> fieldMatchesRangeLong = Maps.newHashMap();
+
+        Criterion matchPiBytes = PiCriterion.builder().matchRange(piIpv4HeaderFieldId, matchRangeBytes1,
+                matchRangeHighBytes)
+                .build();
+        PiCriterion piCriterionBytes =
+                checkAndConvert(matchPiBytes, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesRangeBytes.put(piIpv4HeaderFieldId, new PiRangeFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchRangeBytes1),
+                copyFrom(matchRangeHighBytes)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionBytes.fieldMatches(), fieldMatchesRangeBytes.values()));
+        assertThat(piCriterionBytes.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+        Criterion matchPiShort = PiCriterion.builder().matchRange(piIpv4HeaderFieldId, matchRangeShort1,
+                matchRangeHighShort)
+                .build();
+        PiCriterion piCriterionShort =
+                checkAndConvert(matchPiShort, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesRangeShort.put(piIpv4HeaderFieldId, new PiRangeFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchRangeShort1),
+                copyFrom(matchRangeHighShort)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionShort.fieldMatches(), fieldMatchesRangeShort.values()));
+        assertThat(piCriterionShort.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+        Criterion matchPiInt = PiCriterion.builder().matchRange(piIpv4HeaderFieldId, matchRangeInt1,
+                matchRangeHighInt)
+                .build();
+        PiCriterion piCriterionInt =
+                checkAndConvert(matchPiInt, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesRangeInt.put(piIpv4HeaderFieldId, new PiRangeFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchRangeInt1),
+                copyFrom(matchRangeHighInt)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionInt.fieldMatches(), fieldMatchesRangeInt.values()));
+        assertThat(piCriterionInt.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+
+        Criterion matchPiLong = PiCriterion.builder().matchRange(piIpv4HeaderFieldId, matchRangeLong1,
+                matchRangeHighLong)
+                .build();
+        PiCriterion piCriterionLong =
+                checkAndConvert(matchPiLong, Criterion.Type.PROTOCOL_INDEPENDENT, PiCriterion.class);
+        fieldMatchesRangeLong.put(piIpv4HeaderFieldId, new PiRangeFieldMatch(piIpv4HeaderFieldId,
+                copyFrom(matchRangeLong1),
+                copyFrom(matchRangeHighLong)));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piCriterionLong.fieldMatches(), fieldMatchesRangeLong.values()));
+        assertThat(piCriterionLong.type(), is(equalTo(Criterion.Type.PROTOCOL_INDEPENDENT)));
+    }
+
+    /**
+     * Test the equals() method of the PiCriterion class.
+     */
+    @Test
+    public void testPiExactCriterionEquals() {
+        new EqualsTester()
+                .addEqualityGroup(matchPiExactByte1)
+                .addEqualityGroup(matchPiExactByte2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiExactShort1)
+                .addEqualityGroup(matchPiExactShort2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiExactInt1)
+                .addEqualityGroup(matchPiExactInt2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiExactLong1)
+                .addEqualityGroup(matchPiExactLong2)
+                .testEquals();
+    }
+
+    /**
+     * Test the equals() method of the PiCriterion class.
+     */
+    @Test
+    public void testPiLpmCriterionEquals() {
+        new EqualsTester()
+                .addEqualityGroup(matchPiLpmByte1)
+                .addEqualityGroup(matchPiLpmByte2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiLpmShort1)
+                .addEqualityGroup(matchPiLpmShort2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiLpmInt1)
+                .addEqualityGroup(matchPiLpmInt2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiLpmLong1)
+                .addEqualityGroup(matchPiLpmLong2)
+                .testEquals();
+    }
+
+    /**
+     * Test the equals() method of the PiCriterion class.
+     */
+    @Test
+    public void testPiTernaryCriterionEquals() {
+        new EqualsTester()
+                .addEqualityGroup(matchPiTernaryByte1)
+                .addEqualityGroup(matchPiTernaryByte2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiTernaryShort1)
+                .addEqualityGroup(matchPiTernaryShort2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiTernaryInt1)
+                .addEqualityGroup(matchPiTernaryInt2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiTernaryLong1)
+                .addEqualityGroup(matchPiTernaryLong2)
+                .testEquals();
+    }
+
+    /**
+     * Test the equals() method of the PiCriterion class.
+     */
+    @Test
+    public void testPiValidCriterionEquals() {
+        new EqualsTester()
+                .addEqualityGroup(matchPiValid1)
+                .addEqualityGroup(matchPiValid2)
+                .testEquals();
+    }
+
+    /**
+     * Test the equals() method of the PiCriterion class.
+     */
+    @Test
+    public void testPiRangeCriterionEquals() {
+        new EqualsTester()
+                .addEqualityGroup(matchPiRangeByte1)
+                .addEqualityGroup(matchPiRangeByte2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiRangeShort1)
+                .addEqualityGroup(matchPiRangeShort2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiRangeInt1)
+                .addEqualityGroup(matchPiRangeInt2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchPiRangeLong1)
+                .addEqualityGroup(matchPiRangeLong2)
+                .testEquals();
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java b/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
index fa8d5db..f6d1406 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
@@ -23,6 +23,7 @@
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.VlanId;
+import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.core.GroupId;
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.DeviceId;
@@ -31,6 +32,11 @@
 import org.onosproject.net.OduSignalId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionId;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.pi.runtime.PiActionParamId;
+import org.onosproject.net.pi.runtime.PiTableAction;
 
 import java.util.List;
 
@@ -111,6 +117,7 @@
         assertThatClassIsImmutable(L2ModificationInstruction.ModMplsBosInstruction.class);
         assertThatClassIsImmutable(L2ModificationInstruction.ModMplsTtlInstruction.class);
         assertThatClassIsImmutable(L2ModificationInstruction.ModTunnelIdInstruction.class);
+        assertThatClassIsImmutable(PiInstruction.class);
     }
 
     //  NoActionInstruction
@@ -1307,4 +1314,43 @@
                 .testEquals();
     }
 
+    // PiInstruction
+    PiTableAction piTableAction1 = PiAction.builder()
+            .withId(PiActionId.of("set_egress_port_0"))
+            .withParameter(new PiActionParam(PiActionParamId.of("port"),
+                                                             ImmutableByteSequence.copyFrom(10))).build();
+    PiTableAction piTableAction2 = PiAction.builder()
+            .withId(PiActionId.of("set_egress_port_0"))
+            .withParameter(new PiActionParam(PiActionParamId.of("port"),
+                    ImmutableByteSequence.copyFrom(20))).build();
+    private final Instruction piSetEgressPortInstruction1 = new PiInstruction(piTableAction1);
+    private final Instruction sameAsPiSetEgressPortInstruction1 = new PiInstruction(piTableAction1);
+    private final Instruction piSetEgressPortInstruction2 = new PiInstruction(piTableAction2);
+
+    /**
+     * Test the PiInstruction() method.
+     */
+    @Test
+    public void testPiMethod() {
+        final Instruction instruction = new PiInstruction(piTableAction1);
+        final PiInstruction piInstruction = checkAndConvert(instruction,
+                Instruction.Type.PROTOCOL_INDEPENDENT, PiInstruction.class);
+
+        assertThat(piInstruction.action(), is(piTableAction1));
+        assertThat(piInstruction.type(), is(Instruction.Type.PROTOCOL_INDEPENDENT));
+    }
+
+    /**
+     * Tests the equals(), hashCode() and toString() methods of the
+     * PiInstruction class.
+     */
+
+    @Test
+    public void testPiInstructionEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piSetEgressPortInstruction1, sameAsPiSetEgressPortInstruction1)
+                .addEqualityGroup(piSetEgressPortInstruction2)
+                .testEquals();
+    }
+
 }
diff --git a/core/api/src/test/java/org/onosproject/net/pi/model/PiPipeconfIdTest.java b/core/api/src/test/java/org/onosproject/net/pi/model/PiPipeconfIdTest.java
new file mode 100644
index 0000000..0ab8eb9
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/model/PiPipeconfIdTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017-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.net.pi.model;
+
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Unit tests for PiPipeconfId class.
+ */
+public class PiPipeconfIdTest {
+    final String id1 = "pipeline1";
+    final String id2 = "pipeline2";
+    final PiPipeconfId piPipeconfId1 = new PiPipeconfId(id1);
+    final PiPipeconfId sameAsPiPipeconfId1 = new PiPipeconfId(id1);
+    final PiPipeconfId piPipeconfId2 = new PiPipeconfId(id2);
+
+    /**
+     * Checks that the PiPipeconfId class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiPipeconfId.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piPipeconfId1, sameAsPiPipeconfId1)
+                .addEqualityGroup(piPipeconfId2)
+                .testEquals();
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionIdTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionIdTest.java
new file mode 100644
index 0000000..55ccf8b
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionIdTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DEC_TTL;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.MOD_VLAN_VID;
+
+/**
+ * Unit tests for PiActionId class.
+ */
+public class PiActionIdTest {
+    final String id1 = DEC_TTL;
+    final String id2 = MOD_VLAN_VID;
+    final PiActionId actionId1 = PiActionId.of(id1);
+    final PiActionId sameAsActionId1 = PiActionId.of(id1);
+    final PiActionId actionId2 = PiActionId.of(id2);
+
+    /**
+     * Checks that the PiActionId class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiActionId.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(actionId1, sameAsActionId1)
+                .addEqualityGroup(actionId2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiActionId object.
+     */
+    @Test
+    public void testConstruction() {
+        final String id = DEC_TTL;
+        final PiActionId actionId = PiActionId.of(id);
+        assertThat(actionId, is(notNullValue()));
+        assertThat(actionId.name(), is(id));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionParamIdTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionParamIdTest.java
new file mode 100644
index 0000000..e783850
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionParamIdTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.PORT;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.SRC_ADDR;
+
+/**
+ * Unit tests for PiActionParamId class.
+ */
+public class PiActionParamIdTest {
+    final PiActionParamId piActionParamId1 = PiActionParamId.of(PORT);
+    final PiActionParamId sameAsPiActionParamId1 = PiActionParamId.of(PORT);
+    final PiActionParamId piActionParamId2 = PiActionParamId.of(DST_ADDR);
+
+    /**
+     * Checks that the PiActionParamId class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiActionParamId.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piActionParamId1, sameAsPiActionParamId1)
+                .addEqualityGroup(piActionParamId2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiActionParamId object.
+     */
+    @Test
+    public void testConstruction() {
+        final String param = SRC_ADDR;
+        final PiActionParamId actionParamId = PiActionParamId.of(SRC_ADDR);
+        assertThat(actionParamId, is(notNullValue()));
+        assertThat(actionParamId.name(), is(param));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionParamTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionParamTest.java
new file mode 100644
index 0000000..574a321
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionParamTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.SRC_ADDR;
+
+/**
+ * Unit tests for PiActionParam class.
+ */
+public class PiActionParamTest {
+    ImmutableByteSequence value1 = copyFrom(0x0a010101);
+    ImmutableByteSequence value2 = copyFrom(0x0a010102);
+    final PiActionParam piActionParam1 = new PiActionParam(PiActionParamId.of(DST_ADDR), value1);
+    final PiActionParam sameAsPiActionParam1 = new PiActionParam(PiActionParamId.of(DST_ADDR), value1);
+    final PiActionParam piActionParam2 = new PiActionParam(PiActionParamId.of(DST_ADDR), value2);
+
+    /**
+     * Checks that the PiActionParam class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiActionParam.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piActionParam1, sameAsPiActionParam1)
+                .addEqualityGroup(piActionParam2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiActionParam object.
+     */
+    @Test
+    public void testConstruction() {
+        ImmutableByteSequence value = copyFrom(0x0b010102);
+        final PiActionParamId piActionParamId = PiActionParamId.of(SRC_ADDR);
+        final PiActionParam piActionParam = new PiActionParam(piActionParamId, value);
+        assertThat(piActionParam, is(notNullValue()));
+        assertThat(piActionParam.id(), is(piActionParamId));
+        assertThat(piActionParam.value(), is(value));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionTest.java
new file mode 100644
index 0000000..430badb
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.collect.Lists;
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+
+import java.util.Collection;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.MOD_NW_DST;
+
+/**
+ * Unit tests for PiAction class.
+ */
+public class PiActionTest {
+    final PiAction piAction1 = PiAction.builder().withId(PiActionId.of(MOD_NW_DST))
+            .withParameter(new PiActionParam(PiActionParamId.of(DST_ADDR), copyFrom(0x0a010101)))
+            .build();
+    final PiAction sameAsPiAction1 = PiAction.builder().withId(PiActionId.of(MOD_NW_DST))
+            .withParameter(new PiActionParam(PiActionParamId.of(DST_ADDR), copyFrom(0x0a010101)))
+            .build();
+    final PiAction piAction2 = PiAction.builder().withId(PiActionId.of("set_egress_port_0")).build();
+
+    /**
+     * Checks that the PiAction class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiAction.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piAction1, sameAsPiAction1)
+                .addEqualityGroup(piAction2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiAction object with parameter.
+     */
+    @Test
+    public void testMethodWithParameter() {
+        PiActionId piActionId = PiActionId.of(MOD_NW_DST);
+        PiActionParam piActionParam = new PiActionParam(PiActionParamId.of(DST_ADDR), copyFrom(0x0a010101));
+        final PiAction piAction = PiAction.builder().withId(piActionId)
+                .withParameter(piActionParam)
+                .build();
+        assertThat(piAction, is(notNullValue()));
+        assertThat(piAction.id(), is(piActionId));
+        assertThat(piAction.type(), is(PiTableAction.Type.ACTION));
+    }
+
+    /**
+     * Checks the construction of a PiAction object with parameters.
+     */
+    @Test
+    public void testMethodWithParameters() {
+        PiActionId piActionId = PiActionId.of(MOD_NW_DST);
+        Collection<PiActionParam> runtimeParams = Lists.newArrayList();
+        PiActionParam piActionParam = new PiActionParam(PiActionParamId.of(DST_ADDR), copyFrom(0x0a010101));
+
+        runtimeParams.add(piActionParam);
+        final PiAction piAction = PiAction.builder().withId(piActionId)
+                .withParameters(runtimeParams)
+                .build();
+        assertThat(piAction, is(notNullValue()));
+        assertThat(piAction.id(), is(piActionId));
+        assertThat(piAction.parameters(), is(runtimeParams));
+        assertThat(piAction.type(), is(PiTableAction.Type.ACTION));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiConstantsTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiConstantsTest.java
new file mode 100644
index 0000000..e551a9e
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiConstantsTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+/**
+ * Unit tests for PiActionId class.
+ */
+public final class PiConstantsTest {
+    private PiConstantsTest() {
+    }
+    public static final String MOD_NW_DST = "mod_nw_dst";
+    public static final String DEC_TTL = "dec_ttl";
+    public static final String MOD_VLAN_VID = "mod_vlan_vid";
+    public static final String DROP = "drop";
+
+    public static final String IPV4_HEADER_NAME = "ipv4_t";
+    public static final String ETH_HEADER_NAME = "ethernet_t";
+    public static final String VLAN_HEADER_NAME = "vlan_tag_t";
+
+    public static final String ETH_TYPE = "etherType";
+    public static final String DST_ADDR = "dstAddr";
+    public static final String SRC_ADDR = "srcAddr";
+    public static final String VID = "vid";
+    public static final String PORT = "port";
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiExactFieldMatchTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiExactFieldMatchTest.java
new file mode 100644
index 0000000..990a3bd
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiExactFieldMatchTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchType;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.ETH_HEADER_NAME;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.ETH_TYPE;
+
+/**
+ * Unit tests for PiExactFieldMatch class.
+ */
+public class PiExactFieldMatchTest {
+    final ImmutableByteSequence value1 = copyFrom(0x0800);
+    final ImmutableByteSequence value2 = copyFrom(0x0806);
+    final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(ETH_HEADER_NAME, ETH_TYPE);
+    PiExactFieldMatch piExactFieldMatch1 = new PiExactFieldMatch(piHeaderField, value1);
+    PiExactFieldMatch sameAsPiExactFieldMatch1 = new PiExactFieldMatch(piHeaderField, value1);
+    PiExactFieldMatch piExactFieldMatch2 = new PiExactFieldMatch(piHeaderField, value2);
+
+    /**
+     * Checks that the PiExactFieldMatch class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiExactFieldMatch.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piExactFieldMatch1, sameAsPiExactFieldMatch1)
+                .addEqualityGroup(piExactFieldMatch2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiExactFieldMatch object.
+     */
+    @Test
+    public void testConstruction() {
+        final ImmutableByteSequence value = copyFrom(0x0806);
+        final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(ETH_HEADER_NAME, ETH_TYPE);
+        PiExactFieldMatch piExactFieldMatch = new PiExactFieldMatch(piHeaderField, value);
+        assertThat(piExactFieldMatch, is(notNullValue()));
+        assertThat(piExactFieldMatch.value(), is(value));
+        assertThat(piExactFieldMatch.type(), is(PiMatchType.EXACT));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiFieldMatchTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiFieldMatchTest.java
new file mode 100644
index 0000000..b08134d
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiFieldMatchTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import org.junit.Test;
+import org.onosproject.net.pi.model.PiMatchType;
+
+import static org.junit.Assert.assertEquals;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutableBaseClass;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.ETH_HEADER_NAME;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.ETH_TYPE;
+
+/**
+ * Unit tests for PiFieldMatch class.
+ */
+public class PiFieldMatchTest {
+
+    /**
+     * Checks that the PiFieldMatch class is immutable but can be
+     * inherited from.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutableBaseClass(PiFieldMatch.class);
+    }
+
+    @Test
+    public void basics() {
+        final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(ETH_HEADER_NAME, ETH_TYPE);
+        PiFieldMatch piFieldMatch = new PiExactFieldMatch(piHeaderField, copyFrom(0x0806));
+
+        assertEquals(piFieldMatch.fieldId(), piHeaderField);
+        assertEquals(piFieldMatch.type(), PiMatchType.EXACT);
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiHeaderFieldIdTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiHeaderFieldIdTest.java
new file mode 100644
index 0000000..808e0c0
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiHeaderFieldIdTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.ETH_HEADER_NAME;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.ETH_TYPE;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.IPV4_HEADER_NAME;
+
+/**
+ * Unit tests for PiHeaderFieldId class.
+ */
+public class PiHeaderFieldIdTest {
+    final String headerName = ETH_HEADER_NAME;
+    final String dstAddr = DST_ADDR;
+    final String etherType = ETH_TYPE;
+
+    final PiHeaderFieldId piHeaderFieldId1 = PiHeaderFieldId.of(headerName, dstAddr);
+    final PiHeaderFieldId sameAsPiHeaderFieldId1 = PiHeaderFieldId.of(headerName, dstAddr);
+    final PiHeaderFieldId piHeaderFieldId2 = PiHeaderFieldId.of(headerName, etherType);
+
+    int index = 10;
+    final PiHeaderFieldId piHeaderFieldId1WithIndex = PiHeaderFieldId.of(headerName, dstAddr, index);
+    final PiHeaderFieldId sameAsPiHeaderFieldId1WithIndex = PiHeaderFieldId.of(headerName, dstAddr, index);
+    final PiHeaderFieldId piHeaderFieldId2WithIndex = PiHeaderFieldId.of(headerName, etherType, index);
+
+    /**
+     * Checks that the PiHeaderFieldId class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiHeaderFieldId.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piHeaderFieldId1, sameAsPiHeaderFieldId1)
+                .addEqualityGroup(piHeaderFieldId2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEqualsWithIndex() {
+        new EqualsTester()
+                .addEqualityGroup(piHeaderFieldId1WithIndex, sameAsPiHeaderFieldId1WithIndex)
+                .addEqualityGroup(piHeaderFieldId2WithIndex)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiHeaderFieldId object.
+     */
+    @Test
+    public void testConstruction() {
+        final String name = IPV4_HEADER_NAME;
+        final String field = DST_ADDR;
+
+        final PiHeaderFieldId piHeaderFieldId = PiHeaderFieldId.of(name, field);
+        assertThat(piHeaderFieldId, is(notNullValue()));
+        assertThat(piHeaderFieldId.headerName(), is(name));
+        assertThat(piHeaderFieldId.fieldName(), is(field));
+    }
+
+    /**
+     * Checks the construction of a PiHeaderFieldId object with index.
+     */
+    @Test
+    public void testConstructionWithIndex() {
+        final String name = IPV4_HEADER_NAME;
+        final String field = DST_ADDR;
+        final int index = 1;
+        final PiHeaderFieldId piHeaderFieldId = PiHeaderFieldId.of(name, field, index);
+        assertThat(piHeaderFieldId, is(notNullValue()));
+        assertThat(piHeaderFieldId.headerName(), is(name));
+        assertThat(piHeaderFieldId.fieldName(), is(field));
+        assertThat(piHeaderFieldId.index(), is(index));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiLpmFieldMatchTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiLpmFieldMatchTest.java
new file mode 100644
index 0000000..f8edb87
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiLpmFieldMatchTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchType;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.IPV4_HEADER_NAME;
+
+/**
+ * Unit tests for PiLpmFieldMatch class.
+ */
+public class PiLpmFieldMatchTest {
+    final ImmutableByteSequence value1 = copyFrom(0x0a010101);
+    final ImmutableByteSequence value2 = copyFrom(0x0a010102);
+    int prefixLength = 24;
+    final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(IPV4_HEADER_NAME, DST_ADDR);
+    PiLpmFieldMatch piLpmFieldMatch1 = new PiLpmFieldMatch(piHeaderField, value1, prefixLength);
+    PiLpmFieldMatch sameAsPiLpmFieldMatch1 = new PiLpmFieldMatch(piHeaderField, value1, prefixLength);
+    PiLpmFieldMatch piLpmFieldMatch2 = new PiLpmFieldMatch(piHeaderField, value2, prefixLength);
+
+    /**
+     * Checks that the PiLpmFieldMatch class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiLpmFieldMatch.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piLpmFieldMatch1, sameAsPiLpmFieldMatch1)
+                .addEqualityGroup(piLpmFieldMatch2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiLpmFieldMatch object.
+     */
+    @Test
+    public void testConstruction() {
+        final ImmutableByteSequence value = copyFrom(0x0a01010a);
+        int prefix = 24;
+        final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(IPV4_HEADER_NAME, DST_ADDR);
+        PiLpmFieldMatch piLpmFieldMatch = new PiLpmFieldMatch(piHeaderField, value, prefix);
+        assertThat(piLpmFieldMatch, is(notNullValue()));
+        assertThat(piLpmFieldMatch.value(), is(value));
+        assertThat(piLpmFieldMatch.prefixLength(), is(prefix));
+        assertThat(piLpmFieldMatch.type(), is(PiMatchType.LPM));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiRangeFieldMatchTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiRangeFieldMatchTest.java
new file mode 100644
index 0000000..c78622e
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiRangeFieldMatchTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchType;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.VID;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.VLAN_HEADER_NAME;
+
+/**
+ * Unit tests for PiRangeFieldMatch class.
+ */
+public class PiRangeFieldMatchTest {
+    final ImmutableByteSequence high1 = copyFrom(0x10);
+    final ImmutableByteSequence low1 = copyFrom(0x00);
+    final ImmutableByteSequence high2 = copyFrom(0x30);
+    final ImmutableByteSequence low2 = copyFrom(0x40);
+
+    final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(VLAN_HEADER_NAME, VID);
+    PiRangeFieldMatch piRangeFieldMatch1 = new PiRangeFieldMatch(piHeaderField, low1, high1);
+    PiRangeFieldMatch sameAsPiRangeFieldMatch1 = new PiRangeFieldMatch(piHeaderField, low1, high1);
+    PiRangeFieldMatch piRangeFieldMatch2 = new PiRangeFieldMatch(piHeaderField, low2, high2);
+
+    /**
+     * Checks that the PiRangeFieldMatch class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiRangeFieldMatch.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piRangeFieldMatch1, sameAsPiRangeFieldMatch1)
+                .addEqualityGroup(piRangeFieldMatch2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiRangeFieldMatch object.
+     */
+    @Test
+    public void testConstruction() {
+        final ImmutableByteSequence high = copyFrom(0x50);
+        final ImmutableByteSequence low = copyFrom(0x00);
+        final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(VLAN_HEADER_NAME, VID);
+        PiRangeFieldMatch piRangeFieldMatch = new PiRangeFieldMatch(piHeaderField, low, high);
+        assertThat(piRangeFieldMatch, is(notNullValue()));
+        assertThat(piRangeFieldMatch.lowValue(), is(low));
+        assertThat(piRangeFieldMatch.highValue(), is(high));
+        assertThat(piRangeFieldMatch.type(), is(PiMatchType.RANGE));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java
new file mode 100644
index 0000000..0246227
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.collect.Maps;
+import com.google.common.testing.EqualsTester;
+import org.apache.commons.collections.CollectionUtils;
+import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
+
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DROP;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.IPV4_HEADER_NAME;
+
+/**
+ * Unit tests for PiTableEntry class.
+ */
+public class PiTableEntryTest {
+    final PiTableEntry piTableEntry1 = PiTableEntry.builder()
+            .forTable(PiTableId.of("Table10"))
+            .withCookie(0xac)
+            .withPriority(10)
+            .withAction(PiAction.builder().withId(PiActionId.of(DROP)).build())
+            .withTimeout(100)
+            .build();
+    final PiTableEntry sameAsPiTableEntry1 = PiTableEntry.builder()
+            .forTable(PiTableId.of("Table10"))
+            .withCookie(0xac)
+            .withPriority(10)
+            .withAction(PiAction.builder().withId(PiActionId.of(DROP)).build())
+            .withTimeout(100)
+            .build();
+    final PiTableEntry piTableEntry2 = PiTableEntry.builder()
+            .forTable(PiTableId.of("Table20"))
+            .withCookie(0xac)
+            .withPriority(10)
+            .withAction(PiAction.builder().withId(PiActionId.of(DROP)).build())
+            .withTimeout(1000)
+            .build();
+
+    /**
+     * Checks that the PiTableEntry class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiTableEntry.class);
+    }
+
+    /**
+     * Tests the equals, hashCode and toString methods using Guava EqualsTester.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piTableEntry1, sameAsPiTableEntry1)
+                .addEqualityGroup(piTableEntry2)
+                .testEquals();
+    }
+
+    /**
+     * Tests creation of a DefaultFlowRule using a FlowRule constructor.
+     */
+    @Test
+    public void testBuilder() {
+
+        PiTableId piTableId = PiTableId.of("table10");
+        long cookie = 0xfff0323;
+        int priority = 100;
+        double timeout = 1000;
+        PiHeaderFieldId piHeaderFieldId = PiHeaderFieldId.of(IPV4_HEADER_NAME, DST_ADDR);
+        PiFieldMatch piFieldMatch = new PiExactFieldMatch(piHeaderFieldId, ImmutableByteSequence.copyFrom(0x0a010101));
+        PiAction piAction = PiAction.builder().withId(PiActionId.of(DROP)).build();
+        final Map<PiHeaderFieldId, PiFieldMatch> fieldMatches = Maps.newHashMap();
+        fieldMatches.put(piHeaderFieldId, piFieldMatch);
+        final PiTableEntry piTableEntry = PiTableEntry.builder()
+                .forTable(piTableId)
+                .withFieldMatches(fieldMatches.values())
+                .withAction(piAction)
+                .withCookie(cookie)
+                .withPriority(priority)
+                .withTimeout(timeout)
+                .build();
+
+        assertThat(piTableEntry.table(), is(piTableId));
+        assertThat(piTableEntry.cookie(), is(cookie));
+        assertThat(piTableEntry.priority().get(), is(priority));
+        assertThat(piTableEntry.timeout().get(), is(timeout));
+        assertThat("Incorrect match param value",
+                CollectionUtils.isEqualCollection(piTableEntry.fieldMatches(), fieldMatches.values()));
+        assertThat(piTableEntry.action(), is(piAction));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableIdTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableIdTest.java
new file mode 100644
index 0000000..4e421ea
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableIdTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Unit tests for PiTableId class.
+ */
+public class PiTableIdTest {
+    final String table10 = "table10";
+    final String table20 = "table20";
+    final PiTableId piTableId1 = PiTableId.of(table10);
+    final PiTableId sameAsPiTableId1 = PiTableId.of(table10);
+    final PiTableId piTableId2 = PiTableId.of(table20);
+
+    final String tableScope = "local";
+    final PiTableId piTableIdWithScope1 = PiTableId.of(tableScope, table10);
+    final PiTableId sameAsPiTableIdWithScope1 = PiTableId.of(tableScope, table10);
+    final PiTableId piTableIdWithScope2 = PiTableId.of(tableScope, table20);
+    /**
+     * Checks that the PiTableId class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiTableId.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piTableId1, sameAsPiTableId1)
+                .addEqualityGroup(piTableId2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEqualsWithScope() {
+        new EqualsTester()
+                .addEqualityGroup(piTableIdWithScope1, sameAsPiTableIdWithScope1)
+                .addEqualityGroup(piTableIdWithScope2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiTableId object.
+     */
+    @Test
+    public void testConstruction() {
+        final String name = "table1";
+        final PiTableId piTableId = PiTableId.of(name);
+        assertThat(piTableId, is(notNullValue()));
+        assertThat(piTableId.name(), is(name));
+    }
+
+    /**
+     * Checks the construction of a PiTableId object.
+     */
+    @Test
+    public void testConstructionWithScope() {
+        final String name = "table1";
+        final String scope = "local";
+        final PiTableId piTableId = PiTableId.of(scope, name);
+        assertThat(piTableId, is(notNullValue()));
+        assertThat(piTableId.name(), is(name));
+        assertThat(piTableId.scope().get(), is(scope));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTernaryFieldMatchTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTernaryFieldMatchTest.java
new file mode 100644
index 0000000..eb3b491
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTernaryFieldMatchTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchType;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.IPV4_HEADER_NAME;
+
+/**
+ * Unit tests for PiTernaryFieldMatch class.
+ */
+public class PiTernaryFieldMatchTest {
+    final ImmutableByteSequence value1 = copyFrom(0x0a010101);
+    final ImmutableByteSequence mask1 = copyFrom(0x00ffffff);
+    final ImmutableByteSequence value2 = copyFrom(0x0a010102);
+    final ImmutableByteSequence mask2 = copyFrom(0x0000ffff);
+
+    final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(IPV4_HEADER_NAME, DST_ADDR);
+    PiTernaryFieldMatch piTernaryFieldMatch1 = new PiTernaryFieldMatch(piHeaderField, value1, mask1);
+    PiTernaryFieldMatch sameAsPiTernaryFieldMatch1 = new PiTernaryFieldMatch(piHeaderField, value1, mask1);
+    PiTernaryFieldMatch piTernaryFieldMatch2 = new PiTernaryFieldMatch(piHeaderField, value2, mask2);
+
+    /**
+     * Checks that the PiTernaryFieldMatch class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiTernaryFieldMatch.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piTernaryFieldMatch1, sameAsPiTernaryFieldMatch1)
+                .addEqualityGroup(piTernaryFieldMatch2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiTernaryFieldMatch object.
+     */
+    @Test
+    public void testConstruction() {
+        final ImmutableByteSequence value = copyFrom(0x0a01010a);
+        final ImmutableByteSequence mask = copyFrom(0x00ffffff);
+        final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(IPV4_HEADER_NAME, DST_ADDR);
+        PiTernaryFieldMatch piTernaryFieldMatch = new PiTernaryFieldMatch(piHeaderField, value, mask);
+        assertThat(piTernaryFieldMatch, is(notNullValue()));
+        assertThat(piTernaryFieldMatch.value(), is(value));
+        assertThat(piTernaryFieldMatch.mask(), is(mask));
+        assertThat(piTernaryFieldMatch.type(), is(PiMatchType.TERNARY));
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiValidFieldMatchTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiValidFieldMatchTest.java
new file mode 100644
index 0000000..587d8b8
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiValidFieldMatchTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017-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.net.pi.runtime;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.pi.model.PiMatchType;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.VID;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.VLAN_HEADER_NAME;
+
+/**
+ * Unit tests for PiValidFieldMatch class.
+ */
+public class PiValidFieldMatchTest {
+
+    final boolean isValid1 = true;
+    final boolean isValid2 = false;
+    final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(VLAN_HEADER_NAME, VID);
+    PiValidFieldMatch piValidFieldMatch1 = new PiValidFieldMatch(piHeaderField, isValid1);
+    PiValidFieldMatch sameAsPiValidFieldMatch1 = new PiValidFieldMatch(piHeaderField, isValid1);
+    PiValidFieldMatch piValidFieldMatch2 = new PiValidFieldMatch(piHeaderField, isValid2);
+
+    /**
+     * Checks that the PiValidFieldMatch class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiValidFieldMatch.class);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(piValidFieldMatch1, sameAsPiValidFieldMatch1)
+                .addEqualityGroup(piValidFieldMatch2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a PiValidFieldMatch object.
+     */
+    @Test
+    public void testConstruction() {
+        final boolean isValid = true;
+        final PiHeaderFieldId piHeaderField = PiHeaderFieldId.of(VLAN_HEADER_NAME, VID);
+        PiValidFieldMatch piTernaryFieldMatch = new PiValidFieldMatch(piHeaderField, isValid);
+        assertThat(piTernaryFieldMatch, is(notNullValue()));
+        assertThat(piTernaryFieldMatch.isValid(), is(isValid));
+        assertThat(piTernaryFieldMatch.type(), is(PiMatchType.VALID));
+    }
+}