Merge lp:~facundo/magicicada-gui/simple-api into lp:magicicada-gui

Proposed by Facundo Batista
Status: Merged
Approved by: Natalia Bidart
Approved revision: 12
Merge reported by: Facundo Batista
Merged at revision: not available
Proposed branch: lp:~facundo/magicicada-gui/simple-api
Merge into: lp:magicicada-gui
Diff against target: 310 lines (+207/-28)
2 files modified
magicicada/syncdaemon.py (+52/-4)
magicicada/tests/test_syncdaemon.py (+155/-24)
To merge this branch: bzr merge lp:~facundo/magicicada-gui/simple-api
Reviewer Review Type Date Requested Status
Natalia Bidart Approve
Review via email: mp+25248@code.launchpad.net

Description of the change

Expose a simple API to the GUI.

These are the attributes (exposed in SyncDaemon.current_state):

  is_started
  is_connected
  is_online

And the methods:

  start
  stop
  connect
  disconnect

To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Is there a typo here: """Tells the SyncDaemon that the user wants it to DISconnect.""" ?

When running nose, I get:

======================================================================
ERROR: Check that it polls mq until no more is needed.
----------------------------------------------------------------------
TimeoutError: <test_syncdaemon.MetaQueueChangedTests testMethod=test_mq_polling_untilfinish> (test_mq_polling_untilfinish) still running at 2.0 secs

======================================================================
ERROR: Check that it polls mq until no more is needed.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 778, in runUntilCurrent
    call.func(*call.args, **call.kw)
  File "/home/nessita/projects/magicicada/review_simple-api/magicicada/tests/test_syncdaemon.py", line 365, in check
    self.assertEqual(len(calls), 3)
  File "/usr/lib/python2.6/dist-packages/twisted/trial/unittest.py", line 287, in failUnlessEqual
    % (msg, pformat(first), pformat(second)))
FailTest: not equal:
a = 2
b = 3

----------------------------------------------------------------------
Ran 70 tests in 3.033s

FAILED (errors=2)

review: Needs Fixing
9. By Facundo Batista

Merged trunk in

10. By Facundo Batista

Typo, and refactored test

11. By Facundo Batista

Not beyond 80 columns!

12. By Facundo Batista

