Merge lp:~nataliabidart/ubuntuone-control-panel/start-dc-on-backend into lp:ubuntuone-control-panel

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 56
Merged at revision: 45
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/start-dc-on-backend
Merge into: lp:ubuntuone-control-panel
Prerequisite: lp:~nataliabidart/ubuntuone-control-panel/replication-to-the-backend
Diff against target: 932 lines (+326/-242)
7 files modified
data/install.ui (+7/-2)
po/POTFILES.in (+2/-1)
pylintrc (+1/-1)
ubuntuone/controlpanel/gtk/gui.py (+114/-81)
ubuntuone/controlpanel/gtk/tests/__init__.py (+14/-13)
ubuntuone/controlpanel/gtk/tests/test_gui.py (+187/-140)
ubuntuone/controlpanel/logger.py (+1/-4)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/start-dc-on-backend
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Eric Casteleijn (community) Approve
Review via email: mp+45448@code.launchpad.net

This proposal supersedes a proposal from 2011-01-06.

Commit message

Desktopcouch replication service is accessed through the backend using its DBus service (LP: #696782).

Description of the change

To test, first check that all the desktopcouch related packages are uninstalled, and that no desktopcouch nor couchdb process is running. This meaning:

sudo apt-cache search couch

should show no package installed, and:

ps aux | grep couch

should return no results. Then, from this branch, run:

terminal 1: DEBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-backend
terminal 2: DEBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-gtk

And go to the third tab and install dc-u1 from there, and keep playing. You should get no hangs at all in the UI.

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

Avoiding conflicts.

56. By Natalia Bidart

Merged dependency branch in.

Revision history for this message
Eric Casteleijn (thisfred) wrote :

Works as advertised, code looks good. I don't like the desktopcouch pairing functionality as it stands, but that's in the design, so we'll discuss elsewhere.

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

Does what it says. +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/install.ui'
2--- data/install.ui 2010-12-24 14:53:03 +0000
3+++ data/install.ui 2011-01-06 21:03:06 +0000
4@@ -23,11 +23,12 @@
5 <property name="visible">True</property>
6 <child>
7 <object class="GtkButton" id="install_button">
8- <property name="label">gtk-ok</property>
9+ <property name="label" translatable="yes">_Install now</property>
10 <property name="visible">True</property>
11 <property name="can_focus">True</property>
12 <property name="receives_default">True</property>
13- <property name="use_stock">True</property>
14+ <property name="image">image1</property>
15+ <property name="use_underline">True</property>
16 <signal name="clicked" handler="on_install_button_clicked"/>
17 </object>
18 <packing>
19@@ -43,4 +44,8 @@
20 </packing>
21 </child>
22 </object>
23+ <object class="GtkImage" id="image1">
24+ <property name="visible">True</property>
25+ <property name="stock">gtk-ok</property>
26+ </object>
27 </interface>
28
29=== modified file 'po/POTFILES.in'
30--- po/POTFILES.in 2011-01-04 16:12:56 +0000
31+++ po/POTFILES.in 2011-01-06 21:03:06 +0000
32@@ -1,8 +1,9 @@
33 ubuntuone-control-panel-gtk.desktop.in
34 ubuntuone/controlpanel/gtk/gui.py
35 [type: gettext/glade] data/dashboard.ui
36-[type: gettext/glade] data/services.ui
37 [type: gettext/glade] data/device.ui
38 [type: gettext/glade] data/devices.ui
39+[type: gettext/glade] data/install.ui
40 [type: gettext/glade] data/management.ui
41 [type: gettext/glade] data/overview.ui
42+[type: gettext/glade] data/services.ui
43
44=== modified file 'pylintrc'
45--- pylintrc 2010-10-13 18:55:23 +0000
46+++ pylintrc 2011-01-06 21:03:06 +0000
47@@ -272,7 +272,7 @@
48 max-line-length=79
49
50 # Maximum number of lines in a module
51-max-module-lines=2000
52+max-module-lines=2500
53
54 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
55 # tab).
56
57=== modified file 'ubuntuone/controlpanel/gtk/gui.py'
58--- ubuntuone/controlpanel/gtk/gui.py 2010-12-26 15:02:52 +0000
59+++ ubuntuone/controlpanel/gtk/gui.py 2011-01-06 21:03:06 +0000
60@@ -78,6 +78,12 @@
61 VALUE_ERROR = _('Value could not be retrieved.')
62 WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE
63 KILOBYTES = 1024
64+NO_OP = lambda *a, **kw: None
65+
66+
67+def error_handler(*args, **kwargs):
68+ """Log errors when calling D-Bus methods in a async way."""
69+ logger.error('Error handler received: %r, %r', args, kwargs)
70
71
72 def filter_by_app_name(f):
73@@ -352,7 +358,8 @@
74 settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION,
75 WINDOW_ID_KEY: str(self._window_id),
76 PING_URL_KEY: U1_PING_URL}
77- self.sso_backend.register(U1_APP_NAME, settings)
78+ self.sso_backend.register(U1_APP_NAME, settings,
79+ reply_handler=NO_OP, error_handler=error_handler)
80 self.set_property('greyed', True)
81 self.warning_label.set_text('')
82
83@@ -361,7 +368,8 @@
84 settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION,
85 WINDOW_ID_KEY: str(self._window_id),
86 PING_URL_KEY: U1_PING_URL}
87- self.sso_backend.login(U1_APP_NAME, settings)
88+ self.sso_backend.login(U1_APP_NAME, settings,
89+ reply_handler=NO_OP, error_handler=error_handler)
90 self.set_property('greyed', True)
91 self.warning_label.set_text('')
92
93@@ -408,7 +416,8 @@
94 else:
95 self.set_sensitive(True)
96 self.warning_label.set_text('')
97- self.sso_backend.find_credentials(U1_APP_NAME, {})
98+ self.sso_backend.find_credentials(U1_APP_NAME, {},
99+ reply_handler=NO_OP, error_handler=error_handler)
100
101
102 class DashboardPanel(UbuntuOneBin, ControlPanelMixin):
103@@ -516,11 +525,13 @@
104 volume_id = checkbutton.get_label()
105 subscribed = bool_str(checkbutton.get_active())
106 self.backend.change_volume_settings(volume_id,
107- {'subscribed': subscribed})
108+ {'subscribed': subscribed},
109+ reply_handler=NO_OP, error_handler=error_handler)
110
111 def load(self):
112 """Load the volume list."""
113- self.backend.volumes_info()
114+ self.backend.volumes_info(reply_handler=NO_OP,
115+ error_handler=error_handler)
116 self.message.start()
117
118
119@@ -565,7 +576,8 @@
120 # Not disabling the GUI to avoid annyong twitchings
121 #self.set_sensitive(False)
122 self.warning_label.set_text('')
123- self.backend.change_device_settings(self.id, self.__dict__)
124+ self.backend.change_device_settings(self.id, self.__dict__,
125+ reply_handler=NO_OP, error_handler=error_handler)
126
127 def _block_signals(f):
128 """Execute 'f' while having the _updating flag set."""
129@@ -591,7 +603,8 @@
130
131 def on_remove_clicked(self, widget):
132 """Remove button was clicked or activated."""
133- self.backend.remove_device(self.id)
134+ self.backend.remove_device(self.id,
135+ reply_handler=NO_OP, error_handler=error_handler)
136 self.set_sensitive(False)
137
138 @_block_signals
139@@ -747,7 +760,8 @@
140
141 def load(self):
142 """Load the device list."""
143- self.backend.devices_info()
144+ self.backend.devices_info(reply_handler=NO_OP,
145+ error_handler=error_handler)
146 self.message.start()
147
148
149@@ -818,12 +832,18 @@
150 class Service(gtk.VBox, ControlPanelMixin):
151 """A service."""
152
153- def __init__(self, name, localized_name, *args, **kwargs):
154+ CHANGE_ERROR = _('The settings could not be changed,\n'
155+ 'previous values were restored.')
156+
157+ def __init__(self, service_id, name, *args, **kwargs):
158 gtk.VBox.__init__(self)
159 ControlPanelMixin.__init__(self)
160- self.service_name = name
161-
162- self.button = gtk.CheckButton(label=localized_name)
163+ self.id = service_id
164+
165+ self.warning_label = gtk.Label()
166+ self.pack_start(self.warning_label, expand=False)
167+
168+ self.button = gtk.CheckButton(label=name)
169 self.pack_start(self.button, expand=False)
170
171 self.show_all()
172@@ -835,15 +855,18 @@
173 FILES_SERVICE_NAME = _('Files')
174
175 def __init__(self):
176- Service.__init__(self, name='files',
177- localized_name=self.FILES_SERVICE_NAME)
178+ Service.__init__(self, service_id='files',
179+ name=self.FILES_SERVICE_NAME)
180
181 self.set_sensitive(False)
182+
183 self.backend.connect_to_signal('FileSyncStatusChanged',
184 self.on_file_sync_status_changed)
185 self.backend.connect_to_signal('FilesEnabled', self.on_files_enabled)
186 self.backend.connect_to_signal('FilesDisabled', self.on_files_disabled)
187- self.backend.file_sync_status()
188+
189+ self.backend.file_sync_status(reply_handler=NO_OP,
190+ error_handler=error_handler)
191
192 @log_call(logger.debug)
193 def on_file_sync_status_changed(self, status):
194@@ -869,19 +892,24 @@
195 """Button was toggled, exclude/replicate the service properly."""
196 logger.info('File sync enabled? %r', self.button.get_active())
197 if self.button.get_active():
198- self.backend.enable_files()
199+ self.backend.enable_files(reply_handler=NO_OP,
200+ error_handler=error_handler)
201 else:
202- self.backend.disable_files()
203+ self.backend.disable_files(reply_handler=NO_OP,
204+ error_handler=error_handler)
205
206
207 class DesktopcouchService(Service):
208 """A desktopcouch service."""
209
210- def __init__(self, name, localized_name,
211- replication_service, dependency=None):
212- Service.__init__(self, name, localized_name)
213- self.replication_service = replication_service
214- enabled = name not in self.replication_service.all_exclusions()
215+ def __init__(self, service_id, name, enabled, dependency=None):
216+ Service.__init__(self, service_id, name)
217+
218+ self.backend.connect_to_signal('ReplicationSettingsChanged',
219+ self.on_replication_settings_changed)
220+ self.backend.connect_to_signal('ReplicationSettingsChangeError',
221+ self.on_replication_settings_change_error)
222+
223 self.button.set_active(enabled)
224
225 self.dependency = None
226@@ -903,11 +931,27 @@
227 def on_button_toggled(self, button):
228 """Button was toggled, exclude/replicate the service properly."""
229 logger.info('Starting replication for %r? %r',
230- self.service_name, self.button.get_active())
231- if self.button.get_active():
232- self.replication_service.replicate(self.service_name)
233- else:
234- self.replication_service.exclude(self.service_name)
235+ self.id, self.button.get_active())
236+
237+ args = {'enabled': bool_str(self.button.get_active())}
238+ self.backend.change_replication_settings(self.id, args,
239+ reply_handler=NO_OP, error_handler=error_handler)
240+
241+ @log_call(logger.info)
242+ def on_replication_settings_changed(self, replication_id):
243+ """The change of settings for this replication succeded."""
244+ if replication_id != self.id:
245+ return
246+ self.warning_label.set_text('')
247+
248+ @log_call(logger.error)
249+ def on_replication_settings_change_error(self, replication_id,
250+ error_dict=None):
251+ """The change of settings for this replication failed."""
252+ if replication_id != self.id:
253+ return
254+ self.button.set_active(not self.button.get_active())
255+ self._set_warning(self.CHANGE_ERROR, self.warning_label)
256
257
258 class ServicesPanel(UbuntuOneBin, ControlPanelMixin):
259@@ -916,32 +960,33 @@
260 TITLE = _('Ubuntu One services including data sync are enabled for the '
261 'data types and services listed below.')
262 CHOOSE_SERVICES = _('Choose services to synchronize with this computer:')
263- DESKTOPCOUCH_PKG = 'desktopcouch'
264- BINDWOOD_PKG = 'xul-ext-bindwood'
265- EVOCOUCH_PKG = 'evolution-couchdb'
266+ DESKTOPCOUCH_PKG = 'desktopcouch-ubuntuone'
267 BOOKMARKS = _('Bookmarks (Firefox)')
268 CONTACTS = _('Contacts (Evolution)')
269 NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.')
270
271- def __init__(self, replication_exclusion_class=None):
272+ def __init__(self):
273 UbuntuOneBin.__init__(self)
274 ControlPanelMixin.__init__(self, filename='services.ui')
275 self.add(self.itself)
276
277- self.replication_exclusion_class = replication_exclusion_class
278- self.replication_service = None
279- self.has_desktopcouch = False
280- self.has_bindwood = False
281- self.has_evocouch = False
282 self.package_manager = package_manager.PackageManager()
283 self.install_box = None
284- self.bookmarks = None
285- self.contacts = None
286+
287+ self.backend.connect_to_signal('ReplicationsInfoReady',
288+ self.on_replications_info_ready)
289+ self.backend.connect_to_signal('ReplicationsInfoError',
290+ self.on_replications_info_error)
291
292 self.files.pack_start(FilesService(), expand=False)
293
294 self.show()
295
296+ @property
297+ def has_desktopcouch(self):
298+ """Is desktopcouch installed?"""
299+ return self.package_manager.is_installed(self.DESKTOPCOUCH_PKG)
300+
301 @log_call(logger.debug)
302 def load(self):
303 """Load info."""
304@@ -949,16 +994,7 @@
305 self.itself.remove(self.install_box)
306 self.install_box = None
307
308- self.has_desktopcouch = \
309- self.package_manager.is_installed(self.DESKTOPCOUCH_PKG)
310- self.has_bindwood = \
311- self.package_manager.is_installed(self.BINDWOOD_PKG)
312- self.has_evocouch = \
313- self.package_manager.is_installed(self.EVOCOUCH_PKG)
314-
315- logger.info('load: has_desktopcouch? %r has_bindwood? %s '
316- 'has_evocouch? %s', self.has_desktopcouch,
317- self.has_bindwood, self.has_evocouch)
318+ logger.info('load: has_desktopcouch? %r', self.has_desktopcouch)
319 if not self.has_desktopcouch:
320 self.message.set_text('')
321 self.replications.hide()
322@@ -975,46 +1011,41 @@
323 @log_call(logger.debug)
324 def load_replications(self, *args):
325 """Load replications info."""
326+ # ask replications to the backend
327+ self.message.start()
328+ self.backend.replications_info(reply_handler=NO_OP,
329+ error_handler=error_handler)
330+
331+ @log_call(logger.debug)
332+ def on_replications_info_ready(self, info):
333+ """The replication info is ready."""
334+ self.on_success(self.CHOOSE_SERVICES)
335+
336 self.replications.show()
337
338 if self.install_box is not None:
339 self.itself.remove(self.install_box)
340 self.install_box = None
341
342- self.message.set_text(self.CHOOSE_SERVICES)
343 for child in self.replications.get_children():
344 self.replications.remove(child)
345
346- # Unable to import 'desktopcouch.application.replication_services'
347- # pylint: disable=F0401
348- if self.replication_exclusion_class is None:
349- from desktopcouch.application.replication_services import \
350- ubuntuone as u1rep
351- self.replication_exclusion_class = u1rep.ReplicationExclusion
352-
353- if self.replication_service is None:
354- try:
355- self.replication_service = self.replication_exclusion_class()
356- except ValueError:
357- logger.exception('Can not load replications:')
358- self._set_warning(self.NO_PAIRING_RECORD, self.message)
359- return
360-
361- pkg = None
362- if not self.has_bindwood:
363- pkg = self.BINDWOOD_PKG
364- self.bookmarks = DesktopcouchService('bookmarks', self.BOOKMARKS,
365- self.replication_service,
366- dependency=pkg)
367- self.replications.pack_start(self.bookmarks, expand=False)
368-
369- pkg = None
370- if not self.has_evocouch:
371- pkg = self.EVOCOUCH_PKG
372- self.contacts = DesktopcouchService('contacts', self.CONTACTS,
373- self.replication_service,
374- dependency=pkg)
375- self.replications.pack_start(self.contacts, expand=False)
376+ for item in info:
377+ pkg = item['dependency']
378+ child = DesktopcouchService(service_id=item['replication_id'],
379+ name=item['name'], # self.BOOKMARKS,
380+ enabled=bool(item['enabled']),
381+ dependency=pkg if pkg else None)
382+ self.replications.pack_start(child, expand=False)
383+
384+ @log_call(logger.error)
385+ def on_replications_info_error(self, error_dict=None):
386+ """The replication info can not be retrieved."""
387+ if error_dict is not None and \
388+ error_dict.get('error_type', None) == 'NoPairingRecord':
389+ self.on_error(self.NO_PAIRING_RECORD)
390+ else:
391+ self.on_error()
392
393
394 class ManagementPanel(gtk.VBox, ControlPanelMixin):
395@@ -1107,8 +1138,10 @@
396
397 def load(self):
398 """Load the account info and file sync status list."""
399- self.backend.account_info()
400- self.backend.file_sync_status()
401+ self.backend.account_info(reply_handler=NO_OP,
402+ error_handler=error_handler)
403+ self.backend.file_sync_status(reply_handler=NO_OP,
404+ error_handler=error_handler)
405 self.dashboard_button.clicked()
406
407 @log_call(logger.debug)
408
409=== modified file 'ubuntuone/controlpanel/gtk/tests/__init__.py'
410--- ubuntuone/controlpanel/gtk/tests/__init__.py 2010-12-23 19:17:53 +0000
411+++ ubuntuone/controlpanel/gtk/tests/__init__.py 2011-01-06 21:03:06 +0000
412@@ -53,6 +53,15 @@
413 'max_upload_speed': '1000', 'max_download_speed': '72548'}, # local
414 ]
415
416+FAKE_REPLICATIONS_INFO = [
417+ {'replication_id': 'foo', 'name': 'Bar',
418+ 'enabled': 'True', 'dependency': ''},
419+ {'replication_id': 'yadda', 'name': 'Foo',
420+ 'enabled': '', 'dependency': 'a very weird one'},
421+ {'replication_id': 'yoda', 'name': 'Figthers',
422+ 'enabled': 'True', 'dependency': 'other dep'},
423+]
424+
425
426 class FakedObject(object):
427 """Fake an object, record every call."""
428@@ -117,9 +126,11 @@
429 object_path = gui.DBUS_PREFERENCES_PATH
430 iface = gui.DBUS_PREFERENCES_IFACE
431 exposed_methods = [
432- 'account_info', 'devices_info', 'change_device_settings',
433- 'volumes_info', 'change_volume_settings', 'file_sync_status',
434- 'remove_device', 'enable_files', 'disable_files',
435+ 'account_info', # account
436+ 'devices_info', 'change_device_settings', 'remove_device', # devices
437+ 'volumes_info', 'change_volume_settings', # volumes
438+ 'replications_info', 'change_replication_settings', # replications
439+ 'file_sync_status', 'enable_files', 'disable_files', # files
440 ]
441
442
443@@ -157,13 +168,3 @@
444 yield
445 self._installed[package_name] = True
446 gui.package_manager.return_value(FakedTransaction([package_name]))
447-
448-
449-class FakedReplication(object):
450- """Faked a DC replication exclusion."""
451-
452- def __init__(self):
453- self._exclusions = set()
454- self.all_exclusions = lambda: self._exclusions
455- self.replicate = self._exclusions.remove
456- self.exclude = self._exclusions.add
457
458=== modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py'
459--- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-26 15:02:52 +0000
460+++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-01-06 21:03:06 +0000
461@@ -26,9 +26,10 @@
462
463 from ubuntuone.controlpanel.gtk import gui
464 from ubuntuone.controlpanel.gtk.tests import (FAKE_ACCOUNT_INFO,
465- FAKE_VOLUMES_INFO, FAKE_DEVICE_INFO, FAKE_DEVICES_INFO,
466+ FAKE_DEVICE_INFO, FAKE_DEVICES_INFO,
467+ FAKE_VOLUMES_INFO, FAKE_REPLICATIONS_INFO,
468 FakedNMState, FakedSSOBackend, FakedSessionBus, FakedInterface,
469- FakedPackageManager, FakedReplication,
470+ FakedPackageManager,
471 )
472 from ubuntuone.controlpanel.tests import TOKEN, TestCase
473 from ubuntuone.controlpanel.gtk.tests.test_package_manager import (
474@@ -37,6 +38,8 @@
475
476 # Attribute 'yyy' defined outside __init__, access to a protected member
477 # pylint: disable=W0201, W0212
478+# Too many lines in module
479+# pylint: disable=C0302
480
481
482 class BaseTestCase(TestCase):
483@@ -96,7 +99,9 @@
484 if backend is None:
485 backend = self.ui.backend
486 self.assertIn(method_name, backend._called)
487- self.assertEqual(backend._called[method_name], (args, {}))
488+ kwargs = {'reply_handler': gui.NO_OP,
489+ 'error_handler': gui.error_handler}
490+ self.assertEqual(backend._called[method_name], (args, kwargs))
491
492 def assert_warning_correct(self, warning, text):
493 """Check that 'warning' is visible, showing 'text'."""
494@@ -1361,9 +1366,9 @@
495 """The test suite for a service."""
496
497 klass = gui.Service
498- name = 'dc_test'
499- localized_name = u'Qué lindo test!'
500- kwargs = {'name': 'dc_test', 'localized_name': u'Qué lindo test!'}
501+ service_id = 'dc_test'
502+ name = u'Qué lindo test!'
503+ kwargs = {'service_id': service_id, 'name': name}
504
505 def test_is_an_box(self):
506 """Inherits from gtk.VBox."""
507@@ -1373,30 +1378,35 @@
508 """Is visible."""
509 self.assertTrue(self.ui.get_visible())
510
511+ def test_warning_label_is_cleared(self):
512+ """The warning label is cleared."""
513+ self.assertEqual(self.ui.warning_label.get_text(), '')
514+
515+ def test_warning_label_packed(self):
516+ """The warning label is packed as child."""
517+ self.assertIn(self.ui.warning_label, self.ui.get_children())
518+
519 def test_check_button_packed(self):
520- """A check button is packed as only child."""
521+ """A check button is packed as child."""
522 self.assertIn(self.ui.button, self.ui.get_children())
523
524 def test_label(self):
525 """The label is set."""
526- self.assertEqual(self.localized_name, self.ui.button.get_label())
527+ self.assertEqual(self.name, self.ui.button.get_label())
528
529- def test_service_name(self):
530- """The service_name is set."""
531- self.assertEqual(self.name, self.ui.service_name)
532+ def test_service_id(self):
533+ """The service id is set."""
534+ self.assertEqual(self.service_id, self.ui.id)
535
536
537 class FilesServiceTestCase(ServiceTestCase):
538 """The test suite for the file sync service."""
539
540 klass = gui.FilesService
541+ service_id = 'files'
542+ name = gui.FilesService.FILES_SERVICE_NAME
543 kwargs = {}
544
545- def setUp(self):
546- self.name = 'files'
547- self.localized_name = gui.FilesService.FILES_SERVICE_NAME
548- super(FilesServiceTestCase, self).setUp()
549-
550 def test_backend_account_signals(self):
551 """The proper signals are connected to the backend."""
552 self.assertEqual(self.ui.backend._signals['FileSyncStatusChanged'],
553@@ -1461,35 +1471,36 @@
554 """The test suite for a desktopcouch service."""
555
556 klass = gui.DesktopcouchService
557+ enabled = True
558
559 def setUp(self):
560- self.replication = FakedReplication()
561- self.name = self.kwargs['name']
562- self.kwargs['replication_service'] = self.replication
563+ self.kwargs['enabled'] = self.enabled
564 super(DesktopcouchServiceTestCase, self).setUp()
565
566+ def modify_settings(self):
567+ """Modify settings so values actually change."""
568+ self.ui.button.set_active(not self.ui.button.get_active())
569+
570+ def test_backend_account_signals(self):
571+ """The proper signals are connected to the backend."""
572+ self.assertEqual(
573+ self.ui.backend._signals['ReplicationSettingsChanged'],
574+ [self.ui.on_replication_settings_changed])
575+ self.assertEqual(
576+ self.ui.backend._signals['ReplicationSettingsChangeError'],
577+ [self.ui.on_replication_settings_change_error])
578+
579 def test_active(self):
580- """Is active since replication has an empty database."""
581- self.assertTrue(self.ui.button.get_active())
582-
583- def test_not_active(self):
584- """Is not active since 'name' is excluded on replication database."""
585- self.replication.exclude(self.name)
586- self.ui = self.klass(**self.kwargs)
587- self.assertFalse(self.ui.button.get_active())
588+ """Is active if enabled."""
589+ self.assertEqual(self.enabled, self.ui.button.get_active())
590
591 def test_on_button_toggled(self):
592 """When toggling the button, the DC exclude list is updated."""
593- assert self.ui.button.get_active()
594 self.ui.button.set_active(not self.ui.button.get_active())
595- self.assertEqual(set([self.name]), self.replication.all_exclusions())
596
597- def test_on_button_toggled_twice(self):
598- """When toggling the button twice, the DC exclude list is updated."""
599- assert self.ui.button.get_active()
600- self.ui.button.set_active(not self.ui.button.get_active())
601- self.ui.button.set_active(not self.ui.button.get_active())
602- self.assertEqual(set(), self.replication.all_exclusions())
603+ args = (self.service_id,
604+ {'enabled': gui.bool_str(self.ui.button.get_active())})
605+ self.assert_backend_called('change_replication_settings', args)
606
607 def test_dependency(self):
608 """The dependency box is None."""
609@@ -1499,6 +1510,72 @@
610 """The check button is sensitive."""
611 self.assertTrue(self.ui.button.get_sensitive())
612
613+ def test_on_replication_settings_changed(self):
614+ """When settings were changed for this replication, enable it."""
615+ new_val = not self.ui.button.get_active()
616+ self.ui.button.set_active(new_val)
617+
618+ self.ui.on_replication_settings_changed(replication_id=self.ui.id)
619+
620+ self.assertEqual(self.ui.warning_label.get_text(), '')
621+ self.assertEqual(new_val, self.ui.button.get_active())
622+
623+ def test_on_replication_settings_changed_after_error(self):
624+ """Change success after error."""
625+ self.ui.button.set_active(not self.ui.button.get_active())
626+ self.ui.on_replication_settings_change_error(replication_id=self.ui.id)
627+
628+ self.test_on_replication_settings_changed()
629+
630+ def test_on_replication_settings_changed_different_id(self):
631+ """When settings were changed for other rep, nothing changes."""
632+ self.ui.button.set_active(not self.ui.button.get_active())
633+ self.ui.on_replication_settings_changed(replication_id='yadda')
634+
635+ self.assertEqual(self.ui.warning_label.get_text(), '')
636+
637+ def test_on_replication_settings_changed_different_id_after_error(self):
638+ """When settings were changed for other + error, nothing changes."""
639+ self.ui.on_replication_settings_change_error(replication_id=self.ui.id)
640+ self.ui.on_replication_settings_changed(replication_id='yadda')
641+
642+ self.assert_warning_correct(self.ui.warning_label,
643+ self.ui.CHANGE_ERROR)
644+
645+ def test_on_replication_settings_change_error(self):
646+ """When settings were not changed, notify the user.
647+
648+ Also, confirm that old value was restored.
649+
650+ """
651+ old_val = self.ui.button.get_active()
652+ self.ui.button.set_active(not old_val)
653+ self.ui.on_replication_settings_change_error(replication_id=self.ui.id)
654+
655+ self.assert_warning_correct(self.ui.warning_label,
656+ self.ui.CHANGE_ERROR)
657+ self.assertEqual(old_val, self.ui.button.get_active())
658+
659+ def test_on_replication_settings_change_error_after_success(self):
660+ """Change error after success."""
661+ self.ui.button.set_active(not self.ui.button.get_active())
662+ self.ui.on_replication_settings_changed(replication_id=self.ui.id)
663+
664+ self.test_on_replication_settings_change_error()
665+
666+ def test_on_replication_settings_change_error_different_id(self):
667+ """When settings were not changed for other replication, do nothing."""
668+ self.ui.button.set_active(not self.ui.button.get_active())
669+ self.ui.on_replication_settings_change_error(replication_id='yudo')
670+
671+ self.assertEqual(self.ui.warning_label.get_text(), '')
672+
673+
674+class DesktopcouchServiceDisabledAtStartupTestCase(ServiceTestCase):
675+ """The test suite for a desktopcouch service when enabled=False."""
676+
677+ enabled = False
678+
679
680 class DesktopcouchServiceWithDependencyTestCase(DesktopcouchServiceTestCase):
681 """The test suite for a desktopcouch service when it needs a dependency."""
682@@ -1532,7 +1609,8 @@
683 self.ui.dependency.emit('finished')
684
685 self.assertTrue(self.ui.dependency is None)
686- self.assertEqual(self.ui.get_children(), [self.ui.button])
687+ self.assertEqual(sorted(self.ui.get_children()),
688+ sorted([self.ui.button, self.ui.warning_label]))
689
690
691 class ServicesTestCase(ControlPanelMixinTestCase):
692@@ -1562,6 +1640,13 @@
693 """The install box is None."""
694 self.assertTrue(self.ui.install_box is None)
695
696+ def test_backend_signals(self):
697+ """The proper signals are connected to the backend."""
698+ self.assertEqual(self.ui.backend._signals['ReplicationsInfoReady'],
699+ [self.ui.on_replications_info_ready])
700+ self.assertEqual(self.ui.backend._signals['ReplicationsInfoError'],
701+ [self.ui.on_replications_info_error])
702+
703
704 class ServicesFilesTestCase(ServicesTestCase):
705 """The test suite for the services panel (files section)."""
706@@ -1589,22 +1674,10 @@
707 self.assertFalse(self.ui.message.active)
708 self.assertEqual(self.ui.message.get_text(), '')
709
710- def test_replication_service(self):
711- """Has a replication service."""
712- self.assertEqual(self.ui.replication_service, None)
713-
714 def test_has_desktopcouch(self):
715 """Has desktopcouch installed?"""
716 self.assertFalse(self.ui.has_desktopcouch)
717
718- def test_has_bindwood(self):
719- """Has bindwood installed?"""
720- self.assertFalse(self.ui.has_bindwood)
721-
722- def test_has_evocouch(self):
723- """Has evocouch installed?"""
724- self.assertFalse(self.ui.has_evocouch)
725-
726 def test_install_box_is_hidden(self):
727 """The install box is not hidden."""
728 self.assertTrue(self.ui.install_box.get_visible())
729@@ -1629,41 +1702,27 @@
730
731 self.assertEqual(self._called, ((self.ui.install_box,), {}))
732
733+ def test_load_replications(self):
734+ """The load_replications starts the spinner and calls the backend."""
735+ self.ui.load_replications()
736+
737+ self.assertTrue(self.ui.message.active)
738+ self.assert_backend_called('replications_info', ())
739+
740
741 class ServicesWithDesktopcouchTestCase(ServicesTestCase):
742 """The test suite for the services panel."""
743
744- kwargs = {'replication_exclusion_class': FakedReplication}
745-
746 def setUp(self):
747 super(ServicesWithDesktopcouchTestCase, self).setUp()
748 self.ui.package_manager._installed[self.ui.DESKTOPCOUCH_PKG] = True
749- self.ui.load()
750+ self.ui.on_replications_info_ready(info=FAKE_REPLICATIONS_INFO)
751
752 def test_message(self):
753 """Global load message is stopped and proper test is shown."""
754 self.assertFalse(self.ui.message.active)
755 self.assertEqual(self.ui.message.get_text(), self.ui.CHOOSE_SERVICES)
756
757- def test_replication_service(self):
758- """Has a replication service."""
759- self.assertIsInstance(self.ui.replication_service, FakedReplication)
760-
761- def test_no_pairing_record(self):
762- """The pairing record is not in place."""
763-
764- def no_pairing_record(*a):
765- """Fake a ReplicationExclusion with no pairing record."""
766- raise ValueError("No pairing record for ubuntuone.")
767-
768- self.ui.replication_exclusion_class = no_pairing_record
769- self.ui.replication_service = None
770- self.ui.load()
771-
772- self.assertEqual(self.ui.replications.get_children(), [])
773- self.assertFalse(self.ui.message.active)
774- self.assert_warning_correct(self.ui.message, self.ui.NO_PAIRING_RECORD)
775-
776 def test_has_desktopcouch(self):
777 """Has desktopcouch installed?"""
778 self.assertTrue(self.ui.has_desktopcouch)
779@@ -1673,79 +1732,67 @@
780 self.assertTrue(self.ui.replications.get_visible())
781
782 children = self.ui.replications.get_children()
783- self.assertEqual(len(children), 2)
784- for child in children:
785+ self.assertEqual(len(children), len(FAKE_REPLICATIONS_INFO))
786+ for expected, child in zip(FAKE_REPLICATIONS_INFO, children):
787 self.assertIsInstance(child, gui.DesktopcouchService)
788-
789- self.assertTrue(self.ui.bookmarks is children[0])
790- self.assertTrue(self.ui.contacts is children[1])
791-
792- def test_replications_after_loading_twice(self):
793- """Has proper child after loading twice."""
794- self.ui.load()
795+ self.assertEqual(expected['replication_id'], child.id)
796+ self.assertEqual(expected['name'], child.button.get_label())
797+ self.assertEqual(bool(expected['enabled']),
798+ child.button.get_active())
799+
800+ if expected['dependency']:
801+ self.assertTrue(child.dependency is not None)
802+ self.assertEqual(expected['dependency'],
803+ child.dependency.package_name)
804+ else:
805+ self.assertTrue(child.dependency is None)
806+
807+ def test_replications_after_getting_info_twice(self):
808+ """Has proper child after getting backend info twice."""
809+ self.ui.on_replications_info_ready(info=FAKE_REPLICATIONS_INFO)
810 self.test_replications()
811
812- def test_bookmarks(self):
813- """The bookmarks is correct."""
814- self.assertEqual(self.ui.bookmarks.service_name, 'bookmarks')
815- self.assertEqual(self.ui.bookmarks.button.get_label(),
816- self.ui.BOOKMARKS)
817- self.assertTrue(self.ui.bookmarks.replication_service is
818- self.ui.replication_service)
819-
820- def test_bookmarks_dependency(self):
821- """The bookmarks dependency is correct."""
822- self.assertTrue(self.ui.bookmarks.dependency is not None)
823- self.assertEqual(self.ui.bookmarks.dependency.package_name,
824- self.ui.BINDWOOD_PKG)
825-
826- def test_contacts(self):
827- """The contacts is correct."""
828- self.assertEqual(self.ui.contacts.service_name, 'contacts')
829- self.assertEqual(self.ui.contacts.button.get_label(),
830- self.ui.CONTACTS)
831- self.assertTrue(self.ui.contacts.replication_service is
832- self.ui.replication_service)
833-
834- def test_contacts_dependency(self):
835- """The contacts dependency is correct."""
836- self.assertTrue(self.ui.contacts.dependency is not None)
837- self.assertEqual(self.ui.contacts.dependency.package_name,
838- self.ui.EVOCOUCH_PKG)
839-
840-
841-class ServicesWithDCAndBindwoodTestCase(ServicesWithDesktopcouchTestCase):
842- """The test suite for the services panel."""
843-
844- def setUp(self):
845- super(ServicesWithDCAndBindwoodTestCase, self).setUp()
846- self.ui.package_manager._installed[self.ui.BINDWOOD_PKG] = True
847- self.ui.load()
848-
849- def test_has_bindwood(self):
850- """Has bindwood installed?"""
851- self.assertTrue(self.ui.has_bindwood)
852-
853- def test_bookmarks_dependency(self):
854- """The bookmarks dependency is correct."""
855- self.assertTrue(self.ui.bookmarks.dependency is None)
856-
857-
858-class ServicesWithDCAndEvocouchTestCase(ServicesWithDesktopcouchTestCase):
859- """The test suite for the services panel."""
860-
861- def setUp(self):
862- super(ServicesWithDCAndEvocouchTestCase, self).setUp()
863- self.ui.package_manager._installed[self.ui.EVOCOUCH_PKG] = True
864- self.ui.load()
865-
866- def test_has_evocouch(self):
867- """Has evocoucg installed?"""
868- self.assertTrue(self.ui.has_evocouch)
869-
870- def test_contacts_dependency(self):
871- """The bookmarks dependency is correct."""
872- self.assertTrue(self.ui.contacts.dependency is None)
873+
874+class ServicesWithDesktopcouchErrorTestCase(ServicesTestCase):
875+ """The test suite for the services panel."""
876+
877+ def setUp(self):
878+ super(ServicesWithDesktopcouchErrorTestCase, self).setUp()
879+ self.ui.package_manager._installed[self.ui.DESKTOPCOUCH_PKG] = True
880+
881+ def test_no_pairing_record(self):
882+ """The pairing record is not in place."""
883+ error_dict = {'error_type': 'NoPairingRecord'}
884+ self.ui.on_replications_info_error(error_dict)
885+
886+ self.assertEqual(self.ui.replications.get_children(), [])
887+ self.assertFalse(self.ui.message.active)
888+ self.assert_warning_correct(self.ui.message, self.ui.NO_PAIRING_RECORD)
889+
890+ def test_other_error(self):
891+ """There was an error other than no pairing record."""
892+ error_dict = {'error_type': 'OtherError'}
893+ self.ui.on_replications_info_error(error_dict)
894+
895+ self.assertEqual(self.ui.replications.get_children(), [])
896+ self.assertFalse(self.ui.message.active)
897+ self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
898+
899+ def test_empty_dict(self):
900+ """Handle empty dicts errors."""
901+ self.ui.on_replications_info_error(error_dict={})
902+
903+ self.assertEqual(self.ui.replications.get_children(), [])
904+ self.assertFalse(self.ui.message.active)
905+ self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
906+
907+ def test_error_dict_none(self):
908+ """HGandle empty dicts errors."""
909+ self.ui.on_replications_info_error(error_dict=None)
910+
911+ self.assertEqual(self.ui.replications.get_children(), [])
912+ self.assertFalse(self.ui.message.active)
913+ self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
914
915
916 class ManagementPanelTestCase(ControlPanelMixinTestCase):
917
918=== modified file 'ubuntuone/controlpanel/logger.py'
919--- ubuntuone/controlpanel/logger.py 2010-12-23 18:20:56 +0000
920+++ ubuntuone/controlpanel/logger.py 2011-01-06 21:03:06 +0000
921@@ -53,10 +53,7 @@
922 logger.addHandler(MAIN_HANDLER)
923 if os.environ.get('DEBUG'):
924 debug_handler = logging.StreamHandler(sys.stderr)
925- if prefix is not None:
926- fmt = prefix + "%(name)s - %(levelname)s\n%(message)s\n"
927- formatter = logging.Formatter(fmt)
928- debug_handler.setFormatter(formatter)
929+ debug_handler.setFormatter(basic_formatter)
930 logger.addHandler(debug_handler)
931
932 return logger

Subscribers

People subscribed via source and target branches