Detangling incubator: virtual nets, tunnels, resource labels, oh my

- virtual networking moved to /apps/virtual; with CLI & REST API
- tunnels and labels moved to /apps/tunnel; with CLI & REST API; UI disabled for now
- protobuf/models moved to /core/protobuf/models
- defunct grpc/rpc registry stuff left under /graveyard
- compile dependencies on /incubator moved to respective modules for compilation
- run-time dependencies will need to be re-tested for dependent apps

- /graveyard will be removed in not-too-distant future

Change-Id: I0a0b995c635487edcf95a352f50dd162186b0b39
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java
new file mode 100644
index 0000000..8e11e6f
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2016-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.incubator.net.virtual.impl.provider;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.graph.ScalarWeight;
+import org.onlab.graph.Weight;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminServiceAdapter;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+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.FlowRuleServiceAdapter;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.LinkWeigher;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyServiceAdapter;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertEquals;
+
+public class DefaultVirtualFlowRuleProviderTest {
+    private static final ProviderId PID = new ProviderId("of", "foo");
+
+    private static final DeviceId DID1 = DeviceId.deviceId("of:001");
+    private static final DeviceId DID2 = DeviceId.deviceId("of:002");
+    private static final PortNumber PORT_NUM1 = PortNumber.portNumber(1);
+    private static final PortNumber PORT_NUM2 = PortNumber.portNumber(2);
+
+    private static final DefaultAnnotations ANNOTATIONS =
+            DefaultAnnotations.builder().set("foo", "bar").build();
+
+    private static final Device DEV1 =
+            new DefaultDevice(PID, DID1, Device.Type.SWITCH, "", "", "", "", null);
+    private static final Device DEV2 =
+            new DefaultDevice(PID, DID2, Device.Type.SWITCH, "", "", "", "", null);
+    private static final Port PORT11 =
+            new DefaultPort(DEV1, PORT_NUM1, true, ANNOTATIONS);
+    private static final Port PORT12 =
+            new DefaultPort(DEV1, PORT_NUM2, true, ANNOTATIONS);
+    private static final Port PORT21 =
+            new DefaultPort(DEV2, PORT_NUM1, true, ANNOTATIONS);
+    private static final Port PORT22 =
+            new DefaultPort(DEV2, PORT_NUM2, true, ANNOTATIONS);
+
+    private static final ConnectPoint CP11 = new ConnectPoint(DID1, PORT_NUM1);
+    private static final ConnectPoint CP12 = new ConnectPoint(DID1, PORT_NUM2);
+    private static final ConnectPoint CP21 = new ConnectPoint(DID2, PORT_NUM1);
+    private static final ConnectPoint CP22 = new ConnectPoint(DID2, PORT_NUM2);
+    private static final Link LINK1 = DefaultLink.builder()
+            .src(CP12).dst(CP21).providerId(PID).type(Link.Type.DIRECT).build();
+
+    private static final NetworkId VNET_ID = NetworkId.networkId(1);
+    private static final DeviceId VDID = DeviceId.deviceId("of:100");
+
+    private static final VirtualNetwork VNET = new DefaultVirtualNetwork(
+            VNET_ID, TenantId.tenantId("t1"));
+    private static final VirtualDevice VDEV =
+            new DefaultVirtualDevice(VNET_ID, VDID);
+    private static final VirtualPort VPORT1 =
+            new DefaultVirtualPort(VNET_ID, VDEV, PORT_NUM1, CP11);
+    private static final VirtualPort VPORT2 =
+            new DefaultVirtualPort(VNET_ID, VDEV, PORT_NUM2, CP22);
+
+    private static final int TIMEOUT = 10;
+
+    protected DefaultVirtualFlowRuleProvider virtualProvider;
+
+    private ApplicationId vAppId;
+
+    @Before
+    public void setUp() {
+        virtualProvider = new DefaultVirtualFlowRuleProvider();
+
+        virtualProvider.deviceService = new TestDeviceService();
+        virtualProvider.coreService = new TestCoreService();
+        virtualProvider.vnService =
+                new TestVirtualNetworkAdminService();
+        virtualProvider.topologyService = new TestTopologyService();
+        virtualProvider.flowRuleService = new TestFlowRuleService();
+        virtualProvider.providerRegistryService = new VirtualProviderManager();
+
+        virtualProvider.activate();
+        vAppId = new TestApplicationId(0, "Virtual App");
+    }
+
+    @After
+    public void tearDown() {
+        virtualProvider.deactivate();
+        virtualProvider.deviceService = null;
+        virtualProvider.coreService = null;
+    }
+
+    @Test
+    public void devirtualizeFlowRuleWithInPort() {
+        TrafficSelector ts = DefaultTrafficSelector.builder()
+                .matchInPort(PORT_NUM1).build();
+        TrafficTreatment tr = DefaultTrafficTreatment.builder()
+                .setOutput(PORT_NUM2).build();
+
+        FlowRule r1 = DefaultFlowRule.builder()
+                .forDevice(VDID)
+                .withSelector(ts)
+                .withTreatment(tr)
+                .withPriority(10)
+                .fromApp(vAppId)
+                .makeTemporary(TIMEOUT)
+                .build();
+
+        virtualProvider.applyFlowRule(VNET_ID, r1);
+
+        assertEquals("2 rules should exist", 2,
+                     virtualProvider.flowRuleService.getFlowRuleCount());
+
+        Set<FlowEntry> phyRules = new HashSet<>();
+        for (FlowEntry i : virtualProvider.flowRuleService.getFlowEntries(DID1)) {
+            phyRules.add(i);
+        }
+        for (FlowEntry i : virtualProvider.flowRuleService.getFlowEntries(DID2)) {
+            phyRules.add(i);
+        }
+
+        FlowRule in = null;
+        FlowRule out = null;
+
+        for (FlowRule rule : phyRules) {
+
+            L2ModificationInstruction i = (L2ModificationInstruction)
+                    rule.treatment().allInstructions().get(0);
+
+            if (i.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+                in = rule;
+            } else {
+                out = rule;
+            }
+
+        }
+
+        assertEquals(DID1, in.deviceId());
+        assertEquals(DID2, out.deviceId());
+    }
+
+    @Test
+    public void devirtualizeFlowRuleWithoutInPort() {
+        TrafficSelector ts = DefaultTrafficSelector.builder().build();
+        TrafficTreatment tr = DefaultTrafficTreatment.builder()
+                .setOutput(PORT_NUM2).build();
+
+        FlowRule r1 = DefaultFlowRule.builder()
+                .forDevice(VDID)
+                .withSelector(ts)
+                .withTreatment(tr)
+                .withPriority(10)
+                .fromApp(vAppId)
+                .makeTemporary(TIMEOUT)
+                .build();
+
+        virtualProvider.applyFlowRule(VNET_ID, r1);
+
+        assertEquals("3 rules should exist", 3,
+                     virtualProvider.flowRuleService.getFlowRuleCount());
+
+        FlowRule inFromDID1 = null;
+        FlowRule inFromDID2 = null;
+        FlowRule out = null;
+
+        Set<FlowEntry> phyRules = new HashSet<>();
+        for (FlowEntry i : virtualProvider.flowRuleService.getFlowEntries(DID1)) {
+            phyRules.add(i);
+        }
+        for (FlowEntry i : virtualProvider.flowRuleService.getFlowEntries(DID2)) {
+            phyRules.add(i);
+        }
+
+        for (FlowRule rule : phyRules) {
+            for (Instruction inst : rule.treatment().allInstructions()) {
+                if (inst.type() == Instruction.Type.L2MODIFICATION) {
+                    L2ModificationInstruction i = (L2ModificationInstruction) inst;
+                    if (i.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+                        inFromDID1 = rule;
+                        break;
+                    } else {
+                        out = rule;
+                        break;
+                    }
+                } else {
+                    inFromDID2 = rule;
+                    break;
+                }
+            }
+        }
+
+        assertEquals(DID1, inFromDID1.deviceId());
+        assertEquals(DID2, inFromDID2.deviceId());
+        assertEquals(DID2, out.deviceId());
+    }
+
+    @Test
+    public void removeVirtualizeFlowRule() {
+        TrafficSelector ts = DefaultTrafficSelector.builder().build();
+        TrafficTreatment tr = DefaultTrafficTreatment.builder()
+                .setOutput(PORT_NUM2).build();
+
+        FlowRule r1 = DefaultFlowRule.builder()
+                .forDevice(VDID)
+                .withSelector(ts)
+                .withTreatment(tr)
+                .withPriority(10)
+                .fromApp(vAppId)
+                .makeTemporary(TIMEOUT)
+                .build();
+
+        virtualProvider.removeFlowRule(VNET_ID, r1);
+
+        assertEquals("0 rules should exist", 0,
+                     virtualProvider.flowRuleService.getFlowRuleCount());
+    }
+
+
+    private static class TestDeviceService extends DeviceServiceAdapter {
+        @Override
+        public int getDeviceCount() {
+            return 2;
+        }
+
+        @Override
+        public Iterable<Device> getDevices() {
+            return ImmutableList.of(DEV1, DEV2);
+        }
+
+        @Override
+        public Iterable<Device> getAvailableDevices() {
+            return getDevices();
+        }
+
+        @Override
+        public Device getDevice(DeviceId deviceId) {
+            return deviceId.equals(DID2) ? DEV2 : DEV1;
+        }
+    }
+
+    private static class TestCoreService extends CoreServiceAdapter {
+
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return new TestApplicationId(1, name);
+        }
+    }
+
+    private static class TestApplicationId extends DefaultApplicationId {
+        public TestApplicationId(int id, String name) {
+            super(id, name);
+        }
+    }
+
+    private class TestVirtualNetworkAdminService
+            extends VirtualNetworkAdminServiceAdapter {
+
+        @Override
+        public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) {
+            return ImmutableSet.of(VDEV);
+        }
+
+        @Override
+        public Set<VirtualLink> getVirtualLinks(NetworkId networkId) {
+            return new HashSet<>();
+        }
+
+        @Override
+        public Set<VirtualPort> getVirtualPorts(NetworkId networkId,
+                                                DeviceId deviceId) {
+            return ImmutableSet.of(VPORT1, VPORT2);
+        }
+
+        @Override
+        public ApplicationId getVirtualNetworkApplicationId(NetworkId networkId) {
+            return vAppId;
+        }
+    }
+
+    private static class TestTopologyService extends TopologyServiceAdapter {
+
+        Weight oneHundred = ScalarWeight.toWeight(100);
+        @Override
+        public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
+            DefaultPath path = new DefaultPath(PID, ImmutableList.of(LINK1),
+                                               oneHundred, ANNOTATIONS);
+            return ImmutableSet.of(path);
+        }
+
+        @Override
+        public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
+                                  LinkWeigher weigher) {
+            DefaultPath path = new DefaultPath(PID, ImmutableList.of(LINK1),
+                                               oneHundred, ANNOTATIONS);
+            return ImmutableSet.of(path);
+        }
+
+    }
+
+    private static class TestFlowRuleService extends FlowRuleServiceAdapter {
+        static Set<FlowRule> ruleCollection = new HashSet<>();
+
+        @Override
+        public int getFlowRuleCount() {
+            return ruleCollection.size();
+        }
+
+        @Override
+        public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+            return ruleCollection.stream()
+                    .filter(r -> r.deviceId().equals(deviceId))
+                    .map(DefaultFlowEntry::new)
+                    .collect(Collectors.toSet());
+        }
+
+        @Override
+        public void applyFlowRules(FlowRule... flowRules) {
+            ruleCollection.addAll(Arrays.asList(flowRules));
+        }
+
+        @Override
+        public void removeFlowRules(FlowRule... flowRules) {
+            Set<FlowRule> candidates = new HashSet<>();
+            for (FlowRule rule : flowRules) {
+                ruleCollection.stream()
+                        .filter(r -> r.exactMatch(rule))
+                        .forEach(candidates::add);
+            }
+            ruleCollection.removeAll(candidates);
+        }
+    }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProviderTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProviderTest.java
new file mode 100644
index 0000000..a17a0db
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProviderTest.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2016-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.incubator.net.virtual.impl.provider;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminServiceAdapter;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProviderService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+public class DefaultVirtualPacketProviderTest {
+    private static final String SRC_MAC_ADDR = "00:00:00:00:00:00";
+    private static final String DST_MAC_ADDR = "00:00:00:00:00:01";
+    private static final ProviderId PID = new ProviderId("of", "foo");
+
+    private static final DeviceId DID1 = DeviceId.deviceId("of:001");
+    private static final DeviceId DID2 = DeviceId.deviceId("of:002");
+    private static final PortNumber PORT_NUM1 = PortNumber.portNumber(1);
+    private static final PortNumber PORT_NUM2 = PortNumber.portNumber(2);
+    private static final PortNumber PORT_NUM3 = PortNumber.portNumber(3);
+    private static final PortNumber PORT_NUM4 = PortNumber.portNumber(4);
+
+    private static final DefaultAnnotations ANNOTATIONS =
+            DefaultAnnotations.builder().set("foo", "bar").build();
+
+    private static final Device DEV1 =
+            new DefaultDevice(PID, DID1, Device.Type.SWITCH, "", "", "", "", null);
+    private static final Device DEV2 =
+            new DefaultDevice(PID, DID2, Device.Type.SWITCH, "", "", "", "", null);
+    private static final Port PORT11 =
+            new DefaultPort(DEV1, PORT_NUM1, true, ANNOTATIONS);
+    private static final Port PORT12 =
+            new DefaultPort(DEV1, PORT_NUM2, true, ANNOTATIONS);
+    private static final Port PORT21 =
+            new DefaultPort(DEV2, PORT_NUM3, true, ANNOTATIONS);
+    private static final Port PORT22 =
+            new DefaultPort(DEV2, PORT_NUM4, true, ANNOTATIONS);
+
+    private static final ConnectPoint CP11 = new ConnectPoint(DID1, PORT_NUM1);
+    private static final ConnectPoint CP12 = new ConnectPoint(DID1, PORT_NUM2);
+    private static final ConnectPoint CP21 = new ConnectPoint(DID2, PORT_NUM3);
+    private static final ConnectPoint CP22 = new ConnectPoint(DID2, PORT_NUM4);
+    private static final Link LINK1 = DefaultLink.builder()
+            .src(CP12).dst(CP21).providerId(PID).type(Link.Type.DIRECT).build();
+
+    private static final TenantId TENANT_ID = TenantId.tenantId("1");
+    private static final NetworkId VNET_ID = NetworkId.networkId(1);
+    private static final DeviceId VDID = DeviceId.deviceId("of:100");
+
+    private static final PortNumber VPORT_NUM1 = PortNumber.portNumber(10);
+    private static final PortNumber VPORT_NUM2 = PortNumber.portNumber(11);
+
+    private static final VirtualNetwork VNET = new DefaultVirtualNetwork(
+            VNET_ID, TenantId.tenantId("t1"));
+    private static final VirtualDevice VDEV =
+            new DefaultVirtualDevice(VNET_ID, VDID);
+    private static final VirtualPort VPORT1 =
+            new DefaultVirtualPort(VNET_ID, VDEV, VPORT_NUM1, CP11);
+    private static final VirtualPort VPORT2 =
+            new DefaultVirtualPort(VNET_ID, VDEV, VPORT_NUM2, CP22);
+    private static final ConnectPoint VCP11 = new ConnectPoint(VDID, VPORT_NUM1);
+    private static final ConnectPoint VCP12 = new ConnectPoint(VDID, VPORT_NUM2);
+
+    protected DefaultVirtualPacketProvider virtualProvider;
+    protected TestPacketService testPacketService;
+    protected TestVirtualPacketProviderService providerService;
+
+    private VirtualProviderManager providerManager;
+
+    private ApplicationId vAppId;
+
+    @Before
+    public void setUp() {
+        virtualProvider = new DefaultVirtualPacketProvider();
+
+        virtualProvider.coreService = new CoreServiceAdapter();
+        virtualProvider.vnaService =
+                new TestVirtualNetworkAdminService();
+
+        providerService = new TestVirtualPacketProviderService();
+
+        testPacketService = new TestPacketService();
+        virtualProvider.packetService = testPacketService;
+
+        providerManager = new VirtualProviderManager();
+        virtualProvider.providerRegistryService = providerManager;
+        providerManager.registerProviderService(VNET_ID, providerService);
+
+        virtualProvider.activate();
+        vAppId = new TestApplicationId(0, "Virtual App");
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.matchEthType(Ethernet.TYPE_IPV4);
+
+        virtualProvider.startPacketHandling();
+    }
+
+    @After
+    public void tearDown() {
+        virtualProvider.deactivate();
+        virtualProvider.coreService = null;
+        virtualProvider.vnaService = null;
+    }
+
+
+    /** Test the virtual outbound packet is delivered to a proper (physical)
+     *  device.
+     */
+    @Test
+    public void devirtualizePacket() {
+        TrafficTreatment tr = DefaultTrafficTreatment.builder()
+                .setOutput(VPORT_NUM1).build();
+        ByteBuffer data = ByteBuffer.wrap("abc".getBytes());
+
+        OutboundPacket vOutPacket = new DefaultOutboundPacket(VDID, tr, data);
+
+        virtualProvider.emit(VNET_ID, vOutPacket);
+
+        assertEquals("The count should be 1", 1,
+                     testPacketService.getRequestedPacketCount());
+
+        OutboundPacket pOutPacket = testPacketService.getRequestedPacket(0);
+
+        assertEquals("The packet should be requested on DEV1", DID1,
+                     pOutPacket.sendThrough());
+
+        PortNumber outPort = pOutPacket.treatment()
+                .allInstructions()
+                .stream()
+                .filter(i -> i.type() == Instruction.Type.OUTPUT)
+                .map(i -> (Instructions.OutputInstruction) i)
+                .map(i -> i.port())
+                .findFirst().get();
+        assertEquals("The packet should be out at PORT1 of DEV1", PORT_NUM1,
+                     outPort);
+    }
+
+    /** Test the physical packet context is delivered to a proper (physical)
+     *  virtual network and device.
+     */
+    @Test
+    public void virtualizePacket() {
+        Ethernet eth = new Ethernet();
+        eth.setSourceMACAddress(SRC_MAC_ADDR);
+        eth.setDestinationMACAddress(DST_MAC_ADDR);
+        eth.setVlanID((short) 1);
+        eth.setPayload(null);
+
+        InboundPacket pInPacket =
+                new DefaultInboundPacket(CP22, eth,
+                                         ByteBuffer.wrap(eth.serialize()));
+
+        PacketContext pContext =
+                new TestPacketContext(System.nanoTime(), pInPacket, null, false);
+
+        testPacketService.sendTestPacketContext(pContext);
+
+        PacketContext vContext = providerService.getRequestedPacketContext(0);
+        InboundPacket vInPacket = vContext.inPacket();
+
+        assertEquals("the packet should be received from VCP12",
+                     VCP12, vInPacket.receivedFrom());
+
+        assertEquals("VLAN tag should be excludede", VlanId.UNTAGGED,
+                     vInPacket.parsed().getVlanID());
+    }
+
+    private class TestPacketContext extends DefaultPacketContext {
+
+        /**
+         * Creates a new packet context.
+         *
+         * @param time   creation time
+         * @param inPkt  inbound packet
+         * @param outPkt outbound packet
+         * @param block  whether the context is blocked or not
+         */
+        protected TestPacketContext(long time, InboundPacket inPkt,
+                                    OutboundPacket outPkt, boolean block) {
+            super(time, inPkt, outPkt, block);
+        }
+
+        @Override
+        public void send() {
+
+        }
+    }
+
+    private static class TestApplicationId extends DefaultApplicationId {
+        public TestApplicationId(int id, String name) {
+            super(id, name);
+        }
+    }
+
+    private static class TestVirtualNetworkAdminService
+            extends VirtualNetworkAdminServiceAdapter {
+
+        @Override
+        public Set<VirtualNetwork> getVirtualNetworks(TenantId tenantId) {
+            return ImmutableSet.of(VNET);
+        }
+
+        @Override
+        public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) {
+            return ImmutableSet.of(VDEV);
+        }
+
+        @Override
+        public Set<VirtualPort> getVirtualPorts(NetworkId networkId,
+                                                DeviceId deviceId) {
+            return ImmutableSet.of(VPORT1, VPORT2);
+        }
+
+        @Override
+        public Set<TenantId> getTenantIds() {
+            return ImmutableSet.of(TENANT_ID);
+        }
+
+    }
+
+    private static class TestVirtualPacketProviderService
+            extends AbstractVirtualProviderService<VirtualPacketProvider>
+            implements VirtualPacketProviderService {
+
+        static List<PacketContext> requestedContext = new LinkedList();
+        static List<NetworkId> requestedNetworkId = new LinkedList();
+
+        @Override
+        public VirtualPacketProvider provider() {
+            return null;
+        }
+
+        PacketContext getRequestedPacketContext(int index) {
+            return requestedContext.get(index);
+        }
+
+        @Override
+        public void processPacket(PacketContext context) {
+            requestedContext.add(context);
+        }
+    }
+
+    private static class TestPacketService extends PacketServiceAdapter {
+        static List<OutboundPacket> requestedPacket = new LinkedList();
+        static PacketProcessor processor = null;
+
+        @Override
+        public void addProcessor(PacketProcessor processor, int priority) {
+            this.processor = processor;
+        }
+
+        @Override
+        public void emit(OutboundPacket packet) {
+            requestedPacket.add(packet);
+        }
+
+        OutboundPacket getRequestedPacket(int index) {
+            return requestedPacket.get(index);
+        }
+
+        int getRequestedPacketCount() {
+            return requestedPacket.size();
+        }
+
+        void sendTestPacketContext(PacketContext context) {
+            processor.process(context);
+        }
+    }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualNetworkTopologyProviderTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualNetworkTopologyProviderTest.java
new file mode 100644
index 0000000..ee03783
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualNetworkTopologyProviderTest.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2016-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.incubator.net.virtual.impl.provider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.event.Event;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderRegistry;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderService;
+import org.onosproject.incubator.net.virtual.impl.VirtualNetworkManager;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+import org.onosproject.net.intent.FakeIntentManager;
+import org.onosproject.net.intent.TestableIntentService;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.*;
+
+/**
+ * Junit tests for VirtualNetworkTopologyProvider.
+ */
+public class VirtualNetworkTopologyProviderTest extends TestDeviceParams {
+
+    private final String tenantIdValue1 = "TENANT_ID1";
+
+    private VirtualNetwork virtualNetwork;
+    private VirtualDevice virtualDevice1;
+    private VirtualDevice virtualDevice2;
+    private VirtualDevice virtualDevice3;
+    private VirtualDevice virtualDevice4;
+    private VirtualDevice virtualDevice5;
+    private ConnectPoint cp1;
+    private ConnectPoint cp2;
+    private ConnectPoint cp3;
+    private ConnectPoint cp4;
+    private ConnectPoint cp5;
+    private ConnectPoint cp6;
+    private ConnectPoint cp7;
+    private ConnectPoint cp8;
+    private ConnectPoint cp9;
+
+    private VirtualNetworkManager manager;
+    private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+    private CoreService coreService;
+    private DefaultVirtualNetworkProvider topologyProvider;
+    private TopologyService topologyService;
+    private TestableIntentService intentService = new FakeIntentManager();
+    private TestServiceDirectory testDirectory;
+    private final VirtualNetworkRegistryAdapter virtualNetworkRegistry = new VirtualNetworkRegistryAdapter();
+
+    private static final int MAX_WAIT_TIME = 5;
+    private static final int MAX_PERMITS = 1;
+    private static Semaphore changed;
+
+    private Set<Set<ConnectPoint>> clusters;
+
+    @Before
+    public void setUp() throws Exception {
+
+        virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+        coreService = new VirtualNetworkTopologyProviderTest.TestCoreService();
+
+        TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+        TestUtils.setField(virtualNetworkManagerStore, "storageService",
+                           new TestStorageService());
+        virtualNetworkManagerStore.activate();
+
+        manager = new VirtualNetworkManager();
+        TestUtils.setField(manager, "coreService", coreService);
+        TestUtils.setField(manager, "store", virtualNetworkManagerStore);
+        TestUtils.setField(manager, "intentService", intentService);
+        NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+
+        testDirectory = new TestServiceDirectory();
+        TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+        manager.activate();
+
+        manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+        virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+        topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+        topologyProvider = new DefaultVirtualNetworkProvider();
+        topologyProvider.topologyService = topologyService;
+        topologyProvider.providerRegistry = virtualNetworkRegistry;
+        topologyProvider.activate();
+
+        setupVirtualNetworkTopology();
+        changed = new Semaphore(0, true);
+    }
+
+    @After
+    public void tearDown() {
+        topologyProvider.deactivate();
+        virtualNetworkManagerStore.deactivate();
+        manager.deactivate();
+        NetTestTools.injectEventDispatcher(manager, null);
+    }
+
+    /**
+     * Method to create the virtual network for further testing.
+     **/
+    private void setupVirtualNetworkTopology() {
+        virtualDevice1 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID1);
+        virtualDevice2 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID2);
+        virtualDevice3 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID3);
+        virtualDevice4 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID4);
+        virtualDevice5 =
+                manager.createVirtualDevice(virtualNetwork.id(), DID5);
+
+        cp1 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+                                  PortNumber.portNumber(1), cp1);
+
+        cp2 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(2));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+                                  PortNumber.portNumber(2), cp2);
+
+        cp3 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(3));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+                                  PortNumber.portNumber(3), cp3);
+
+        cp4 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(4));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+                                  PortNumber.portNumber(4), cp4);
+
+        cp5 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(5));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+                                  PortNumber.portNumber(5), cp5);
+
+        cp6 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(6));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+                                  PortNumber.portNumber(6), cp6);
+
+        cp7 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(7));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+                                  PortNumber.portNumber(7), cp7);
+
+        cp8 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(8));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+                                  PortNumber.portNumber(8), cp8);
+
+        cp9 = new ConnectPoint(virtualDevice5.id(), PortNumber.portNumber(9));
+        manager.createVirtualPort(virtualNetwork.id(), virtualDevice5.id(),
+                                  PortNumber.portNumber(9), cp9);
+
+        VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
+        virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
+        virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link3 = manager.createVirtualLink(virtualNetwork.id(), cp4, cp5);
+        virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link4 = manager.createVirtualLink(virtualNetwork.id(), cp5, cp4);
+        virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link5 = manager.createVirtualLink(virtualNetwork.id(), cp8, cp9);
+        virtualNetworkManagerStore.updateLink(link5, link5.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link6 = manager.createVirtualLink(virtualNetwork.id(), cp9, cp8);
+        virtualNetworkManagerStore.updateLink(link6, link6.tunnelId(), Link.State.ACTIVE);
+
+        clusters = null;
+    }
+
+    /**
+     * Test isTraversable() method using a null source connect point.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testIsTraversableNullSrc() {
+        // test the isTraversable() method with a null source connect point.
+        topologyProvider.isTraversable(null, cp3);
+    }
+
+    /**
+     * Test isTraversable() method using a null destination connect point.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testIsTraversableNullDst() {
+        // test the isTraversable() method with a null destination connect point.
+        topologyProvider.isTraversable(cp1, null);
+    }
+
+    /**
+     * Test isTraversable() method.
+     */
+    @Test
+    public void testIsTraversable() {
+        // test the isTraversable() method.
+        assertTrue("These two connect points should be traversable.",
+                   topologyProvider.isTraversable(new ConnectPoint(cp1.elementId(), cp1.port()),
+                                                  new ConnectPoint(cp3.elementId(), cp3.port())));
+        assertTrue("These two connect points should be traversable.",
+                   topologyProvider.isTraversable(new ConnectPoint(cp1.elementId(), cp1.port()),
+                                                  new ConnectPoint(cp5.elementId(), cp5.port())));
+        assertFalse("These two connect points should not be traversable.",
+                    topologyProvider.isTraversable(
+                            new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1)),
+                            new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(6))));
+    }
+
+    /**
+     * Test the topologyChanged() method.
+     */
+    @Test
+    public void testTopologyChanged() {
+        // Initial setup is two clusters of devices/links.
+        assertEquals("The cluster count did not match.", 2,
+                     topologyService.currentTopology().clusterCount());
+
+        // Adding this link will join the two clusters together.
+        List<Event> reasons = new ArrayList<>();
+        VirtualLink link = manager.createVirtualLink(virtualNetwork.id(), cp6, cp7);
+        virtualNetworkManagerStore.updateLink(link, link.tunnelId(), Link.State.ACTIVE);
+        VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp7, cp6);
+        virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+
+        reasons.add(new LinkEvent(LinkEvent.Type.LINK_ADDED, link));
+        reasons.add(new LinkEvent(LinkEvent.Type.LINK_ADDED, link2));
+        TopologyEvent event = new TopologyEvent(
+                TopologyEvent.Type.TOPOLOGY_CHANGED,
+                topologyService.currentTopology(),
+                reasons);
+
+        topologyProvider.topologyListener.event(event);
+
+        // Wait for the topology changed event, and that the topologyChanged method was called.
+        try {
+            if (!changed.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+                fail("Failed to wait for topology changed event.");
+            }
+        } catch (InterruptedException e) {
+            fail("Semaphore exception." + e.getMessage());
+        }
+
+        // Validate that the topology changed method received a single cluster of connect points.
+        // This means that the two previous clusters have now joined into a single cluster.
+        assertEquals("The cluster count did not match.", 1, this.clusters.size());
+        assertEquals("The cluster count did not match.", 1,
+                     topologyService.currentTopology().clusterCount());
+
+        // Now remove the virtual link to split it back into two clusters.
+        manager.removeVirtualLink(virtualNetwork.id(), link.src(), link.dst());
+        manager.removeVirtualLink(virtualNetwork.id(), link2.src(), link2.dst());
+        assertEquals("The cluster count did not match.", 2,
+                     topologyService.currentTopology().clusterCount());
+
+        reasons = new ArrayList<>();
+        reasons.add(new LinkEvent(LinkEvent.Type.LINK_REMOVED, link));
+        reasons.add(new LinkEvent(LinkEvent.Type.LINK_REMOVED, link2));
+        event = new TopologyEvent(
+                TopologyEvent.Type.TOPOLOGY_CHANGED,
+                topologyService.currentTopology(),
+                reasons);
+
+        topologyProvider.topologyListener.event(event);
+
+        // Wait for the topology changed event, and that the topologyChanged method was called.
+        try {
+            if (!changed.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+                fail("Failed to wait for topology changed event.");
+            }
+        } catch (InterruptedException e) {
+            fail("Semaphore exception." + e.getMessage());
+        }
+
+        // Validate that the topology changed method received two clusters of connect points.
+        // This means that the single previous clusters has now split into two clusters.
+        assertEquals("The cluster count did not match.", 2, this.clusters.size());
+    }
+
+    /**
+     * Virtual network registry implementation for this test class.
+     */
+    private class VirtualNetworkRegistryAdapter implements VirtualNetworkProviderRegistry {
+        private VirtualNetworkProvider provider;
+
+        @Override
+        public VirtualNetworkProviderService register(VirtualNetworkProvider theProvider) {
+            this.provider = theProvider;
+            return new TestVirtualNetworkProviderService(theProvider);
+        }
+
+        @Override
+        public void unregister(VirtualNetworkProvider theProvider) {
+            this.provider = null;
+        }
+
+        @Override
+        public Set<ProviderId> getProviders() {
+            return null;
+        }
+    }
+
+
+    /**
+     * Virtual network provider service implementation for this test class.
+     */
+    private class TestVirtualNetworkProviderService
+            extends AbstractProviderService<VirtualNetworkProvider>
+            implements VirtualNetworkProviderService {
+
+        /**
+         * Constructor.
+         *
+         * @param provider virtual network test provider
+         */
+        protected TestVirtualNetworkProviderService(VirtualNetworkProvider provider) {
+            super(provider);
+        }
+
+        @Override
+        public void topologyChanged(Set<Set<ConnectPoint>> theClusters) {
+            clusters = theClusters;
+            changed.release();
+        }
+
+        @Override
+        public void tunnelUp(NetworkId networkId, ConnectPoint src,
+                             ConnectPoint dst, TunnelId tunnelId) {
+        }
+
+        @Override
+        public void tunnelDown(NetworkId networkId, ConnectPoint src,
+                               ConnectPoint dst, TunnelId tunnelId) {
+        }
+    }
+
+    /**
+     * Core service test class.
+     */
+    private class TestCoreService extends CoreServiceAdapter {
+
+        @Override
+        public IdGenerator getIdGenerator(String topic) {
+            return new IdGenerator() {
+                private AtomicLong counter = new AtomicLong(0);
+
+                @Override
+                public long getNewId() {
+                    return counter.getAndIncrement();
+                }
+            };
+        }
+    }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java
new file mode 100644
index 0000000..90e1664
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017-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.incubator.net.virtual.impl.provider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.net.provider.ProviderId;
+
+import static org.junit.Assert.assertEquals;
+
+public class VirtualProviderManagerTest {
+
+    private static final String TEST_SCHEME1 = "test1";
+    private static final String TEST_SCHEME2 = "test2";
+    private static final String TEST_ID1 = "org.onosproject.virtual.testprovider1";
+    private static final String TEST_ID2 = "org.onosproject.virtual.testprovider1";
+    private static final NetworkId NETWORK_ID1 = NetworkId.networkId(1);
+    private static final NetworkId NETWORK_ID2 = NetworkId.networkId(2);
+
+    VirtualProviderManager virtualProviderManager;
+
+    @Before
+    public void setUp() throws Exception {
+        virtualProviderManager = new VirtualProviderManager();
+    }
+
+    /**
+     * Tests registerProvider() and unregisterProvider().
+     */
+    @Test
+    public void registerProviderTest() {
+        TestProvider1 provider1 = new TestProvider1();
+        virtualProviderManager.registerProvider(provider1);
+
+        assertEquals("The number of registered provider did not match.", 1,
+                     virtualProviderManager.getProviders().size());
+
+        assertEquals("The registered provider did not match", provider1,
+                     virtualProviderManager.getProvider(TEST_SCHEME1));
+
+        virtualProviderManager.unregisterProvider(provider1);
+
+        TestProvider2 provider2 = new TestProvider2();
+        virtualProviderManager.registerProvider(provider2);
+
+        assertEquals("The number of registered provider did not match.", 1,
+                     virtualProviderManager.getProviders().size());
+
+        virtualProviderManager.unregisterProvider(provider2);
+
+        assertEquals("The number of registered provider did not match.", 0,
+                     virtualProviderManager.getProviders().size());
+    }
+
+    /**
+     * Tests registerProviderService() and getProviderService().
+     */
+    @Test
+    public void registerProviderServiceTest() {
+        TestProvider1 provider1 = new TestProvider1();
+        virtualProviderManager.registerProvider(provider1);
+
+        TestProviderService1 providerService1 = new TestProviderService1();
+        virtualProviderManager.registerProviderService(NETWORK_ID1, providerService1);
+
+        assertEquals(providerService1,
+                     virtualProviderManager.getProviderService(NETWORK_ID1, TestProvider1.class));
+    }
+
+    private class TestProvider1 extends AbstractVirtualProvider {
+        protected TestProvider1() {
+            super(new ProviderId(TEST_SCHEME1, TEST_ID1));
+        }
+    }
+
+    private class TestProvider2 extends AbstractVirtualProvider {
+        protected TestProvider2() {
+            super(new ProviderId(TEST_SCHEME2, TEST_ID2));
+        }
+    }
+
+    private class TestProviderService1 extends AbstractVirtualProviderService<TestProvider1> {
+    }
+
+    private class TestProviderService2 extends AbstractVirtualProviderService<TestProvider2> {
+    }
+}