Merge lp:~rainct/zeitgeist/922620 into lp:~zeitgeist/zeitgeist/bluebird

Proposed by Siegfried Gevatter
Status: Merged
Merged at revision: 386
Proposed branch: lp:~rainct/zeitgeist/922620
Merge into: lp:~zeitgeist/zeitgeist/bluebird
Diff against target: 279 lines (+94/-28)
4 files modified
python/client.py (+33/-21)
test/dbus/blacklist-test.py (+18/-5)
test/dbus/monitor-test.py (+29/-0)
test/dbus/testutils.py (+14/-2)
To merge this branch: bzr merge lp:~rainct/zeitgeist/922620
Reviewer Review Type Date Requested Status
Zeitgeist Framework Team Pending
Review via email: mp+91920@code.launchpad.net
To post a comment you must log in.
lp:~rainct/zeitgeist/922620 updated
388. By Siegfried Gevatter

Add test case for monitor re-connection

389. By Siegfried Gevatter

Make bus initialization lazy so tests connect to the correct D-Bus instance

390. By Siegfried Gevatter

testutils.py: randomize DISPLAY so more than one test can run simultaneously

Revision history for this message
Seif Lotfy (seif) wrote :
Download full text (10.0 KiB)

Ok I had to learn this the hard way since you said it is just going to be
more...
Anyhow looks fine for me

On Tue, Feb 7, 2012 at 10:12 PM, Siegfried Gevatter <email address hidden>wrote:

