Added unit tests for the event abstractions.
Added Element as the notion of common ancestry between Device and Host.
diff --git a/net/api/src/test/java/org/onlab/onos/event/AbstractEventTest.java b/net/api/src/test/java/org/onlab/onos/event/AbstractEventTest.java
new file mode 100644
index 0000000..5ec3669
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/event/AbstractEventTest.java
@@ -0,0 +1,31 @@
+package org.onlab.onos.event;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.onlab.onos.event.TestEvent.Type.FOO;
+
+/**
+ * Tests of the base event abstraction.
+ */
+public class AbstractEventTest {
+
+    @Test
+    public void withTime() {
+        TestEvent event = new TestEvent(FOO, "foo", 123L);
+        assertEquals("incorrect type", FOO, event.type());
+        assertEquals("incorrect subject", "foo", event.subject());
+        assertEquals("incorrect time", 123L, event.time());
+    }
+
+    @Test
+    public void withoutTime() {
+        long before = System.currentTimeMillis();
+        TestEvent event = new TestEvent(FOO, "foo");
+        long after = System.currentTimeMillis();
+        assertEquals("incorrect type", FOO, event.type());
+        assertEquals("incorrect subject", "foo", event.subject());
+        assertTrue("incorrect time", before <= event.time() && event.time() <= after);
+    }
+}
diff --git a/net/api/src/test/java/org/onlab/onos/event/AbstractListenerManagerTest.java b/net/api/src/test/java/org/onlab/onos/event/AbstractListenerManagerTest.java
new file mode 100644
index 0000000..effcc79
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/event/AbstractListenerManagerTest.java
@@ -0,0 +1,49 @@
+package org.onlab.onos.event;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests of the base listener manager.
+ */
+public class AbstractListenerManagerTest {
+
+    @Test
+    public void basics() {
+        TestListener listener = new TestListener();
+        TestListener secondListener = new TestListener();
+        TestListenerManager manager = new TestListenerManager();
+        manager.addListener(listener);
+        manager.addListener(secondListener);
+
+        TestEvent event = new TestEvent(TestEvent.Type.BAR, "bar");
+        manager.process(event);
+        assertTrue("event not processed", listener.events.contains(event));
+        assertTrue("event not processed", secondListener.events.contains(event));
+
+        manager.removeListener(listener);
+
+        TestEvent another = new TestEvent(TestEvent.Type.FOO, "foo");
+        manager.process(another);
+        assertFalse("event processed", listener.events.contains(another));
+        assertTrue("event not processed", secondListener.events.contains(event));
+    }
+
+    @Test
+    public void badListener() {
+        TestListener listener = new BrokenListener();
+        TestListener secondListener = new TestListener();
+        TestListenerManager manager = new TestListenerManager();
+        manager.addListener(listener);
+        manager.addListener(secondListener);
+
+        TestEvent event = new TestEvent(TestEvent.Type.BAR, "bar");
+        manager.process(event);
+        assertFalse("event processed", listener.events.contains(event));
+        assertFalse("error not reported", manager.errors.isEmpty());
+        assertTrue("event not processed", secondListener.events.contains(event));
+    }
+
+}
diff --git a/net/api/src/test/java/org/onlab/onos/event/BrokenListener.java b/net/api/src/test/java/org/onlab/onos/event/BrokenListener.java
new file mode 100644
index 0000000..dc83419
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/event/BrokenListener.java
@@ -0,0 +1,13 @@
+package org.onlab.onos.event;
+
+/**
+ * Test event listener fixture.
+ */
+public class BrokenListener extends TestListener {
+
+    public void event(TestEvent event) {
+        throw new IllegalStateException("boom");
+    }
+
+}
+
diff --git a/net/api/src/test/java/org/onlab/onos/event/DefaultEventSinkBrokerTest.java b/net/api/src/test/java/org/onlab/onos/event/DefaultEventSinkBrokerTest.java
new file mode 100644
index 0000000..f676325
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/event/DefaultEventSinkBrokerTest.java
@@ -0,0 +1,52 @@
+package org.onlab.onos.event;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests of the default event sink broker.
+ */
+public class DefaultEventSinkBrokerTest {
+
+    private DefaultEventSinkBroker broker;
+
+    private static class FooEvent extends TestEvent {
+        public FooEvent(String subject) { super(Type.FOO, subject); }
+    }
+
+    private static class BarEvent extends TestEvent {
+        public BarEvent(String subject) { super(Type.BAR, subject); }
+    }
+
+    private static class FooSink implements EventSink<FooEvent> {
+        @Override public void process(FooEvent event) {}
+    }
+
+    private static class BarSink implements EventSink<BarEvent> {
+        @Override public void process(BarEvent event) {}
+    }
+
+    @Before
+    public void setUp() {
+        broker = new DefaultEventSinkBroker();
+    }
+
+    @Test
+    public void basics() {
+        FooSink fooSink = new FooSink();
+        BarSink barSink = new BarSink();
+        broker.addSink(FooEvent.class, fooSink);
+        broker.addSink(BarEvent.class, barSink);
+
+        assertEquals("incorrect sink count", 2, broker.getSinks().size());
+        assertEquals("incorrect sink", fooSink, broker.getSink(FooEvent.class));
+        assertEquals("incorrect sink", barSink, broker.getSink(BarEvent.class));
+
+        broker.removeSink(FooEvent.class);
+        assertNull("incorrect sink", broker.getSink(FooEvent.class));
+        assertEquals("incorrect sink", barSink, broker.getSink(BarEvent.class));
+
+    }
+}
diff --git a/net/api/src/test/java/org/onlab/onos/event/TestEvent.java b/net/api/src/test/java/org/onlab/onos/event/TestEvent.java
new file mode 100644
index 0000000..25c8f46
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/event/TestEvent.java
@@ -0,0 +1,19 @@
+package org.onlab.onos.event;
+
+/**
+ * Test event fixture.
+ */
+public class TestEvent extends AbstractEvent<TestEvent.Type, String> {
+
+    public enum Type { FOO, BAR };
+
+    public TestEvent(Type type, String subject) {
+        super(type, subject);
+    }
+
+    public TestEvent(Type type, String subject, long timestamp) {
+        super(type, subject, timestamp);
+    }
+
+}
+
diff --git a/net/api/src/test/java/org/onlab/onos/event/TestListener.java b/net/api/src/test/java/org/onlab/onos/event/TestListener.java
new file mode 100644
index 0000000..ec2185a
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/event/TestListener.java
@@ -0,0 +1,19 @@
+package org.onlab.onos.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test event listener fixture.
+ */
+public class TestListener implements EventListener<TestEvent> {
+
+    public final List<TestEvent> events = new ArrayList<>();
+
+    @Override
+    public void event(TestEvent event) {
+        events.add(event);
+    }
+
+}
+
diff --git a/net/api/src/test/java/org/onlab/onos/event/TestListenerManager.java b/net/api/src/test/java/org/onlab/onos/event/TestListenerManager.java
new file mode 100644
index 0000000..a9f6213
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/event/TestListenerManager.java
@@ -0,0 +1,21 @@
+package org.onlab.onos.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test event listener manager fixture.
+ */
+public class TestListenerManager
+        extends AbstractListenerManager<TestEvent, TestListener> {
+
+    public final List<Throwable> errors = new ArrayList<>();
+
+    @Override
+    protected void reportProblem(TestEvent event, Throwable error) {
+        super.reportProblem(event, error);
+        errors.add(error);
+    }
+
+}
+