Index: src/org/openstreetmap/josm/data/osm/AbstractDataSourceChangeEvent.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/AbstractDataSourceChangeEvent.java	(nonexistent)
+++ src/org/openstreetmap/josm/data/osm/AbstractDataSourceChangeEvent.java	(working copy)
@@ -0,0 +1,43 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import java.util.Set;
+
+import org.openstreetmap.josm.data.DataSource;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * The base class for data source change events
+ *
+ * @author Taylor Smock
+ * @since xxx
+ */
+public abstract class AbstractDataSourceChangeEvent implements DataSourceChangeEvent {
+
+    private DataSet source;
+    private Set<DataSource> old;
+
+    /**
+     * Create a Data Source change event
+     *
+     * @param source The DataSet that is originating the change
+     * @param old    The previous set of DataSources
+     */
+    public AbstractDataSourceChangeEvent(DataSet source, Set<DataSource> old) {
+        CheckParameterUtil.ensureParameterNotNull(source, "source");
+        CheckParameterUtil.ensureParameterNotNull(old, "old");
+        this.source = source;
+        this.old = old;
+    }
+
+    @Override
+    public Set<DataSource> getOldDataSources() {
+        return old;
+    }
+
+    @Override
+    public DataSet getSource() {
+        return source;
+    }
+}
+
Index: src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 15598)
+++ src/org/openstreetmap/josm/data/osm/DataSet.java	(working copy)
@@ -10,6 +10,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -42,6 +43,8 @@
 import org.openstreetmap.josm.data.osm.event.ChangesetIdChangedEvent;
 import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
 import org.openstreetmap.josm.data.osm.event.DataSetListener;
+import org.openstreetmap.josm.data.osm.event.DataSourceAddedEvent;
+import org.openstreetmap.josm.data.osm.event.DataSourceRemovedEvent;
 import org.openstreetmap.josm.data.osm.event.FilterChangedEvent;
 import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
 import org.openstreetmap.josm.data.osm.event.PrimitiveFlagsChangedEvent;
@@ -165,6 +168,11 @@
      */
     private final Collection<DataSource> dataSources = new LinkedList<>();
 
+    /**
+     * A list of listeners that listen to DataSource changes on this layer
+     */
+    private final ListenerList<DataSourceListener> dataSourceListeners = ListenerList.create();
+
     private final ConflictCollection conflicts = new ConflictCollection();
 
     private short mappaintCacheIdx = 1;
@@ -225,9 +233,12 @@
                         .map(rm -> new RelationMember(rm.getRole(), primMap.get(rm.getMember())))
                         .collect(Collectors.toList()));
             }
+            DataSourceAddedEvent addedEvent = new DataSourceAddedEvent(this,
+                    new LinkedHashSet<>(dataSources), copyFrom.dataSources.stream());
             for (DataSource source : copyFrom.dataSources) {
                 dataSources.add(new DataSource(source));
             }
+            dataSourceListeners.fireEvent(d -> d.dataSourceChange(addedEvent));
             version = copyFrom.version;
             uploadPolicy = copyFrom.uploadPolicy;
             downloadPolicy = copyFrom.downloadPolicy;
@@ -271,11 +282,14 @@
      * @since 11626
      */
     public synchronized boolean addDataSources(Collection<DataSource> sources) {
+        DataSourceAddedEvent addedEvent = new DataSourceAddedEvent(this,
+                new LinkedHashSet<>(dataSources), sources.stream());
         boolean changed = dataSources.addAll(sources);
         if (changed) {
             cachedDataSourceArea = null;
             cachedDataSourceBounds = null;
         }
+        dataSourceListeners.fireEvent(d -> d.dataSourceChange(addedEvent));
         return changed;
     }
 
@@ -574,6 +588,30 @@
         highlightUpdateListeners.removeListener(listener);
     }
 
