Finished unit tests for trivial link manager.
diff --git a/net/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java b/net/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
index 952c1dc..ffdc2fa 100644
--- a/net/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
+++ b/net/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
@@ -37,7 +37,7 @@
 
     @Override
     public Link.Type type() {
-        return null;
+        return type;
     }
 
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java b/net/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java
index 18987ea..a85965a 100644
--- a/net/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java
+++ b/net/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java
@@ -18,6 +18,11 @@
         LINK_ADDED,
 
         /**
+         * Signifies that a link has been updated.
+         */
+        LINK_UPDATED,
+
+        /**
          * Signifies that a link has been removed.
          */
         LINK_REMOVED
diff --git a/net/api/src/main/java/org/onlab/onos/net/link/LinkService.java b/net/api/src/main/java/org/onlab/onos/net/link/LinkService.java
index 77afb7a..1257d97 100644
--- a/net/api/src/main/java/org/onlab/onos/net/link/LinkService.java
+++ b/net/api/src/main/java/org/onlab/onos/net/link/LinkService.java
@@ -48,7 +48,7 @@
      * @param deviceId device identifier
      * @return set of device ingress links
      */
-    Set<Link> getDeviceInressLinks(DeviceId deviceId);
+    Set<Link> getDeviceIngressLinks(DeviceId deviceId);
 
     /**
      * Returns set of all infrastructure links leading to and from the
@@ -75,17 +75,17 @@
      * @param connectPoint connection point
      * @return set of device ingress links
      */
-    Set<Link> getInressLinks(ConnectPoint connectPoint);
+    Set<Link> getIngressLinks(ConnectPoint connectPoint);
 
     /**
-     * Returns set of all infrastructure links between the specified source
+     * Returns the infrastructure links between the specified source
      * and destination connection points.
      *
      * @param src source connection point
      * @param dst destination connection point
-     * @return set of links
+     * @return link from source to destination; null if none found
      */
