[ONOS-6073] Implement init version of MappingManager with unit test

Change-Id: Ib2b198e51556bb29e44d20b7bdf9130bbd0aa96d
diff --git a/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/DefaultMapping.java b/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/DefaultMapping.java
index 42415ff..17f3ce8 100644
--- a/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/DefaultMapping.java
+++ b/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/DefaultMapping.java
@@ -175,7 +175,7 @@
         @Override
         public Mapping build() {
 
-            checkArgument((id != null) ^ (appId != null), "Either an application" +
+            checkArgument((id != null) || (appId != null), "Either an application" +
                     " id or a mapping id must be supplied");
             checkNotNull(key, "Mapping key cannot be null");
             checkNotNull(deviceId, "Must refer to a device");
diff --git a/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/MappingService.java b/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/MappingService.java
index e1862e1..e15507f 100644
--- a/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/MappingService.java
+++ b/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/MappingService.java
@@ -15,11 +15,76 @@
  */
 package org.onosproject.mapping;
 
+import org.onosproject.core.ApplicationId;
 import org.onosproject.event.ListenerService;
+import org.onosproject.mapping.MappingStore.Type;
+import org.onosproject.net.DeviceId;
 
 /**
  * Interface of mapping management service.
  */
 public interface MappingService
-                    extends ListenerService<MappingEvent, MappingListener> {
+        extends ListenerService<MappingEvent, MappingListener> {
+
+    /**
+     * Obtains the number of mappings in the system.
+     *
+     * @param type mapping store type
+     * @return mapping count
+     */
+    int getMappingCount(Type type);
+
+    /**
+     * Stores a mapping entry.
+     *
+     * @param type  mapping store type
+     * @param entry mapping entry to be stored
+     */
+    void storeMappingEntry(Type type, MappingEntry entry);
+
+    /**
+     * Obtains the collection of mapping entries applied on the specific device.
+     * The will include mapping which may not yet have been applied to device.
+     *
+     * @param type     mapping store type
+     * @param deviceId device identifier
+     * @return collection of mapping entries
+     */
+    Iterable<MappingEntry> getMappingEntries(Type type, DeviceId deviceId);
+
+    /**
+     * Obtains the collection of mapping entries with a given application ID.
+     *
+     * @param type  mapping store type
+     * @param appId application identifier
+     * @return collection of mapping entries
+     */
+    Iterable<MappingEntry> getMappingEntriesByAddId(Type type, ApplicationId appId);
+
+    /**
+     * Removes the specified mapping entries from their respective devices and
+     * mapping store.
+     *
+     * @param type     mapping store type
+     * @param entries  one or more mapping entries
+     */
+    void removeMappingEntries(Type type, MappingEntry... entries);
+
+    /**
+     * Removes all mapping entries submitted by a particular application.
+     *
+     * @param type  mapping store type
+     * @param appId identifier of application whose mapping entries will be removed
+     */
+    void removeMappingEntriesByAppId(Type type, ApplicationId appId);
+
+    /**
+     * Purges all mappings on the specified device and mapping store.
+     * Note that the mappings will only be removed from storage, the mappings
+     * are still remaining in the device.
+     *
+     * @param type     mapping store type
+     * @param deviceId device identifier
+     */
+    void purgeMappings(Type type, DeviceId deviceId);
 }
diff --git a/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/MappingStore.java b/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/MappingStore.java
index 49d8169..e2d6109 100644
--- a/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/MappingStore.java
+++ b/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/MappingStore.java
@@ -79,9 +79,18 @@
      * provider indicates that the mapping has been removed.
      *
      * @param type    store type
-     * @param mapping the mapping to delete
+     * @param mapping the mapping to be marked as delete
      */
-    void deleteMapping(Type type, Mapping mapping);
+    void pendingDeleteMapping(Type type, Mapping mapping);
+
+    /**
+     * Removes an existing mapping from the specified store.
+     *
+     * @param type    store type
+     * @param mapping the mapping to remove
+     * @return mapping_removed event, or null if nothing removed
+     */
+    MappingEvent removeMapping(Type type, Mapping mapping);
 
     /**
      * Stores a new mapping or updates an existing entry from/to the
@@ -94,15 +103,6 @@
     MappingEvent addOrUpdateMappingEntry(Type type, MappingEntry entry);
 
     /**
-     * Removes an existing mapping from the specified store.
-     *
-     * @param type  store type
-     * @param entry the mapping to remove
-     * @return mapping_removed event, or null if nothing removed
-     */
-    MappingEvent removeMappingEntry(Type type, MappingEntry entry);
-
-    /**
      * Marks a mapping as PENDING_ADD during retry.
      * <p>
      * Emits mapping_update event if the state is changed
diff --git a/apps/mappingmanagement/api/src/test/java/org/onosproject/mapping/MappingServiceAdapter.java b/apps/mappingmanagement/api/src/test/java/org/onosproject/mapping/MappingServiceAdapter.java
index 7eb68cb..c767c36 100644
--- a/apps/mappingmanagement/api/src/test/java/org/onosproject/mapping/MappingServiceAdapter.java
+++ b/apps/mappingmanagement/api/src/test/java/org/onosproject/mapping/MappingServiceAdapter.java
@@ -15,6 +15,10 @@
  */
 package org.onosproject.mapping;
 
+import org.onosproject.core.ApplicationId;
+import org.onosproject.mapping.MappingStore.Type;
+import org.onosproject.net.DeviceId;
+
 /**
  * Adapter for testing against mapping service.
  */
@@ -28,4 +32,39 @@
     public void removeListener(MappingListener listener) {
 
     }
+
+    @Override
+    public int getMappingCount(Type type) {
+        return 0;
+    }
+
+    @Override
+    public void storeMappingEntry(Type type, MappingEntry entry) {
+
+    }
+
+    @Override
+    public Iterable<MappingEntry> getMappingEntries(Type type, DeviceId deviceId) {
+        return null;
+    }
+
+    @Override
+    public Iterable<MappingEntry> getMappingEntriesByAddId(Type type, ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public void removeMappingEntries(Type type, MappingEntry... mappingEntries) {
+
+    }
+
+    @Override
+    public void removeMappingEntriesByAppId(Type type, ApplicationId appId) {
+
+    }
+
+    @Override
+    public void purgeMappings(Type type, DeviceId deviceId) {
+
+    }
 }
diff --git a/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/DistributedMappingStore.java b/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/DistributedMappingStore.java
index 0125fe5..55208ce 100644
--- a/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/DistributedMappingStore.java
+++ b/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/DistributedMappingStore.java
@@ -188,20 +188,20 @@
     }
 
     @Override
-    public void deleteMapping(Type type, Mapping mapping) {
+    public MappingEvent removeMapping(Type type, Mapping mapping) {
 
         getStore(type).remove(mapping.id());
-    }
-
-    @Override
-    public MappingEvent addOrUpdateMappingEntry(Type type, MappingEntry entry) {
-        // TODO: this will be implemented when management plane is ready
-        log.error("This method will be available when management plane is ready");
         return null;
     }
 
     @Override
-    public MappingEvent removeMappingEntry(Type type, MappingEntry entry) {
+    public void pendingDeleteMapping(Type type, Mapping mapping) {
+        // TODO: this will be implemented when management plane is ready
+        log.error("This method will be available when management plane is ready");
+    }
+
+    @Override
+    public MappingEvent addOrUpdateMappingEntry(Type type, MappingEntry entry) {
         // TODO: this will be implemented when management plane is ready
         log.error("This method will be available when management plane is ready");
         return null;
diff --git a/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/MappingManager.java b/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/MappingManager.java
index 7668375..72be9e7 100644
--- a/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/MappingManager.java
+++ b/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/MappingManager.java
@@ -15,20 +15,152 @@
  */
 package org.onosproject.mapping.impl;
 
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.mapping.MappingEntry;
+import org.onosproject.mapping.MappingEvent;
 import org.onosproject.mapping.MappingListener;
+import org.onosproject.mapping.MappingProvider;
+import org.onosproject.mapping.MappingProviderRegistry;
+import org.onosproject.mapping.MappingProviderService;
 import org.onosproject.mapping.MappingService;
+import org.onosproject.mapping.MappingStore;
+import org.onosproject.mapping.MappingStore.Type;
+import org.onosproject.mapping.MappingStoreDelegate;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.slf4j.Logger;
+
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Implementation of mapping management service.
  */
-public class MappingManager implements MappingService {
-    @Override
-    public void addListener(MappingListener listener) {
+@Component(immediate = true)
+@Service
+public class MappingManager
+        extends AbstractListenerProviderRegistry<MappingEvent, MappingListener,
+                                                 MappingProvider, MappingProviderService>
+        implements MappingService, MappingProviderRegistry {
 
+    private final Logger log = getLogger(getClass());
+
+    private static final String MAPPING_OP_TOPIC = "mapping-ops-ids";
+    private final MappingStoreDelegate delegate = new InternalStoreDelegate();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MappingStore store;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Activate
+    public void activate() {
+        store.setDelegate(delegate);
+        eventDispatcher.addSink(MappingEvent.class, listenerRegistry);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        store.unsetDelegate(delegate);
+        eventDispatcher.removeSink(MappingEvent.class);
+        log.info("Stopped");
     }
 
     @Override
-    public void removeListener(MappingListener listener) {
+    public int getMappingCount(Type type) {
+        return store.getMappingCount(type);
+    }
 
+    @Override
+    public void storeMappingEntry(Type type, MappingEntry entry) {
+        store.storeMapping(type, entry);
+    }
+
+    @Override
+    public Iterable<MappingEntry> getMappingEntries(Type type, DeviceId deviceId) {
+        return store.getMappingEntries(type, deviceId);
+    }
+
+    @Override
+    public Iterable<MappingEntry> getMappingEntriesByAddId(Type type, ApplicationId appId) {
+
+        Set<MappingEntry> mappingEntries = Sets.newHashSet();
+        for (Device d : deviceService.getDevices()) {
+            for (MappingEntry mappingEntry : store.getMappingEntries(type, d.id())) {
+                if (mappingEntry.appId() == appId.id()) {
+                    mappingEntries.add(mappingEntry);
+                }
+            }
+        }
+        return mappingEntries;
+    }
+
+    @Override
+    public void removeMappingEntries(Type type, MappingEntry... mappingEntries) {
+        for (MappingEntry entry : mappingEntries) {
+            store.removeMapping(type, entry);
+        }
+    }
+
+    @Override
+    public void removeMappingEntriesByAppId(Type type, ApplicationId appId) {
+        removeMappingEntries(type, Iterables.toArray(
+                    getMappingEntriesByAddId(type, appId), MappingEntry.class));
+    }
+
+    @Override
+    public void purgeMappings(Type type, DeviceId deviceId) {
+        store.purgeMappingEntry(type, deviceId);
+    }
+
+    @Override
+    protected MappingProviderService createProviderService(MappingProvider provider) {
+        return new InternalMappingProviderService(provider);
+    }
+
+    /**
+     * Store delegate.
+     */
+    private class InternalStoreDelegate implements MappingStoreDelegate {
+
+        @Override
+        public void notify(MappingEvent event) {
+            post(event);
+        }
+    }
+
+    /**
+     * Internal mapping provider service.
+     */
+    private class InternalMappingProviderService
+            extends AbstractProviderService<MappingProvider> implements MappingProviderService {
+
+        /**
+         * Initializes internal mapping provider service.
+         *
+         * @param provider mapping provider
+         */
+        protected InternalMappingProviderService(MappingProvider provider) {
+            super(provider);
+        }
+
+        @Override
+        public void mappingAdded(MappingEntry mappingEntry, Type type) {
+            storeMappingEntry(type, mappingEntry);
+        }
     }
 }
diff --git a/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/SimpleMappingStore.java b/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/SimpleMappingStore.java
index 17b403f..c349423 100644
--- a/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/SimpleMappingStore.java
+++ b/apps/mappingmanagement/mgr/src/main/java/org/onosproject/mapping/impl/SimpleMappingStore.java
@@ -225,7 +225,7 @@
     }
 
     @Override
-    public void deleteMapping(Type type, Mapping mapping) {
+    public void pendingDeleteMapping(Type type, Mapping mapping) {
 
         List<StoredMappingEntry> entries =
                 getMappingEntriesInternal(type, mapping.deviceId(), mapping.id());
@@ -264,13 +264,13 @@
     }
 
     @Override
-    public MappingEvent removeMappingEntry(Type type, MappingEntry entry) {
+    public MappingEvent removeMapping(Type type, Mapping mapping) {
 
         List<StoredMappingEntry> entries =
-                getMappingEntriesInternal(type, entry.deviceId(), entry.id());
+                getMappingEntriesInternal(type, mapping.deviceId(), mapping.id());
         synchronized (entries) {
-            if (entries.remove(entry)) {
-                return new MappingEvent(MAPPING_REMOVED, entry);
+            if (entries.remove(mapping)) {
+                return new MappingEvent(MAPPING_REMOVED, mapping);
             }
         }
         return null;
diff --git a/apps/mappingmanagement/mgr/src/test/java/org/onosproject/mapping/impl/DistributedMappingStoreTest.java b/apps/mappingmanagement/mgr/src/test/java/org/onosproject/mapping/impl/DistributedMappingStoreTest.java
index 57b7ed3..9246f6d 100644
--- a/apps/mappingmanagement/mgr/src/test/java/org/onosproject/mapping/impl/DistributedMappingStoreTest.java
+++ b/apps/mappingmanagement/mgr/src/test/java/org/onosproject/mapping/impl/DistributedMappingStoreTest.java
@@ -164,7 +164,7 @@
                         .iterator().next()));
         assertTrue("The mapping1 should be identical.",
                 mappingStore.getMappingEntry(MAP_DATABASE, mapping1).equals(mapping1));
-        mappingStore.deleteMapping(MAP_DATABASE, mapping1);
+        mappingStore.removeMapping(MAP_DATABASE, mapping1);
         assertFalse("There should not be any mapping1 in the map database.",
                 mappingStore.getMappingEntries(MAP_DATABASE, DEVICE_ID_1).iterator().hasNext());
     }
diff --git a/apps/mappingmanagement/mgr/src/test/java/org/onosproject/mapping/impl/MappingManagerTest.java b/apps/mappingmanagement/mgr/src/test/java/org/onosproject/mapping/impl/MappingManagerTest.java
new file mode 100644
index 0000000..54fc652
--- /dev/null
+++ b/apps/mappingmanagement/mgr/src/test/java/org/onosproject/mapping/impl/MappingManagerTest.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mapping.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.mapping.DefaultMapping;
+import org.onosproject.mapping.DefaultMappingEntry;
+import org.onosproject.mapping.Mapping;
+import org.onosproject.mapping.MappingEntry;
+import org.onosproject.mapping.MappingEvent;
+import org.onosproject.mapping.MappingKey;
+import org.onosproject.mapping.MappingListener;
+import org.onosproject.mapping.MappingProvider;
+import org.onosproject.mapping.MappingProviderRegistry;
+import org.onosproject.mapping.MappingProviderService;
+import org.onosproject.mapping.MappingService;
+import org.onosproject.mapping.MappingStore.Type;
+import org.onosproject.mapping.MappingTreatment;
+import org.onosproject.mapping.MappingValue;
+import org.onosproject.mapping.actions.MappingAction;
+import org.onosproject.mapping.addresses.MappingAddress;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.mapping.MappingStore.Type.MAP_DATABASE;
+import static org.onosproject.net.NetTestTools.injectEventDispatcher;
+
+/**
+ * Unit tests for mapping manager.
+ */
+public class MappingManagerTest {
+
+    private static final ProviderId LISP_PID = new ProviderId("lisp", "lisp");
+    private static final ProviderId BAR_PID = new ProviderId("bar", "bar");
+
+    private static final DeviceId LISP_DID = DeviceId.deviceId("lisp:001");
+    private static final DeviceId BAR_DID = DeviceId.deviceId("bar:002");
+
+    private static final DefaultAnnotations ANNOTATIONS =
+            DefaultAnnotations.builder().set(AnnotationKeys.DRIVER, "bar").build();
+
+    private static final Device LISP_DEV =
+            new DefaultDevice(LISP_PID, LISP_DID, Device.Type.SWITCH, "", "", "", "", null);
+    private static final Device BAR_DEV =
+            new DefaultDevice(BAR_PID, BAR_DID, Device.Type.SWITCH, "", "", "", "", null, ANNOTATIONS);
+
+
+    private MappingManager manager;
+
+    private MappingService service;
+    private MappingProviderRegistry registry;
+    private MappingProviderService providerService;
+    private TestProvider provider;
+    private TestListener listener = new TestListener();
+    private ApplicationId appId;
+
+    @Before
+    public void setUp() {
+        manager = new MappingManager();
+        manager.store = new SimpleMappingStore();
+        injectEventDispatcher(manager, new TestEventDispatcher());
+        manager.deviceService = new TestDeviceService();
+
+        service = manager;
+        registry = manager;
+
+        manager.activate();
+        manager.addListener(listener);
+        provider = new TestProvider(LISP_PID);
+        providerService = registry.register(provider);
+        appId = new TestApplicationId(0, "MappingManagerTest");
+        assertTrue("provider should be registered",
+                registry.getProviders().contains(provider.id()));
+    }
+
+    @After
+    public void tearDown() {
+        registry.unregister(provider);
+        assertFalse("provider should not be registered",
+                registry.getProviders().contains(provider.id()));
+        service.removeListener(listener);
+        manager.deactivate();
+        injectEventDispatcher(manager, null);
+        manager.deviceService = null;
+    }
+
+    /**
+     * Creates a mapping from a specified deviceID, key and value.
+     *
+     * @param did   device identifier
+     * @param key   test value for mapping key
+     * @param value test value for mapping value
+     * @return mapping instance
+     */
+    private Mapping mapping(DeviceId did, int key, int value) {
+        TestMappingKey mappingKey = new TestMappingKey(key);
+        TestMappingValue mappingValue = new TestMappingValue(value);
+
+        return DefaultMapping.builder()
+                .forDevice(did)
+                .withKey(mappingKey)
+                .withValue(mappingValue)
+                .fromApp(appId)
+                .withId(key + value)
+                .build();
+    }
+
+    /**
+     * Creates a mapping from a specified key and value.
+     *
+     * @param key   test value for mapping key
+     * @param value test value for mapping value
+     * @return mapping instance
+     */
+    private Mapping mapping(int key, int value) {
+        return mapping(LISP_DID, key, value);
+    }
+
+    /**
+     * Adds a new mapping into the mapping store.
+     *
+     * @param type mapping store type
+     * @param tval test value
+     * @return a mapping that has been added to the store
+     */
+    private Mapping addMapping(Type type, int tval) {
+        Mapping mapping = mapping(tval, tval);
+        MappingEntry entry = new DefaultMappingEntry(mapping);
+        service.storeMappingEntry(type, entry);
+
+        assertNotNull("mapping should be found",
+                                service.getMappingEntries(type, LISP_DID));
+        return mapping;
+    }
+
+    /**
+     * Obtains the number of mappings.
+     *
+     * @param type mapping store type
+     * @return number of mappings
+     */
+    private int mappingCount(Type type) {
+        return Sets.newHashSet(service.getMappingEntries(type, LISP_DID)).size();
+    }
+
+    /**
+     * Tests retrieving mapping entries method.
+     */
+    @Test
+    public void getMappingEntries() {
+
+        assertTrue("Store should be empty", Sets.newHashSet(
+                service.getMappingEntries(MAP_DATABASE, LISP_DID)).isEmpty());
+        addMapping(MAP_DATABASE, 1);
+        addMapping(MAP_DATABASE, 2);
+        assertEquals("2 mappings should exist", 2, mappingCount(MAP_DATABASE));
+
+        addMapping(MAP_DATABASE, 1);
+        assertEquals("should still be 2 mappings", 2, mappingCount(MAP_DATABASE));
+    }
+
+    /**
+     * Tests storing mapping entry method.
+     */
+    @Test
+    public void storeMappingEntry() {
+
+        Mapping m1 = mapping(1, 1);
+        Mapping m2 = mapping(2, 2);
+        Mapping m3 = mapping(3, 3);
+
+        MappingEntry me1 = new DefaultMappingEntry(m1);
+        MappingEntry me2 = new DefaultMappingEntry(m2);
+        MappingEntry me3 = new DefaultMappingEntry(m3);
+
+        assertTrue("store should be empty", Sets.newHashSet(
+                service.getMappingEntries(MAP_DATABASE, LISP_DID)).isEmpty());
+        service.storeMappingEntry(MAP_DATABASE, me1);
+        service.storeMappingEntry(MAP_DATABASE, me2);
+        service.storeMappingEntry(MAP_DATABASE, me3);
+        assertEquals("3 mappings should exist", 3, mappingCount(MAP_DATABASE));
+    }
+
+    /**
+     * Tests removing mapping entries method.
+     */
+    @Test
+    public void removeMappingEntries() {
+
+        Mapping m1 = addMapping(MAP_DATABASE, 1);
+        Mapping m2 = addMapping(MAP_DATABASE, 2);
+        addMapping(MAP_DATABASE, 3);
+        assertEquals("3 mappings should exist", 3, mappingCount(MAP_DATABASE));
+
+        MappingEntry me1 = new DefaultMappingEntry(m1);
+        MappingEntry me2 = new DefaultMappingEntry(m2);
+
+        service.removeMappingEntries(MAP_DATABASE, me1, me2);
+        assertEquals("1 mappings should exist", 1, mappingCount(MAP_DATABASE));
+    }
+
+    /**
+     * Tests purging all mappings.
+     */
+    @Test
+    public void purgeMappings() {
+
+        addMapping(MAP_DATABASE, 1);
+        addMapping(MAP_DATABASE, 2);
+        addMapping(MAP_DATABASE, 3);
+        assertEquals("3 mappings should exist", 3, mappingCount(MAP_DATABASE));
+
+        service.purgeMappings(MAP_DATABASE, LISP_DID);
+        assertEquals("0 mappings should exist", 0, mappingCount(MAP_DATABASE));
+    }
+
+    /**
+     * Tests obtaining mapping entries by application ID.
+     */
+    @Test
+    public void getMappingEntriesByAddId() {
+        addMapping(MAP_DATABASE, 1);
+        addMapping(MAP_DATABASE, 2);
+
+        assertTrue("should have two mappings",
+                Lists.newLinkedList(
+                        service.getMappingEntriesByAddId(MAP_DATABASE, appId)).size() == 2);
+    }
+
+    /**
+     * Tests removing mapping entries by application ID.
+     */
+    @Test
+    public void removeMappingEntriesByAppId() {
+        addMapping(MAP_DATABASE, 1);
+        addMapping(MAP_DATABASE, 2);
+
+        service.removeMappingEntriesByAppId(MAP_DATABASE, appId);
+
+        assertTrue("should not have any mappings",
+                Lists.newLinkedList(
+                        service.getMappingEntriesByAddId(MAP_DATABASE, appId)).size() == 0);
+    }
+
+    private static class TestDeviceService extends DeviceServiceAdapter {
+        @Override
+        public int getDeviceCount() {
+            return 2;
+        }
+
+        @Override
+        public Iterable<Device> getDevices() {
+            return ImmutableList.of(LISP_DEV, BAR_DEV);
+        }
+
+        @Override
+        public Iterable<Device> getAvailableDevices() {
+            return getDevices();
+        }
+
+        @Override
+        public Device getDevice(DeviceId deviceId) {
+            return deviceId.equals(BAR_DID) ? BAR_DEV : LISP_DEV;
+        }
+    }
+
+    private static class TestListener implements MappingListener {
+        final List<MappingEvent> events = new ArrayList<>();
+
+        @Override
+        public void event(MappingEvent event) {
+            events.add(event);
+        }
+    }
+
+    private class TestProvider extends AbstractProvider implements MappingProvider {
+
+        /**
+         * Creates a provider with the supplied identifier.
+         *
+         * @param id provider id
+         */
+        TestProvider(ProviderId id) {
+            super(id);
+        }
+    }
+
+    private class TestApplicationId extends DefaultApplicationId {
+        TestApplicationId(int id, String name) {
+            super(id, name);
+        }
+    }
+
+    private class TestMappingKey implements MappingKey {
+
+        private final int val;
+
+        TestMappingKey(int val) {
+            this.val = val;
+        }
+
+        @Override
+        public MappingAddress address() {
+            return null;
+        }
+
+        @Override
+        public int hashCode() {
+            return val;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof TestMappingKey && this.val == ((TestMappingKey) o).val;
+        }
+    }
+
+    private class TestMappingValue implements MappingValue {
+
+        private final int val;
+
+        TestMappingValue(int val) {
+            this.val = val;
+        }
+
+        @Override
+        public MappingAction action() {
+            return null;
+        }
+
+        @Override
+        public List<MappingTreatment> treatments() {
+            return null;
+        }
+
+        @Override
+        public int hashCode() {
+            return val;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof TestMappingValue && this.val == ((TestMappingValue) o).val;
+        }
+    }
+}