+    /**
+     * Adds a listener that gets notified whenever the data sources change
+     *
+     * @param listener The listener
+     * @see #removeDataSourceListener
+     * @see #getDataSources
+     * @since xxx
+     */
+    public void addDataSourceListener(DataSourceListener listener) {
+        dataSourceListeners.addListener(listener);
+    }
+
+    /**
+     * Removes a listener that gets notified whenever the data sources change
+     *
+     * @param listener The listener
+     * @see #addDataSourceListener
+     * @see #getDataSources
+     * @since xxx
+     */
+    public void removeDataSourceListener(DataSourceListener listener) {
+        dataSourceListeners.removeListener(listener);
+    }
+
     @Override
     public Collection<OsmPrimitive> getAllSelected() {
         return currentSelectedPrimitives;
@@ -1084,7 +1122,12 @@
             new DataSetMerger(this, from).merge(progressMonitor);
             synchronized (from) {
                 if (!from.dataSources.isEmpty()) {
-                    if (dataSources.addAll(from.dataSources)) {
+                    DataSourceAddedEvent addedEvent = new DataSourceAddedEvent(
+                            this, new LinkedHashSet<>(dataSources), from.dataSources.stream());
+                    DataSourceRemovedEvent clearEvent = new DataSourceRemovedEvent(
+                            this, new LinkedHashSet<>(from.dataSources), from.dataSources.stream());
+                    if (from.dataSources.stream().filter(dataSource -> !dataSources.contains(dataSource))
+                            .map(dataSources::add).filter(Boolean.TRUE::equals).count() > 0) {
                         cachedDataSourceArea = null;
                         cachedDataSourceBounds = null;
                     }
@@ -1091,6 +1134,8 @@
                     from.dataSources.clear();
                     from.cachedDataSourceArea = null;
                     from.cachedDataSourceBounds = null;
+                    dataSourceListeners.fireEvent(d -> d.dataSourceChange(addedEvent));
+                    from.dataSourceListeners.fireEvent(d -> d.dataSourceChange(clearEvent));
                 }
             }
         }
Index: src/org/openstreetmap/josm/data/osm/DataSourceChangeEvent.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/DataSourceChangeEvent.java	(nonexistent)
+++ src/org/openstreetmap/josm/data/osm/DataSourceChangeEvent.java	(working copy)
@@ -0,0 +1,77 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import java.util.Set;
+
+import org.openstreetmap.josm.data.DataSource;
+
+/**
+ * The event that is fired when the data source list is changed.
+ *
+ * @author Taylor Smock
+ * @since xxx
+ */
+public interface DataSourceChangeEvent {
+    /**
+     * Gets the previous data source list
+     * <p>
+     * This collection cannot be modified and will not change.
+     *
+     * @return The old data source list
+     */
+    Set<DataSource> getOldDataSources();
+
+    /**
+     * Gets the new data sources. New data sources are added to the end of the
+     * collection.
+     * <p>
+     * This collection cannot be modified and will not change.
+     *
+     * @return The new data sources
+     */
+    Set<DataSource> getDataSources();
+
+    /**
+     * Gets the Data Sources that have been removed from the selection.
+     * <p>
+     * Those are the primitives contained in {@link #getOldDataSources()} but not in
+     * {@link #getDataSources()}
+     * <p>
+     * This collection cannot be modified and will not change.
+     *
+     * @return The DataSources that were removed
+     */
+    Set<DataSource> getRemoved();
+
+    /**
+     * Gets the data sources that have been added to the selection.
+     * <p>
+     * Those are the data sources contained in {@link #getDataSources()} but not in
+     * {@link #getOldDataSources()}
+     * <p>
+     * This collection cannot be modified and will not change.
+     *
+     * @return The data sources that were added
+     */
+    Set<DataSource> getAdded();
+
+    /**
+     * Gets the data set that triggered this selection event.
+     *
+     * @return The data set.
+     */
+    DataSet getSource();
+
+    /**
+     * Test if this event did not change anything.
+     * <p>
+     * This will return <code>false</code> for all events that are sent to
+     * listeners, so you don't need to test it.
+     *
+     * @return <code>true</code> if this did not change the selection.
+     */
+    default boolean isNop() {
+        return getAdded().isEmpty() && getRemoved().isEmpty();
+    }
+}
+
Index: src/org/openstreetmap/josm/data/osm/DataSourceListener.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/DataSourceListener.java	(nonexistent)
+++ src/org/openstreetmap/josm/data/osm/DataSourceListener.java	(working copy)
@@ -0,0 +1,22 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+/**
+ * This is a listener that listens to selection change events in the data set.
+ *
+ * @author Taylor Smock
+ * @since xxx
+ */
+@FunctionalInterface
+public interface DataSourceListener {
+    /**
+     * Called whenever the data source list is changed.
+     *
+     * You get notified about the new data source list, the sources that were added
+     * and removed and the dataset that triggered the event.
+     *
+     * @param event The data source change event.
+     * @see DataSourceChangeEvent
+     */
+    void dataSourceChange(DataSourceChangeEvent event);
+}
Index: src/org/openstreetmap/josm/data/osm/event/DataSourceAddedEvent.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/event/DataSourceAddedEvent.java	(nonexistent)
+++ src/org/openstreetmap/josm/data/osm/event/DataSourceAddedEvent.java	(working copy)
@@ -0,0 +1,66 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.event;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.openstreetmap.josm.data.DataSource;
+import org.openstreetmap.josm.data.osm.AbstractDataSourceChangeEvent;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * There is a new data source
+ *
+ * @author Taylor Smock
+ * @since xxx
+ */
+public class DataSourceAddedEvent extends AbstractDataSourceChangeEvent {
+    private Set<DataSource> current;
+    private Set<DataSource> removed;
+    private final Set<DataSource> added;
+
+    /**
+     * Create a Data Source change event
+     *
+     * @param source         The DataSet that is originating the change
+     * @param old            The previous set of DataSources
+     * @param newDataSources The data sources that are being added
+     */
+    public DataSourceAddedEvent(DataSet source, Set<DataSource> old, Stream<DataSource> newDataSources) {
+        super(source, old);
+        CheckParameterUtil.ensureParameterNotNull(newDataSources, "newDataSources");
+        this.added = newDataSources.collect(Collectors.toCollection(LinkedHashSet::new));
+    }
+
+    @Override
+    public Set<DataSource> getDataSources() {
+        if (current == null) {
+            current = new LinkedHashSet<>(getOldDataSources());
+            current.addAll(added);
+        }
+        return current;
+    }
+
+    @Override
+    public synchronized Set<DataSource> getRemoved() {
+        if (removed == null) {
+            removed = getOldDataSources().stream().filter(s -> !getDataSources().contains(s))
+                    .collect(Collectors.toCollection(LinkedHashSet::new));
+        }
+        return removed;
+    }
+
+    @Override
+    public synchronized Set<DataSource> getAdded() {
+        return added;
+    }
+
+    @Override
+    public String toString() {
+        return "DataSourceAddedEvent [current=" + current + ", removed=" + removed + ", added=" + added + ']';
+    }
+}
+
Index: src/org/openstreetmap/josm/data/osm/event/DataSourceRemovedEvent.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/event/DataSourceRemovedEvent.java	(nonexistent)
+++ src/org/openstreetmap/josm/data/osm/event/DataSourceRemovedEvent.java	(working copy)
@@ -0,0 +1,66 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.event;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.openstreetmap.josm.data.DataSource;
+import org.openstreetmap.josm.data.osm.AbstractDataSourceChangeEvent;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * A data source was removed
+ *
+ * @author Taylor Smock
+ * @since xxx
+ */
+public class DataSourceRemovedEvent extends AbstractDataSourceChangeEvent {
+    private Set<DataSource> current;
+    private final Set<DataSource> removed;
+    private Set<DataSource> added;
+
+    /**
+     * Create a Data Source change event
+     *
+     * @param source             The DataSet that is originating the change
+     * @param old                The previous set of DataSources
+     * @param removedDataSources The data sources that are being removed
+     */
+    public DataSourceRemovedEvent(DataSet source, Set<DataSource> old, Stream<DataSource> removedDataSources) {
+        super(source, old);
+        CheckParameterUtil.ensureParameterNotNull(removedDataSources, "removedDataSources");
+        this.removed = removedDataSources.collect(Collectors.toCollection(LinkedHashSet::new));
+    }
+
+    @Override
+    public Set<DataSource> getDataSources() {
+        if (current == null) {
+            current = getOldDataSources().stream().filter(s -> !removed.contains(s))
+                    .collect(Collectors.toCollection(LinkedHashSet::new));
+        }
+        return current;
+    }
+
+    @Override
+    public synchronized Set<DataSource> getRemoved() {
+        return removed;
+    }
+
+    @Override
+    public synchronized Set<DataSource> getAdded() {
+        if (added == null) {
+            added = getDataSources().stream().filter(s -> !getOldDataSources().contains(s))
+                    .collect(Collectors.toCollection(LinkedHashSet::new));
+        }
+        return added;
+    }
+
+    @Override
+    public String toString() {
+        return "DataSourceAddedEvent [current=" + current + ", removed=" + removed + ", added=" + added + ']';
+    }
+}
+
Index: test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java	(revision 15598)
+++ test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java	(working copy)
@@ -13,7 +13,11 @@
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.DataSource;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.event.DataSourceAddedEvent;
+import org.openstreetmap.josm.data.osm.event.DataSourceRemovedEvent;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -247,4 +251,40 @@
         assertTrue(UploadPolicy.BLOCKED.compareTo(UploadPolicy.DISCOURAGED) > 0);
         assertTrue(UploadPolicy.DISCOURAGED.compareTo(UploadPolicy.NORMAL) > 0);
     }