-    Set<Link> getLinks(ConnectPoint src, ConnectPoint dst);
+    Link getLink(ConnectPoint src, ConnectPoint dst);
 
     /**
      * Adds the specified link listener.
diff --git a/net/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkManager.java b/net/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkManager.java
index 63fb2cc..6e7c567 100644
--- a/net/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkManager.java
+++ b/net/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkManager.java
@@ -93,7 +93,7 @@
     }
 
     @Override
-    public Set<Link> getDeviceInressLinks(DeviceId deviceId) {
+    public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
         checkNotNull(deviceId, DEVICE_ID_NULL);
         return store.getDeviceIngressLinks(deviceId);
     }
@@ -112,17 +112,16 @@
     }
 
     @Override
-    public Set<Link> getInressLinks(ConnectPoint connectPoint) {
+    public Set<Link> getIngressLinks(ConnectPoint connectPoint) {
         checkNotNull(connectPoint, CONNECT_POINT_NULL);
         return store.getIngressLinks(connectPoint);
     }
 
     @Override
-    public Set<Link> getLinks(ConnectPoint src, ConnectPoint dst) {
+    public Link getLink(ConnectPoint src, ConnectPoint dst) {
         checkNotNull(src, CONNECT_POINT_NULL);
         checkNotNull(dst, CONNECT_POINT_NULL);
-        return Sets.intersection(store.getEgressLinks(src),
-                                 store.getIngressLinks(dst));
+        return store.getLink(src, dst);
     }
 
     @Override
diff --git a/net/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java b/net/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java
index c1d6273..d10c3a4 100644
--- a/net/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java
+++ b/net/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java
@@ -18,6 +18,12 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static org.onlab.onos.net.Link.Type.DIRECT;
+import static org.onlab.onos.net.Link.Type.INDIRECT;
+import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
+import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
+import static org.onlab.onos.net.link.LinkEvent.Type.LINK_UPDATED;
+
 /**
  * Manages inventory of infrastructure links using trivial in-memory link
  * implementation.
@@ -72,6 +78,17 @@
     }
 
     /**
+     * Returns the link between the two end-points.
+     *
+     * @param src source connection point
+     * @param dst destination connection point
+     * @return link or null if one not found between the end-points
+     */
+    Link getLink(ConnectPoint src, ConnectPoint dst) {
+        return links.get(new LinkKey(src, dst));
+    }
+
+    /**
      * Returns all links egressing from the specified connection point.
      *
      * @param src source connection point
@@ -96,14 +113,13 @@
     Set<Link> getIngressLinks(ConnectPoint dst) {
         Set<Link> ingress = new HashSet<>();
         for (Link link : dstLinks.get(dst.deviceId())) {
-            if (link.src().equals(dst)) {
+            if (link.dst().equals(dst)) {
                 ingress.add(link);
             }
         }
         return ingress;
     }
 
-
     /**
      * Creates a new link, or updates an existing one, based on the given
      * information.
@@ -119,7 +135,7 @@
         if (link == null) {
             return createLink(providerId, key, linkDescription);
         }
-        return updateLink(link, linkDescription);
+        return updateLink(providerId, link, key, linkDescription);
     }
 
     // Creates and stores the link and returns the appropriate event.
@@ -132,11 +148,26 @@
             srcLinks.put(link.src().deviceId(), link);
             dstLinks.put(link.dst().deviceId(), link);
         }
-        return new LinkEvent(LinkEvent.Type.LINK_ADDED, link);
+        return new LinkEvent(LINK_ADDED, link);
     }
 
     // Updates, if necessary the specified link and returns the appropriate event.
-    private LinkEvent updateLink(DefaultLink link, LinkDescription linkDescription) {
+    private LinkEvent updateLink(ProviderId providerId, DefaultLink link,
+                                 LinkKey key, LinkDescription linkDescription) {
+        if (link.type() == INDIRECT && linkDescription.type() == DIRECT) {
+            synchronized (this) {
+                srcLinks.remove(link.src().deviceId(), link);
+                dstLinks.remove(link.dst().deviceId(), link);
+
+                DefaultLink updated =
+                        new DefaultLink(providerId, link.src(), link.dst(),
+                                        linkDescription.type());
+                links.put(key, updated);
+                srcLinks.put(link.src().deviceId(), updated);
+                dstLinks.put(link.dst().deviceId(), updated);
+                return new LinkEvent(LINK_UPDATED, updated);
+            }
+        }
         return null;
     }
 
@@ -150,9 +181,12 @@
     LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
         synchronized (this) {
             Link link = links.remove(new LinkKey(src, dst));
-            srcLinks.remove(link.src().deviceId(), link);
-            dstLinks.remove(link.dst().deviceId(), link);
-            return link == null ? null : new LinkEvent(LinkEvent.Type.LINK_REMOVED, link);
+            if (link != null) {
+                srcLinks.remove(link.src().deviceId(), link);
+                dstLinks.remove(link.dst().deviceId(), link);
+                return new LinkEvent(LINK_REMOVED, link);
+            }
+            return null;
         }
     }
 
diff --git a/net/core/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleLinkManagerTest.java b/net/core/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleLinkManagerTest.java
index d8465ef..8dd9e1d 100644
--- a/net/core/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleLinkManagerTest.java
+++ b/net/core/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleLinkManagerTest.java
@@ -1,5 +1,6 @@
 package org.onlab.onos.net.trivial.impl;
 
+import com.google.common.collect.ImmutableSet;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -12,7 +13,6 @@
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.link.DefaultLinkDescription;
 import org.onlab.onos.net.link.LinkAdminService;
-import org.onlab.onos.net.link.LinkDescription;
 import org.onlab.onos.net.link.LinkEvent;
 import org.onlab.onos.net.link.LinkListener;
 import org.onlab.onos.net.link.LinkProvider;
@@ -23,10 +23,15 @@
 import org.onlab.onos.net.provider.ProviderId;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import static org.junit.Assert.*;
 import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.Link.Type.DIRECT;
+import static org.onlab.onos.net.Link.Type.INDIRECT;
+import static org.onlab.onos.net.link.LinkEvent.Type.*;
 
 /**
  * Test codifying the link service & link provider service contracts.
@@ -36,6 +41,7 @@
     private static final ProviderId PID = new ProviderId("foo");
     private static final DeviceId DID1 = deviceId("of:foo");
     private static final DeviceId DID2 = deviceId("of:bar");
+    private static final DeviceId DID3 = deviceId("of:goo");
 
     private static final PortNumber P1 = PortNumber.portNumber(1);
     private static final PortNumber P2 = PortNumber.portNumber(2);
@@ -77,13 +83,146 @@
         mgr.deactivate();
     }
 
-
     @Test
     public void createLink() {
-        LinkDescription ld = new DefaultLinkDescription(new ConnectPoint(DID1, P1),
-                                                        new ConnectPoint(DID2, P2),
-                                                        Link.Type.DIRECT);
-        providerService.linkDetected(ld);
+        addLink(DID1, P1, DID2, P2, DIRECT);
+        addLink(DID2, P2, DID1, P1, DIRECT);
+        assertEquals("incorrect link count", 2, service.getLinkCount());
+
+        Iterator<Link> it = service.getLinks().iterator();
+        it.next();
+        it.next();
+        assertFalse("incorrect link count", it.hasNext());
+    }
+
+    @Test
+    public void updateLink() {
+        addLink(DID1, P1, DID2, P2, DIRECT);
+        addLink(DID2, P2, DID1, P1, INDIRECT);
+        assertEquals("incorrect link count", 2, service.getLinkCount());
+
+        providerService.linkDetected(new DefaultLinkDescription(cp(DID2, P2), cp(DID1, P1), DIRECT));
+        validateEvents(LINK_UPDATED);
+        assertEquals("incorrect link count", 2, service.getLinkCount());
+
+        providerService.linkDetected(new DefaultLinkDescription(cp(DID2, P2), cp(DID1, P1), INDIRECT));
+        providerService.linkDetected(new DefaultLinkDescription(cp(DID2, P2), cp(DID1, P1), DIRECT));
+        assertEquals("no events expected", 0, listener.events.size());
+    }
+
+    @Test
+    public void removeLink() {
+        addLink(DID1, P1, DID2, P2, DIRECT);
+        addLink(DID2, P2, DID1, P1, DIRECT);
+        assertEquals("incorrect link count", 2, service.getLinkCount());
+
+        providerService.linkVanished(new DefaultLinkDescription(cp(DID1, P1), cp(DID2, P2), DIRECT));
+        validateEvents(LINK_REMOVED);
+        assertEquals("incorrect link count", 1, service.getLinkCount());
+        assertNull("link should not be found", service.getLink(cp(DID1, P1), cp(DID2, P2)));
+        assertNotNull("link should be found", service.getLink(cp(DID2, P2), cp(DID1, P1)));
+
+        providerService.linkVanished(new DefaultLinkDescription(cp(DID1, P1), cp(DID2, P2), DIRECT));
+        assertEquals("no events expected", 0, listener.events.size());
+    }
+
+    @Test
+    public void removeLinksByConnectionPoint() {
+        Link l1 = addLink(DID1, P1, DID2, P2, DIRECT);
+        Link l2 = addLink(DID2, P2, DID1, P1, DIRECT);
+        addLink(DID3, P3, DID2, P1, DIRECT);
+        addLink(DID2, P1, DID3, P3, DIRECT);
+        assertEquals("incorrect link count", 4, service.getLinkCount());
+
+        providerService.linksVanished(cp(DID1, P1));
+        assertEquals("incorrect link count", 2, service.getLinkCount());
+        assertNull("link should be gone", service.getLink(l1.src(), l1.dst()));
+        assertNull("link should be gone", service.getLink(l2.src(), l2.dst()));
+    }
+
+    @Test
+    public void removeLinksByDevice() {
+        addLink(DID1, P1, DID2, P2, DIRECT);
+        addLink(DID2, P2, DID1, P1, DIRECT);
+        addLink(DID3, P3, DID2, P1, DIRECT);
+        addLink(DID2, P1, DID3, P3, DIRECT);
+        Link l5 = addLink(DID3, P1, DID1, P2, DIRECT);
+        Link l6 = addLink(DID1, P2, DID3, P1, DIRECT);
+        assertEquals("incorrect link count", 6, service.getLinkCount());
+
+        providerService.linksVanished(DID2);
+        assertEquals("incorrect link count", 2, service.getLinkCount());
+        assertNotNull("link should not be gone", service.getLink(l5.src(), l5.dst()));
+        assertNotNull("link should not be gone", service.getLink(l6.src(), l6.dst()));
+    }
+
+    @Test
+    public void removeLinksAsAdminByConnectionPoint() {
+        Link l1 = addLink(DID1, P1, DID2, P2, DIRECT);
+        Link l2 = addLink(DID2, P2, DID1, P1, DIRECT);
+        addLink(DID3, P3, DID2, P1, DIRECT);
+        addLink(DID2, P1, DID3, P3, DIRECT);
+        assertEquals("incorrect link count", 4, service.getLinkCount());
+
+        admin.removeLinks(cp(DID1, P1));
+        assertEquals("incorrect link count", 2, service.getLinkCount());
+        assertNull("link should be gone", service.getLink(l1.src(), l1.dst()));
+        assertNull("link should be gone", service.getLink(l2.src(), l2.dst()));
+    }
+
+    @Test
+    public void removeLinksAsAdminByDevice() {
+        addLink(DID1, P1, DID2, P2, DIRECT);
+        addLink(DID2, P2, DID1, P1, DIRECT);
+        addLink(DID3, P3, DID2, P1, DIRECT);
+        addLink(DID2, P1, DID3, P3, DIRECT);
+        Link l5 = addLink(DID3, P1, DID1, P2, DIRECT);
+        Link l6 = addLink(DID1, P2, DID3, P1, DIRECT);
+        assertEquals("incorrect link count", 6, service.getLinkCount());
+
+        admin.removeLinks(DID2);
+        assertEquals("incorrect link count", 2, service.getLinkCount());
+        assertNotNull("link should not be gone", service.getLink(l5.src(), l5.dst()));
+        assertNotNull("link should not be gone", service.getLink(l6.src(), l6.dst()));
+    }
+
+    @Test
+    public void getLinks() {
+        Link l1 = addLink(DID1, P1, DID2, P2, DIRECT);
+        Link l2 = addLink(DID2, P2, DID1, P1, DIRECT);
+        Link l3 = addLink(DID3, P3, DID2, P1, DIRECT);
+        Link l4 = addLink(DID2, P1, DID3, P3, DIRECT);
+        assertEquals("incorrect link count", 4, service.getLinkCount());
+
+        Set<Link> links = service.getLinks(cp(DID1, P1));
+        assertEquals("incorrect links", ImmutableSet.of(l1, l2), links);
+        links = service.getEgressLinks(cp(DID1, P1));
+        assertEquals("incorrect links", ImmutableSet.of(l1), links);
+        links = service.getIngressLinks(cp(DID1, P1));
+        assertEquals("incorrect links", ImmutableSet.of(l2), links);
+
+        links = service.getDeviceLinks(DID2);
+        assertEquals("incorrect links", ImmutableSet.of(l1, l2, l3, l4), links);
+        links = service.getDeviceLinks(DID3);
+        assertEquals("incorrect links", ImmutableSet.of(l3, l4), links);
+
+        links = service.getDeviceEgressLinks(DID2);
+        assertEquals("incorrect links", ImmutableSet.of(l2, l4), links);
+        links = service.getDeviceIngressLinks(DID2);
+        assertEquals("incorrect links", ImmutableSet.of(l1, l3), links);
+    }
+
+
+    private Link addLink(DeviceId sd, PortNumber sp, DeviceId dd, PortNumber dp,
+                         Link.Type type) {
+        providerService.linkDetected(new DefaultLinkDescription(cp(sd, sp), cp(dd, dp), type));
+        Link link = listener.events.get(0).subject();
+        validateEvents(LINK_ADDED);
+        return link;
+    }
+
+    private ConnectPoint cp(DeviceId id, PortNumber portNumber) {
+        return new ConnectPoint(id, portNumber);
     }
 
     protected void validateEvents(Enum... types) {