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

Proposed by Facundo Batista
Status: Merged
Merge reported by: Facundo Batista
Merged at revision: not available
Proposed branch: lp:~facundo/magicicada-gui/more-api
Merge into: lp:magicicada-gui
Diff against target: 417 lines (+236/-31)
2 files modified
magicicada/syncdaemon.py (+81/-21)
magicicada/tests/test_syncdaemon.py (+155/-10)
To merge this branch: bzr merge lp:~facundo/magicicada-gui/more-api
Reviewer Review Type Date Requested Status
Natalia Bidart Approve
Review via email: mp+25560@code.launchpad.net

Description of the change

More API implemented!

- on_started

- on_connected

- on_online

- content_queue

- meta_queue

To post a comment you must log in.
Revision history for this message
Facundo Batista (facundo) wrote :

The resto of the simple API from the bug

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

I personally prefer name.startswith('_') than if name[0] == "_", is there any reason to pick the second? (I know is not a change in this branch).

Other than that, great job!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'magicicada/syncdaemon.py'
2--- magicicada/syncdaemon.py 2010-05-18 22:00:50 +0000
3+++ magicicada/syncdaemon.py 2010-05-19 20:56:30 +0000
4@@ -29,11 +29,6 @@
5
6 from magicicada.helpers import NO_OP
7
8-# structures that hold content and queue information
9-QueueData = collections.namedtuple('QueueData', 'operation path share node')
10-State = collections.namedtuple('State', 'name description is_error '
11- 'is_connected is_online queues connection')
12-
13 # regular expressions for parsing MetaQueue data
14 RE_OP_LISTDIR = re.compile("(ListDir)\(share_id=(.*?), node_id=(.*?), .*")
15 RE_OP_UNLINK = re.compile("(Unlink)\(share_id=(.*?), node_id=(.*?), .*")
16@@ -50,19 +45,23 @@
17 _attrs = ['name', 'description', 'is_error', 'is_connected',
18 'is_online', 'queues', 'connection', 'is_started']
19
20- def __init__(self, syncdaemon):
21- self._sd = syncdaemon
22+ def __init__(self):
23+ # starting defaults
24+ self.name = ''
25+ self.description = ''
26+ self.is_error = False
27+ self.is_connected = False
28+ self.is_online = False
29+ self.queues = ''
30+ self.connection = ''
31+ self.is_started = False
32
33 def __getattribute__(self, name):
34 """Return the value if there."""
35 if name[0] == "_":
36 return object.__getattribute__(self, name)
37 else:
38- if name == 'is_started':
39- v = self._sd._get_started()
40- else:
41- v = self.__dict__.get(name)
42- return v
43+ return self.__dict__[name]
44
45 def _set(self, **data):
46 """Sets the attributes fromd data, if allowed."""
47@@ -80,16 +79,20 @@
48 loop = DBusGMainLoop(set_as_default=True)
49 self._bus = bus = dbus.SessionBus(mainloop=loop)
50 self.sync_daemon_tool = tools.SyncDaemonTool(bus)
51- self.current_state = State(self)
52+ self.current_state = State()
53
54 # hook up for signals and store info for the shutdown
55 _signals = [
56 (self._on_status_changed, 'Status', 'StatusChanged'),
57 (self._on_content_queue_changed, 'Status', 'ContentQueueChanged'),
58+ (self._on_name_owner_changed, None, 'NameOwnerChanged'),
59 ]
60 self._dbus_matches = []
61 for method, dbus_lastname, signal_name in _signals:
62- dbus_interface = 'com.ubuntuone.SyncDaemon.' + dbus_lastname
63+ if dbus_lastname is None:
64+ dbus_interface = None
65+ else:
66+ dbus_interface = 'com.ubuntuone.SyncDaemon.' + dbus_lastname
67 match = bus.add_signal_receiver(method,
68 dbus_interface=dbus_interface,
69 signal_name=signal_name)
70@@ -99,11 +102,16 @@
71 self.status_changed_callback = NO_OP
72 self.content_queue_changed_callback = NO_OP
73 self.meta_queue_changed_callback = NO_OP
74+ self.on_started_callback = NO_OP
75+ self.on_stopped_callback = NO_OP
76+ self.on_connected_callback = NO_OP
77+ self.on_disconnected_callback = NO_OP
78+ self.on_online_callback = NO_OP
79+ self.on_offline_callback = NO_OP
80
81 # calls to obtain data from SDT
82 self._get_content_queue = self.sync_daemon_tool.waiting_content
83 self._get_meta_queue = self.sync_daemon_tool.waiting_metadata
84- self._get_started = tools.is_running
85 self._do_start = self.sync_daemon_tool.start
86 self._do_quit = self.sync_daemon_tool.quit
87 self._do_connect = self.sync_daemon_tool.connect
88@@ -128,6 +136,20 @@
89 if self._mqcaller is not None and self._mqcaller.active():
90 self._mqcaller.cancel()
91
92+ def _on_name_owner_changed(self, name, oldowner, newowner):
93+ """Receives the NameOwnerChanged signal from DBus."""
94+ if name == 'com.ubuntuone.SyncDaemon':
95+ old = bool(oldowner)
96+ new = bool(newowner)
97+ if old == new:
98+ raise ValueError("Owners should have changed! Old: %r "
99+ "New: %r" % (oldowner, newowner))
100+ self.current_state._set(is_started=new)
101+ if new:
102+ self.on_started_callback()
103+ else:
104+ self.on_stopped_callback()
105+
106 def _on_status_changed(self, state):
107 """Receives the StatusChanged signal and send its data."""
108 name = state['name']
109@@ -137,12 +159,44 @@
110 is_online = bool(state['is_online'])
111 queues = state['queues']
112 connection = state['connection']
113+
114+ # check status changes to call callbacks
115+ if is_connected and not self.current_state.is_connected:
116+ self.on_connected_callback()
117+ if not is_connected and self.current_state.is_connected:
118+ self.on_disconnected_callback()
119+ if is_online and not self.current_state.is_online:
120+ self.on_online_callback()
121+ if not is_online and self.current_state.is_online:
122+ self.on_offline_callback()
123+
124+
125+ # set current state to new values and call status changed cb
126 self.current_state._set(**state)
127 self.status_changed_callback(name, description, is_error,
128 is_connected, is_online,
129 queues, connection)
130+
131+ # if corresponds, supervise MQ
132 self._check_mq()
133
134+ def _generate_cq_info(self, data):
135+ """Genereates an api friendly version of the data."""
136+ all_items = []
137+ for d in data:
138+ cq = QueueData(operation=d['operation'], path=d['path'],
139+ node=d['node'], share=d['share'])
140+ all_items.append(cq)
141+ return all_items
142+
143+ @property
144+ def content_queue(self):
145+ """Returns the last known CQ info."""
146+ if self._last_CQ_data is None:
147+ return []
148+ else:
149+ return self._generate_cq_info(self._last_CQ_data)
150+
151 def _process_cq(self, data):
152 """Processes ContentQueue data."""
153 # if same data than before, abort notification; else store it for later
154@@ -150,12 +204,7 @@
155 return
156 self._last_CQ_data = data
157
158- all_items = []
159- for d in data:
160- cq = QueueData(operation=d['operation'], path=d['path'],
161- node=d['node'], share=d['share'])
162- all_items.append(cq)
163-
164+ all_items = self._generate_cq_info(data)
165 self.content_queue_changed_callback(all_items)
166
167 def _on_content_queue_changed(self, _):
168@@ -164,6 +213,17 @@
169 d = self._get_content_queue()
170 d.addCallback(self._process_cq)
171
172+ @property
173+ def meta_queue(self):
174+ """Returns the last known MQ info."""
175+ if self._last_MQ_data is None:
176+ return []
177+ else:
178+ all_items = []
179+ for d in self._last_MQ_data:
180+ all_items.append(self._parse_mq(d))
181+ return all_items
182+
183 def _parse_mq(self, data):
184 """Parse MetaQueue string to extract its data."""
185 if data in ('AccountInquiry', 'FreeSpaceInquiry', 'GetPublicFiles',
186
187=== modified file 'magicicada/tests/test_syncdaemon.py'
188--- magicicada/tests/test_syncdaemon.py 2010-05-18 19:53:38 +0000
189+++ magicicada/tests/test_syncdaemon.py 2010-05-19 20:56:30 +0000
190@@ -173,6 +173,44 @@
191 self.assertEqual(received.share, original['share'])
192 self.assertEqual(received.node, original['node'])
193
194+ def test_CQ_state_none(self):
195+ """Check the ContentQueue info, being none."""
196+ self.assertEqual(len(self.sd.content_queue), 0)
197+
198+ def test_CQ_state_one(self):
199+ """Check the ContentQueue info, being one."""
200+ d = dict(operation='oper', path='path', share='share', node='node')
201+ self.sd._process_cq([d])
202+ self.assertEqual(len(self.sd.content_queue), 1)
203+
204+ # check the data
205+ cqit = self.sd.content_queue[0]
206+ self.assertEqual(cqit.operation, 'oper')
207+ self.assertEqual(cqit.path, 'path')
208+ self.assertEqual(cqit.share, 'share')
209+ self.assertEqual(cqit.node, 'node')
210+
211+ def test_CQ_state_several(self):
212+ """Check the ContentQueue info, several calls, last one is bigger."""
213+ c = dict(operation='oper1', path='path1', share='share1', node='node1')
214+ d = dict(operation='oper2', path='path2', share='share2', node='node2')
215+ self.sd._process_cq([c])
216+ self.sd._process_cq([d])
217+ self.sd._process_cq([c, d])
218+ self.assertEqual(len(self.sd.content_queue), 2)
219+
220+ # check the data
221+ cqit = self.sd.content_queue[0]
222+ self.assertEqual(cqit.operation, 'oper1')
223+ self.assertEqual(cqit.path, 'path1')
224+ self.assertEqual(cqit.share, 'share1')
225+ self.assertEqual(cqit.node, 'node1')
226+ cqit = self.sd.content_queue[1]
227+ self.assertEqual(cqit.operation, 'oper2')
228+ self.assertEqual(cqit.path, 'path2')
229+ self.assertEqual(cqit.share, 'share2')
230+ self.assertEqual(cqit.node, 'node2')
231+
232
233 class MetaQueueChangedTests(SignalsBaseTest):
234 """Check the MetaQueueChanged handling."""
235@@ -373,37 +411,74 @@
236 deferred = defer.Deferred()
237 return deferred
238
239+ def test_MQ_state_none(self):
240+ """Check the MetaQueue info, being none."""
241+ self.assertEqual(len(self.sd.meta_queue), 0)
242+
243+ def test_MQ_state_one(self):
244+ """Check the MetaQueue info, being one."""
245+ self.sd._process_mq(['ListShares'])
246+ self.assertEqual(len(self.sd.meta_queue), 1)
247+
248+ # check the data
249+ mqit = self.sd.meta_queue[0]
250+ self.assertEqual(mqit.operation, 'ListShares')
251+ self.assertEqual(mqit.path, None)
252+ self.assertEqual(mqit.share, None)
253+ self.assertEqual(mqit.node, None)
254+
255+ def test_MQ_state_several(self):
256+ """Check the MetaQueue info, several calls, last one is bigger."""
257+ cmd1 = 'MakeDir(share_id=a, parent_id=b, name=c, marker=d)'
258+ cmd2 = 'GetPublicFiles'
259+ self.sd._process_mq([cmd1])
260+ self.sd._process_mq([cmd2])
261+ self.sd._process_mq([cmd1, cmd2])
262+ self.assertEqual(len(self.sd.meta_queue), 2)
263+
264+ # check the data
265+ mqit = self.sd.meta_queue[0]
266+ self.assertEqual(mqit.operation, 'MakeDir')
267+ self.assertEqual(mqit.path, '/?.../c')
268+ self.assertEqual(mqit.share, 'a')
269+ self.assertEqual(mqit.node, None)
270+ mqit = self.sd.meta_queue[1]
271+ self.assertEqual(mqit.operation, 'GetPublicFiles')
272+ self.assertEqual(mqit.path, None)
273+ self.assertEqual(mqit.share, None)
274+ self.assertEqual(mqit.node, None)
275+
276
277 class StateTests(unittest.TestCase):
278 """Test State class."""
279
280 def test_initial(self):
281 """Initial state for vals."""
282- st = State(None)
283- self.assertEqual(st.name, None)
284+ st = State()
285+ self.assertEqual(st.name, '')
286
287 def test_set_one_value(self):
288 """Set one value."""
289- st = State(None)
290+ st = State()
291 st._set(name=55)
292
293 # check the one is set, the rest not
294 self.assertEqual(st.name, 55)
295- self.assertEqual(st.description, None)
296+ self.assertEqual(st.description, '')
297
298 def test_set_two_values(self):
299 """Set two values."""
300- st = State(None)
301+ st = State()
302 st._set(name=55, description=77)
303
304 # check those two are set, the rest not
305 self.assertEqual(st.name, 55)
306 self.assertEqual(st.description, 77)
307- self.assertEqual(st.is_error, None)
308+ self.assertFalse(st.is_error)
309
310 def test_bad_value(self):
311 """Set a value that should not."""
312- st = State(None)
313+ st = State()
314 self.assertRaises(AttributeError, st._set, not_really_allowed=44)
315
316
317@@ -429,10 +504,15 @@
318 f = lambda *a, **k: setattr(self, 'called', True)
319 setattr(obj, method_name, f)
320
321+ def flag_called(self, obj, method_name):
322+ """Replace callback to flag called."""
323+ f = lambda *a, **k: setattr(self, 'called', True)
324+ setattr(obj, method_name, f)
325+
326 def test_defaults_are_callable(self):
327 """Check the attributes are callable."""
328 meths = (self.sd._get_content_queue, self.sd._get_meta_queue,
329- self.sd._get_started, self.sd._do_start, self.sd._do_quit,
330+ self.sd._do_start, self.sd._do_quit,
331 self.sd._do_connect, self.sd._do_disconnect)
332 for meth in meths:
333 self.assertTrue(callable(meth), "Meth %r is not callable" % meth)
334@@ -471,12 +551,13 @@
335
336 def test_is_started_yes(self):
337 """Check is_started, True."""
338- self.sd._get_started = lambda: True
339+ # simulate the signal that indicates the name was registered
340+ self.sd._on_name_owner_changed('com.ubuntuone.SyncDaemon', '', 'yes')
341 self.assertTrue(self.sd.current_state.is_started)
342
343 def test_is_started_no(self):
344 """Check is_started, False."""
345- self.sd._get_started = lambda: False
346+ self.sd._on_name_owner_changed('com.ubuntuone.SyncDaemon', 'yes', '')
347 self.assertFalse(self.sd.current_state.is_started)
348
349 def test_start(self):
350@@ -502,3 +583,67 @@
351 self.mpatch_called(self.sd, '_do_disconnect')
352 self.sd.disconnect()
353 self.assertTrue(self.called)
354+
355+ def test_on_started(self):
356+ """Called when SD started."""
357+ self.flag_called(self.sd, 'on_started_callback')
358+
359+ # simulate the signal that indicates the name was registered
360+ self.sd._on_name_owner_changed('com.ubuntuone.SyncDaemon', '', 'yes')
361+ self.assertTrue(self.called)
362+
363+ def test_on_stopped(self):
364+ """Called when SD stopped."""
365+ self.flag_called(self.sd, 'on_stopped_callback')
366+
367+ # simulate the signal that indicates the name was registered
368+ self.sd._on_name_owner_changed('com.ubuntuone.SyncDaemon', 'yes', '')
369+ self.assertTrue(self.called)
370+
371+ def test_on_connected(self):
372+ """Called when SD connected."""
373+ self.flag_called(self.sd, 'on_connected_callback')
374+
375+ # first signal with connected in True
376+ d = dict(name='name', description='description', is_error='',
377+ is_connected='True', is_online='', queues='queues',
378+ connection='connection')
379+ self.sd._on_status_changed(d)
380+ self.assertTrue(self.called)
381+
382+ def test_on_disconnected(self):
383+ """Called when SD disconnected."""
384+ self.flag_called(self.sd, 'on_disconnected_callback')
385+
386+ # connect and disconnect
387+ d = dict(name='name', description='description', is_error='',
388+ is_connected='True', is_online='', queues='queues',
389+ connection='connection')
390+ self.sd._on_status_changed(d)
391+ d['is_connected'] = ''
392+ self.sd._on_status_changed(d)
393+ self.assertTrue(self.called)
394+
395+ def test_on_online(self):
396+ """Called when SD goes online."""
397+ self.flag_called(self.sd, 'on_online_callback')
398+
399+ # first signal with online in True
400+ d = dict(name='name', description='description', is_error='',
401+ is_connected='True', is_online='True', queues='queues',
402+ connection='connection')
403+ self.sd._on_status_changed(d)
404+ self.assertTrue(self.called)
405+
406+ def test_on_offline(self):
407+ """Called when SD goes offline."""
408+ self.flag_called(self.sd, 'on_offline_callback')
409+
410+ # go online and then offline
411+ d = dict(name='name', description='description', is_error='',
412+ is_connected='True', is_online='True', queues='queues',
413+ connection='connection')
414+ self.sd._on_status_changed(d)
415+ d['is_online'] = ''
416+ self.sd._on_status_changed(d)
417+ self.assertTrue(self.called)

Subscribers

People subscribed via source and target branches