+
+    /**
+     * Checks that data source listeners get called when a data source is added
+     */
+    @Test
+    public void testAddDataSourceListener() {
+        DataSourceListener addListener = new DataSourceListener() {
+            @Override
+            public void dataSourceChange(DataSourceChangeEvent event) {
+                assertTrue(event instanceof DataSourceAddedEvent);
+            }
+        };
+
+        DataSet ds = new DataSet();
+        ds.addDataSourceListener(addListener);
+        ds.addDataSource(new DataSource(new Bounds(0, 0, 0.1, 0.1), "fake source"));
+
+    }
+
+    /**
+     * Checks that data source listeners get called when a data source is removed
+     */
+    @Test
+    public void testRemoveDataSourceListener() {
+        DataSourceListener removeListener = new DataSourceListener() {
+            @Override
+            public void dataSourceChange(DataSourceChangeEvent event) {
+                assertTrue(event instanceof DataSourceRemovedEvent);
+            }
+        };
+
+        DataSet ds = new DataSet();
+        ds.addDataSource(new DataSource(new Bounds(0, 0, 0.1, 0.1), "fake source"));
+        ds.addDataSourceListener(removeListener);
+        new DataSet().mergeFrom(ds);
+    }
 }
Index: test/unit/org/openstreetmap/josm/data/osm/event/DataSourceAddedEventTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/event/DataSourceAddedEventTest.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/data/osm/event/DataSourceAddedEventTest.java	(working copy)
@@ -0,0 +1,84 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.event;
+
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.DataSource;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.DataSourceChangeEvent;
+
+/**
+ * Test class for {@link DataSourceAddedEvent}
+ *
+ * @author Taylor Smock
+ */
+public class DataSourceAddedEventTest {
+    /**
+     * Get getting the originating data source
+     */
+    @Test
+    public void testGetDataEventSource() {
+        DataSource fakeAdd = new DataSource(new Bounds(0, 0, 0, 0), "fake-source");
+        DataSet ds = new DataSet();
+        assertSame(ds, new DataSourceAddedEvent(ds, Collections.emptySet(), Stream.of(fakeAdd)).getSource());
+    }
+
+    /**
+     * Test that added sources are processed properly
+     */
+    @Test
+    public void testGetAddedSource() {
+        DataSource fakeAdd = new DataSource(new Bounds(0, 0, 0, 0), "fake-source");
+        assertTrue(
+                new DataSourceAddedEvent(new DataSet(), Collections.emptySet(), Stream.empty()).getAdded().isEmpty());
+        DataSourceChangeEvent event = new DataSourceAddedEvent(new DataSet(), Collections.emptySet(),
+                Stream.of(fakeAdd));
+        assertSame(event.getAdded(), event.getAdded());
+        assertSame(fakeAdd, new DataSourceAddedEvent(new DataSet(), Collections.emptySet(), Stream.of(fakeAdd))
+                .getAdded().iterator().next());
+    }
+
+    /**
+     * Test that there are no removed sources
+     */
+    @Test
+    public void testGetRemovedSource() {
+        DataSource fakeAdd = new DataSource(new Bounds(0, 0, 0, 0), "fake-source");
+        assertTrue(
+                new DataSourceAddedEvent(new DataSet(), Collections.emptySet(), Stream.empty()).getRemoved().isEmpty());
+        DataSourceChangeEvent event = new DataSourceAddedEvent(new DataSet(), Collections.emptySet(),
+                Stream.of(fakeAdd));
+        assertSame(event.getRemoved(), event.getRemoved());
+        assertTrue(new DataSourceAddedEvent(new DataSet(), Collections.emptySet(), Stream.of(fakeAdd)).getRemoved()
+                .isEmpty());
+    }
+
+    /**
+     * Check that the sources include newly added data
+     */
+    @Test
+    public void testGetDataSources() {
+        DataSource fakeAdd = new DataSource(new Bounds(0, 0, 0, 0), "fake-source");
+        DataSourceChangeEvent event = new DataSourceAddedEvent(new DataSet(), Collections.emptySet(),
+                Stream.of(fakeAdd));
+        assertSame(event.getDataSources(), event.getDataSources());
+        assertSame(fakeAdd, event.getDataSources().iterator().next());
+    }
+
+    /**
+     * Check that a string is returned with added/current/deleted
+     */
+    @Test
+    public void testToString() {
+        String toString = new DataSourceAddedEvent(new DataSet(), Collections.emptySet(), Stream.empty()).toString();
+        assertTrue(toString.contains("added"));
+        assertTrue(toString.contains("current"));
+        assertTrue(toString.contains("removed"));
+    }
+}
Index: test/unit/org/openstreetmap/josm/data/osm/event/DataSourceRemovedEventTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/event/DataSourceRemovedEventTest.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/data/osm/event/DataSourceRemovedEventTest.java	(working copy)
@@ -0,0 +1,91 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.event;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.DataSource;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.DataSourceChangeEvent;
+
+/**
+ * Test class for {@link DataSourceRemovedEvent}
+ *
+ * @author Taylor Smock
+ */
+
+public class DataSourceRemovedEventTest {
+    /**
+     * Get getting the originating data source
+     */
+    @Test
+    public void testGetDataEventSource() {
+        DataSource fakeRemove = new DataSource(new Bounds(0, 0, 0, 0), "fake-source");
+        DataSet ds = new DataSet();
+        assertSame(ds, new DataSourceRemovedEvent(ds, Collections.emptySet(), Stream.of(fakeRemove)).getSource());
+    }
+
+    /**
+     * Test that no sources are added
+     */
+    @Test
+    public void testGetAddedSource() {
+        DataSource fakeRemove = new DataSource(new Bounds(0, 0, 0, 0), "fake-source");
+        assertTrue(
+                new DataSourceRemovedEvent(new DataSet(), Collections.emptySet(), Stream.empty()).getAdded().isEmpty());
+        DataSourceChangeEvent event = new DataSourceRemovedEvent(new DataSet(), Collections.emptySet(),
+                Stream.of(fakeRemove));
+        assertSame(event.getAdded(), event.getAdded());
+        assertTrue(new DataSourceRemovedEvent(new DataSet(), Collections.emptySet(), Stream.of(fakeRemove)).getAdded()
+                .isEmpty());
+    }
+
+    /**
+     * Test that the getting the removed source(s) works properly
+     */
+    @Test
+    public void testGetRemovedSource() {
+        DataSource fakeRemove = new DataSource(new Bounds(0, 0, 0, 0), "fake-source");
+        assertTrue(new DataSourceRemovedEvent(new DataSet(), Collections.emptySet(), Stream.empty()).getRemoved()
+                .isEmpty());
+        DataSourceChangeEvent event = new DataSourceRemovedEvent(new DataSet(), Collections.emptySet(),
+                Stream.of(fakeRemove));
+        assertSame(event.getRemoved(), event.getRemoved());
+        assertSame(fakeRemove, new DataSourceRemovedEvent(new DataSet(), Collections.emptySet(), Stream.of(fakeRemove))
+                .getRemoved().iterator().next());
+    }
+
+    /**
+     * Check that the sources don't include removed data
+     */
+    @Test
+    public void testGetDataSources() {
+        DataSource fakeRemove = new DataSource(new Bounds(0, 0, 0, 0), "fake-source");
+        DataSourceChangeEvent event = new DataSourceRemovedEvent(new DataSet(), Collections.emptySet(),
+                Stream.of(fakeRemove));
+        assertSame(event.getDataSources(), event.getDataSources());
+        assertFalse(event.getDataSources().contains(fakeRemove));
+        assertTrue(new DataSourceRemovedEvent(new DataSet(), Collections.singleton(fakeRemove), Stream.of(fakeRemove))
+                .getDataSources().isEmpty());
+        assertSame(fakeRemove,
+                new DataSourceRemovedEvent(new DataSet(), Collections.singleton(fakeRemove), Stream.empty())
+                        .getDataSources().iterator().next());
+    }
+
+    /**
+     * Check that a string is returned with added/current/deleted
+     */
+    @Test
+    public void testToString() {
+        String toString = new DataSourceRemovedEvent(new DataSet(), Collections.emptySet(), Stream.empty()).toString();
+        assertTrue(toString.contains("added"));
+        assertTrue(toString.contains("current"));
+        assertTrue(toString.contains("removed"));
+    }
+}
