Rev 5: Implement revision announcing broadcast service. in file:///home/robertc/source/baz/plugins/dbus/trunk/
Robert Collins
robertc at robertcollins.net
Tue Feb 6 07:12:43 GMT 2007
------------------------------------------------------------
revno: 5
revision-id: robertc at robertcollins.net-20070206071242-f1htyq1f1utkj77a
parent: robertc at robertcollins.net-20070206050548-fr9um4fo1070o1sw
committer: Robert Collins <robertc at robertcollins.net>
branch nick: trunk
timestamp: Tue 2007-02-06 18:12:42 +1100
message:
Implement revision announcing broadcast service.
modified:
TODO todo-20070206032729-2b5teqiwctqrfgei-3
activity.py activity.py-20070206034725-q208d0jtkshwu0fx-1
tests/test_activity.py test_activity.py-20070206035206-bnhzvtm6m6hpgylz-1
=== modified file 'TODO'
--- a/TODO 2007-02-06 04:16:36 +0000
+++ b/TODO 2007-02-06 07:12:42 +0000
@@ -3,10 +3,10 @@
* advertise_branch sends the message 'announce_revision' when the service is
running
- * announce_revision when recieved sends the revision to all activity
- subscribers
- * can subscribe to activity
* simple subscriber that sits in a corner and shows revisions
* dbus service to act as the broadcast point for revisions
+ * .service details for the dbus service.
* bzr server integration to republish tip revisions from file:/// to public
URLs when one is available.
+ * debug why raising a dbus returned error skips regular error handling and
+ jumps to sys.excepthook [wager: crack from the dbus python bindings]
=== modified file 'activity.py'
--- a/activity.py 2007-02-06 04:16:36 +0000
+++ b/activity.py 2007-02-06 07:12:42 +0000
@@ -22,7 +22,9 @@
about bzr activity: See the class for more detail.
"""
-import dbus
+import dbus.mainloop
+import dbus.service
+import gobject
class Activity(object):
@@ -52,3 +54,47 @@
:return: None
:raises: Nothing should be raised.
"""
+
+ def serve_agency(self):
+ """Serve an advertising agency.
+
+ This service contains a list of subscribers, but no state per
+ subscriber, so it should have minimal footprint.
+ """
+ # lazy import to not pollute bzr during startup.
+ import dbus.mainloop.glib
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ mainloop = gobject.MainLoop()
+ mainloop.run()
+
+
+class Broadcast(dbus.service.Object):
+
+ # Dont try to put a '-' in bazaar-vcs here, its NOT dns and dbus considers -
+ # illegal.
+ DBUS_NAME = "org.bazaarvcs.plugins.dbus.Broadcast"
+ DBUS_PATH = "/org/bazaarvcs/plugins/dbus/Broadcast"
+ DBUS_INTERFACE = "org.bazaarvcs.plugins.dbus.Broadcast"
+
+ def __init__(self, bus):
+ """Create a Broadcast service.
+
+ :param bus: The bus to serve on.
+ """
+ bus_name = dbus.service.BusName(
+ Broadcast.DBUS_NAME, bus=bus)
+ dbus.service.Object.__init__(self, bus, Broadcast.DBUS_PATH, bus_name)
+
+ @dbus.service.method(DBUS_INTERFACE,
+ in_signature='ss', out_signature='')
+ def announce_revision(self, revision_id, url):
+ """Announce revision_id as being now present at url.
+
+ To avoid information disclosure, no details are handed over the wire:
+ clients should access the revision to determine its contents.
+ """
+ self.Revision(revision_id, url)
+
+ @dbus.service.signal(DBUS_INTERFACE)
+ def Revision(self, revision, url):
+ """A revision has been observed at url."""
=== modified file 'tests/test_activity.py'
--- a/tests/test_activity.py 2007-02-06 05:05:48 +0000
+++ b/tests/test_activity.py 2007-02-06 07:12:42 +0000
@@ -26,7 +26,14 @@
import weakref
import dbus
+# to create a private Bus
import _dbus_bindings
+import dbus.mainloop.glib
+import gobject
+
+# done here to just do it once, and not in the plugin module to avoid doing it
+# by default.
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
from bzrlib.tests import TestCaseWithMemoryTransport
@@ -95,6 +102,7 @@
the GIL)"""
bus.add_message_filter(bus.__class__._signal_func)
+ bus.set_exit_on_disconnect(False)
return bus
@@ -110,7 +118,7 @@
self._bus_type, id(self))
-class TestActivity(TestCaseWithMemoryTransport):
+class TestCaseWithDBus(TestCaseWithMemoryTransport):
def setUp(self):
TestCaseWithMemoryTransport.setUp(self)
@@ -118,11 +126,40 @@
# the users desktop!
self.bus = PrivateBus()
self.addCleanup(self.bus.nuke)
+
+
+class TestActivity(TestCaseWithDBus):
def test_advertise_branch_no_service_running(self):
# should not error: just call it
activity.Activity(bus=self.bus).advertise_branch(self.make_branch('.'))
+ def in_dev_test_advertise_branch_service_running(self):
+ # We could construct a test-broadcaster to test with, but for now the
+ # sheer overhead of that scares me.
+ # attach a Broadcaster to our test branch: creates a running service.
+ broadcaster = activity.Broadcast(self.bus)
+ # get the object so we can subscribe to callbacks from it.
+ dbus_object = self.bus.get_object(activity.Broadcast.DBUS_NAME,
+ activity.Broadcast.DBUS_PATH)
+ # we want to recieve the callbacks to inspect them.
+ branches = []
+ def catch_branch(revision, url):
+ branches.append((revision, url))
+ dbus_object.connect_to_signal("Revision", catch_branch,
+ dbus_interface=activity.Broadcast.DBUS_INTERFACE)
+
+ # now call our convenience JustDoIt method.
+ branch = self.make_branch('.')
+ activity.Activity(bus=self.bus).advertise_branch(branch)
+ # now, let the broadcast method interactions all happen
+
+ mainloop = gobject.MainLoop()
+ gobject.idle_add(mainloop.quit)
+ mainloop.run()
+ self.assertEqual([('revision1', 'url1'), ('revision2', 'url2')],
+ branches)
+
def test_constructor(self):
"""The constructor should setup a good working environment."""
obj = activity.Activity()
@@ -130,3 +167,61 @@
# it has __eq__ or if it would be reliable, so we use object identity.
self.assertTrue(obj.bus is dbus.SessionBus(),
"%r is not %r" % (obj.bus, dbus.SessionBus()))
+
+ def test_server(self):
+ """Calling Activity.serve() provides a convient means to serve."""
+ obj = activity.Activity(bus=self.bus)
+ calls = []
+ #obj.serve_agency()
+
+
+class TestBroadcast(TestCaseWithDBus):
+
+ def test_announce_revision(self):
+ """Calling announce_revision via dbus should work and return nothing."""
+ # attach a Broadcaster.
+ broadcaster = activity.Broadcast(self.bus)
+ dbus_object = self.bus.get_object(activity.Broadcast.DBUS_NAME,
+ activity.Broadcast.DBUS_PATH)
+ dbus_iface = dbus.Interface(dbus_object,
+ activity.Broadcast.DBUS_INTERFACE)
+ # we want to recieve the callbacks to inspect them.
+ signals1 = []
+ def catch_signal1(revision, url):
+ signals1.append((revision, url))
+ # second method to avoid any possible dbus muppetry
+ signals2 = []
+ def catch_signal2(revision, url):
+ signals2.append((revision, url))
+ dbus_object.connect_to_signal("Revision", catch_signal1,
+ dbus_interface=activity.Broadcast.DBUS_INTERFACE)
+ dbus_object.connect_to_signal("Revision", catch_signal2,
+ dbus_interface=activity.Broadcast.DBUS_INTERFACE)
+
+ # now try to call the announce method, which should call the signal
+ # handlers - all of them..
+ errors = []
+ results = []
+ def handle_reply():
+ results.append(None)
+ if len(results) + len(errors) == 2:
+ mainloop.quit() # end the mainloop
+ def handle_error(error):
+ errors.append(error)
+ if len(results) + len(errors) == 2:
+ mainloop.quit() # end the mainloop
+ dbus_iface.announce_revision('revision1', 'url1',
+ reply_handler=handle_reply,
+ error_handler=handle_error)
+ # for each revision.
+ dbus_iface.announce_revision('revision2', 'url2',
+ reply_handler=handle_reply,
+ error_handler=handle_error)
+ mainloop = gobject.MainLoop()
+ mainloop.run()
+ if errors:
+ raise errors[0]
+ self.assertEqual([('revision1', 'url1'), ('revision2', 'url2')],
+ signals1)
+ self.assertEqual([('revision1', 'url1'), ('revision2', 'url2')],
+ signals2)
More information about the bazaar-commits
mailing list