[CORD-2432][CORD-2431] Initial t3 implementation

Change-Id: I1ed421f82c234bb006ed2cefefea53d773f1efc9
diff --git a/src/test/org/onosproject/t3/impl/T3TestObjects.java b/src/test/org/onosproject/t3/impl/T3TestObjects.java
new file mode 100644
index 0000000..8a3f6eb
--- /dev/null
+++ b/src/test/org/onosproject/t3/impl/T3TestObjects.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.t3.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.EthType;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultHost;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.provider.ProviderId;
+
+/**
+ * Helper class for objects related to the Troubleshoot Manager Test.
+ */
+final class T3TestObjects {
+
+    private static final String HOST_ONE_MAC = "00:00:00:00:00:01";
+    private static final String HOST_TWO_MAC = "00:00:00:00:00:02";
+    private static final String HOST_ONE_VLAN = "None";
+    private static final String HOST_TWO_VLAN = "None";
+    private static final String HOST_ONE = HOST_ONE_MAC + "/" + HOST_ONE_VLAN;
+    private static final String HOST_TWO = HOST_TWO_MAC + "/" + HOST_TWO_VLAN;
+
+    //Single Flow Test
+    static final DeviceId SINGLE_FLOW_DEVICE = DeviceId.deviceId("SingleFlowDevice");
+    private static final TrafficSelector SINGLE_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
+            .build();
+
+    private static final TrafficTreatment OUTPUT_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .setOutput(PortNumber.portNumber(2)).build();
+    private static final FlowRule SINGLE_FLOW = DefaultFlowEntry.builder().forDevice(SINGLE_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(SINGLE_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry SINGLE_FLOW_ENTRY = new DefaultFlowEntry(SINGLE_FLOW);
+
+    static final ConnectPoint SINGLE_FLOW_IN_CP = ConnectPoint.deviceConnectPoint(SINGLE_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint SINGLE_FLOW_OUT_CP = ConnectPoint.deviceConnectPoint(SINGLE_FLOW_DEVICE + "/" + 2);
+
+    //Dual Flow Test
+    static final DeviceId DUAL_FLOW_DEVICE = DeviceId.deviceId("DualFlowDevice");
+    private static final TrafficTreatment TRANSITION_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .setVlanId(VlanId.vlanId((short) 100))
+            .transition(10)
+            .build();
+    private static final TrafficSelector VLAN_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchVlanId(VlanId.vlanId((short) 100))
+            .build();
+    private static final FlowRule FIRST_FLOW = DefaultFlowEntry.builder().forDevice(DUAL_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(SINGLE_FLOW_SELECTOR)
+            .withTreatment(TRANSITION_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry FIRST_FLOW_ENTRY = new DefaultFlowEntry(FIRST_FLOW);
+    private static final FlowRule SECOND_FLOW = DefaultFlowEntry.builder().forDevice(DUAL_FLOW_DEVICE)
+            .forTable(10)
+            .withPriority(100)
+            .withSelector(VLAN_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry SECOND_FLOW_ENTRY = new DefaultFlowEntry(SECOND_FLOW);
+
+    static final ConnectPoint DUAL_FLOW_IN_CP = ConnectPoint.deviceConnectPoint(DUAL_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint DUAL_FLOW_OUT_CP = ConnectPoint.deviceConnectPoint(DUAL_FLOW_DEVICE + "/" + 2);
+
+    //Flow and Group Test
+    static final DeviceId GROUP_FLOW_DEVICE = DeviceId.deviceId("GroupFlowDevice");
+
+    private static final GroupId GROUP_ID = GroupId.valueOf(1);
+
+    private static final TrafficTreatment GROUP_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .group(GROUP_ID)
+            .build();
+    private static final FlowRule GROUP_FLOW = DefaultFlowEntry.builder().forDevice(GROUP_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(SINGLE_FLOW_SELECTOR)
+            .withTreatment(GROUP_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry GROUP_FLOW_ENTRY = new DefaultFlowEntry(GROUP_FLOW);
+
+    private static final GroupBucket BUCKET = DefaultGroupBucket.createSelectGroupBucket(OUTPUT_FLOW_TREATMENT);
+
+    private static final GroupBuckets BUCKETS = new GroupBuckets(ImmutableList.of(BUCKET));
+
+    static final Group GROUP = new DefaultGroup(GROUP_ID, GROUP_FLOW_DEVICE, Group.Type.SELECT, BUCKETS);
+
+    static final ConnectPoint GROUP_FLOW_IN_CP = ConnectPoint.deviceConnectPoint(GROUP_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint GROUP_FLOW_OUT_CP = ConnectPoint.deviceConnectPoint(GROUP_FLOW_DEVICE + "/" + 2);
+
+    //topology
+
+    static final DeviceId TOPO_FLOW_DEVICE = DeviceId.deviceId("SingleFlowDevice1");
+
+    static final DeviceId TOPO_FLOW_2_DEVICE = DeviceId.deviceId("SingleFlowDevice2");
+
+    static final DeviceId TOPO_FLOW_3_DEVICE = DeviceId.deviceId("SingleFlowDevice3");
+
+    private static final TrafficSelector TOPO_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.3/32"))
+            .build();
+
+    private static final FlowRule TOPO_SINGLE_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(TOPO_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+
+    static final FlowEntry TOPO_SINGLE_FLOW_ENTRY = new DefaultFlowEntry(TOPO_SINGLE_FLOW);
+
+    static final ConnectPoint TOPO_FLOW_1_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_1_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_DEVICE + "/" + 2);
+
+    static final ConnectPoint TOPO_FLOW_2_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_2_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_2_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_2_DEVICE + "/" + 2);
+
+    static final ConnectPoint TOPO_FLOW_3_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_3_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_3_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_3_DEVICE + "/" + 2);
+
+
+    //Topology with Groups
+
+    static final DeviceId TOPO_GROUP_FLOW_DEVICE = DeviceId.deviceId("TopoGroupFlowDevice");
+
+    private static final TrafficSelector TOPO_SECOND_INPUT_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(3))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.3/32"))
+            .build();
+
+    private static final FlowRule TOPO_SECOND_INPUT_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_3_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(TOPO_SECOND_INPUT_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+
+    private static final TrafficTreatment OUTPUT_2_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .setOutput(PortNumber.portNumber(3)).build();
+
+
+    private static final GroupId TOPO_GROUP_ID = GroupId.valueOf(1);
+
+    private static final TrafficTreatment TOPO_GROUP_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .group(TOPO_GROUP_ID)
+            .build();
+    private static final FlowRule TOPO_GROUP_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_GROUP_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(TOPO_FLOW_SELECTOR)
+            .withTreatment(TOPO_GROUP_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry TOPO_GROUP_FLOW_ENTRY = new DefaultFlowEntry(TOPO_GROUP_FLOW);
+
+    private static final GroupBucket BUCKET_2 = DefaultGroupBucket.createSelectGroupBucket(OUTPUT_2_FLOW_TREATMENT);
+
+    private static final GroupBuckets BUCKETS_MULTIPLE = new GroupBuckets(ImmutableList.of(BUCKET, BUCKET_2));
+
+    static final Group TOPO_GROUP = new DefaultGroup(TOPO_GROUP_ID, TOPO_GROUP_FLOW_DEVICE, Group.Type.SELECT, BUCKETS_MULTIPLE);
+
+    static final FlowEntry TOPO_SECOND_INPUT_FLOW_ENTRY = new DefaultFlowEntry(TOPO_SECOND_INPUT_FLOW);
+
+    static final DeviceId TOPO_FLOW_4_DEVICE = DeviceId.deviceId("SingleFlowDevice4");
+
+    static final ConnectPoint TOPO_FLOW_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_GROUP_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_OUT_CP_1 = ConnectPoint.deviceConnectPoint(TOPO_GROUP_FLOW_DEVICE + "/" + 2);
+
+    protected static final ConnectPoint TOPO_FLOW_OUT_CP_2 = ConnectPoint.deviceConnectPoint(TOPO_GROUP_FLOW_DEVICE + "/" + 3);
+
+    static final ConnectPoint TOPO_FLOW_4_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_4_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_3_IN_2_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_3_DEVICE + "/" + 3);
+
+    static final ConnectPoint TOPO_FLOW_4_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_4_DEVICE + "/" + 2);
+
+
+    //hardware
+
+    static final DeviceId HARDWARE_DEVICE = DeviceId.deviceId("HardwareDevice");
+
+    static final ConnectPoint HARDWARE_DEVICE_IN_CP = ConnectPoint.deviceConnectPoint(HARDWARE_DEVICE + "/" + 1);
+
+    static final ConnectPoint HARDWARE_DEVICE_OUT_CP = ConnectPoint.deviceConnectPoint(HARDWARE_DEVICE + "/" + 2);
+
+    private static final TrafficSelector HARDWARE_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
+            .build();
+
+    private static final TrafficTreatment HW_TRANSITION_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .pushMpls()
+            .transition(27)
+            .build();
+
+    private static final FlowRule HARDWARE_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_3_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(HARDWARE_FLOW_SELECTOR)
+            .withTreatment(HW_TRANSITION_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+
+    static final FlowEntry HARDWARE_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_FLOW);
+
+    private static final TrafficSelector HARDWARE_ETH_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
+            .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
+            .build();
+
+    private static final FlowRule HARDWARE_ETH_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_3_DEVICE)
+            .forTable(30)
+            .withPriority(100)
+            .withSelector(HARDWARE_ETH_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+
+    static final FlowEntry HARDWARE_ETH_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_ETH_FLOW);
+
+
+
+
+    //helper elements
+
+    static final Host H1 = new DefaultHost(ProviderId.NONE, HostId.hostId(HOST_ONE), MacAddress.valueOf(100),
+            VlanId.NONE, new HostLocation(SINGLE_FLOW_DEVICE, PortNumber.portNumber(2), 0),
+            ImmutableSet.of(IpAddress.valueOf("127.0.0.2")));
+
+    static final Host H2 = new DefaultHost(ProviderId.NONE, HostId.hostId(HOST_TWO), MacAddress.valueOf(100),
+            VlanId.NONE, new HostLocation(TOPO_FLOW_3_DEVICE, PortNumber.portNumber(2), 0),
+            ImmutableSet.of(IpAddress.valueOf("127.0.0.3")));
+
+    static final TrafficSelector PACKET_OK = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
+            .build();
+
+    static final TrafficSelector PACKET_OK_TOPO = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.3/32"))
+            .build();
+
+    static final TrafficSelector PACKET_FAIL = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.99/32"))
+            .build();
+}
diff --git a/src/test/org/onosproject/t3/impl/TroubleshootManagerTest.java b/src/test/org/onosproject/t3/impl/TroubleshootManagerTest.java
new file mode 100644
index 0000000..0417faf
--- /dev/null
+++ b/src/test/org/onosproject/t3/impl/TroubleshootManagerTest.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.t3.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.EthType;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.driver.DefaultDriver;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverServiceAdapter;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRuleServiceAdapter;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupServiceAdapter;
+import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.link.LinkServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.t3.api.StaticPacketTrace;
+import org.slf4j.Logger;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.t3.impl.T3TestObjects.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Test Class for Troubleshoot Manager.
+ */
+public class TroubleshootManagerTest {
+
+    private static final Logger log = getLogger(TroubleshootManager.class);
+
+    private TroubleshootManager mngr;
+
+    @Before
+    public void setUp() throws Exception {
+        mngr = new TroubleshootManager();
+        mngr.flowRuleService = new TestFlowRuleService();
+        mngr.hostService = new TestHostService();
+        mngr.linkService = new TestLinkService();
+        mngr.driverService = new TestDriverService();
+        mngr.groupService = new TestGroupService();
+
+        assertNotNull("Manager should not be null", mngr);
+
+        assertNotNull("Flow rule Service should not be null", mngr.flowRuleService);
+        assertNotNull("Host Service should not be null", mngr.hostService);
+        assertNotNull("Group Service should not be null", mngr.groupService);
+        assertNotNull("Driver Service should not be null", mngr.driverService);
+        assertNotNull("Link Service should not be null", mngr.linkService);
+    }
+
+    /**
+     * Tests failure on device with no flows
+     */
+    @Test
+    public void noFlows() {
+        StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint("test/1"));
+        assertNotNull("Trace should not be null", traceFail);
+        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(SINGLE_FLOW_DEVICE));
+        log.info("trace {}", traceFail.resultMessage());
+    }
+
+    /**
+     * Test a single flow rule that has output port in it.
+     */
+    @Test
+    public void testSingleFlowRule() {
+
+        testSuccess(PACKET_OK, SINGLE_FLOW_IN_CP, SINGLE_FLOW_DEVICE, SINGLE_FLOW_OUT_CP, 1);
+
+        testFaliure(PACKET_FAIL, SINGLE_FLOW_IN_CP, SINGLE_FLOW_DEVICE);
+    }
+
+    /**
+     * Tests two flow rule the last one of which has output port in it.
+     */
+    @Test
+    public void testDualFlowRule() {
+
+        //Test Success
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, DUAL_FLOW_IN_CP, DUAL_FLOW_DEVICE, DUAL_FLOW_OUT_CP, 1);
+
+        //Testing Vlan
+        Criterion criterion = traceSuccess.getGroupOuputs(DUAL_FLOW_DEVICE).get(0).
+                getFinalPacket().getCriterion(Criterion.Type.VLAN_VID);
+        assertNotNull("Packet Should have Vlan", criterion);
+
+        VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) criterion;
+
+        assertEquals("Vlan should be 100", VlanId.vlanId((short) 100), vlanIdCriterion.vlanId());
+
+        //Test Faliure
+        testFaliure(PACKET_FAIL, DUAL_FLOW_IN_CP, DUAL_FLOW_DEVICE);
+
+    }
+
+    /**
+     * Test a single flow rule that points to a group with output port in it.
+     */
+    @Test
+    public void flowAndGroup() throws Exception {
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, GROUP_FLOW_IN_CP, GROUP_FLOW_DEVICE, GROUP_FLOW_OUT_CP, 1);
+
+        assertTrue("Wrong Output Group", traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
+                .get(0).getGroups().contains(GROUP));
+
+    }
+
+    /**
+     * Test path through a 3 device topology.
+     */
+    @Test
+    public void singlePathTopology() throws Exception {
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK_TOPO, TOPO_FLOW_1_IN_CP,
+                TOPO_FLOW_3_DEVICE, TOPO_FLOW_3_OUT_CP, 1);
+
+        assertTrue("Incorrect path",
+                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_2_IN_CP));
+        assertTrue("Incorrect path",
+                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_2_OUT_CP));
+        assertTrue("Incorrect path",
+                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_3_IN_CP));
+
+    }
+
+    /**
+     * Test path through a 4 device topology with first device that has groups with multiple output buckets.
+     */
+    @Test
+    public void testGroupTopo() throws Exception {
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK_TOPO, TOPO_FLOW_IN_CP,
+                TOPO_FLOW_3_DEVICE, TOPO_FLOW_3_OUT_CP, 2);
+
+        assertTrue("Incorrect groups",
+                traceSuccess.getGroupOuputs(TOPO_GROUP_FLOW_DEVICE).get(0).getGroups().contains(TOPO_GROUP));
+        assertTrue("Incorrect bucket",
+                traceSuccess.getGroupOuputs(TOPO_GROUP_FLOW_DEVICE).get(1).getGroups().contains(TOPO_GROUP));
+    }
+
+    /**
+     * Test HW support in a single device with 2 flow rules to check hit of static HW rules.
+     */
+    @Test
+    public void hardwareTest() throws Exception {
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, HARDWARE_DEVICE_IN_CP,
+                HARDWARE_DEVICE, HARDWARE_DEVICE_OUT_CP, 1);
+
+        assertEquals("wrong ETH type", EthType.EtherType.IPV4.ethType(),
+                ((EthTypeCriterion) traceSuccess.getGroupOuputs(HARDWARE_DEVICE).get(0).getFinalPacket()
+                        .getCriterion(Criterion.Type.ETH_TYPE)).ethType());
+
+    }
+
+    private StaticPacketTrace testSuccess(TrafficSelector packet, ConnectPoint in, DeviceId deviceId, ConnectPoint out,
+                                          int paths) {
+        StaticPacketTrace traceSuccess = mngr.trace(packet, in);
+
+        log.info("trace {}", traceSuccess);
+
+        log.info("trace {}", traceSuccess.resultMessage());
+
+        assertNotNull("trace should not be null", traceSuccess);
+        assertEquals("Trace should have " + paths + " output", paths, traceSuccess.getGroupOuputs(deviceId).size());
+        assertEquals("Trace should only have " + paths + "output", paths, traceSuccess.getCompletePaths().size());
+        assertTrue("Trace should be successful",
+                traceSuccess.resultMessage().contains("Reached required destination Host"));
+        assertEquals("Incorrect Output CP", out,
+                traceSuccess.getGroupOuputs(deviceId).get(0).getOutput());
+
+        return traceSuccess;
+    }
+
+    private void testFaliure(TrafficSelector packet, ConnectPoint in, DeviceId deviceId) {
+        StaticPacketTrace traceFail = mngr.trace(packet, in);
+
+        log.info("trace {}", traceFail.resultMessage());
+
+        assertNotNull("Trace should not be null", traceFail);
+        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(deviceId));
+    }
+
+    private class TestFlowRuleService extends FlowRuleServiceAdapter {
+        @Override
+        public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+            if (deviceId.equals(SINGLE_FLOW_DEVICE)) {
+                return ImmutableList.of(SINGLE_FLOW_ENTRY);
+            } else if (deviceId.equals(DUAL_FLOW_DEVICE)) {
+                return ImmutableList.of(FIRST_FLOW_ENTRY, SECOND_FLOW_ENTRY);
+            } else if (deviceId.equals(GROUP_FLOW_DEVICE)) {
+                return ImmutableList.of(GROUP_FLOW_ENTRY);
+            } else if (deviceId.equals(TOPO_FLOW_DEVICE) ||
+                    deviceId.equals(TOPO_FLOW_2_DEVICE) ||
+                    deviceId.equals(TOPO_FLOW_3_DEVICE) ||
+                    deviceId.equals(TOPO_FLOW_4_DEVICE)) {
+                return ImmutableList.of(TOPO_SINGLE_FLOW_ENTRY, TOPO_SECOND_INPUT_FLOW_ENTRY);
+            } else if (deviceId.equals(TOPO_GROUP_FLOW_DEVICE)) {
+                return ImmutableList.of(TOPO_GROUP_FLOW_ENTRY);
+            } else if (deviceId.equals(HARDWARE_DEVICE)){
+                return ImmutableList.of(HARDWARE_ETH_FLOW_ENTRY, HARDWARE_FLOW_ENTRY);
+            }
+            return ImmutableList.of();
+        }
+    }
+
+    private class TestDriverService extends DriverServiceAdapter {
+        @Override
+        public Driver getDriver(DeviceId deviceId) {
+            if(deviceId.equals(HARDWARE_DEVICE)){
+                return new DefaultDriver("ofdpa", ImmutableList.of(),
+                        "test", "test", "test", new HashMap<>(), new HashMap<>());
+            }
+            return new DefaultDriver("NotHWDriver", ImmutableList.of(),
+                    "test", "test", "test", new HashMap<>(), new HashMap<>());
+        }
+    }
+
+    private class TestGroupService extends GroupServiceAdapter {
+        @Override
+        public Iterable<Group> getGroups(DeviceId deviceId) {
+            if (deviceId.equals(GROUP_FLOW_DEVICE)) {
+                return ImmutableList.of(GROUP);
+            } else if (deviceId.equals(TOPO_GROUP_FLOW_DEVICE)) {
+                return ImmutableList.of(TOPO_GROUP);
+            }
+            return ImmutableList.of();
+        }
+    }
+
+    private class TestHostService extends HostServiceAdapter {
+        @Override
+        public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+            if (connectPoint.equals(TOPO_FLOW_3_OUT_CP)) {
+                return ImmutableSet.of(H2);
+            }
+            return ImmutableSet.of(H1);
+        }
+
+        @Override
+        public Set<Host> getHostsByMac(MacAddress mac) {
+            if (mac.equals(H1.mac())) {
+                return ImmutableSet.of(H1);
+            } else if (mac.equals(H2.mac())) {
+                return ImmutableSet.of(H2);
+            }
+            return ImmutableSet.of();
+        }
+
+        @Override
+        public Set<Host> getHostsByIp(IpAddress ip) {
+            if ((H1.ipAddresses().contains(ip))) {
+                return ImmutableSet.of(H1);
+            } else if ((H2.ipAddresses().contains(ip))) {
+                return ImmutableSet.of(H2);
+            }
+            return ImmutableSet.of();
+        }
+    }
+
+    private class TestLinkService extends LinkServiceAdapter {
+        @Override
+        public Set<Link> getEgressLinks(ConnectPoint connectPoint) {
+            if (connectPoint.equals(TOPO_FLOW_1_OUT_CP)
+                    || connectPoint.equals(TOPO_FLOW_OUT_CP_1)) {
+                return ImmutableSet.of(DefaultLink.builder()
+                        .providerId(ProviderId.NONE)
+                        .type(Link.Type.DIRECT)
+                        .src(connectPoint)
+                        .dst(TOPO_FLOW_2_IN_CP)
+                        .build());
+            } else if (connectPoint.equals(TOPO_FLOW_2_OUT_CP)) {
+                return ImmutableSet.of(DefaultLink.builder()
+                        .providerId(ProviderId.NONE)
+                        .type(Link.Type.DIRECT)
+                        .src(TOPO_FLOW_2_OUT_CP)
+                        .dst(TOPO_FLOW_3_IN_CP)
+                        .build());
+            } else if (connectPoint.equals(TOPO_FLOW_OUT_CP_2)) {
+                return ImmutableSet.of(DefaultLink.builder()
+                        .providerId(ProviderId.NONE)
+                        .type(Link.Type.DIRECT)
+                        .src(TOPO_FLOW_OUT_CP_2)
+                        .dst(TOPO_FLOW_4_IN_CP)
+                        .build());
+            } else if (connectPoint.equals(TOPO_FLOW_4_OUT_CP)) {
+                return ImmutableSet.of(DefaultLink.builder()
+                        .providerId(ProviderId.NONE)
+                        .type(Link.Type.DIRECT)
+                        .src(TOPO_FLOW_4_OUT_CP)
+                        .dst(TOPO_FLOW_3_IN_2_CP)
+                        .build());
+            }
+            return ImmutableSet.of();
+        }
+    }
+}
\ No newline at end of file