Currently, SADIS caches Subscriber records defining Subscriber Tagging requirements, authentication data and other service flow information. The OF Flow Table ID used to reference a Technology Profile for a given Subscriber needs to be able to be added to the Subscriber Record and a Reference to an Upstream and Downstream OF Meter also supported.

Change-Id: I2951d777e557c5354eb7ef0d9d8645c22b763740
diff --git a/app/src/test/java/org/opencord/sadis/impl/BandwidthProfileManagerTest.java b/app/src/test/java/org/opencord/sadis/impl/BandwidthProfileManagerTest.java
new file mode 100644
index 0000000..9ea46c4
--- /dev/null
+++ b/app/src/test/java/org/opencord/sadis/impl/BandwidthProfileManagerTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.opencord.sadis.impl;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opencord.sadis.BandwidthProfileInformation;
+import org.opencord.sadis.BaseConfig;
+import org.opencord.sadis.BaseInformation;
+import org.opencord.sadis.BaseInformationService;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class BandwidthProfileManagerTest extends BaseSadis {
+
+    BandwidthProfileBuilder bp1 = BandwidthProfileBuilder.build("High Speed", 1000000000, 384000L,
+            100000000, 384000L, 100000000);
+
+    BandwidthProfileBuilder bp2 = BandwidthProfileBuilder.build("Home User Speed", 1000000000, 200000L,
+            100000000, 200000L, 100000000);
+
+    @Before
+    public void setUp() throws Exception {
+        config = new BandwidthProfileConfig();
+        super.setUp("/LocalBpConfig.json", BandwidthProfileConfig.class);
+    }
+
+    @Test
+    public void testConfiguration() {
+        BandwidthProfileConfig bpConfig = sadis.cfgService.getConfig(null, BandwidthProfileConfig.class);
+        checkConfigInfo(60, "PT1m", bpConfig);
+        checkEntriesForBandwidthProfiles(bpConfig);
+    }
+
+    @Test
+    public void testLocalMode() throws Exception {
+
+        BaseInformationService<BandwidthProfileInformation> bpService = sadis.getBandwidthProfileService();
+        checkGetForExisting("High Speed", bp1, bpService);
+        checkGetForExisting("Home User Speed", bp2, bpService);
+
+        invalidateId("High Speed", bpService);
+        checkFromBoth("High Speed", bp1, bpService);
+
+        invalidateAll(bpService);
+        checkFromBoth("Home User Speed", bp2, bpService);
+    }
+
+    @Test
+    public void testRemoteMode() throws Exception {
+        BaseInformationService<BandwidthProfileInformation> service = sadis.getBandwidthProfileService();
+        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
+        configListener.event(event);
+
+        checkGetForExisting("HighSpeed", bp1, service);
+
+        invalidateId("HighSpeed", service);
+        checkFromBoth("HighSpeed", bp1, service);
+
+        invalidateAll(service);
+        checkFromBoth("HighSpeed", bp1, service);
+    }
+
+    private void checkEntriesForBandwidthProfiles(BaseConfig config) {
+        List<BandwidthProfileInformation> entries = config.getEntries();
+        assertEquals(2, entries.size());
+
+        BandwidthProfileInformation bpi = BandwidthProfileBuilder.build("High Speed", 1000000000, 384000L, 100000000,
+                384000L, 100000000);
+        assertTrue(checkEquality(bpi, entries.get(0)));
+
+        bpi = BandwidthProfileBuilder.build("Home User Speed", 1000000000, 200000L, 100000000,
+                200000L, 100000000);
+        assertTrue(checkEquality(bpi, entries.get(1)));
+    }
+
+    private static final class BandwidthProfileBuilder extends BandwidthProfileInformation {
+
+        public static BandwidthProfileBuilder build(String id, long cir, Long cbs, long eir, Long ebs, long air) {
+            BandwidthProfileBuilder info = new BandwidthProfileBuilder();
+            info.setId(id);
+
+            if (cbs != null) {
+                info.setCommittedBurstSize(cbs);
+            } else {
+                info.setCommittedBurstSize(0L);
+            }
+            info.setCommittedInformationRate(cir);
+
+            info.setExceededInformationRate(eir);
+            if (ebs != null) {
+                info.setExceededBurstSize(ebs);
+            } else {
+                info.setExceededBurstSize(0L);
+            }
+
+            info.setAssuredInformationRate(air);
+            return info;
+        }
+    }
+
+    @Override
+    boolean checkEquality(BaseInformation localEntry, BaseInformation entry) {
+        BandwidthProfileInformation bpi = (BandwidthProfileInformation) localEntry;
+        BandwidthProfileInformation other = (BandwidthProfileInformation) entry;
+
+        if (other == null) {
+            return false;
+        }
+
+        if (bpi.id() == null) {
+            if (other.id() != null) {
+                return false;
+            }
+        } else if (!bpi.id().equals(other.id())) {
+            return false;
+        }
+
+        if (bpi.committedInformationRate() != other.committedInformationRate()) {
+            return false;
+        }
+
+        if (!bpi.committedBurstSize().equals(other.committedBurstSize())) {
+            return false;
+        }
+
+        if (bpi.exceededInformationRate() != other.exceededInformationRate()) {
+            return false;
+        }
+
+        if (!bpi.exceededBurstSize().equals(other.exceededBurstSize())) {
+            return false;
+        }
+
+        if (bpi.assuredInformationRate() != other.assuredInformationRate()) {
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/app/src/test/java/org/opencord/sadis/impl/BaseSadis.java b/app/src/test/java/org/opencord/sadis/impl/BaseSadis.java
new file mode 100644
index 0000000..7d2604a
--- /dev/null
+++ b/app/src/test/java/org/opencord/sadis/impl/BaseSadis.java
@@ -0,0 +1,221 @@
+/*
+ * 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.opencord.sadis.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.opencord.sadis.BaseConfig;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.BaseInformation;
+
+import java.io.InputStream;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+public abstract class BaseSadis {
+
+    protected SadisManager sadis;
+    protected ConfigApplyDelegate delegate;
+    protected ObjectMapper mapper;
+    protected ApplicationId subject;
+    protected BaseConfig config;
+    protected NetworkConfigEvent event;
+    protected static NetworkConfigListener configListener;
+
+    public void setUp(String localConfig, Class configClass) throws Exception {
+        sadis = new SadisManager();
+        sadis.coreService = new MockCoreService();
+        delegate = new MockConfigDelegate();
+        mapper = new ObjectMapper();
+        subject = sadis.coreService.registerApplication("org.opencord.sadis");
+
+        config.init(subject, "sadis-local-mode-test", node(localConfig), mapper, delegate);
+        sadis.cfgService = new MockNetworkConfigRegistry(subject, config);
+
+        event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED, subject, configClass);
+
+        sadis.codecService = new CodecManager();
+        sadis.activate();
+    }
+
+    public void tearDown() {
+        this.sadis.deactivate();
+    }
+
+    protected JsonNode node(String jsonFile) throws Exception {
+        final InputStream jsonStream = BaseSadis.class.getResourceAsStream(jsonFile);
+        final JsonNode testConfig = mapper.readTree(jsonStream);
+        return testConfig;
+    }
+
+    protected void checkConfigInfo(int cacheMaxSize, String cacheTtl, BaseConfig config) {
+        assertEquals(cacheMaxSize, config.getCacheMaxSize());
+        assertEquals(Duration.parse(cacheTtl), config.getCacheTtl());
+    }
+
+    protected void invalidateId(String id, BaseInformationService service) {
+        service.invalidateId(id);
+    }
+
+    protected void invalidateAll(BaseInformationService service) {
+        service.invalidateAll();
+    }
+
+    protected void checkFromBoth(String id, BaseInformation localEntry, BaseInformationService service) {
+        BaseInformation entry = service.getfromCache(id);
+        assertNull(entry);
+        checkGetForExisting(id, localEntry, service);
+    }
+
+    abstract boolean checkEquality(BaseInformation localEntry, BaseInformation entry);
+
+    protected void checkGetForExisting(String id, BaseInformation localEntry, BaseInformationService service) {
+        BaseInformation entry = service.get(id);
+        assertNotNull(entry);
+        System.out.println(entry);
+        if (localEntry != null) {
+            assertTrue(checkEquality(localEntry, entry));
+        }
+    }
+
+
+    /**
+     * Mocks an ONOS configuration delegate to allow JSON based configuration to
+     * be tested.
+     */
+    private static final class MockConfigDelegate implements ConfigApplyDelegate {
+        @Override
+        public void onApply(@SuppressWarnings("rawtypes") Config config) {
+            config.apply();
+        }
+    }
+
+    /**
+     * Mocks an instance of {@link ApplicationId} so that the application
+     * component under test can query and use its application ID.
+     */
+    private static final class MockApplicationId implements ApplicationId {
+
+        private final short id;
+        private final String name;
+
+        public MockApplicationId(short id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        @Override
+        public short id() {
+            return id;
+        }
+
+        @Override
+        public String name() {
+            return name;
+        }
+    }
+
+    /**
+     * Mocks the core services of ONOS so that the application under test can
+     * register and query application IDs.
+     */
+    private static final class MockCoreService extends CoreServiceAdapter {
+
+        private List<ApplicationId> idList = new ArrayList<ApplicationId>();
+        private Map<String, ApplicationId> idMap = new HashMap<String, ApplicationId>();
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see
+         * org.onosproject.core.CoreServiceAdapter#getAppId(java.lang.Short)
+         */
+        @Override
+        public ApplicationId getAppId(Short id) {
+            if (id >= idList.size()) {
+                return null;
+            }
+            return idList.get(id);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see
+         * org.onosproject.core.CoreServiceAdapter#getAppId(java.lang.String)
+         */
+        @Override
+        public ApplicationId getAppId(String name) {
+            return idMap.get(name);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see
+         * org.onosproject.core.CoreServiceAdapter#registerApplication(java.lang
+         * .String)
+         */
+        @Override
+        public ApplicationId registerApplication(String name) {
+            ApplicationId appId = idMap.get(name);
+            if (appId == null) {
+                appId = new MockApplicationId((short) idList.size(), name);
+                idList.add(appId);
+                idMap.put(name, appId);
+            }
+            return appId;
+        }
+
+    }
+
+
+    /**
+     * Mocks the ONOS network configuration registry so that the application
+     * under test can access a JSON defined configuration.
+     */
+    static final class MockNetworkConfigRegistry<S> extends NetworkConfigRegistryAdapter {
+        private final BaseConfig config;
+
+        public MockNetworkConfigRegistry(final S subject, final BaseConfig config) {
+            this.config = config;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <S, C extends Config<S>> C getConfig(final S subject, final Class<C> configClass) {
+            return (C) config;
+        }
+
+        @Override
+        public void addListener(NetworkConfigListener listener) {
+            configListener = listener;
+        }
+    }
+}
diff --git a/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java b/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java
deleted file mode 100644
index 4111995..0000000
--- a/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * 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.opencord.sadis.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import java.io.InputStream;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreServiceAdapter;
-import org.onosproject.net.config.Config;
-import org.onosproject.net.config.ConfigApplyDelegate;
-import org.onosproject.net.config.NetworkConfigRegistryAdapter;
-import org.onosproject.codec.impl.CodecManager;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-
-import org.opencord.sadis.SubscriberAndDeviceInformation;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
- * Set of tests of the SADIS ONOS application component.
- */
-public class SadisManagerTest {
-
-    private SadisManager sadis;
-    private ObjectMapper mapper;
-    private ApplicationId subject;
-    private ConfigApplyDelegate delegate;
-    private SadisConfig config;
-    private NetworkConfigEvent event;
-    private static NetworkConfigListener configListener;
-
-     SubscriberAndDeviceInformationBuilder entry1 = SubscriberAndDeviceInformationBuilder.build("1", (short) 2,
-                  (short) 2, "1/1/2", (short) 125, (short) 3, "aa:bb:cc:dd:ee:ff", "XXX-NASID", "10.10.10.10",
-                  "circuit123", "remote123");
-     SubscriberAndDeviceInformationBuilder entry2 = SubscriberAndDeviceInformationBuilder.build("2", (short) 4,
-                  (short) 4, "1/1/2", (short) 129, (short) 4, "aa:bb:cc:dd:ee:ff", "YYY-NASID", "1.1.1.1",
-                  "circuit234", "remote234");
-     SubscriberAndDeviceInformationBuilder entry3 = SubscriberAndDeviceInformationBuilder.build("3", (short) 7,
-                  (short) 8, "1/1/2", (short) 130, (short) 7, "ff:aa:dd:cc:bb:ee", "MNO-NASID", "30.30.30.30",
-                  "circuit567", "remote567");
-     SubscriberAndDeviceInformationBuilder entry4 = SubscriberAndDeviceInformationBuilder.build("4", (short) 2,
-                  (short) 1, "1/1/2", (short) 132, (short) 1, "ff:cc:dd:aa:ee:bb", "PQR-NASID", "15.15.15.15",
-                  "circuit678", "remote678");
-
-    @Before
-    public void setUp() throws Exception {
-        sadis = new SadisManager();
-        sadis.coreService = new MockCoreService();
-        delegate = new MockConfigDelegate();
-        mapper = new ObjectMapper();
-        config = new SadisConfig();
-        subject = sadis.coreService.registerApplication("org.opencord.sadis");
-        config.init(subject, "sadis-local-mode-test", node("/LocalConfig.json"), mapper, delegate);
-        sadis.cfgService = new MockNetworkConfigRegistry(config);
-        event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED, subject, config.getClass());
-        sadis.codecService = new CodecManager();
-        sadis.activate();
-    }
-
-    @After
-    public void tearDown() {
-        this.sadis.deactivate();
-    }
-
-    @Test
-    public void testConfiguration() {
-        SadisConfig config = sadis.cfgService.getConfig(null, SadisConfig.class);
-        assertEquals(50, config.getCacheMaxSize());
-        assertEquals(Duration.parse("PT1m"), config.getCacheTtl());
-        List<SubscriberAndDeviceInformation> entries = config.getEntries();
-        assertEquals(3, entries.size());
-        assertTrue(SubscriberAndDeviceInformationBuilder.build("1", (short) 2, (short) 2, "1/1/2", (short) 125,
-                (short) 3, "aa:bb:cc:dd:ee:ff", "XXX-NASID", "10.10.10.10", "circuit123", "remote123")
-                .checkEquals(entries.get(0)));
-        assertTrue(SubscriberAndDeviceInformationBuilder.build("2", (short) 4, (short) 4, "1/1/2", (short) 129,
-                (short) 4, "aa:bb:cc:dd:ee:ff", "YYY-NASID", "1.1.1.1", "circuit234", "remote234")
-                .checkEquals(entries.get(1)));
-        assertTrue(SubscriberAndDeviceInformationBuilder.build("cc:dd:ee:ff:aa:bb", (short) -1, (short) -1, null,
-                (short) -1, (short) -1, "cc:dd:ee:ff:aa:bb", "CCC-NASID", "12.12.12.12", "circuit345", "remote345")
-                .checkEquals(entries.get(2)));
-
-    }
-
-    @Test
-    public void testLocalMode() throws Exception {
-        SubscriberAndDeviceInformation entry = sadis.get("3");
-        assertNull(entry);
-
-        entry = sadis.get("1");
-        assertNotNull(entry);
-        assertTrue(entry1.checkEquals(entry));
-
-        entry = sadis.get("2");
-        assertNotNull(entry);
-        assertTrue(entry2.checkEquals(entry));
-
-        sadis.invalidateId("1");
-        entry = sadis.getfromCache("1");
-        assertNull(entry);
-        entry = sadis.get("1");
-        assertNotNull(entry);
-        assertTrue(entry1.checkEquals(entry));
-
-        sadis.invalidateAll();
-        entry = sadis.getfromCache("2");
-        assertNull(entry);
-        entry = sadis.get("2");
-        assertNotNull(entry);
-        assertTrue(entry2.checkEquals(entry));
-    }
-
-    @Test
-    public void testRemoteMode() throws Exception {
-        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
-        configListener.event(event);
-
-        SubscriberAndDeviceInformation entry = sadis.get("3");
-        assertNotNull(entry);
-        assertTrue(entry3.checkEquals(entry));
-
-        entry = sadis.get("4");
-        assertNotNull(entry);
-        assertTrue(entry4.checkEquals(entry));
-
-        sadis.invalidateId("3");
-        entry = sadis.getfromCache("3");
-        assertNull(entry);
-        entry = sadis.get("3");
-        assertNotNull(entry);
-        assertTrue(entry3.checkEquals(entry));
-
-        sadis.invalidateAll();
-        entry = sadis.getfromCache("4");
-        assertNull(entry);
-        entry = sadis.get("4");
-        assertNotNull(entry);
-        assertTrue(entry4.checkEquals(entry));
-
-        entry = sadis.get("8");
-        assertNull(entry);
-    }
-
-    @Test
-    public void testModeSwitch() throws Exception {
-        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
-        configListener.event(event);
-        SubscriberAndDeviceInformation entry = sadis.get("3");
-        assertNotNull(entry);
-        entry = sadis.get("1");
-        assertNull(entry);
-
-        config.init(subject, "sadis-local-mode-test", node("/LocalConfig.json"), mapper, delegate);
-        configListener.event(event);
-        entry = sadis.get("1");
-        assertNotNull(entry);
-        entry = sadis.get("3");
-        assertNull(entry);
-    }
-
-    private JsonNode node(String jsonFile) throws Exception {
-        final InputStream jsonStream = SadisManagerTest.class.getResourceAsStream(jsonFile);
-        final JsonNode testConfig = mapper.readTree(jsonStream);
-        return testConfig;
-    }
-
-    // Mocks live here
-
-    private static final class SubscriberAndDeviceInformationBuilder extends SubscriberAndDeviceInformation {
-
-        public static SubscriberAndDeviceInformationBuilder build(String id, short cTag, short sTag, String nasPortId,
-                short port, short slot, String mac, String nasId, String ipAddress, String circuitId, String remoteId) {
-
-            SubscriberAndDeviceInformationBuilder info = new SubscriberAndDeviceInformationBuilder();
-            info.setId(id);
-            if (cTag != -1) {
-                info.setCTag(VlanId.vlanId(cTag));
-            }
-            if (sTag != -1) {
-                info.setSTag(VlanId.vlanId(sTag));
-            }
-            info.setNasPortId(nasPortId);
-            if (port != -1) {
-                info.setUplinkPort(port);
-            }
-            if (slot != -1) {
-                info.setSlot(slot);
-            }
-            info.setHardwareIdentifier(MacAddress.valueOf(mac));
-            info.setIPAddress(Ip4Address.valueOf(ipAddress));
-            info.setNasId(nasId);
-            info.setCircuitId(circuitId);
-            info.setRemoteId(remoteId);
-            return info;
-        }
-
-        public boolean checkEquals(SubscriberAndDeviceInformation other) {
-            if (other == null) {
-                return false;
-            }
-            if (this.cTag() == null) {
-                if (other.cTag() != null) {
-                    return false;
-                }
-            } else if (!this.cTag().equals(other.cTag())) {
-                return false;
-            }
-            if (this.hardwareIdentifier() == null) {
-                if (other.hardwareIdentifier() != null) {
-                    return false;
-                }
-            } else if (!this.hardwareIdentifier().equals(other.hardwareIdentifier())) {
-                return false;
-            }
-            if (this.id() == null) {
-                if (other.id() != null) {
-                    return false;
-                }
-            } else if (!this.id().equals(other.id())) {
-                return false;
-            }
-            if (this.nasPortId() == null) {
-                if (other.nasPortId() != null) {
-                    return false;
-                }
-            } else if (!this.nasPortId().equals(other.nasPortId())) {
-                return false;
-            }
-            if (this.nasId() == null) {
-                if (other.nasId() != null) {
-                    return false;
-                }
-            } else if (!this.nasId().equals(other.nasId())) {
-                return false;
-            }
-            if (this.ipAddress() == null) {
-                if (other.ipAddress() != null) {
-                    return false;
-                }
-            } else if (!this.ipAddress().equals(other.ipAddress())) {
-                return false;
-            }
-            if (this.uplinkPort() != other.uplinkPort()) {
-                return false;
-            }
-            if (this.sTag() == null) {
-                if (other.sTag() != null) {
-                    return false;
-                }
-            } else if (!this.sTag().equals(other.sTag())) {
-                return false;
-            }
-            if (this.slot() != other.slot()) {
-                return false;
-            }
-            if (this.circuitId() == null) {
-                if (other.circuitId() != null) {
-                    return false;
-                }
-            } else if (!this.circuitId().equals(other.circuitId())) {
-                return false;
-            }
-            if (this.remoteId() == null) {
-                if (other.remoteId() != null) {
-                    return false;
-                }
-            } else if (!this.remoteId().equals(other.remoteId())) {
-                return false;
-            }
-            return true;
-        }
-    }
-
-    /**
-     * Mocks an ONOS configuration delegate to allow JSON based configuration to
-     * be tested.
-     */
-    private static final class MockConfigDelegate implements ConfigApplyDelegate {
-        @Override
-        public void onApply(@SuppressWarnings("rawtypes") Config config) {
-            config.apply();
-        }
-    }
-
-    /**
-     * Mocks an instance of {@link ApplicationId} so that the application
-     * component under test can query and use its application ID.
-     */
-    private static final class MockApplicationId implements ApplicationId {
-
-        private final short id;
-        private final String name;
-
-        public MockApplicationId(short id, String name) {
-            this.id = id;
-            this.name = name;
-        }
-
-        @Override
-        public short id() {
-            return id;
-        }
-
-        @Override
-        public String name() {
-            return name;
-        }
-    }
-
-    /**
-     * Mocks the core services of ONOS so that the application under test can
-     * register and query application IDs.
-     */
-    private static final class MockCoreService extends CoreServiceAdapter {
-
-        private List<ApplicationId> idList = new ArrayList<ApplicationId>();
-        private Map<String, ApplicationId> idMap = new HashMap<String, ApplicationId>();
-
-        /*
-         * (non-Javadoc)
-         *
-         * @see
-         * org.onosproject.core.CoreServiceAdapter#getAppId(java.lang.Short)
-         */
-        @Override
-        public ApplicationId getAppId(Short id) {
-            if (id >= idList.size()) {
-                return null;
-            }
-            return idList.get(id);
-        }
-
-        /*
-         * (non-Javadoc)
-         *
-         * @see
-         * org.onosproject.core.CoreServiceAdapter#getAppId(java.lang.String)
-         */
-        @Override
-        public ApplicationId getAppId(String name) {
-            return idMap.get(name);
-        }
-
-        /*
-         * (non-Javadoc)
-         *
-         * @see
-         * org.onosproject.core.CoreServiceAdapter#registerApplication(java.lang
-         * .String)
-         */
-        @Override
-        public ApplicationId registerApplication(String name) {
-            ApplicationId appId = idMap.get(name);
-            if (appId == null) {
-                appId = new MockApplicationId((short) idList.size(), name);
-                idList.add(appId);
-                idMap.put(name, appId);
-            }
-            return appId;
-        }
-
-    }
-
-    /**
-     * Mocks the ONOS network configuration registry so that the application
-     * under test can access a JSON defined configuration.
-     */
-    static final class MockNetworkConfigRegistry extends NetworkConfigRegistryAdapter {
-        private final SadisConfig config;
-
-        public MockNetworkConfigRegistry(final SadisConfig config) {
-            this.config = config;
-        }
-
-        @SuppressWarnings("unchecked")
-        @Override
-        public <S, C extends Config<S>> C getConfig(final S subject, final Class<C> configClass) {
-            return (C) this.config;
-        }
-
-        @Override
-        public void addListener(NetworkConfigListener listener) {
-            configListener = listener;
-        }
-    }
-}
diff --git a/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java b/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java
new file mode 100644
index 0000000..6ff5cc3
--- /dev/null
+++ b/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java
@@ -0,0 +1,290 @@
+/*
+ * 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.opencord.sadis.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.opencord.sadis.BaseConfig;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.BaseInformation;
+
+/**
+ * Set of tests of the SADIS ONOS application component.
+ */
+public class SubscriberAndDeviceManagerTest extends BaseSadis {
+
+    SubscriberAndDeviceInformationBuilder entry1 = SubscriberAndDeviceInformationBuilder.build("1", (short) 2,
+            (short) 2, "1/1/2", (short) 125, (short) 3, "aa:bb:cc:dd:ee:ff", "XXX-NASID", "10.10.10.10",
+            "circuit123", "remote123", 64, "1Gb", "1Gb");
+    SubscriberAndDeviceInformationBuilder entry2 = SubscriberAndDeviceInformationBuilder.build("2", (short) 4,
+            (short) 4, "1/1/2", (short) 129, (short) 4, "aa:bb:cc:dd:ee:ff", "YYY-NASID", "1.1.1.1",
+            "circuit234", "remote234", 64, "10Gb", "10Gb");
+    SubscriberAndDeviceInformationBuilder entry3 = SubscriberAndDeviceInformationBuilder.build("3", (short) 7,
+            (short) 8, "1/1/2", (short) 130, (short) 7, "ff:aa:dd:cc:bb:ee", "MNO-NASID", "30.30.30.30",
+            "circuit567", "remote567", 64, "10Gb", "10Gb");
+    SubscriberAndDeviceInformationBuilder entry4 = SubscriberAndDeviceInformationBuilder.build("4", (short) 2,
+            (short) 1, "1/1/2", (short) 132, (short) 1, "ff:cc:dd:aa:ee:bb", "PQR-NASID", "15.15.15.15",
+            "circuit678", "remote678", 64, "5Gb", "5Gb");
+
+
+    @Before
+    public void setUp() throws Exception {
+        config = new SubscriberAndDeviceInformationConfig();
+        super.setUp("/LocalSubConfig.json", SubscriberAndDeviceInformationConfig.class);
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+    }
+
+    @Test
+    public void testConfiguration() {
+        SubscriberAndDeviceInformationConfig config = sadis.cfgService.getConfig(null,
+                SubscriberAndDeviceInformationConfig.class);
+        checkConfigInfo(50, "PT1m", config);
+        checkEntriesForSubscriberAndAccessDevice(config);
+    }
+
+    private void checkEntriesForSubscriberAndAccessDevice(BaseConfig config) {
+        List<SubscriberAndDeviceInformation> entries = config.getEntries();
+        assertEquals(3, entries.size());
+
+        SubscriberAndDeviceInformation sub = SubscriberAndDeviceInformationBuilder.build("1", (short) 2, (short) 2,
+                "1/1/2", (short) 125,
+                (short) 3, "aa:bb:cc:dd:ee:ff", "XXX-NASID", "10.10.10.10", "circuit123", "remote123",
+                64, "1Gb", "1Gb");
+        assertTrue(checkEquality(sub, entries.get(0)));
+
+
+        sub = SubscriberAndDeviceInformationBuilder.build("2", (short) 4, (short) 4, "1/1/2", (short) 129,
+                (short) 4, "aa:bb:cc:dd:ee:ff", "YYY-NASID", "1.1.1.1", "circuit234", "remote234",
+                64, "10Gb", "10Gb");
+        assertTrue(checkEquality(sub, entries.get(1)));
+
+        sub = SubscriberAndDeviceInformationBuilder.build("cc:dd:ee:ff:aa:bb", (short) -1, (short) -1, null,
+                (short) -1, (short) -1, "cc:dd:ee:ff:aa:bb", "CCC-NASID", "12.12.12.12", "circuit345", "remote345",
+                64, "10Gb", "10Gb");
+        assertTrue(checkEquality(sub, entries.get(2)));
+    }
+
+    @Test
+    public void testLocalMode() throws Exception {
+
+        BaseInformationService<SubscriberAndDeviceInformation> subscriberService = sadis.getSubscriberInfoService();
+
+        checkGetForExisting("1", entry1, subscriberService);
+        checkGetForExisting("2", entry2, subscriberService);
+
+        invalidateId("1", subscriberService);
+        checkFromBoth("1", entry1, subscriberService);
+
+        invalidateAll(subscriberService);
+        checkFromBoth("2", entry2, subscriberService);
+    }
+
+
+    private void checkGetForNonExist(String id, BaseInformationService service) {
+        BaseInformation entry = service.get(id);
+        assertNull(entry);
+    }
+
+    @Test
+    public void testRemoteMode() throws Exception {
+        BaseInformationService<SubscriberAndDeviceInformation> subscriberService = sadis.getSubscriberInfoService();
+        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
+        configListener.event(event);
+
+        checkGetForExisting("3", entry3, subscriberService);
+        checkGetForExisting("4", entry4, subscriberService);
+
+        invalidateId("3", subscriberService);
+        checkFromBoth("3", entry3, subscriberService);
+
+        invalidateAll(subscriberService);
+        checkFromBoth("4", entry4, subscriberService);
+    }
+
+    @Test
+    public void testModeSwitch() throws Exception {
+        BaseInformationService<SubscriberAndDeviceInformation> service = sadis.getSubscriberInfoService();
+        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
+        configListener.event(event);
+
+        checkGetForExisting("3", null, service);
+        checkGetForNonExist("1", service);
+
+        config.init(subject, "sadis-local-mode-test", node("/LocalSubConfig.json"), mapper, delegate);
+        configListener.event(event);
+
+        checkGetForExisting("1", null, service);
+        checkGetForNonExist("3", service);
+    }
+
+
+    private static final class SubscriberAndDeviceInformationBuilder extends SubscriberAndDeviceInformation {
+
+        public static SubscriberAndDeviceInformationBuilder build(String id, short cTag, short sTag, String nasPortId,
+                                                                  short port, short slot, String mac, String nasId,
+                                                                  String ipAddress, String circuitId, String remoteId,
+                                                                  int technologyProfileId,
+                                                                  String upstreamBandwidthProfile,
+                                                                  String downstreamBandwidthProfile) {
+
+            SubscriberAndDeviceInformationBuilder info = new SubscriberAndDeviceInformationBuilder();
+            info.setId(id);
+            if (cTag != -1) {
+                info.setCTag(VlanId.vlanId(cTag));
+            }
+            if (sTag != -1) {
+                info.setSTag(VlanId.vlanId(sTag));
+            }
+            info.setNasPortId(nasPortId);
+            if (port != -1) {
+                info.setUplinkPort(port);
+            }
+            if (slot != -1) {
+                info.setSlot(slot);
+            }
+            info.setHardwareIdentifier(MacAddress.valueOf(mac));
+            info.setIPAddress(Ip4Address.valueOf(ipAddress));
+            info.setNasId(nasId);
+            info.setCircuitId(circuitId);
+            info.setRemoteId(remoteId);
+
+            if (technologyProfileId != -1) {
+                info.setTechnologyProfileId(technologyProfileId);
+            }
+
+            info.setUpstreamBandwidthProfile(upstreamBandwidthProfile);
+            info.setDownstreamBandwidthProfile(downstreamBandwidthProfile);
+
+            return info;
+        }
+
+    }
+
+    @Override
+    public boolean checkEquality(BaseInformation localEntry, BaseInformation entry) {
+        SubscriberAndDeviceInformation sub = (SubscriberAndDeviceInformation) localEntry;
+        SubscriberAndDeviceInformation other = (SubscriberAndDeviceInformation) localEntry;
+
+        if (other == null) {
+            return false;
+        }
+        if (sub.cTag() == null) {
+            if (other.cTag() != null) {
+                return false;
+            }
+        } else if (!sub.cTag().equals(other.cTag())) {
+            return false;
+        }
+        if (sub.hardwareIdentifier() == null) {
+            if (other.hardwareIdentifier() != null) {
+                return false;
+            }
+        } else if (!sub.hardwareIdentifier().equals(other.hardwareIdentifier())) {
+            return false;
+        }
+        if (sub.id() == null) {
+            if (other.id() != null) {
+                return false;
+            }
+        } else if (!sub.id().equals(other.id())) {
+            return false;
+        }
+        if (sub.nasPortId() == null) {
+            if (other.nasPortId() != null) {
+                return false;
+            }
+        } else if (!sub.nasPortId().equals(other.nasPortId())) {
+            return false;
+        }
+        if (sub.nasId() == null) {
+            if (other.nasId() != null) {
+                return false;
+            }
+        } else if (!sub.nasId().equals(other.nasId())) {
+            return false;
+        }
+        if (sub.ipAddress() == null) {
+            if (other.ipAddress() != null) {
+                return false;
+            }
+        } else if (!sub.ipAddress().equals(other.ipAddress())) {
+            return false;
+        }
+        if (sub.uplinkPort() != other.uplinkPort()) {
+            return false;
+        }
+        if (sub.sTag() == null) {
+            if (other.sTag() != null) {
+                return false;
+            }
+        } else if (!sub.sTag().equals(other.sTag())) {
+            return false;
+        }
+        if (sub.slot() != other.slot()) {
+            return false;
+        }
+        if (sub.circuitId() == null) {
+            if (other.circuitId() != null) {
+                return false;
+            }
+        } else if (!sub.circuitId().equals(other.circuitId())) {
+            return false;
+        }
+        if (sub.remoteId() == null) {
+            if (other.remoteId() != null) {
+                return false;
+            }
+        } else if (!sub.remoteId().equals(other.remoteId())) {
+            return false;
+        }
+        if (sub.technologyProfileId() != other.technologyProfileId()) {
+            return false;
+        }
+        if (sub.upstreamBandwidthProfile() == null) {
+            if (other.upstreamBandwidthProfile() != null) {
+                return false;
+            }
+        } else if (!sub.upstreamBandwidthProfile().equals(other.upstreamBandwidthProfile())) {
+            return false;
+        }
+        if (sub.downstreamBandwidthProfile() == null) {
+            if (other.downstreamBandwidthProfile() != null) {
+                return false;
+            }
+        } else if (!sub.downstreamBandwidthProfile().equals(other.downstreamBandwidthProfile())) {
+            return false;
+        }
+        return true;
+    }
+
+
+}
diff --git a/app/src/test/resources/3 b/app/src/test/resources/3
index a7fead8..496bf28 100644
--- a/app/src/test/resources/3
+++ b/app/src/test/resources/3
@@ -9,5 +9,8 @@
       "ipAddress":"30.30.30.30",
       "nasId":"MNO-NASID",
       "circuitId":"circuit567",
-      "remoteId":"remote567"
+      "remoteId":"remote567",
+      "technologyProfileId":64,
+      "upstreamBandwidthProfile":"10Gb",
+      "downstreamBandwidthProfile":"10Gb"
 }
diff --git a/app/src/test/resources/4 b/app/src/test/resources/4
index 19f00f8..757e741 100644
--- a/app/src/test/resources/4
+++ b/app/src/test/resources/4
@@ -9,5 +9,8 @@
       "ipAddress":"15.15.15.15",
       "nasId":"PQR-NASID",
       "circuitId":"circuit678",
-      "remoteId":"remote678"
+      "remoteId":"remote678",
+      "technologyProfileId":64,
+      "upstreamBandwidthProfile":"5Gb",
+      "downstreamBandwidthProfile":"5Gb"
 }
diff --git a/app/src/test/resources/HighSpeed b/app/src/test/resources/HighSpeed
new file mode 100644
index 0000000..f57465f
--- /dev/null
+++ b/app/src/test/resources/HighSpeed
@@ -0,0 +1,8 @@
+{
+      "id": "High Speed",
+      "cir": 1000000000,
+      "cbs": 384000,
+      "eir": 100000000,
+      "ebs": 384000,
+      "air": 100000000
+}
\ No newline at end of file
diff --git a/app/src/test/resources/LocalBpConfig.json b/app/src/test/resources/LocalBpConfig.json
new file mode 100644
index 0000000..aee463b
--- /dev/null
+++ b/app/src/test/resources/LocalBpConfig.json
@@ -0,0 +1,26 @@
+{
+  "integration": {
+    "cache": {
+      "maxsize": 60,
+      "ttl": "PT1m"
+    }
+  },
+  "entries": [
+    {
+      "id": "High Speed",
+      "cir": 1000000000,
+      "cbs": 384000,
+      "eir": 100000000,
+      "ebs": 384000,
+      "air": 100000000
+    },
+    {
+      "id": "Home User Speed",
+      "cir": 1000000000,
+      "cbs": 200000,
+      "eir": 100000000,
+      "ebs": 200000,
+      "air": 100000000
+    }
+  ]
+}
\ No newline at end of file
diff --git a/app/src/test/resources/LocalConfig.json b/app/src/test/resources/LocalSubConfig.json
similarity index 65%
rename from app/src/test/resources/LocalConfig.json
rename to app/src/test/resources/LocalSubConfig.json
index d72de31..99c861a 100644
--- a/app/src/test/resources/LocalConfig.json
+++ b/app/src/test/resources/LocalSubConfig.json
@@ -21,7 +21,10 @@
 			"ipAddress":"10.10.10.10",
 			"nasId":"XXX-NASID",
 			"circuitId":"circuit123",
-			"remoteId":"remote123"
+			"remoteId":"remote123",
+			"technologyProfileId":64,
+			"upstreamBandwidthProfile":"1Gb",
+			"downstreamBandwidthProfile":"1Gb"
 		},
 
 		{
@@ -35,7 +38,10 @@
 			"ipAddress":"1.1.1.1",
 			"nasId":"YYY-NASID",
 			"circuitId":"circuit234",
-			"remoteId":"remote234"
+			"remoteId":"remote234",
+			"technologyProfileId":64,
+			"upstreamBandwidthProfile":"10Gb",
+			"downstreamBandwidthProfile":"10Gb"
 		},
 
 		{
@@ -44,7 +50,10 @@
 			"ipAddress":"12.12.12.12",
 			"nasId":"CCC-NASID",
 			"circuitId":"circuit345",
-                        "remoteId":"remote345"
+			"remoteId":"remote345",
+			"technologyProfileId":64,
+			"upstreamBandwidthProfile":"10Gb",
+			"downstreamBandwidthProfile":"10Gb"
 		}
 	]
 }