VOL-943: Fix for event publish loop dropping subscribers

Change-Id: I96ca0a5bf0b06ee12140966dbfb0a15290e130df
diff --git a/common/event_bus.py b/common/event_bus.py
index 8c903d9..e717c16 100644
--- a/common/event_bus.py
+++ b/common/event_bus.py
@@ -91,6 +91,7 @@
         :param msg: Arbitrary python data as message
         :return: None
         """
+        from copy import copy
 
         def passes(msg, predicate):
             try:
@@ -105,7 +106,8 @@
         subscribers.extend(s for s in self.subscriptions.get(None, [])
                            if s.topic.match(topic))
 
-        for candidate in subscribers:
+        # iterate over a shallow-copy of subscribers
+        for candidate in copy(subscribers):
             predicate = candidate.predicate
             if predicate is None or passes(msg, predicate):
                 try:
diff --git a/tests/utests/common/test_event_bus.py b/tests/utests/common/test_event_bus.py
index 532befb..67d8c8f 100644
--- a/tests/utests/common/test_event_bus.py
+++ b/tests/utests/common/test_event_bus.py
@@ -195,3 +195,26 @@
             msg = yield queue.get()
             self.assertEqual(msg, i)
         self.assertEqual(len(queue.pending), 0)
+
+    def test_subscribers_that_unsubscribe_when_called(self):
+        # VOL-943 bug fix check
+        ebc = EventBusClient(EventBus())
+
+        class UnsubscribeWhenCalled(object):
+            def __init__(self):
+                self.subscription = ebc.subscribe('news', self.unsubscribe)
+                self.called = False
+
+            def unsubscribe(self, _topic, _msg):
+                self.called = True
+                ebc.unsubscribe(self.subscription)
+
+        ebc1 = UnsubscribeWhenCalled()
+        ebc2 = UnsubscribeWhenCalled()
+        ebc3 = UnsubscribeWhenCalled()
+
+        ebc.publish('news', 'msg1')
+
+        self.assertTrue(ebc1.called)
+        self.assertTrue(ebc2.called)
+        self.assertTrue(ebc3.called)