> Siegfried Gevatter has proposed merging lp:~rainct/zeitgeist/922620 into
> lp:zeitgeist.
>
> Requested reviews:
> Zeitgeist Framework Team (zeitgeist)
> Related bugs:
> Bug #922620 in Zeitgeist Framework: "Fix failing test cases:
> testBlacklistSignals and testDataSourceSignals"
> https://bugs.launchpad.net/zeitgeist/+bug/922620
>
> For more details, see:
> https://code.launchpad.net/~rainct/zeitgeist/922620/+merge/91920
> --
> https://code.launchpad.net/~rainct/zeitgeist/922620/+merge/91920
> Your team Zeitgeist Framework Team is requested to review the proposed
> merge of lp:~rainct/zeitgeist/922620 into lp:zeitgeist.
>
> === modified file 'python/client.py'
> --- python/client.py 2011-10-29 13:31:12 +0000
> +++ python/client.py 2012-02-07 21:11:18 +0000
> @@ -40,6 +40,15 @@
>
> log = logging.getLogger("zeitgeist.client")
>
> +# This is here so testutils.py can override it with a private bus
> connection
> +global session_bus
> +session_bus = dbus.SessionBus()
> +def get_bus():
> + return session_bus
> +def _set_bus(bus):
> + global session_bus
> + session_bus = bus
> +
> class _DBusInterface(object):
> """Wrapper around dbus.Interface adding convenience methods."""
>
> @@ -47,7 +56,6 @@
> # that here because otherwise all instances would share their state.
> _disconnect_callbacks = None
> _reconnect_callbacks = None
> - _generic_callbacks = None
>
> @staticmethod
> def get_members(introspection_xml):
> @@ -69,8 +77,9 @@
> def reconnect(self):
> if not self._reconnect_when_needed:
> return
> - self.__proxy = dbus.SessionBus().get_object(
> - self.__iface.requested_bus_name,
> self.__object_path)
> + self.__proxy = get_bus().get_object(
> + self.__iface.requested_bus_name,
> self.__object_path,
> + follow_name_owner_changes=True)
> self.__iface = dbus.Interface(self.__proxy,
> self.__interface_name)
> self._load_introspection_data()
>
> @@ -131,8 +140,7 @@
> self.reconnect()
> if signal not in self.__signals:
> raise TypeError("Unknown signal name: %s" % signal)
> - self._generic_callbacks.add((signal, callback))
> - self.__proxy.connect_to_signal(
> + return self.__proxy.connect_to_signal(
> signal,
> callback,
> dbus_interface=self.__interface_name,
> @@ -169,7 +177,6 @@
>
> self._disconnect_callbacks = set()
> self._reconnect_callbacks = set()
> - self._generic_callbacks = set()
>
> # Listen to (dis)connection notifications, for connect_exit
> and connect_join
> def name_owner_changed(connection_name):
> @@ -181,15 +188,9 @@
> retu...

lp:~rainct/zeitgeist/922620 updated
391. By Siegfried Gevatter

Fix monitors being installed twice; python-dbus always gives us a name
owner changed notification, so we want to ignore this first one.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'python/client.py'
2--- python/client.py 2011-10-29 13:31:12 +0000
3+++ python/client.py 2012-02-07 22:51:18 +0000
4@@ -40,6 +40,19 @@
5
6 log = logging.getLogger("zeitgeist.client")
7
8+# This is here so testutils.py can override it with a private bus connection.
9+# Init needs to be lazy so tests will use the private D-Bus instance.
10+global session_bus
11+session_bus = None
12+def get_bus():
13+ global session_bus
14+ if session_bus is None:
15+ session_bus = dbus.SessionBus()
16+ return session_bus
17+def _set_bus(bus):
18+ global session_bus
19+ session_bus = bus
20+
21 class _DBusInterface(object):
22 """Wrapper around dbus.Interface adding convenience methods."""
23
24@@ -47,7 +60,6 @@
25 # that here because otherwise all instances would share their state.
26 _disconnect_callbacks = None
27 _reconnect_callbacks = None
28- _generic_callbacks = None
29
30 @staticmethod
31 def get_members(introspection_xml):
32@@ -69,8 +81,9 @@
33 def reconnect(self):
34 if not self._reconnect_when_needed:
35 return
36- self.__proxy = dbus.SessionBus().get_object(
37- self.__iface.requested_bus_name, self.__object_path)
38+ self.__proxy = get_bus().get_object(
39+ self.__iface.requested_bus_name, self.__object_path,
40+ follow_name_owner_changes=True)
41 self.__iface = dbus.Interface(self.__proxy, self.__interface_name)
42 self._load_introspection_data()
43
44@@ -131,8 +144,7 @@
45 self.reconnect()
46 if signal not in self.__signals:
47 raise TypeError("Unknown signal name: %s" % signal)
48- self._generic_callbacks.add((signal, callback))
49- self.__proxy.connect_to_signal(
50+ return self.__proxy.connect_to_signal(
51 signal,
52 callback,
53 dbus_interface=self.__interface_name,
54@@ -167,29 +179,28 @@
55 self._reconnect_when_needed = reconnect
56 self._load_introspection_data()
57
58+ self._first_connection = True
59 self._disconnect_callbacks = set()
60 self._reconnect_callbacks = set()
61- self._generic_callbacks = set()
62
63 # Listen to (dis)connection notifications, for connect_exit and connect_join
64 def name_owner_changed(connection_name):
65 if connection_name == "":
66- callbacks = self._disconnect_callbacks
67 self.__methods = self.__signals = None
68+ for callback in self._disconnect_callbacks:
69+ callback()
70+ elif self._first_connection:
71+ # python-dbus guarantees that it'll call NameOwnerChanged at startup
72+ # (even if the service was already running). When that happens, we
73+ # don't want to connect the signals a second time.
74+ self._first_connection = False
75 else:
76 if not self._reconnect_when_needed:
77 return
78 self.reconnect()
79- callbacks = self._reconnect_callbacks
80- for signal, callback in self._generic_callbacks:
81- try:
82- self.connect(signal, callback)
83- except TypeError:
84- log.exception("Failed to reconnect to signal \"%s\" "
85- "after engine disconnection." % signal)
86- for callback in callbacks:
87- callback()
88- dbus.SessionBus().watch_name_owner(self.__iface.requested_bus_name,
89+ for callback in self._reconnect_callbacks:
90+ callback()
91+ get_bus().watch_name_owner(self.__iface.requested_bus_name,
92 name_owner_changed)
93
94 class ZeitgeistDBusInterface(object):
95@@ -233,7 +244,8 @@
96 if not name in cls.__shared_state["extension_interfaces"]:
97 interface_name = "org.gnome.zeitgeist.%s" % name
98 object_path = "/org/gnome/zeitgeist/%s" % path
99- proxy = dbus.SessionBus().get_object(busname, object_path)
100+ proxy = get_bus().get_object(busname, object_path,
101+ follow_name_owner_changes=True)
102 iface = _DBusInterface(proxy, interface_name, object_path)
103 iface.BUS_NAME = busname
104 iface.INTERFACE_NAME = interface_name
105@@ -244,8 +256,8 @@
106 def __init__(self, reconnect=True):
107 if not "dbus_interface" in self.__shared_state:
108 try:
109- proxy = dbus.SessionBus().get_object(self.BUS_NAME,
110- self.OBJECT_PATH)
111+ proxy = get_bus().get_object(self.BUS_NAME,
112+ self.OBJECT_PATH, follow_name_owner_changes=True)
113 except dbus.exceptions.DBusException, e:
114 if e.get_dbus_name() == "org.freedesktop.DBus.Error.ServiceUnknown":
115 raise RuntimeError(
116@@ -292,7 +304,7 @@
117 self._path = monitor_path
118 self._insert_callback = insert_callback
119 self._delete_callback = delete_callback
120- dbus.service.Object.__init__(self, dbus.SessionBus(), monitor_path)
121+ dbus.service.Object.__init__(self, get_bus(), monitor_path)
122
123 def get_path (self): return self._path
124 path = property(get_path,
125
126=== modified file 'test/dbus/blacklist-test.py'
127--- test/dbus/blacklist-test.py 2011-09-07 20:48:27 +0000
128+++ test/dbus/blacklist-test.py 2012-02-07 22:51:18 +0000
129@@ -110,11 +110,13 @@
130 newAllTemplates = blacklist.GetTemplates()
131 self.assertEquals(len(newAllTemplates), 0)
132
133- def testBlacklistSignals(self):
134+ def testBlacklistSignals(self, mainloop=None, connect_signals=True):
135 self.blacklist = self._get_blacklist_iface()
136- mainloop = self.create_mainloop()
137+ if mainloop is None:
138+ mainloop = self.create_mainloop()
139
140 template1 = Event.new_for_values(
141+ timestamp=0,
142 interpretation=Interpretation.ACCESS_EVENT,
143 subject_uri="http://nothingtoseehere.gov")
144
145@@ -124,7 +126,6 @@
146 @asyncTestMethod(mainloop)
147 def cb_added(template_id, event_template):
148 global hit
149- print "*** cb_added with hit", hit
150 self.assertEquals(hit, 0)
151 hit = 1
152 self.assertEquals(template_id, "TestTemplate")
153@@ -140,8 +141,9 @@
154 mainloop.quit()
155
156 # Connect to signals
157- self.blacklist.connect('TemplateAdded', cb_added)
158- self.blacklist.connect('TemplateRemoved', cb_removed)
159+ if connect_signals:
160+ self.blacklist.connect('TemplateAdded', cb_added)
161+ self.blacklist.connect('TemplateRemoved', cb_removed)
162
163 def launch_tests():
164 self.blacklist.AddTemplate("TestTemplate", template1)
165@@ -150,5 +152,16 @@
166
167 mainloop.run()
168
169+ def testBlacklistSignalWithReconnection(self):
170+ mainloop = self.create_mainloop()
171+ self.testBlacklistSignals(mainloop)
172+
173+ # Restart the Zeitgeist daemon...
174+ self.kill_daemon()
175+ self.spawn_daemon()
176+
177+ # ... and try again without re-connecting to the signals
178+ self.testBlacklistSignals(mainloop, connect_signals=False)
179+
180 if __name__ == "__main__":
181 unittest.main()
182
183=== modified file 'test/dbus/monitor-test.py'
184--- test/dbus/monitor-test.py 2011-09-07 20:48:27 +0000
185+++ test/dbus/monitor-test.py 2012-02-07 22:51:18 +0000
186@@ -286,5 +286,34 @@
187 self.assertEquals(1, len(result))
188 self.assertEquals(1, result.pop())
189
190+ def testMonitorReconnection(self):
191+ result = []
192+ mainloop = self.create_mainloop()
193+ events = parse_events("test/data/three_events.js")
194+
195+ @asyncTestMethod(mainloop)
196+ def notify_insert_handler(time_range, events):
197+ result.extend(events)
198+ mainloop.quit()
199+
200+ @asyncTestMethod(mainloop)
201+ def notify_delete_handler(time_range, event_ids):
202+ mainloop.quit()
203+ self.fail("Unexpected delete notification")
204+
205+ self.client.install_monitor(TimeRange.always(), [],
206+ notify_insert_handler, notify_delete_handler)
207+
208+ # Restart the Zeitgeist daemon to test automagic monitor re-connection
209+ self.kill_daemon()
210+ self.spawn_daemon()
211+
212+ # Insert events in idle loop to give the reconnection logic enough time
213+ gobject.idle_add(lambda *args: self.client.insert_events(events))
214+
215+ mainloop.run()
216+
217+ self.assertEquals(3, len(result))
218+
219 if __name__ == "__main__":
220 unittest.main()
221
222=== modified file 'test/dbus/testutils.py'
223--- test/dbus/testutils.py 2012-02-07 17:49:49 +0000
224+++ test/dbus/testutils.py 2012-02-07 22:51:18 +0000
225@@ -21,12 +21,14 @@
226 # along with this program. If not, see <http://www.gnu.org/licenses/>.
227
228 import unittest
229+import dbus
230 import os
231 import time
232 import sys
233 import signal
234 import tempfile
235 import shutil
236+import random
237 from subprocess import Popen, PIPE
238
239 # DBus setup
240@@ -36,7 +38,8 @@
241
242 # Import local Zeitgeist modules
243 sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
244-from zeitgeist.client import ZeitgeistDBusInterface, ZeitgeistClient
245+from zeitgeist.client import ZeitgeistDBusInterface, ZeitgeistClient, \
246+ get_bus, _set_bus
247 from zeitgeist.datamodel import Event, Subject, Interpretation, Manifestation, \
248 TimeRange, NULL_EVENT
249
250@@ -175,11 +178,18 @@
251
252 # hack to clear the state of the interface
253 ZeitgeistDBusInterface._ZeitgeistDBusInterface__shared_state = {}
254+
255+ # Replace the bus connection with a private one for each test case,
256+ # so that they don't share signals or other state
257+ _set_bus(dbus.SessionBus(private=True))
258+ get_bus().set_exit_on_disconnect(False)
259+
260 self.client = ZeitgeistClient()
261
262 def tearDown(self):
263 assert self.daemon is not None
264 assert self.client is not None
265+ get_bus().close()
266 self.kill_daemon()
267 if 'ZEITGEIST_TESTS_KEEP_TMP' in os.environ:
268 print '\n\nAll temporary files have been preserved in %s\n' \
269@@ -389,7 +399,9 @@
270 self.assertEqual(ev1, ev2)
271
272 class DBusPrivateMessageBus(object):
273- DISPLAY = ":27"
274+ # Choose a random number so it's possible to have more than
275+ # one test running at once.
276+ DISPLAY = ":%d" % random.randint(20, 100)
277
278 def _run(self):
279 os.environ.update({"DISPLAY": self.DISPLAY})

Subscribers

People subscribed via source and target branches