Merge lp:~nataliabidart/ubuntuone-control-panel/add-file-sync-status into lp:ubuntuone-control-panel

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 38
Merged at revision: 31
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/add-file-sync-status
Merge into: lp:ubuntuone-control-panel
Diff against target: 1208 lines (+763/-65)
11 files modified
ubuntuone/controlpanel/backend.py (+85/-2)
ubuntuone/controlpanel/dbus_client.py (+35/-1)
ubuntuone/controlpanel/dbus_service.py (+68/-14)
ubuntuone/controlpanel/gtk/gui.py (+67/-3)
ubuntuone/controlpanel/gtk/tests/test_gui.py (+73/-1)
ubuntuone/controlpanel/integrationtests/__init__.py (+5/-0)
ubuntuone/controlpanel/integrationtests/test_dbus_client_sd.py (+124/-2)
ubuntuone/controlpanel/integrationtests/test_dbus_service.py (+87/-19)
ubuntuone/controlpanel/logger.py (+24/-1)
ubuntuone/controlpanel/tests/test_backend.py (+195/-2)
ubuntuone/controlpanel/utils.py (+0/-20)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/add-file-sync-status
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Approve
Roberto Alsina (community) Approve
Review via email: mp+43264@code.launchpad.net

Commit message

* File sync status is retrieved and displayed in the right top corner (LP: #673670).

 * Adding handling for file sync disabled (only backend for now).

 * Management panel is not twined itself if CredentialsFound signal is received
   several times (LP: #683649).

Description of the change

To test, do the following (you will need to point PYTHONPATH to current's u1client trunk):

in terminal 1:

PYTHONPATH=~/canonical/ubuntuone/client/trunk/:. ./bin/ubuntuone-control-panel-backend

in terminal 2:

PYTHONPATH=~/canonical/ubuntuone/client/trunk/:. ./bin/ubuntuone-control-panel-gtk

You can check in the top right corner how the file sync is updated.

To post a comment you must log in.
37. By Natalia Bidart

Files sync availability is queried to SyncdaemonTool.

Revision history for this message
Roberto Alsina (ralsina) wrote :

I have checked the code and I see nothing wrong with it. Keep in mind that I am not very familiar with the codebase yet, so the second reviewer should be extra careful ;-)

review: Approve
38. By Natalia Bidart

Fixing typo.

Revision history for this message
Alejandro J. Cura (alecu) wrote :

Very nice!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntuone/controlpanel/backend.py'
2--- ubuntuone/controlpanel/backend.py 2010-12-02 16:23:03 +0000
3+++ ubuntuone/controlpanel/backend.py 2010-12-10 15:05:12 +0000
4@@ -22,8 +22,12 @@
5 from twisted.internet.defer import inlineCallbacks, returnValue
6
7 from ubuntuone.controlpanel import dbus_client
8+from ubuntuone.controlpanel.logger import setup_logging, log_call
9 from ubuntuone.controlpanel.webclient import WebClient
10
11+
12+logger = setup_logging('backend')
13+
14 ACCOUNT_API = "account/"
15 QUOTA_API = "quota/"
16 DEVICES_API = "1.0/devices/"
17@@ -33,6 +37,17 @@
18 UPLOAD_KEY = "max_upload_speed"
19 DOWNLOAD_KEY = "max_download_speed"
20
21+FILE_SYNC_DISABLED = 'file-sync-disabled'
22+FILE_SYNC_DISCONNECTED = 'file-sync-disconnected'
23+FILE_SYNC_ERROR = 'file-sync-error'
24+FILE_SYNC_IDLE = 'file-sync-idle'
25+FILE_SYNC_STARTING = 'file-sync-starting'
26+FILE_SYNC_SYNCING = 'file-sync-syncing'
27+FILE_SYNC_UNKNOWN = 'file-sync-unknown'
28+
29+MSG_KEY = 'message'
30+STATUS_KEY = 'status'
31+
32
33 class ControlBackend(object):
34 """The control panel backend."""
35@@ -40,6 +55,68 @@
36 def __init__(self):
37 """Initialize the webclient."""
38 self.wc = WebClient(dbus_client.get_credentials)
39+ self._status_changed_handler = None
40+ self.status_changed_handler = lambda *a: None
41+
42+ def _process_file_sync_status(self, status):
43+ """Process raw file sync status into custom format.
44+
45+ Return a dictionary with two members:
46+ * STATUS_KEY: the current status of syncdaemon, can be one of:
47+ FILE_SYNC_DISABLED, FILE_SYNC_STARTING, FILE_SYNC_DISCONNECTED,
48+ FILE_SYNC_SYNCING, FILE_SYNC_IDLE, FILE_SYNC_ERROR,
49+ FILE_SYNC_UNKNOWN
50+ * MSG_KEY: a non translatable but human readable string of the status.
51+
52+ """
53+ if not status:
54+ return {MSG_KEY: '', STATUS_KEY: FILE_SYNC_DISABLED}
55+
56+ msg = '%s (%s)' % (status['description'], status['name'])
57+ result = {MSG_KEY: msg}
58+
59+ # file synch is enabled
60+ is_error = bool(status['is_error'])
61+ is_synching = bool(status['is_connected'])
62+ is_idle = bool(status['is_online']) and status['queues'] == 'IDLE'
63+ is_disconnected = status['name'] == 'READY' and \
64+ 'Not User' in status['connection']
65+ is_starting = status['name'] in ('INIT', 'LOCAL_RESCAN', 'READY')
66+
67+ if is_error:
68+ result[STATUS_KEY] = FILE_SYNC_ERROR
69+ elif is_idle:
70+ result[STATUS_KEY] = FILE_SYNC_IDLE
71+ elif is_synching:
72+ result[STATUS_KEY] = FILE_SYNC_SYNCING
73+ elif is_disconnected:
74+ result[STATUS_KEY] = FILE_SYNC_DISCONNECTED
75+ elif is_starting:
76+ result[STATUS_KEY] = FILE_SYNC_STARTING
77+ else:
78+ logger.warning('file_sync_status: unknown (got %r)', status)
79+ result[STATUS_KEY] = FILE_SYNC_UNKNOWN
80+
81+ return result
82+
83+ def _set_status_changed_handler(self, handler):
84+ """Set 'handler' to be called when file sync status changes."""
85+ logger.debug('setting status_changed_handler to %r', handler)
86+
87+ def process_and_callback(status):
88+ """Process syncdaemon's status and callback 'handler'."""
89+ result = self._process_file_sync_status(status)
90+ handler(result)
91+
92+ self._status_changed_handler = process_and_callback
93+ dbus_client.set_status_changed_handler(process_and_callback)
94+
95+ def _get_status_changed_handler(self, handler):
96+ """Return the handler to be called when file sync status changes."""
97+ return self._status_changed_handler
98+
99+ status_changed_handler = property(_get_status_changed_handler,
100+ _set_status_changed_handler)
101
102 @inlineCallbacks
103 def get_token(self):
104@@ -145,10 +222,16 @@
105 yield self.wc.call_api(api)
106 returnValue(device_id)
107
108+ @log_call(logger.info)
109+ @inlineCallbacks
110 def file_sync_status(self):
111 """Return the status of the file sync service."""
112- # still pending (LP: #673670)
113- returnValue((True, "Synchronizing"))
114+ enabled = yield dbus_client.files_sync_enabled()
115+ if enabled:
116+ status = yield dbus_client.get_current_status()
117+ else:
118+ status = {}
119+ returnValue(self._process_file_sync_status(status))
120
121 @inlineCallbacks
122 def volumes_info(self):
123
124=== modified file 'ubuntuone/controlpanel/dbus_client.py'
125--- ubuntuone/controlpanel/dbus_client.py 2010-12-02 16:35:31 +0000
126+++ ubuntuone/controlpanel/dbus_client.py 2010-12-10 15:05:12 +0000
127@@ -25,7 +25,8 @@
128 from twisted.internet import defer
129
130 from ubuntuone.clientdefs import APP_NAME
131-from ubuntuone.syncdaemon import dbus_interface as sd_dbus_iface
132+from ubuntuone.platform.linux import dbus_interface as sd_dbus_iface
133+from ubuntuone.platform.linux.tools import SyncDaemonTool
134
135 from ubuntuone.controlpanel.logger import setup_logging
136
137@@ -106,6 +107,12 @@
138 sd_dbus_iface.DBUS_IFACE_FOLDERS_NAME)
139
140
141+def get_status_syncdaemon_proxy():
142+ """Get a DBus proxy for syncdaemon status calls."""
143+ return get_syncdaemon_proxy("/status",
144+ sd_dbus_iface.DBUS_IFACE_STATUS_NAME)
145+
146+
147 def get_throttling_limits():
148 """Get the speed limits from the syncdaemon."""
149 d = defer.Deferred()
150@@ -183,3 +190,30 @@
151
152 d.addBoth(cleanup_signals)
153 return d
154+
155+
156+def get_current_status():
157+ """Retrieve the current status from syncdaemon."""
158+ d = defer.Deferred()
159+ proxy = get_status_syncdaemon_proxy()
160+ proxy.current_status(reply_handler=d.callback, error_handler=d.errback)
161+ return d
162+
163+
164+def set_status_changed_handler(handler):
165+ """Connect 'handler' with syncdaemon's StatusChanged signal."""
166+ proxy = get_status_syncdaemon_proxy()
167+ sig = proxy.connect_to_signal('StatusChanged', handler)
168+ return proxy, sig
169+
170+
171+def files_sync_enabled():
172+ """Get if file sync service is enabled."""
173+ enabled = SyncDaemonTool(bus=None).is_files_sync_enabled()
174+ return defer.succeed(enabled)
175+
176+
177+@defer.inlineCallbacks
178+def set_files_sync_enabled(enabled):
179+ """Set the file sync service to be 'enabled'."""
180+ yield SyncDaemonTool(bus=dbus.SessionBus()).enable_files_sync(enabled)
181
182=== modified file 'ubuntuone/controlpanel/dbus_service.py'
183--- ubuntuone/controlpanel/dbus_service.py 2010-12-02 16:35:31 +0000
184+++ ubuntuone/controlpanel/dbus_service.py 2010-12-10 15:05:12 +0000
185@@ -28,9 +28,15 @@
186 from twisted.python.failure import Failure
187
188 from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH,
189- DBUS_PREFERENCES_IFACE, utils)
190-from ubuntuone.controlpanel.backend import ControlBackend
191-from ubuntuone.controlpanel.logger import setup_logging
192+ DBUS_PREFERENCES_IFACE)
193+from ubuntuone.controlpanel.backend import (
194+ ControlBackend, FILE_SYNC_DISABLED, FILE_SYNC_DISCONNECTED,
195+ FILE_SYNC_ERROR, FILE_SYNC_IDLE, FILE_SYNC_STARTING, FILE_SYNC_SYNCING,
196+ MSG_KEY, STATUS_KEY,
197+)
198+from ubuntuone.controlpanel.logger import setup_logging, log_call
199+from ubuntuone.controlpanel.utils import (ERROR_TYPE, ERROR_MESSAGE,
200+ failure_to_error_dict, exception_to_error_dict)
201
202
203 logger = setup_logging('dbus_service')
204@@ -55,15 +61,15 @@
205 """
206 result = {}
207 if isinstance(error, Failure):
208- result = utils.failure_to_error_dict(error)
209+ result = failure_to_error_dict(error)
210 elif isinstance(error, Exception):
211- result = utils.exception_to_error_dict(error)
212+ result = exception_to_error_dict(error)
213 elif isinstance(error, dict):
214 # ensure that both keys and values are unicodes
215 result = dict(map(make_unicode, i) for i in error.iteritems())
216 else:
217 msg = 'Got unexpected error argument %r' % error
218- result = {utils.ERROR_TYPE: 'UnknownError', utils.ERROR_MESSAGE: msg}
219+ result = {ERROR_TYPE: 'UnknownError', ERROR_MESSAGE: msg}
220
221 return result
222
223@@ -89,7 +95,10 @@
224 """Create this instance of the backend."""
225 super(ControlPanelBackend, self).__init__(*args, **kwargs)
226 self.backend = backend
227- logger.debug('ControlPanelBackend created with %r, %r', args, kwargs)
228+ self.backend.status_changed_handler = self.process_status
229+ logger.debug('ControlPanelBackend: created with %r, %r.\n'
230+ 'status_changed_handler is %r.',
231+ args, kwargs, self.process_status)
232
233 # pylint: disable=C0103
234
235@@ -161,17 +170,62 @@
236
237 #---
238
239+ def process_status(self, status_dict):
240+ """Match status with signals."""
241+ logger.info('process_status: new status received %r', status_dict)
242+ status = status_dict[STATUS_KEY]
243+ msg = status_dict[MSG_KEY]
244+ if status == FILE_SYNC_DISABLED:
245+ self.FileSyncStatusDisabled(msg)
246+ elif status == FILE_SYNC_STARTING:
247+ self.FileSyncStatusStarting(msg)
248+ elif status == FILE_SYNC_DISCONNECTED:
249+ self.FileSyncStatusDisconnected(msg)
250+ elif status == FILE_SYNC_SYNCING:
251+ self.FileSyncStatusSyncing(msg)
252+ elif status == FILE_SYNC_IDLE:
253+ self.FileSyncStatusIdle(msg)
254+ elif status == FILE_SYNC_ERROR:
255+ error_dict = {ERROR_TYPE: 'FileSyncStatusError',
256+ ERROR_MESSAGE: msg}
257+ self.FileSyncStatusError(error_dict)
258+ else:
259+ self.FileSyncStatusError(error_handler(status_dict))
260+
261+ @log_call(logger.info)
262 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
263 def file_sync_status(self):
264 """Get the status of the file sync service."""
265 d = self.backend.file_sync_status()
266- d.addCallback(lambda args: self.FileSyncStatusReady(*args))
267+ d.addCallback(self.process_status)
268 d.addErrback(transform_failure(self.FileSyncStatusError))
269
270- @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="bs")
271- def FileSyncStatusReady(self, enabled, status):
272- """The new file sync status is available."""
273-
274+ @log_call(logger.debug)
275+ @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
276+ def FileSyncStatusDisabled(self, msg):
277+ """The file sync status is disabled."""
278+
279+ @log_call(logger.debug)
280+ @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
281+ def FileSyncStatusStarting(self, msg):
282+ """The file sync service is starting."""
283+
284+ @log_call(logger.debug)
285+ @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
286+ def FileSyncStatusDisconnected(self, msg):
287+ """The file sync service is waiting for user to request connection."""
288+
289+ @log_call(logger.debug)
290+ @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
291+ def FileSyncStatusSyncing(self, msg):
292+ """The file sync service is currently syncing."""
293+
294+ @log_call(logger.debug)
295+ @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
296+ def FileSyncStatusIdle(self, msg):
297+ """The file sync service is idle."""
298+
299+ @log_call(logger.error)
300 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
301 def FileSyncStatusError(self, error):
302 """Problem getting the file sync status."""
303@@ -185,12 +239,12 @@
304 d.addCallback(self.VolumesInfoReady)
305 d.addErrback(transform_failure(self.VolumesInfoError))
306
307- @utils.log_call(logger.info)
308+ @log_call(logger.debug)
309 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}")
310 def VolumesInfoReady(self, info):
311 """The info for the volumes is available right now."""
312
313- @utils.log_call(logger.error)
314+ @log_call(logger.error)
315 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
316 def VolumesInfoError(self, error):
317 """The info for the volumes is currently unavailable."""
318
319=== modified file 'ubuntuone/controlpanel/gtk/gui.py'
320--- ubuntuone/controlpanel/gtk/gui.py 2010-12-01 12:51:55 +0000
321+++ ubuntuone/controlpanel/gtk/gui.py 2010-12-10 15:05:12 +0000
322@@ -46,8 +46,8 @@
323
324 from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH,
325 DBUS_PREFERENCES_IFACE)
326-from ubuntuone.controlpanel.logger import setup_logging
327-from ubuntuone.controlpanel.utils import get_data_file, log_call
328+from ubuntuone.controlpanel.logger import setup_logging, log_call
329+from ubuntuone.controlpanel.utils import get_data_file
330
331
332 logger = setup_logging('gtk.gui')
333@@ -188,7 +188,9 @@
334
335 def on_show_management_panel(self, *args, **kwargs):
336 """Show the netbook (main panel)."""
337- self.add(ManagementPanel())
338+ if self.overview in self.get_children():
339+ self.remove(self.overview)
340+ self.add(ManagementPanel())
341
342
343 class UbuntuOneBin(gtk.VBox):
344@@ -486,6 +488,16 @@
345 """
346
347 QUOTA_LABEL = _('%(used)s used of %(total)s (%(percentage).1f%%)')
348+ FILE_SYNC_DISABLED = _('File synchronization service is not enabled.')
349+ FILE_SYNC_STARTING = _('File synchronization service is starting,\n'
350+ 'please wait...')
351+ FILE_SYNC_DISCONNECTED = _('File synchronization service is ready,\nplease'
352+ ' connect it to access your personal cloud. ')
353+ FILE_SYNC_SYNCING = _('File synchronization service is fully functional,\n'
354+ 'performing synchronization now...')
355+ FILE_SYNC_IDLE = _('File synchronization service is idle,\n'
356+ 'all the files are synchronized.')
357+ FILE_SYNC_ERROR = _('File synchronization status can not be retrieved.')
358
359 def __init__(self):
360 gtk.VBox.__init__(self)
361@@ -498,6 +510,19 @@
362 self.backend.connect_to_signal('AccountInfoError',
363 self.on_account_info_error)
364
365+ self.backend.connect_to_signal('FileSyncStatusDisabled',
366+ self.on_file_sync_status_disabled)
367+ self.backend.connect_to_signal('FileSyncStatusStarting',
368+ self.on_file_sync_status_starting)
369+ self.backend.connect_to_signal('FileSyncStatusDisconnected',
370+ self.on_file_sync_status_disconnected)
371+ self.backend.connect_to_signal('FileSyncStatusSyncing',
372+ self.on_file_sync_status_syncing)
373+ self.backend.connect_to_signal('FileSyncStatusIdle',
374+ self.on_file_sync_status_idle)
375+ self.backend.connect_to_signal('FileSyncStatusError',
376+ self.on_file_sync_status_error)
377+
378 self.header.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(DEFAULT_BG))
379 self.quota_label = LabelLoading(LOADING, fg_color=DEFAULT_FG)
380 self.status_label = LabelLoading(LOADING, fg_color=DEFAULT_FG)
381@@ -521,6 +546,7 @@
382 self.notebook.insert_page(getattr(self, tab), position=page_num)
383
384 self.backend.account_info()
385+ self.backend.file_sync_status()
386
387 def _update_quota(self, msg, data=None):
388 """Update the quota info."""
389@@ -532,6 +558,11 @@
390 fraction = data.get('percentage', 0.0) / 100
391 self.quota_progressbar.set_fraction(fraction)
392
393+ def _update_status(self, msg):
394+ """Update the status info."""
395+ self.status_label.set_markup(msg)
396+ self.status_label.stop()
397+
398 @log_call(logger.debug)
399 def on_account_info_ready(self, info):
400 """Backend notifies of account info."""
401@@ -546,6 +577,39 @@
402 """Backend notifies of an error when fetching account info."""
403 self._update_quota(WARNING_MARKUP % self.VALUE_ERROR)
404
405+ @log_call(logger.info)
406+ def on_file_sync_status_disabled(self, msg):
407+ """Backend notifies of file sync status being disabled."""
408+ self._update_status(self.FILE_SYNC_DISABLED)
409+
410+ @log_call(logger.info)
411+ def on_file_sync_status_starting(self, msg):
412+ """Backend notifies of file sync status being starting."""
413+ self._update_status(self.FILE_SYNC_STARTING)
414+
415+ @log_call(logger.info)
416+ def on_file_sync_status_disconnected(self, msg):
417+ """Backend notifies of file sync status being ready."""
418+ self._update_status(self.FILE_SYNC_DISCONNECTED)
419+
420+ @log_call(logger.info)
421+ def on_file_sync_status_syncing(self, msg):
422+ """Backend notifies of file sync status being syncing."""
423+ self._update_status(self.FILE_SYNC_SYNCING)
424+
425+ @log_call(logger.info)
426+ def on_file_sync_status_idle(self, msg):
427+ """Backend notifies of file sync status being idle."""
428+ self._update_status(self.FILE_SYNC_IDLE)
429+
430+ @log_call(logger.error)
431+ def on_file_sync_status_error(self, error_dict=None):
432+ """Backend notifies of an error when fetching file sync status."""
433+ msg = error_dict.get('error_msg', None)
434+ if msg is None:
435+ msg = error_dict.get('message', self.VALUE_ERROR)
436+ self._update_status(WARNING_MARKUP % msg)
437+
438
439 gobject.signal_new('credentials-found', OverviewPanel,
440 gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
441
442=== modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py'
443--- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-02 16:23:03 +0000
444+++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-10 15:05:12 +0000
445@@ -302,6 +302,13 @@
446 children = self.ui.get_children()
447 self.assertIsInstance(children[-1], gui.ManagementPanel)
448
449+ def test_on_show_management_panel_is_idempotent(self):
450+ """Only one ManagementPanel is shown."""
451+ self.ui.on_show_management_panel()
452+ self.ui.on_show_management_panel()
453+
454+ self.assertEqual(1, len(self.ui.get_children()))
455+
456
457 class UbuntuOneBinTestCase(BaseTestCase):
458 """The test suite for a Ubuntu One panel."""
459@@ -842,7 +849,7 @@
460 class ManagementPanelAccountTestCase(ManagementPanelTestCase):
461 """The test suite for the management panel (account tab)."""
462
463- def test_backend_signals(self):
464+ def test_backend_account_signals(self):
465 """The proper signals are connected to the backend."""
466 self.assertEqual(self.ui.backend._signals['AccountInfoReady'],
467 [self.ui.on_account_info_ready])
468@@ -898,3 +905,68 @@
469 for widget in (self.ui.quota_label,):
470 self.assert_warning_correct(widget, self.ui.VALUE_ERROR)
471 self.assertFalse(widget.active)
472+
473+ def test_backend_file_sync_signals(self):
474+ """The proper signals are connected to the backend."""
475+ matches = (
476+ ('FileSyncStatusDisabled', [self.ui.on_file_sync_status_disabled]),
477+ ('FileSyncStatusStarting', [self.ui.on_file_sync_status_starting]),
478+ ('FileSyncStatusDisconnected',
479+ [self.ui.on_file_sync_status_disconnected]),
480+ ('FileSyncStatusSyncing', [self.ui.on_file_sync_status_syncing]),
481+ ('FileSyncStatusIdle', [self.ui.on_file_sync_status_idle]),
482+ ('FileSyncStatusError', [self.ui.on_file_sync_status_error]),
483+ )
484+ for sig, handlers in matches:
485+ self.assertEqual(self.ui.backend._signals[sig], handlers)
486+
487+ def test_file_sync_status_is_requested(self):
488+ """The file sync status is requested to the backend."""
489+ self.assert_backend_called('file_sync_status', ())
490+
491+ def test_on_file_sync_status_disabled(self):
492+ """The file sync is disabled."""
493+ self.ui.on_file_sync_status_disabled('msg')
494+
495+ self.assertFalse(self.ui.status_label.active)
496+ self.assertEqual(self.ui.status_label.get_text(),
497+ self.ui.FILE_SYNC_DISABLED)
498+
499+ def test_on_file_sync_status_starting(self):
500+ """The file sync status is starting."""
501+ self.ui.on_file_sync_status_starting('msg')
502+
503+ self.assertFalse(self.ui.status_label.active)
504+ self.assertEqual(self.ui.status_label.get_text(),
505+ self.ui.FILE_SYNC_STARTING)
506+
507+ def test_on_file_sync_status_disconnected(self):
508+ """The file sync status is disconnected."""
509+ self.ui.on_file_sync_status_disconnected('msg')
510+
511+ self.assertFalse(self.ui.status_label.active)
512+ self.assertEqual(self.ui.status_label.get_text(),
513+ self.ui.FILE_SYNC_DISCONNECTED)
514+
515+ def test_on_file_sync_status_syncing(self):
516+ """The file sync status is syncing."""
517+ self.ui.on_file_sync_status_syncing('msg')
518+
519+ self.assertFalse(self.ui.status_label.active)
520+ self.assertEqual(self.ui.status_label.get_text(),
521+ self.ui.FILE_SYNC_SYNCING)
522+
523+ def test_on_file_sync_status_idle(self):
524+ """The file sync status is idle."""
525+ self.ui.on_file_sync_status_idle('msg')
526+
527+ self.assertFalse(self.ui.status_label.active)
528+ self.assertEqual(self.ui.status_label.get_text(),
529+ self.ui.FILE_SYNC_IDLE)
530+
531+ def test_on_file_sync_status_error(self):
532+ """The file sync status couldn't be retrieved."""
533+ self.ui.on_file_sync_status_error({'error_msg': 'error msg'})
534+
535+ self.assert_warning_correct(self.ui.status_label, 'error msg')
536+ self.assertFalse(self.ui.status_label.active)
537
538=== modified file 'ubuntuone/controlpanel/integrationtests/__init__.py'
539--- ubuntuone/controlpanel/integrationtests/__init__.py 2010-12-02 16:23:03 +0000
540+++ ubuntuone/controlpanel/integrationtests/__init__.py 2010-12-10 15:05:12 +0000
541@@ -43,8 +43,13 @@
542 def setUp(self):
543 super(DBusClientTestCase, self).setUp()
544 self.mock = None
545+ self._called = False
546 dbus_service.init_mainloop()
547
548+ def _set_called(self, *args, **kwargs):
549+ """Keep track of function calls, useful for monkeypatching."""
550+ self._called = (args, kwargs)
551+
552 def register_mockserver(self, bus_name, object_path, object_class,
553 **kwargs):
554 """The mock service is registered on the DBus."""
555
556=== modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_client_sd.py'
557--- ubuntuone/controlpanel/integrationtests/test_dbus_client_sd.py 2010-12-06 15:03:58 +0000
558+++ ubuntuone/controlpanel/integrationtests/test_dbus_client_sd.py 2010-12-10 15:05:12 +0000
559@@ -204,13 +204,13 @@
560
561
562 class FoldersMockDBusSyncDaemon(sd_dbus_iface.Folders):
563- """A mock object that mimicks syncdaemon regarding the folders iface."""
564+ """A mock object that mimicks syncdaemon regarding the Folders iface."""
565
566 # __init__ method from a non direct base class 'Object' is called
567 # __init__ method from base class 'Folders' is not called
568 # pylint: disable=W0231, W0233
569
570- def __init__(self, object_path, conn, vm=None):
571+ def __init__(self, object_path, conn):
572 self.udfs = {}
573 self.udf_id = 1
574 dbus.service.Object.__init__(self,
575@@ -327,6 +327,25 @@
576 self.assertEqual(result, [sd_dbus_iface._get_udf_dict(expected)])
577
578 @inlineCallbacks
579+ def test_get_folders_error(self):
580+ """Handle error when retrieving current syncdaemon status."""
581+ path = '~/bar/baz'
582+ yield dbus_client.create_volume(path)
583+
584+ def fail(value):
585+ """Fake an error."""
586+ raise TestDBusException(value)
587+
588+ self.patch(sd_dbus_iface, '_get_udf_dict', fail)
589+
590+ try:
591+ yield dbus_client.get_volumes()
592+ except dbus.DBusException:
593+ pass # test passes!
594+ else:
595+ self.fail('dbus_client.get_folders should be errbacking')
596+
597+ @inlineCallbacks
598 def test_create_volume(self):
599 """Create a new volume."""
600 path = '~/bar/baz'
601@@ -342,3 +361,106 @@
602 yield dbus_client.create_volume(path='')
603 except dbus_client.VolumesError, e:
604 self.assertEqual(e[0], {'path': ''})
605+
606+
607+class StatusMockDBusSyncDaemon(dbus.service.Object):
608+ """A mock object that mimicks syncdaemon regarding the Status iface."""
609+
610+ state_dict = {
611+ 'name': 'TEST',
612+ 'description': 'Some test state, nothing else.',
613+ 'is_error': '',
614+ 'is_connected': 'True',
615+ 'is_online': '',
616+ 'queues': 'GORGEOUS',
617+ 'connection': '',
618+ }
619+
620+ def _get_current_state(self):
621+ """Get the current status of the system."""
622+ return self.state_dict
623+
624+ @dbus.service.method(sd_dbus_iface.DBUS_IFACE_STATUS_NAME,
625+ in_signature='', out_signature='a{ss}')
626+ def current_status(self):
627+ """Return the current faked status of the system."""
628+ return self._get_current_state()
629+
630+ # pylint: disable=C0103
631+ # Invalid name "StatusChanged"
632+
633+ @dbus.service.signal(sd_dbus_iface.DBUS_IFACE_STATUS_NAME)
634+ def StatusChanged(self, status):
635+ """Fire a signal to notify that the status of the system changed."""
636+
637+ def emit_status_changed(self, state=None):
638+ """Emit StatusChanged."""
639+ self.StatusChanged(self._get_current_state())
640+
641+
642+class StatusTestCase(DBusClientTestCase):
643+ """Test for the volumes dbus client methods."""
644+
645+ def setUp(self):
646+ super(StatusTestCase, self).setUp()
647+ self.register_mockserver(sd_dbus_iface.DBUS_IFACE_NAME,
648+ "/status", StatusMockDBusSyncDaemon)
649+
650+ @inlineCallbacks
651+ def test_get_current_status(self):
652+ """Retrieve current syncdaemon status."""
653+ status = yield dbus_client.get_current_status()
654+
655+ self.assertEqual(StatusMockDBusSyncDaemon.state_dict, status)
656+
657+ @inlineCallbacks
658+ def test_get_current_status_error(self):
659+ """Handle error when retrieving current syncdaemon status."""
660+
661+ def fail(value):
662+ """Fake an error."""
663+ raise TestDBusException(value)
664+
665+ self.patch(StatusMockDBusSyncDaemon, '_get_current_state', fail)
666+
667+ try:
668+ yield dbus_client.get_current_status()
669+ except dbus.DBusException:
670+ pass # test passes!
671+ else:
672+ self.fail('dbus_client.get_current_status should be errbacking')
673+
674+ def test_status_changed(self):
675+ """A proper callback can be connected to StatusChanged signal."""
676+ _, sig = dbus_client.set_status_changed_handler(self._set_called)
677+
678+ self.assertEqual(sig._handler, self._set_called)
679+ self.assertEqual(sig._member, 'StatusChanged')
680+ self.assertEqual(sig._path, '/status')
681+ self.assertEqual(sig._interface, sd_dbus_iface.DBUS_IFACE_STATUS_NAME)
682+
683+
684+class FileSyncTestCase(DBusClientTestCase):
685+ """Test for the files sync enabled dbus client methods."""
686+
687+ @inlineCallbacks
688+ def test_files_sync_enabled(self):
689+ """Retrieve whether file sync is enabled."""
690+ expected = object()
691+ self.patch(dbus_client.SyncDaemonTool, 'is_files_sync_enabled',
692+ lambda _: expected)
693+
694+ enabled = yield dbus_client.files_sync_enabled()
695+
696+ self.assertEqual(expected, enabled)
697+
698+ @inlineCallbacks
699+ def test_set_files_sync_enabled(self):
700+ """Set if file sync is enabled or not."""
701+ self.patch(dbus_client.SyncDaemonTool, 'enable_files_sync',
702+ self._set_called)
703+ expected = object()
704+ # set the opposite value
705+ yield dbus_client.set_files_sync_enabled(expected)
706+
707+ self.assertEqual(self._called, ((expected,), {}))
708
709=== modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_service.py'
710--- ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2010-12-02 16:35:31 +0000
711+++ ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2010-12-10 15:05:12 +0000
712@@ -115,7 +115,13 @@
713
714 class MockBackend(object):
715 """A mock backend."""
716+
717 exception = None
718+ sample_status = {
719+ dbus_service.MSG_KEY: 'test me please',
720+ dbus_service.STATUS_KEY: dbus_service.FILE_SYNC_IDLE,
721+ }
722+ status_changed_handler = None
723
724 def _process(self, result):
725 """Process the request with the given result."""
726@@ -142,7 +148,7 @@
727
728 def file_sync_status(self):
729 """Return the status of the file sync service."""
730- return self._process((True, "Synchronizing"))
731+ return self._process(self.sample_status)
732
733 def volumes_info(self):
734 """Get the user volumes info."""
735@@ -170,6 +176,11 @@
736 """Initialize each test run."""
737 super(DBusServiceTestCase, self).setUp()
738 dbus_service.init_mainloop()
739+ self._called = False
740+
741+ def _set_called(self, *args, **kwargs):
742+ """Keep track of function calls, useful for monkeypatching."""
743+ self._called = (args, kwargs)
744
745 def test_register_service(self):
746 """The DBus service is successfully registered."""
747@@ -193,7 +204,7 @@
748 def test_error_handler_with_failure(self):
749 """Ensure to build a string-string dict to pass to error signals."""
750 error = dbus_service.Failure(TypeError('oh no!'))
751- expected = dbus_service.utils.failure_to_error_dict(error)
752+ expected = dbus_service.failure_to_error_dict(error)
753
754 result = dbus_service.error_handler(error)
755
756@@ -202,7 +213,7 @@
757 def test_error_handler_with_exception(self):
758 """Ensure to build a string-string dict to pass to error signals."""
759 error = TypeError('oh no, no again!')
760- expected = dbus_service.utils.exception_to_error_dict(error)
761+ expected = dbus_service.exception_to_error_dict(error)
762
763 result = dbus_service.error_handler(error)
764
765@@ -231,8 +242,8 @@
766 def test_error_handler_default(self):
767 """Ensure to build a string-string dict to pass to error signals."""
768 msg = 'Got unexpected error argument %r' % None
769- expected = {dbus_service.utils.ERROR_TYPE: 'UnknownError',
770- dbus_service.utils.ERROR_MESSAGE: msg}
771+ expected = {dbus_service.ERROR_TYPE: 'UnknownError',
772+ dbus_service.ERROR_MESSAGE: msg}
773
774 result = dbus_service.error_handler(None)
775
776@@ -372,19 +383,6 @@
777 self.backend.remove_device, sample_token)
778 return self.assert_correct_method_call(*args)
779
780- def test_file_sync_status(self):
781- """The file sync status is reported."""
782-
783- def got_signal(enabled, status):
784- """The correct status was received."""
785- self.assertEqual(enabled, True)
786- self.assertEqual(status, "Synchronizing")
787- self.deferred.callback("success")
788-
789- args = ("FileSyncStatusReady", "FileSyncStatusError", got_signal,
790- self.backend.file_sync_status)
791- return self.assert_correct_method_call(*args)
792-
793 def test_volumes_info(self):
794 """The volumes info is reported."""
795
796@@ -453,9 +451,79 @@
797
798 def got_error_signal(error_dict):
799 """The error signal was received."""
800- self.assertEqual(error_dict[dbus_service.utils.ERROR_TYPE],
801+ self.assertEqual(error_dict[dbus_service.ERROR_TYPE],
802 'AssertionError')
803 self.deferred.callback("success")
804
805 return super(OperationsErrorTestCase, self).assert_correct_method_call(
806 error_sig, success_sig, got_error_signal, method, *args)
807+
808+
809+class FileSyncTestCase(OperationsTestCase):
810+ """Test for the DBus service when requesting file sync status."""
811+
812+ def assert_correct_status_signal(self, status, sync_signal,
813+ error_signal="FileSyncStatusError",
814+ expected_msg=None):
815+ """The file sync status is reported properly."""
816+ MockBackend.sample_status[dbus_service.STATUS_KEY] = status
817+ if expected_msg is None:
818+ expected_msg = MockBackend.sample_status[dbus_service.MSG_KEY]
819+
820+ def got_signal(msg):
821+ """The correct status was received."""
822+ self.assertEqual(msg, expected_msg)
823+ self.deferred.callback("success")
824+
825+ args = (sync_signal, error_signal, got_signal,
826+ self.backend.file_sync_status)
827+ return self.assert_correct_method_call(*args)
828+
829+ def test_file_sync_status_unknown(self):
830+ """The file sync status is reported properly."""
831+ msg = MockBackend.sample_status
832+ args = ('invalid-file-sync-status', "FileSyncStatusError",
833+ "FileSyncStatusIdle", msg)
834+ return self.assert_correct_status_signal(*args)
835+
836+ def test_file_sync_status_error(self):
837+ """The file sync status is reported properly."""
838+ msg = MockBackend.sample_status[dbus_service.MSG_KEY]
839+ err_dict = {dbus_service.ERROR_TYPE: 'FileSyncStatusError',
840+ dbus_service.ERROR_MESSAGE: msg}
841+ args = (dbus_service.FILE_SYNC_ERROR, "FileSyncStatusError",
842+ "FileSyncStatusIdle", err_dict)
843+ return self.assert_correct_status_signal(*args)
844+
845+ def test_file_sync_status_disabled(self):
846+ """The file sync status is reported properly."""
847+ args = (dbus_service.FILE_SYNC_DISABLED, "FileSyncStatusDisabled")
848+ return self.assert_correct_status_signal(*args)
849+
850+ def test_file_sync_status_starting(self):
851+ """The file sync status is reported properly."""
852+ args = (dbus_service.FILE_SYNC_STARTING, "FileSyncStatusStarting")
853+ return self.assert_correct_status_signal(*args)
854+
855+ def test_file_sync_status_disconnected(self):
856+ """The file sync status is reported properly."""
857+ args = (dbus_service.FILE_SYNC_DISCONNECTED,
858+ "FileSyncStatusDisconnected")
859+ return self.assert_correct_status_signal(*args)
860+
861+ def test_file_sync_status_syncing(self):
862+ """The file sync status is reported properly."""
863+ args = (dbus_service.FILE_SYNC_SYNCING, "FileSyncStatusSyncing")
864+ return self.assert_correct_status_signal(*args)
865+
866+ def test_file_sync_status_idle(self):
867+ """The file sync status is reported properly."""
868+ args = (dbus_service.FILE_SYNC_IDLE, "FileSyncStatusIdle")
869+ return self.assert_correct_status_signal(*args)
870+
871+ def test_status_changed_handler(self):
872+ """The status changed handler is properly set."""
873+ be = MockBackend()
874+ cpbe = dbus_service.ControlPanelBackend(backend=be)
875+
876+ self.assertEqual(be.status_changed_handler, cpbe.process_status)
877
878=== modified file 'ubuntuone/controlpanel/logger.py'
879--- ubuntuone/controlpanel/logger.py 2010-12-02 16:35:31 +0000
880+++ ubuntuone/controlpanel/logger.py 2010-12-10 15:05:12 +0000
881@@ -22,6 +22,7 @@
882 import os
883 import sys
884
885+from functools import wraps
886 from logging.handlers import RotatingFileHandler
887
888 # pylint: disable=F0401,E0611
889@@ -41,7 +42,7 @@
890 MAIN_HANDLER.setLevel(LOG_LEVEL)
891
892
893-def setup_logging(log_domain):
894+def setup_logging(log_domain, prefix=None):
895 """Create a logger for 'log_domain'.
896
897 Final domain will be 'ubuntuone.controlpanel.<log_domain>.
898@@ -52,6 +53,28 @@
899 logger.addHandler(MAIN_HANDLER)
900 if os.environ.get('DEBUG'):
901 debug_handler = logging.StreamHandler(sys.stderr)
902+ if prefix is not None:
903+ fmt = prefix + "%(name)s - %(levelname)s\n%(message)s\n"
904+ formatter = logging.Formatter(fmt)
905+ debug_handler.setFormatter(formatter)
906 logger.addHandler(debug_handler)
907
908 return logger
909+
910+
911+def log_call(log_func):
912+ """Decorator to add log info using 'log_func'."""
913+
914+ def middle(f):
915+ """Add logging when calling 'f'."""
916+
917+ @wraps(f)
918+ def inner(*args, **kwargs):
919+ """Call f(*args, **kwargs)."""
920+ log_func('%s: args %r, kwargs %r.', f.__name__, args, kwargs)
921+ res = f(*args, **kwargs)
922+ return res
923+
924+ return inner
925+
926+ return middle
927
928=== modified file 'ubuntuone/controlpanel/tests/test_backend.py'
929--- ubuntuone/controlpanel/tests/test_backend.py 2010-12-02 16:23:03 +0000
930+++ ubuntuone/controlpanel/tests/test_backend.py 2010-12-10 15:05:12 +0000
931@@ -23,10 +23,21 @@
932
933 from twisted.internet import defer
934 from twisted.internet.defer import inlineCallbacks
935+from ubuntuone.devtools.handlers import MementoHandler
936
937 from ubuntuone.controlpanel import backend
938-from ubuntuone.controlpanel.backend import (ACCOUNT_API, QUOTA_API,
939- DEVICES_API, DEVICE_REMOVE_API)
940+from ubuntuone.controlpanel.backend import (ACCOUNT_API,
941+ DEVICES_API, DEVICE_REMOVE_API, QUOTA_API,
942+ FILE_SYNC_DISABLED,
943+ FILE_SYNC_DISCONNECTED,
944+ FILE_SYNC_ERROR,
945+ FILE_SYNC_IDLE,
946+ FILE_SYNC_STARTING,
947+ FILE_SYNC_SYNCING,
948+ FILE_SYNC_UNKNOWN,
949+ MSG_KEY, STATUS_KEY,
950+)
951+
952 from ubuntuone.controlpanel.tests import TestCase
953 from ubuntuone.controlpanel.webclient import WebClientError
954
955@@ -152,6 +163,13 @@
956 creds = SAMPLE_CREDENTIALS
957 throttling = False
958 limits = {"download": -1, "upload": -1}
959+ file_sync = True
960+ status = {
961+ 'name': 'TEST', 'queues': 'GORGEOUS', 'connection': '',
962+ 'description': 'Some test state, nothing else.',
963+ 'is_error': '', 'is_connected': 'True', 'is_online': '',
964+ }
965+ status_changed_handler = None
966
967 def get_credentials(self):
968 """Return the mock credentials."""
969@@ -178,10 +196,26 @@
970 """Disable bw throttling."""
971 self.throttling = False
972
973+ def files_sync_enabled(self):
974+ """Get if file sync service is enabled."""
975+ return self.file_sync
976+
977+ def set_files_sync_enabled(self, enabled):
978+ """Set the file sync service to be 'enabled'."""
979+ self.file_sync = enabled
980+
981 def get_volumes(self):
982 """Grab list of folders and shares."""
983 return SAMPLE_VOLUMES
984
985+ def get_current_status(self):
986+ """Grab syncdaemon status."""
987+ return MockDBusClient.status
988+
989+ def set_status_changed_handler(self, handler):
990+ """Connect a handler for tracking syncdaemon status changes."""
991+ self.status_changed_handler = handler
992+
993
994 class BackendBasicTestCase(TestCase):
995 """Simple tests for the backend."""
996@@ -189,11 +223,15 @@
997 timeout = 3
998
999 def setUp(self):
1000+ super(BackendBasicTestCase, self).setUp()
1001 self.patch(backend, "WebClient", MockWebClient)
1002 self.patch(backend, "dbus_client", MockDBusClient())
1003 self.local_token = "Computer" + SAMPLE_CREDENTIALS["token"]
1004 self.be = backend.ControlBackend()
1005
1006+ self.memento = MementoHandler()
1007+ backend.logger.addHandler(self.memento)
1008+
1009 def test_backend_creation(self):
1010 """The backend instance is successfully created."""
1011 self.assertEqual(self.be.wc.__class__, MockWebClient)
1012@@ -314,3 +352,158 @@
1013 """The volumes_info method exercises its callback."""
1014 result = yield self.be.volumes_info()
1015 self.assertEqual(result, SAMPLE_VOLUMES)
1016+
1017+
1018+class BackendSyncStatusTestCase(BackendBasicTestCase):
1019+ """Syncdaemon state for the backend."""
1020+
1021+ def _build_msg(self):
1022+ """Build expected message regarding file sync status."""
1023+ return '%s (%s)' % (MockDBusClient.status['description'],
1024+ MockDBusClient.status['name'])
1025+
1026+ @inlineCallbacks
1027+ def assert_correct_status(self, status, msg=None):
1028+ """Check that the resulting status is correct."""
1029+ expected = {MSG_KEY: self._build_msg() if msg is None else msg,
1030+ STATUS_KEY: status}
1031+ result = yield self.be.file_sync_status()
1032+ self.assertEqual(expected, result)
1033+
1034+ @inlineCallbacks
1035+ def test_disabled(self):
1036+ """The syncdaemon status is processed and emitted."""
1037+ self.patch(MockDBusClient, 'file_sync', False)
1038+ yield self.assert_correct_status(FILE_SYNC_DISABLED, msg='')
1039+
1040+ @inlineCallbacks
1041+ def test_error(self):
1042+ """The syncdaemon status is processed and emitted."""
1043+ MockDBusClient.status = {
1044+ 'is_error': 'True', # nothing else matters
1045+ 'is_online': '', 'is_connected': '',
1046+ 'name': 'AUTH_FAILED', 'connection': '', 'queues': '',
1047+ 'description': 'auth failed',
1048+ }
1049+ yield self.assert_correct_status(FILE_SYNC_ERROR)
1050+
1051+ @inlineCallbacks
1052+ def test_starting_when_init_not_user(self):
1053+ """The syncdaemon status is processed and emitted."""
1054+ MockDBusClient.status = {
1055+ 'is_error': '', 'is_online': '', 'is_connected': '',
1056+ 'connection': 'Not User With Network', 'queues': '',
1057+ 'name': 'INIT', 'description': 'something new',
1058+ }
1059+ yield self.assert_correct_status(FILE_SYNC_STARTING)
1060+
1061+ @inlineCallbacks
1062+ def test_starting_when_init_with_user(self):
1063+ """The syncdaemon status is processed and emitted."""
1064+ MockDBusClient.status = {
1065+ 'is_error': '', 'is_online': '', 'is_connected': '',
1066+ 'connection': 'With User With Network', 'queues': '',
1067+ 'name': 'INIT', 'description': 'something new',
1068+ }
1069+ yield self.assert_correct_status(FILE_SYNC_STARTING)
1070+
1071+ @inlineCallbacks
1072+ def test_starting_when_local_rescan_not_user(self):
1073+ """The syncdaemon status is processed and emitted."""
1074+ MockDBusClient.status = {
1075+ 'is_error': '', 'is_online': '', 'is_connected': '',
1076+ 'connection': 'Not User With Network', 'queues': '',
1077+ 'name': 'LOCAL_RESCAN', 'description': 'something new',
1078+ }
1079+ yield self.assert_correct_status(FILE_SYNC_STARTING)
1080+
1081+ @inlineCallbacks
1082+ def test_starting_when_local_rescan_with_user(self):
1083+ """The syncdaemon status is processed and emitted."""
1084+ MockDBusClient.status = {
1085+ 'is_error': '', 'is_online': '', 'is_connected': '',
1086+ 'connection': 'With User With Network', 'queues': '',
1087+ 'name': 'LOCAL_RESCAN', 'description': 'something new',
1088+ }
1089+ yield self.assert_correct_status(FILE_SYNC_STARTING)
1090+
1091+ @inlineCallbacks
1092+ def test_starting_when_ready_with_user(self):
1093+ """The syncdaemon status is processed and emitted."""
1094+ MockDBusClient.status = {
1095+ 'is_error': '', 'is_online': '', 'is_connected': '',
1096+ 'connection': 'With User With Network', 'queues': '',
1097+ 'name': 'READY', 'description': 'something nicer',
1098+ }
1099+ yield self.assert_correct_status(FILE_SYNC_STARTING)
1100+
1101+ @inlineCallbacks
1102+ def test_disconnected(self):
1103+ """The syncdaemon status is processed and emitted."""
1104+ MockDBusClient.status = {
1105+ 'is_error': '', 'is_online': '', 'is_connected': '',
1106+ 'queues': '', 'description': 'something new',
1107+ 'connection': 'Not User With Network', # user didn't connect
1108+ 'name': 'READY', # must be READY, otherwise is STARTING
1109+ }
1110+ yield self.assert_correct_status(FILE_SYNC_DISCONNECTED)
1111+
1112+ @inlineCallbacks
1113+ def test_syncing_if_online(self):
1114+ """The syncdaemon status is processed and emitted."""
1115+ MockDBusClient.status = {
1116+ 'is_error': '', 'is_online': 'True', 'is_connected': 'True',
1117+ 'name': 'QUEUE_MANAGER', 'connection': '',
1118+ 'queues': 'WORKING_ON_CONTENT', # anything but IDLE
1119+ 'description': 'something nicer',
1120+ }
1121+ yield self.assert_correct_status(FILE_SYNC_SYNCING)
1122+
1123+ @inlineCallbacks
1124+ def test_syncing_even_if_not_online(self):
1125+ """The syncdaemon status is processed and emitted."""
1126+ MockDBusClient.status = {
1127+ 'is_error': '', 'is_online': '', 'is_connected': 'True',
1128+ 'name': 'CHECK_VERSION', 'connection': '',
1129+ 'queues': 'WORKING_ON_CONTENT',
1130+ 'description': 'something nicer',
1131+ }
1132+ yield self.assert_correct_status(FILE_SYNC_SYNCING)
1133+
1134+ @inlineCallbacks
1135+ def test_idle(self):
1136+ """The syncdaemon status is processed and emitted."""
1137+ MockDBusClient.status = {
1138+ 'is_error': '', 'is_online': 'True', 'is_connected': 'True',
1139+ 'name': 'QUEUE_MANAGER', 'connection': '',
1140+ 'queues': 'IDLE',
1141+ 'description': 'something nice',
1142+ }
1143+ yield self.assert_correct_status(FILE_SYNC_IDLE)
1144+
1145+ @inlineCallbacks
1146+ def test_unknown(self):
1147+ """The syncdaemon status is processed and emitted."""
1148+ MockDBusClient.status = {
1149+ 'is_error': '', 'is_online': '', 'is_connected': '',
1150+ 'name': '', 'connection': '', 'queues': '',
1151+ 'description': '',
1152+ }
1153+ yield self.assert_correct_status(FILE_SYNC_UNKNOWN)
1154+
1155+ has_warning = self.memento.check_warning('file_sync_status: unknown',
1156+ repr(MockDBusClient.status))
1157+ self.assertTrue(has_warning)
1158+
1159+ def test_status_changed(self):
1160+ """The file_sync_status is the status changed handler."""
1161+ self.be.status_changed_handler = self._set_called
1162+ status = {'name': 'foo', 'description': 'bar', 'is_error': '',
1163+ 'is_connected': '', 'is_online': '', 'queues': ''}
1164+ # pylint: disable=E1101
1165+ backend.dbus_client.status_changed_handler(status)
1166+
1167+ # pylint: disable=W0212
1168+ # Access to a protected member _process_file_sync_status
1169+ expected_status = self.be._process_file_sync_status(status)
1170+ self.assertEqual(self._called, ((expected_status,), {}))
1171
1172=== modified file 'ubuntuone/controlpanel/utils.py'
1173--- ubuntuone/controlpanel/utils.py 2010-12-02 16:35:31 +0000
1174+++ ubuntuone/controlpanel/utils.py 2010-12-10 15:05:12 +0000
1175@@ -20,8 +20,6 @@
1176
1177 import os
1178
1179-from functools import wraps
1180-
1181 import dbus
1182
1183 from ubuntuone.controlpanel.logger import setup_logging
1184@@ -68,24 +66,6 @@
1185 return os.path.join(get_project_dir(), filename)
1186
1187
1188-def log_call(log_func):
1189- """Decorator to add log info using 'log_func'."""
1190-
1191- def middle(f):
1192- """Add logging when calling 'f'."""
1193-
1194- @wraps(f)
1195- def inner(*args, **kwargs):
1196- """Call f(*args, **kwargs)."""
1197- log_func('%s: args %r, kwargs %r.', f.__name__, args, kwargs)
1198- res = f(*args, **kwargs)
1199- return res
1200-
1201- return inner
1202-
1203- return middle
1204-
1205-
1206 def is_dbus_no_reply(failure):
1207 """Decide if 'failure' is a DBus NoReply Error."""
1208 exc = failure.value

Subscribers

People subscribed via source and target branches