Merged trunk in

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Nice!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'magicicada/syncdaemon.py'
--- magicicada/syncdaemon.py 2010-05-17 16:10:28 +0000
+++ magicicada/syncdaemon.py 2010-05-18 22:03:29 +0000
@@ -29,12 +29,12 @@
2929
30from magicicada.helpers import NO_OP30from magicicada.helpers import NO_OP
3131
32
33# structures that hold content and queue information32# structures that hold content and queue information
34QueueData = collections.namedtuple('QueueData', 'operation path share node')33QueueData = collections.namedtuple('QueueData', 'operation path share node')
35State = collections.namedtuple('State', 'name description is_error '34State = collections.namedtuple('State', 'name description is_error '
36 'is_connected is_online queues connection')35 'is_connected is_online queues connection')
3736
37# regular expressions for parsing MetaQueue data
38RE_OP_LISTDIR = re.compile("(ListDir)\(share_id=(.*?), node_id=(.*?), .*")38RE_OP_LISTDIR = re.compile("(ListDir)\(share_id=(.*?), node_id=(.*?), .*")
39RE_OP_UNLINK = re.compile("(Unlink)\(share_id=(.*?), node_id=(.*?), .*")39RE_OP_UNLINK = re.compile("(Unlink)\(share_id=(.*?), node_id=(.*?), .*")
40RE_OP_MAKEFILE = re.compile(40RE_OP_MAKEFILE = re.compile(
@@ -42,6 +42,35 @@
42RE_OP_MAKEDIR = re.compile(42RE_OP_MAKEDIR = re.compile(
43 "(MakeDir)\(share_id=(.*?), parent_id=(.*?), name=(.*?), .*")43 "(MakeDir)\(share_id=(.*?), parent_id=(.*?), name=(.*?), .*")
4444
45# structure that hold content and queue information
46QueueData = collections.namedtuple('QueueData', 'operation path share node')
47
48class State(object):
49 """Holds the state of SD."""
50 _attrs = ['name', 'description', 'is_error', 'is_connected',
51 'is_online', 'queues', 'connection', 'is_started']
52
53 def __init__(self, syncdaemon):
54 self._sd = syncdaemon
55
56 def __getattribute__(self, name):
57 """Return the value if there."""
58 if name[0] == "_":
59 return object.__getattribute__(self, name)
60 else:
61 if name == 'is_started':
62 v = self._sd._get_started()
63 else:
64 v = self.__dict__.get(name)
65 return v
66
67 def _set(self, **data):
68 """Sets the attributes fromd data, if allowed."""
69 for name, value in data.iteritems():
70 if name not in self._attrs:
71 raise AttributeError("Name not in _attrs: %r" % name)
72 self.__dict__[name] = value
73
4574
46class SyncDaemon(object):75class SyncDaemon(object):
47 """Interface to Ubuntu One's SyncDaemon."""76 """Interface to Ubuntu One's SyncDaemon."""
@@ -51,6 +80,7 @@
51 loop = DBusGMainLoop(set_as_default=True)80 loop = DBusGMainLoop(set_as_default=True)
52 self._bus = bus = dbus.SessionBus(mainloop=loop)81 self._bus = bus = dbus.SessionBus(mainloop=loop)
53 self.sync_daemon_tool = tools.SyncDaemonTool(bus)82 self.sync_daemon_tool = tools.SyncDaemonTool(bus)
83 self.current_state = State(self)
5484
55 # hook up for signals and store info for the shutdown85 # hook up for signals and store info for the shutdown
56 _signals = [86 _signals = [
@@ -73,6 +103,11 @@
73 # calls to obtain data from SDT103 # calls to obtain data from SDT
74 self._get_content_queue = self.sync_daemon_tool.waiting_content104 self._get_content_queue = self.sync_daemon_tool.waiting_content
75 self._get_meta_queue = self.sync_daemon_tool.waiting_metadata105 self._get_meta_queue = self.sync_daemon_tool.waiting_metadata
106 self._get_started = tools.is_running
107 self._do_start = self.sync_daemon_tool.start
108 self._do_quit = self.sync_daemon_tool.quit
109 self._do_connect = self.sync_daemon_tool.connect
110 self._do_disconnect = self.sync_daemon_tool.disconnect
76111
77 # previous data112 # previous data
78 self._last_CQ_data = None113 self._last_CQ_data = None
@@ -102,9 +137,7 @@
102 is_online = bool(state['is_online'])137 is_online = bool(state['is_online'])
103 queues = state['queues']138 queues = state['queues']
104 connection = state['connection']139 connection = state['connection']
105 self.current_state = State(name, description, is_error,140 self.current_state._set(**state)
106 is_connected, is_online,
107 queues, connection)
108 self.status_changed_callback(name, description, is_error,141 self.status_changed_callback(name, description, is_error,
109 is_connected, is_online,142 is_connected, is_online,
110 queues, connection)143 queues, connection)
@@ -198,3 +231,18 @@
198 d.addCallback(self._process_mq)231 d.addCallback(self._process_mq)
199 self._mqcaller = reactor.callLater(self._mq_poll_time, self._check_mq)232 self._mqcaller = reactor.callLater(self._mq_poll_time, self._check_mq)
200233
234 def start(self):
235 """Starts the SyncDaemon."""
236 self._do_start()
237
238 def quit(self):
239 """Stops the SyncDaemon and makes it quit."""
240 self._do_quit()
241
242 def connect(self):
243 """Tells the SyncDaemon that the user wants it to connect."""
244 self._do_connect()
245
246 def disconnect(self):
247 """Tells the SyncDaemon that the user wants it to disconnect."""
248 self._do_disconnect()
201249
=== modified file 'magicicada/tests/test_syncdaemon.py'
--- magicicada/tests/test_syncdaemon.py 2010-05-04 01:30:42 +0000
+++ magicicada/tests/test_syncdaemon.py 2010-05-18 22:03:29 +0000
@@ -18,12 +18,14 @@
1818
19"""Tests for the SyncDaemon communication backend."""19"""Tests for the SyncDaemon communication backend."""
2020
21import unittest
22
21import dbus23import dbus
22from dbus.mainloop.glib import DBusGMainLoop24from dbus.mainloop.glib import DBusGMainLoop
2325
24# this should be imported *before* importing the reactor below, as needs to26# this should be imported *before* importing the reactor below, as needs to
25# install the glib connection first27# install the glib connection first
26from magicicada.syncdaemon import SyncDaemon28from magicicada.syncdaemon import SyncDaemon, State
2729
28from dbus.lowlevel import SignalMessage30from dbus.lowlevel import SignalMessage
29from twisted.trial.unittest import TestCase as TwistedTestCase31from twisted.trial.unittest import TestCase as TwistedTestCase
@@ -340,34 +342,163 @@
340342
341 def test_mq_polling_untilfinish(self):343 def test_mq_polling_untilfinish(self):
342 """Check that it polls mq until no more is needed."""344 """Check that it polls mq until no more is needed."""
343345 # prepare different status changed signals
344 # set the callback, and adjust the polling time to .2 seconds346 d1 = dict(name='QUEUE_MANAGER', queues='WORKING_ON_BOTH',
347 description='description', is_error='', is_connected='True',
348 is_online='', connection='conn')
349
350 d2 = dict(name='QUEUE_MANAGER', queues='WORKING_ON_CONTENT',
351 description='description', is_error='', is_connected='True',
352 is_online='', connection='conn')
353
354 # set the callback, and adjust the polling time to faster
345 calls = []355 calls = []
346 def fake_get(*a):356 def fake_get(*a):
347 calls.append(None)357 calls.append(None)
358 if len(calls) < 3:
359 pass # no changes, should keep calling
360 elif len(calls) == 3:
361 self.send_signal(DBUS_IFACE_STATUS_NAME,
362 'StatusChanged', 'a{ss}', d2)
363 # allow time to see if a mistaken call happens
364 reactor.callLater(.5, deferred.callback, True)
365 else:
366 deferred.errback(ValueError("Too many calls"))
348 return defer.Deferred()367 return defer.Deferred()
368
349 self.sd._get_meta_queue = fake_get369 self.sd._get_meta_queue = fake_get
350 self.sd._mq_poll_time = .4370 self.sd._mq_poll_time = .1
351371
352 # prepare different status changed signals
353 d1 = dict(name='QUEUE_MANAGER', queues='WORKING_ON_BOTH',
354 description='description', is_error='', is_connected='True',
355 is_online='', connection='conn')
356
357 d2 = dict(name='QUEUE_MANAGER', queues='WORKING_ON_CONTENT',
358 description='description', is_error='', is_connected='True',
359 is_online='', connection='conn')
360
361 def check():
362 """Verify the polling function was called n times."""
363 self.assertEqual(len(calls), 3)
364 deferred.callback(True)
365
366 # start with the working one, stop it .5 seconds later, and check
367 self.send_signal(DBUS_IFACE_STATUS_NAME, 'StatusChanged', 'a{ss}', d1)372 self.send_signal(DBUS_IFACE_STATUS_NAME, 'StatusChanged', 'a{ss}', d1)
368 reactor.callLater(1, self.send_signal, DBUS_IFACE_STATUS_NAME,
369 'StatusChanged', 'a{ss}', d2)
370 reactor.callLater(1.2, check)
371
372 deferred = defer.Deferred()373 deferred = defer.Deferred()
373 return deferred374 return deferred
375
376
377class StateTests(unittest.TestCase):
378 """Test State class."""
379
380 def test_initial(self):
381 """Initial state for vals."""
382 st = State(None)
383 self.assertEqual(st.name, None)
384
385 def test_set_one_value(self):
386 """Set one value."""
387 st = State(None)
388 st._set(name=55)
389
390 # check the one is set, the rest not
391 self.assertEqual(st.name, 55)
392 self.assertEqual(st.description, None)
393
394 def test_set_two_values(self):
395 """Set two values."""
396 st = State(None)
397 st._set(name=55, description=77)
398
399 # check those two are set, the rest not
400 self.assertEqual(st.name, 55)
401 self.assertEqual(st.description, 77)
402 self.assertEqual(st.is_error, None)
403
404 def test_bad_value(self):
405 """Set a value that should not."""
406 st = State(None)
407 self.assertRaises(AttributeError, st._set, not_really_allowed=44)
408
409
410class APITests(unittest.TestCase):
411 """Check exposed methods and attributes."""
412
413 def setUp(self):
414 """Set up the test."""
415 self.sd = SyncDaemon()
416
417 self._replaced = None
418 self.called = False
419
420 def tearDown(self):
421 """Tear down the test."""
422 if self._replaced is not None:
423 setattr(*self._replaced)
424 self.sd.shutdown()
425
426 def mpatch_called(self, obj, method_name):
427 """Monkeypatch to flag called."""
428 self._replaced = (obj, method_name, getattr(obj, method_name))
429 f = lambda *a, **k: setattr(self, 'called', True)
430 setattr(obj, method_name, f)
431
432 def test_defaults_are_callable(self):
433 """Check the attributes are callable."""
434 meths = (self.sd._get_content_queue, self.sd._get_meta_queue,
435 self.sd._get_started, self.sd._do_start, self.sd._do_quit,
436 self.sd._do_connect, self.sd._do_disconnect)
437 for meth in meths:
438 self.assertTrue(callable(meth), "Meth %r is not callable" % meth)
439
440 def test_is_connected_yes(self):
441 """Check is_connected, True."""
442 d = dict(name='name', description='description', is_error='',
443 is_connected='True', is_online='', queues='queues',
444 connection='connection')
445 self.sd._on_status_changed(d)
446 self.assertTrue(self.sd.current_state.is_connected)
447
448 def test_is_connected_no(self):
449 """Check is_connected, False."""
450 d = dict(name='name', description='description', is_error='',
451 is_connected='', is_online='', queues='queues',
452 connection='connection')
453 self.sd._on_status_changed(d)
454 self.assertFalse(self.sd.current_state.is_connected)
455
456 def test_is_online_yes(self):
457 """Check is_online, True."""
458 d = dict(name='name', description='description', is_error='',
459 is_connected='True', is_online='True', queues='queues',
460 connection='connection')
461 self.sd._on_status_changed(d)
462 self.assertTrue(self.sd.current_state.is_online)
463
464 def test_is_online_no(self):
465 """Check is_online, False."""
466 d = dict(name='name', description='description', is_error='',
467 is_connected='True', is_online='', queues='queues',
468 connection='connection')
469 self.sd._on_status_changed(d)
470 self.assertFalse(self.sd.current_state.is_online)
471
472 def test_is_started_yes(self):
473 """Check is_started, True."""
474 self.sd._get_started = lambda: True
475 self.assertTrue(self.sd.current_state.is_started)
476
477 def test_is_started_no(self):
478 """Check is_started, False."""
479 self.sd._get_started = lambda: False
480 self.assertFalse(self.sd.current_state.is_started)
481
482 def test_start(self):
483 """Test start calls SD."""
484 self.mpatch_called(self.sd, '_do_start')
485 self.sd.start()
486 self.assertTrue(self.called)
487
488 def test_quit(self):
489 """Test quit calls SD."""
490 self.mpatch_called(self.sd, '_do_quit')
491 self.sd.quit()
492 self.assertTrue(self.called)
493
494 def test_connect(self):
495 """Test connect calls SD."""
496 self.mpatch_called(self.sd, '_do_connect')
497 self.sd.connect()
498 self.assertTrue(self.called)
499
500 def test_disconnect(self):
501 """Test disconnect calls SD."""
502 self.mpatch_called(self.sd, '_do_disconnect')
503 self.sd.disconnect()
504 self.assertTrue(self.called)

Subscribers

People subscribed via source and target branches