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

Proposed by Natalia Bidart
Status: Superseded
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/start-dc-on-backend
Merge into: lp:ubuntuone-control-panel
Diff against target: 1989 lines (+1003/-414)
14 files modified
data/install.ui (+7/-2)
po/POTFILES.in (+2/-1)
pylintrc (+1/-1)
ubuntuone/controlpanel/backend.py (+41/-1)
ubuntuone/controlpanel/dbus_service.py (+41/-0)
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/integrationtests/test_dbus_service.py (+55/-7)
ubuntuone/controlpanel/logger.py (+1/-4)
ubuntuone/controlpanel/replication_client.py (+115/-0)
ubuntuone/controlpanel/tests/__init__.py (+149/-1)
ubuntuone/controlpanel/tests/test_backend.py (+116/-163)
ubuntuone/controlpanel/tests/test_replication_client.py (+160/-0)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/start-dc-on-backend
Reviewer Review Type Date Requested Status
Ubuntu One hackers Pending
Review via email: mp+45440@code.launchpad.net

Commit message

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

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

Avoiding conflicts.

56. By Natalia Bidart

Merged dependency branch in.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'data/install.ui'
--- data/install.ui 2010-12-24 14:53:03 +0000
+++ data/install.ui 2011-01-06 20:36:09 +0000
@@ -23,11 +23,12 @@
23 <property name="visible">True</property>23 <property name="visible">True</property>
24 <child>24 <child>
25 <object class="GtkButton" id="install_button">25 <object class="GtkButton" id="install_button">
26 <property name="label">gtk-ok</property>26 <property name="label" translatable="yes">_Install now</property>
27 <property name="visible">True</property>27 <property name="visible">True</property>
28 <property name="can_focus">True</property>28 <property name="can_focus">True</property>
29 <property name="receives_default">True</property>29 <property name="receives_default">True</property>
30 <property name="use_stock">True</property>30 <property name="image">image1</property>
31 <property name="use_underline">True</property>
31 <signal name="clicked" handler="on_install_button_clicked"/>32 <signal name="clicked" handler="on_install_button_clicked"/>
32 </object>33 </object>
33 <packing>34 <packing>
@@ -43,4 +44,8 @@
43 </packing>44 </packing>
44 </child>45 </child>
45 </object>46 </object>
47 <object class="GtkImage" id="image1">
48 <property name="visible">True</property>
49 <property name="stock">gtk-ok</property>
50 </object>
46</interface>51</interface>
4752
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2011-01-04 16:12:56 +0000
+++ po/POTFILES.in 2011-01-06 20:36:09 +0000
@@ -1,8 +1,9 @@
1ubuntuone-control-panel-gtk.desktop.in1ubuntuone-control-panel-gtk.desktop.in
2ubuntuone/controlpanel/gtk/gui.py2ubuntuone/controlpanel/gtk/gui.py
3[type: gettext/glade] data/dashboard.ui3[type: gettext/glade] data/dashboard.ui
4[type: gettext/glade] data/services.ui
5[type: gettext/glade] data/device.ui4[type: gettext/glade] data/device.ui
6[type: gettext/glade] data/devices.ui5[type: gettext/glade] data/devices.ui
6[type: gettext/glade] data/install.ui
7[type: gettext/glade] data/management.ui7[type: gettext/glade] data/management.ui
8[type: gettext/glade] data/overview.ui8[type: gettext/glade] data/overview.ui
9[type: gettext/glade] data/services.ui
910
=== modified file 'pylintrc'
--- pylintrc 2010-10-13 18:55:23 +0000
+++ pylintrc 2011-01-06 20:36:09 +0000
@@ -272,7 +272,7 @@
272max-line-length=79272max-line-length=79
273273
274# Maximum number of lines in a module274# Maximum number of lines in a module
275max-module-lines=2000275max-module-lines=2500
276276
277# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1277# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
278# tab).278# tab).
279279
=== modified file 'ubuntuone/controlpanel/backend.py'
--- ubuntuone/controlpanel/backend.py 2010-12-23 18:20:56 +0000
+++ ubuntuone/controlpanel/backend.py 2011-01-06 20:36:09 +0000
@@ -22,6 +22,7 @@
22from twisted.internet.defer import inlineCallbacks, returnValue22from twisted.internet.defer import inlineCallbacks, returnValue
2323
24from ubuntuone.controlpanel import dbus_client24from ubuntuone.controlpanel import dbus_client
25from ubuntuone.controlpanel import replication_client
25from ubuntuone.controlpanel.logger import setup_logging, log_call26from ubuntuone.controlpanel.logger import setup_logging, log_call
26from ubuntuone.controlpanel.webclient import WebClient27from ubuntuone.controlpanel.webclient import WebClient
2728
@@ -48,6 +49,9 @@
48MSG_KEY = 'message'49MSG_KEY = 'message'
49STATUS_KEY = 'status'50STATUS_KEY = 'status'
5051
52BOOKMARKS_PKG = 'xul-ext-bindwood'
53CONTACTS_PKG = 'evolution-couchdb'
54
5155
52def bool_str(value):56def bool_str(value):
53 """Return the string representation of a bool (dbus-compatible)."""57 """Return the string representation of a bool (dbus-compatible)."""
@@ -300,7 +304,7 @@
300304
301 """305 """
302 if 'subscribed' in settings:306 if 'subscribed' in settings:
303 subscribed = settings['subscribed']307 subscribed = bool(settings['subscribed'])
304 if subscribed:308 if subscribed:
305 yield self.subscribe_volume(volume_id)309 yield self.subscribe_volume(volume_id)
306 else:310 else:
@@ -321,6 +325,42 @@
321 yield dbus_client.unsubscribe_folder(volume_id)325 yield dbus_client.unsubscribe_folder(volume_id)
322326
323 @log_call(logger.debug)327 @log_call(logger.debug)
328 @inlineCallbacks
329 def replications_info(self):
330 """Get the user replications info."""
331 replications = yield replication_client.get_replications()
332 exclusions = yield replication_client.get_exclusions()
333
334 result = []
335 for rep in replications:
336 dependency = ''
337 if rep == replication_client.BOOKMARKS:
338 dependency = BOOKMARKS_PKG
339 elif rep == replication_client.CONTACTS:
340 dependency = CONTACTS_PKG
341
342 repd = {
343 "replication_id": rep,
344 "name": rep, # this may change to be more user friendly
345 "enabled": bool_str(rep not in exclusions),
346 "dependency": dependency,
347 }
348 result.append(repd)
349
350 returnValue(result)
351
352 @log_call(logger.info)
353 @inlineCallbacks
354 def change_replication_settings(self, replication_id, settings):
355 """Change the settings for the given replication."""
356 if 'enabled' in settings:
357 if bool(settings['enabled']):
358 yield replication_client.replicate(replication_id)
359 else:
360 yield replication_client.exclude(replication_id)
361 returnValue(replication_id)
362
363 @log_call(logger.debug)
324 def query_bookmark_extension(self):364 def query_bookmark_extension(self):
325 """True if the bookmark extension has been installed."""365 """True if the bookmark extension has been installed."""
326 # still pending (LP: #673672)366 # still pending (LP: #673672)
327367
=== modified file 'ubuntuone/controlpanel/dbus_service.py'
--- ubuntuone/controlpanel/dbus_service.py 2010-12-23 18:20:56 +0000
+++ ubuntuone/controlpanel/dbus_service.py 2011-01-06 20:36:09 +0000
@@ -339,6 +339,47 @@
339339
340 @log_call(logger.debug)340 @log_call(logger.debug)
341 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")341 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
342 def replications_info(self):
343 """Return the replications info."""
344 d = self.backend.replications_info()
345 d.addCallback(self.ReplicationsInfoReady)
346 d.addErrback(transform_failure(self.ReplicationsInfoError))
347
348 @log_call(logger.debug)
349 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}")
350 def ReplicationsInfoReady(self, info):
351 """The replications info is ready."""
352
353 @log_call(logger.error)
354 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
355 def ReplicationsInfoError(self, error):
356 """Problem getting the replications info."""
357
358 #---
359
360 @log_call(logger.info)
361 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="sa{ss}")
362 def change_replication_settings(self, replication_id, settings):
363 """Configure a given replication."""
364 d = self.backend.change_replication_settings(replication_id, settings)
365 d.addCallback(self.ReplicationSettingsChanged)
366 d.addErrback(transform_failure(self.ReplicationSettingsChangeError),
367 replication_id)
368
369 @log_call(logger.info)
370 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
371 def ReplicationSettingsChanged(self, replication_id):
372 """The settings for the replication were changed."""
373
374 @log_call(logger.error)
375 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="sa{ss}")
376 def ReplicationSettingsChangeError(self, replication_id, error):
377 """Problem changing settings for the replication."""
378
379 #---
380
381 @log_call(logger.debug)
382 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
342 def query_bookmark_extension(self):383 def query_bookmark_extension(self):
343 """Check if the extension to sync bookmarks is installed."""384 """Check if the extension to sync bookmarks is installed."""
344 d = self.backend.query_bookmark_extension()385 d = self.backend.query_bookmark_extension()
345386
=== modified file 'ubuntuone/controlpanel/gtk/gui.py'
--- ubuntuone/controlpanel/gtk/gui.py 2010-12-26 15:02:52 +0000
+++ ubuntuone/controlpanel/gtk/gui.py 2011-01-06 20:36:09 +0000
@@ -78,6 +78,12 @@
78VALUE_ERROR = _('Value could not be retrieved.')78VALUE_ERROR = _('Value could not be retrieved.')
79WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE79WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE
80KILOBYTES = 102480KILOBYTES = 1024
81NO_OP = lambda *a, **kw: None
82
83
84def error_handler(*args, **kwargs):
85 """Log errors when calling D-Bus methods in a async way."""
86 logger.error('Error handler received: %r, %r', args, kwargs)
8187
8288
83def filter_by_app_name(f):89def filter_by_app_name(f):
@@ -352,7 +358,8 @@
352 settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION,358 settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION,
353 WINDOW_ID_KEY: str(self._window_id),359 WINDOW_ID_KEY: str(self._window_id),
354 PING_URL_KEY: U1_PING_URL}360 PING_URL_KEY: U1_PING_URL}
355 self.sso_backend.register(U1_APP_NAME, settings)361 self.sso_backend.register(U1_APP_NAME, settings,
362 reply_handler=NO_OP, error_handler=error_handler)
356 self.set_property('greyed', True)363 self.set_property('greyed', True)
357 self.warning_label.set_text('')364 self.warning_label.set_text('')
358365
@@ -361,7 +368,8 @@
361 settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION,368 settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION,
362 WINDOW_ID_KEY: str(self._window_id),369 WINDOW_ID_KEY: str(self._window_id),
363 PING_URL_KEY: U1_PING_URL}370 PING_URL_KEY: U1_PING_URL}
364 self.sso_backend.login(U1_APP_NAME, settings)371 self.sso_backend.login(U1_APP_NAME, settings,
372 reply_handler=NO_OP, error_handler=error_handler)
365 self.set_property('greyed', True)373 self.set_property('greyed', True)
366 self.warning_label.set_text('')374 self.warning_label.set_text('')
367375
@@ -408,7 +416,8 @@
408 else:416 else:
409 self.set_sensitive(True)417 self.set_sensitive(True)
410 self.warning_label.set_text('')418 self.warning_label.set_text('')
411 self.sso_backend.find_credentials(U1_APP_NAME, {})419 self.sso_backend.find_credentials(U1_APP_NAME, {},
420 reply_handler=NO_OP, error_handler=error_handler)
412421
413422
414class DashboardPanel(UbuntuOneBin, ControlPanelMixin):423class DashboardPanel(UbuntuOneBin, ControlPanelMixin):
@@ -516,11 +525,13 @@
516 volume_id = checkbutton.get_label()525 volume_id = checkbutton.get_label()
517 subscribed = bool_str(checkbutton.get_active())526 subscribed = bool_str(checkbutton.get_active())
518 self.backend.change_volume_settings(volume_id,527 self.backend.change_volume_settings(volume_id,
519 {'subscribed': subscribed})528 {'subscribed': subscribed},
529 reply_handler=NO_OP, error_handler=error_handler)
520530
521 def load(self):531 def load(self):
522 """Load the volume list."""532 """Load the volume list."""
523 self.backend.volumes_info()533 self.backend.volumes_info(reply_handler=NO_OP,
534 error_handler=error_handler)
524 self.message.start()535 self.message.start()
525536
526537
@@ -565,7 +576,8 @@
565 # Not disabling the GUI to avoid annyong twitchings576 # Not disabling the GUI to avoid annyong twitchings
566 #self.set_sensitive(False)577 #self.set_sensitive(False)
567 self.warning_label.set_text('')578 self.warning_label.set_text('')
568 self.backend.change_device_settings(self.id, self.__dict__)579 self.backend.change_device_settings(self.id, self.__dict__,
580 reply_handler=NO_OP, error_handler=error_handler)
569581
570 def _block_signals(f):582 def _block_signals(f):
571 """Execute 'f' while having the _updating flag set."""583 """Execute 'f' while having the _updating flag set."""
@@ -591,7 +603,8 @@
591603
592 def on_remove_clicked(self, widget):604 def on_remove_clicked(self, widget):
593 """Remove button was clicked or activated."""605 """Remove button was clicked or activated."""
594 self.backend.remove_device(self.id)606 self.backend.remove_device(self.id,
607 reply_handler=NO_OP, error_handler=error_handler)
595 self.set_sensitive(False)608 self.set_sensitive(False)
596609
597 @_block_signals610 @_block_signals
@@ -747,7 +760,8 @@
747760
748 def load(self):761 def load(self):
749 """Load the device list."""762 """Load the device list."""
750 self.backend.devices_info()763 self.backend.devices_info(reply_handler=NO_OP,
764 error_handler=error_handler)
751 self.message.start()765 self.message.start()
752766
753767
@@ -818,12 +832,18 @@
818class Service(gtk.VBox, ControlPanelMixin):832class Service(gtk.VBox, ControlPanelMixin):
819 """A service."""833 """A service."""
820834
821 def __init__(self, name, localized_name, *args, **kwargs):835 CHANGE_ERROR = _('The settings could not be changed,\n'
836 'previous values were restored.')
837
838 def __init__(self, service_id, name, *args, **kwargs):
822 gtk.VBox.__init__(self)839 gtk.VBox.__init__(self)
823 ControlPanelMixin.__init__(self)840 ControlPanelMixin.__init__(self)
824 self.service_name = name841 self.id = service_id
825842
826 self.button = gtk.CheckButton(label=localized_name)843 self.warning_label = gtk.Label()
844 self.pack_start(self.warning_label, expand=False)
845
846 self.button = gtk.CheckButton(label=name)
827 self.pack_start(self.button, expand=False)847 self.pack_start(self.button, expand=False)
828848
829 self.show_all()849 self.show_all()
@@ -835,15 +855,18 @@
835 FILES_SERVICE_NAME = _('Files')855 FILES_SERVICE_NAME = _('Files')
836856
837 def __init__(self):857 def __init__(self):
838 Service.__init__(self, name='files',858 Service.__init__(self, service_id='files',
839 localized_name=self.FILES_SERVICE_NAME)859 name=self.FILES_SERVICE_NAME)
840860
841 self.set_sensitive(False)861 self.set_sensitive(False)
862
842 self.backend.connect_to_signal('FileSyncStatusChanged',863 self.backend.connect_to_signal('FileSyncStatusChanged',
843 self.on_file_sync_status_changed)864 self.on_file_sync_status_changed)
844 self.backend.connect_to_signal('FilesEnabled', self.on_files_enabled)865 self.backend.connect_to_signal('FilesEnabled', self.on_files_enabled)
845 self.backend.connect_to_signal('FilesDisabled', self.on_files_disabled)866 self.backend.connect_to_signal('FilesDisabled', self.on_files_disabled)
846 self.backend.file_sync_status()867
868 self.backend.file_sync_status(reply_handler=NO_OP,
869 error_handler=error_handler)
847870
848 @log_call(logger.debug)871 @log_call(logger.debug)
849 def on_file_sync_status_changed(self, status):872 def on_file_sync_status_changed(self, status):
@@ -869,19 +892,24 @@
869 """Button was toggled, exclude/replicate the service properly."""892 """Button was toggled, exclude/replicate the service properly."""
870 logger.info('File sync enabled? %r', self.button.get_active())893 logger.info('File sync enabled? %r', self.button.get_active())
871 if self.button.get_active():894 if self.button.get_active():
872 self.backend.enable_files()895 self.backend.enable_files(reply_handler=NO_OP,
896 error_handler=error_handler)
873 else:897 else:
874 self.backend.disable_files()898 self.backend.disable_files(reply_handler=NO_OP,
899 error_handler=error_handler)
875900
876901
877class DesktopcouchService(Service):902class DesktopcouchService(Service):
878 """A desktopcouch service."""903 """A desktopcouch service."""
879904
880 def __init__(self, name, localized_name,905 def __init__(self, service_id, name, enabled, dependency=None):
881 replication_service, dependency=None):906 Service.__init__(self, service_id, name)
882 Service.__init__(self, name, localized_name)907
883 self.replication_service = replication_service908 self.backend.connect_to_signal('ReplicationSettingsChanged',
884 enabled = name not in self.replication_service.all_exclusions()909 self.on_replication_settings_changed)
910 self.backend.connect_to_signal('ReplicationSettingsChangeError',
911 self.on_replication_settings_change_error)
912
885 self.button.set_active(enabled)913 self.button.set_active(enabled)
886914
887 self.dependency = None915 self.dependency = None
@@ -903,11 +931,27 @@
903 def on_button_toggled(self, button):931 def on_button_toggled(self, button):
904 """Button was toggled, exclude/replicate the service properly."""932 """Button was toggled, exclude/replicate the service properly."""
905 logger.info('Starting replication for %r? %r',933 logger.info('Starting replication for %r? %r',
906 self.service_name, self.button.get_active())934 self.id, self.button.get_active())
907 if self.button.get_active():935
908 self.replication_service.replicate(self.service_name)936 args = {'enabled': bool_str(self.button.get_active())}
909 else:937 self.backend.change_replication_settings(self.id, args,
910 self.replication_service.exclude(self.service_name)938 reply_handler=NO_OP, error_handler=error_handler)
939
940 @log_call(logger.info)
941 def on_replication_settings_changed(self, replication_id):
942 """The change of settings for this replication succeded."""
943 if replication_id != self.id:
944 return
945 self.warning_label.set_text('')
946
947 @log_call(logger.error)
948 def on_replication_settings_change_error(self, replication_id,
949 error_dict=None):
950 """The change of settings for this replication failed."""
951 if replication_id != self.id:
952 return
953 self.button.set_active(not self.button.get_active())
954 self._set_warning(self.CHANGE_ERROR, self.warning_label)
911955
912956
913class ServicesPanel(UbuntuOneBin, ControlPanelMixin):957class ServicesPanel(UbuntuOneBin, ControlPanelMixin):
@@ -916,32 +960,33 @@
916 TITLE = _('Ubuntu One services including data sync are enabled for the '960 TITLE = _('Ubuntu One services including data sync are enabled for the '
917 'data types and services listed below.')961 'data types and services listed below.')
918 CHOOSE_SERVICES = _('Choose services to synchronize with this computer:')962 CHOOSE_SERVICES = _('Choose services to synchronize with this computer:')
919 DESKTOPCOUCH_PKG = 'desktopcouch'963 DESKTOPCOUCH_PKG = 'desktopcouch-ubuntuone'
920 BINDWOOD_PKG = 'xul-ext-bindwood'
921 EVOCOUCH_PKG = 'evolution-couchdb'
922 BOOKMARKS = _('Bookmarks (Firefox)')964 BOOKMARKS = _('Bookmarks (Firefox)')
923 CONTACTS = _('Contacts (Evolution)')965 CONTACTS = _('Contacts (Evolution)')
924 NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.')966 NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.')
925967
926 def __init__(self, replication_exclusion_class=None):968 def __init__(self):
927 UbuntuOneBin.__init__(self)969 UbuntuOneBin.__init__(self)
928 ControlPanelMixin.__init__(self, filename='services.ui')970 ControlPanelMixin.__init__(self, filename='services.ui')
929 self.add(self.itself)971 self.add(self.itself)
930972
931 self.replication_exclusion_class = replication_exclusion_class
932 self.replication_service = None
933 self.has_desktopcouch = False
934 self.has_bindwood = False
935 self.has_evocouch = False
936 self.package_manager = package_manager.PackageManager()973 self.package_manager = package_manager.PackageManager()
937 self.install_box = None974 self.install_box = None
938 self.bookmarks = None975
939 self.contacts = None976 self.backend.connect_to_signal('ReplicationsInfoReady',
977 self.on_replications_info_ready)
978 self.backend.connect_to_signal('ReplicationsInfoError',
979 self.on_replications_info_error)
940980
941 self.files.pack_start(FilesService(), expand=False)981 self.files.pack_start(FilesService(), expand=False)
942982
943 self.show()983 self.show()
944984
985 @property
986 def has_desktopcouch(self):
987 """Is desktopcouch installed?"""
988 return self.package_manager.is_installed(self.DESKTOPCOUCH_PKG)
989
945 @log_call(logger.debug)990 @log_call(logger.debug)
946 def load(self):991 def load(self):
947 """Load info."""992 """Load info."""
@@ -949,16 +994,7 @@
949 self.itself.remove(self.install_box)994 self.itself.remove(self.install_box)
950 self.install_box = None995 self.install_box = None
951996
952 self.has_desktopcouch = \997 logger.info('load: has_desktopcouch? %r', self.has_desktopcouch)
953 self.package_manager.is_installed(self.DESKTOPCOUCH_PKG)
954 self.has_bindwood = \
955 self.package_manager.is_installed(self.BINDWOOD_PKG)
956 self.has_evocouch = \
957 self.package_manager.is_installed(self.EVOCOUCH_PKG)
958
959 logger.info('load: has_desktopcouch? %r has_bindwood? %s '
960 'has_evocouch? %s', self.has_desktopcouch,
961 self.has_bindwood, self.has_evocouch)
962 if not self.has_desktopcouch:998 if not self.has_desktopcouch:
963 self.message.set_text('')999 self.message.set_text('')
964 self.replications.hide()1000 self.replications.hide()
@@ -975,46 +1011,41 @@
975 @log_call(logger.debug)1011 @log_call(logger.debug)
976 def load_replications(self, *args):1012 def load_replications(self, *args):
977 """Load replications info."""1013 """Load replications info."""
1014 # ask replications to the backend
1015 self.message.start()
1016 self.backend.replications_info(reply_handler=NO_OP,
1017 error_handler=error_handler)
1018
1019 @log_call(logger.debug)
1020 def on_replications_info_ready(self, info):
1021 """The replication info is ready."""
1022 self.on_success(self.CHOOSE_SERVICES)
1023
978 self.replications.show()1024 self.replications.show()
9791025
980 if self.install_box is not None:1026 if self.install_box is not None:
981 self.itself.remove(self.install_box)1027 self.itself.remove(self.install_box)
982 self.install_box = None1028 self.install_box = None
9831029
984 self.message.set_text(self.CHOOSE_SERVICES)
985 for child in self.replications.get_children():1030 for child in self.replications.get_children():
986 self.replications.remove(child)1031 self.replications.remove(child)
9871032
988 # Unable to import 'desktopcouch.application.replication_services'1033 for item in info:
989 # pylint: disable=F04011034 pkg = item['dependency']
990 if self.replication_exclusion_class is None:1035 child = DesktopcouchService(service_id=item['replication_id'],
991 from desktopcouch.application.replication_services import \1036 name=item['name'], # self.BOOKMARKS,
992 ubuntuone as u1rep1037 enabled=bool(item['enabled']),
993 self.replication_exclusion_class = u1rep.ReplicationExclusion1038 dependency=pkg if pkg else None)
9941039 self.replications.pack_start(child, expand=False)
995 if self.replication_service is None:1040
996 try:1041 @log_call(logger.error)
997 self.replication_service = self.replication_exclusion_class()1042 def on_replications_info_error(self, error_dict=None):
998 except ValueError:1043 """The replication info can not be retrieved."""
999 logger.exception('Can not load replications:')1044 if error_dict is not None and \
1000 self._set_warning(self.NO_PAIRING_RECORD, self.message)1045 error_dict.get('error_type', None) == 'NoPairingRecord':
1001 return1046 self.on_error(self.NO_PAIRING_RECORD)
10021047 else:
1003 pkg = None1048 self.on_error()
1004 if not self.has_bindwood:
1005 pkg = self.BINDWOOD_PKG
1006 self.bookmarks = DesktopcouchService('bookmarks', self.BOOKMARKS,
1007 self.replication_service,
1008 dependency=pkg)
1009 self.replications.pack_start(self.bookmarks, expand=False)
1010
1011 pkg = None
1012 if not self.has_evocouch:
1013 pkg = self.EVOCOUCH_PKG
1014 self.contacts = DesktopcouchService('contacts', self.CONTACTS,
1015 self.replication_service,
1016 dependency=pkg)
1017 self.replications.pack_start(self.contacts, expand=False)
10181049
10191050
1020class ManagementPanel(gtk.VBox, ControlPanelMixin):1051class ManagementPanel(gtk.VBox, ControlPanelMixin):
@@ -1107,8 +1138,10 @@
11071138
1108 def load(self):1139 def load(self):
1109 """Load the account info and file sync status list."""1140 """Load the account info and file sync status list."""
1110 self.backend.account_info()1141 self.backend.account_info(reply_handler=NO_OP,
1111 self.backend.file_sync_status()1142 error_handler=error_handler)
1143 self.backend.file_sync_status(reply_handler=NO_OP,
1144 error_handler=error_handler)
1112 self.dashboard_button.clicked()1145 self.dashboard_button.clicked()
11131146
1114 @log_call(logger.debug)1147 @log_call(logger.debug)
11151148
=== modified file 'ubuntuone/controlpanel/gtk/tests/__init__.py'
--- ubuntuone/controlpanel/gtk/tests/__init__.py 2010-12-23 19:17:53 +0000
+++ ubuntuone/controlpanel/gtk/tests/__init__.py 2011-01-06 20:36:09 +0000
@@ -53,6 +53,15 @@
53 'max_upload_speed': '1000', 'max_download_speed': '72548'}, # local53 'max_upload_speed': '1000', 'max_download_speed': '72548'}, # local
54]54]
5555
56FAKE_REPLICATIONS_INFO = [
57 {'replication_id': 'foo', 'name': 'Bar',
58 'enabled': 'True', 'dependency': ''},
59 {'replication_id': 'yadda', 'name': 'Foo',
60 'enabled': '', 'dependency': 'a very weird one'},
61 {'replication_id': 'yoda', 'name': 'Figthers',
62 'enabled': 'True', 'dependency': 'other dep'},
63]
64
5665
57class FakedObject(object):66class FakedObject(object):
58 """Fake an object, record every call."""67 """Fake an object, record every call."""
@@ -117,9 +126,11 @@
117 object_path = gui.DBUS_PREFERENCES_PATH126 object_path = gui.DBUS_PREFERENCES_PATH
118 iface = gui.DBUS_PREFERENCES_IFACE127 iface = gui.DBUS_PREFERENCES_IFACE
119 exposed_methods = [128 exposed_methods = [
120 'account_info', 'devices_info', 'change_device_settings',129 'account_info', # account
121 'volumes_info', 'change_volume_settings', 'file_sync_status',130 'devices_info', 'change_device_settings', 'remove_device', # devices
122 'remove_device', 'enable_files', 'disable_files',131 'volumes_info', 'change_volume_settings', # volumes
132 'replications_info', 'change_replication_settings', # replications
133 'file_sync_status', 'enable_files', 'disable_files', # files
123 ]134 ]
124135
125136
@@ -157,13 +168,3 @@
157 yield168 yield
158 self._installed[package_name] = True169 self._installed[package_name] = True
159 gui.package_manager.return_value(FakedTransaction([package_name]))170 gui.package_manager.return_value(FakedTransaction([package_name]))
160
161
162class FakedReplication(object):
163 """Faked a DC replication exclusion."""
164
165 def __init__(self):
166 self._exclusions = set()
167 self.all_exclusions = lambda: self._exclusions
168 self.replicate = self._exclusions.remove
169 self.exclude = self._exclusions.add
170171
=== modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py'
--- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-26 15:02:52 +0000
+++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-01-06 20:36:09 +0000
@@ -26,9 +26,10 @@
2626
27from ubuntuone.controlpanel.gtk import gui27from ubuntuone.controlpanel.gtk import gui
28from ubuntuone.controlpanel.gtk.tests import (FAKE_ACCOUNT_INFO,28from ubuntuone.controlpanel.gtk.tests import (FAKE_ACCOUNT_INFO,
29 FAKE_VOLUMES_INFO, FAKE_DEVICE_INFO, FAKE_DEVICES_INFO,29 FAKE_DEVICE_INFO, FAKE_DEVICES_INFO,
30 FAKE_VOLUMES_INFO, FAKE_REPLICATIONS_INFO,
30 FakedNMState, FakedSSOBackend, FakedSessionBus, FakedInterface,31 FakedNMState, FakedSSOBackend, FakedSessionBus, FakedInterface,
31 FakedPackageManager, FakedReplication,32 FakedPackageManager,
32)33)
33from ubuntuone.controlpanel.tests import TOKEN, TestCase34from ubuntuone.controlpanel.tests import TOKEN, TestCase
34from ubuntuone.controlpanel.gtk.tests.test_package_manager import (35from ubuntuone.controlpanel.gtk.tests.test_package_manager import (
@@ -37,6 +38,8 @@
3738
38# Attribute 'yyy' defined outside __init__, access to a protected member39# Attribute 'yyy' defined outside __init__, access to a protected member
39# pylint: disable=W0201, W021240# pylint: disable=W0201, W0212
41# Too many lines in module
42# pylint: disable=C0302
4043
4144
42class BaseTestCase(TestCase):45class BaseTestCase(TestCase):
@@ -96,7 +99,9 @@
96 if backend is None:99 if backend is None:
97 backend = self.ui.backend100 backend = self.ui.backend
98 self.assertIn(method_name, backend._called)101 self.assertIn(method_name, backend._called)
99 self.assertEqual(backend._called[method_name], (args, {}))102 kwargs = {'reply_handler': gui.NO_OP,
103 'error_handler': gui.error_handler}
104 self.assertEqual(backend._called[method_name], (args, kwargs))
100105
101 def assert_warning_correct(self, warning, text):106 def assert_warning_correct(self, warning, text):
102 """Check that 'warning' is visible, showing 'text'."""107 """Check that 'warning' is visible, showing 'text'."""
@@ -1361,9 +1366,9 @@
1361 """The test suite for a service."""1366 """The test suite for a service."""
13621367
1363 klass = gui.Service1368 klass = gui.Service
1364 name = 'dc_test'1369 service_id = 'dc_test'
1365 localized_name = u'Qué lindo test!'1370 name = u'Qué lindo test!'
1366 kwargs = {'name': 'dc_test', 'localized_name': u'Qué lindo test!'}1371 kwargs = {'service_id': service_id, 'name': name}
13671372
1368 def test_is_an_box(self):1373 def test_is_an_box(self):
1369 """Inherits from gtk.VBox."""1374 """Inherits from gtk.VBox."""
@@ -1373,30 +1378,35 @@
1373 """Is visible."""1378 """Is visible."""
1374 self.assertTrue(self.ui.get_visible())1379 self.assertTrue(self.ui.get_visible())
13751380
1381 def test_warning_label_is_cleared(self):
1382 """The warning label is cleared."""
1383 self.assertEqual(self.ui.warning_label.get_text(), '')
1384
1385 def test_warning_label_packed(self):
1386 """The warning label is packed as child."""
1387 self.assertIn(self.ui.warning_label, self.ui.get_children())
1388
1376 def test_check_button_packed(self):1389 def test_check_button_packed(self):
1377 """A check button is packed as only child."""1390 """A check button is packed as child."""
1378 self.assertIn(self.ui.button, self.ui.get_children())1391 self.assertIn(self.ui.button, self.ui.get_children())
13791392
1380 def test_label(self):1393 def test_label(self):
1381 """The label is set."""1394 """The label is set."""
1382 self.assertEqual(self.localized_name, self.ui.button.get_label())1395 self.assertEqual(self.name, self.ui.button.get_label())
13831396
1384 def test_service_name(self):1397 def test_service_id(self):
1385 """The service_name is set."""1398 """The service id is set."""
1386 self.assertEqual(self.name, self.ui.service_name)1399 self.assertEqual(self.service_id, self.ui.id)
13871400
13881401
1389class FilesServiceTestCase(ServiceTestCase):1402class FilesServiceTestCase(ServiceTestCase):
1390 """The test suite for the file sync service."""1403 """The test suite for the file sync service."""
13911404
1392 klass = gui.FilesService1405 klass = gui.FilesService
1406 service_id = 'files'
1407 name = gui.FilesService.FILES_SERVICE_NAME
1393 kwargs = {}1408 kwargs = {}
13941409
1395 def setUp(self):
1396 self.name = 'files'
1397 self.localized_name = gui.FilesService.FILES_SERVICE_NAME
1398 super(FilesServiceTestCase, self).setUp()
1399
1400 def test_backend_account_signals(self):1410 def test_backend_account_signals(self):
1401 """The proper signals are connected to the backend."""1411 """The proper signals are connected to the backend."""
1402 self.assertEqual(self.ui.backend._signals['FileSyncStatusChanged'],1412 self.assertEqual(self.ui.backend._signals['FileSyncStatusChanged'],
@@ -1461,35 +1471,36 @@
1461 """The test suite for a desktopcouch service."""1471 """The test suite for a desktopcouch service."""
14621472
1463 klass = gui.DesktopcouchService1473 klass = gui.DesktopcouchService
1474 enabled = True
14641475
1465 def setUp(self):1476 def setUp(self):
1466 self.replication = FakedReplication()1477 self.kwargs['enabled'] = self.enabled
1467 self.name = self.kwargs['name']
1468 self.kwargs['replication_service'] = self.replication
1469 super(DesktopcouchServiceTestCase, self).setUp()1478 super(DesktopcouchServiceTestCase, self).setUp()
14701479
1480 def modify_settings(self):
1481 """Modify settings so values actually change."""
1482 self.ui.button.set_active(not self.ui.button.get_active())
1483
1484 def test_backend_account_signals(self):
1485 """The proper signals are connected to the backend."""
1486 self.assertEqual(
1487 self.ui.backend._signals['ReplicationSettingsChanged'],
1488 [self.ui.on_replication_settings_changed])
1489 self.assertEqual(
1490 self.ui.backend._signals['ReplicationSettingsChangeError'],
1491 [self.ui.on_replication_settings_change_error])
1492
1471 def test_active(self):1493 def test_active(self):
1472 """Is active since replication has an empty database."""1494 """Is active if enabled."""
1473 self.assertTrue(self.ui.button.get_active())1495 self.assertEqual(self.enabled, self.ui.button.get_active())
1474
1475 def test_not_active(self):
1476 """Is not active since 'name' is excluded on replication database."""
1477 self.replication.exclude(self.name)
1478 self.ui = self.klass(**self.kwargs)
1479 self.assertFalse(self.ui.button.get_active())
14801496
1481 def test_on_button_toggled(self):1497 def test_on_button_toggled(self):
1482 """When toggling the button, the DC exclude list is updated."""1498 """When toggling the button, the DC exclude list is updated."""
1483 assert self.ui.button.get_active()
1484 self.ui.button.set_active(not self.ui.button.get_active())1499 self.ui.button.set_active(not self.ui.button.get_active())
1485 self.assertEqual(set([self.name]), self.replication.all_exclusions())
14861500
1487 def test_on_button_toggled_twice(self):1501 args = (self.service_id,
1488 """When toggling the button twice, the DC exclude list is updated."""1502 {'enabled': gui.bool_str(self.ui.button.get_active())})
1489 assert self.ui.button.get_active()1503 self.assert_backend_called('change_replication_settings', args)
1490 self.ui.button.set_active(not self.ui.button.get_active())
1491 self.ui.button.set_active(not self.ui.button.get_active())
1492 self.assertEqual(set(), self.replication.all_exclusions())
14931504
1494 def test_dependency(self):1505 def test_dependency(self):
1495 """The dependency box is None."""1506 """The dependency box is None."""
@@ -1499,6 +1510,72 @@
1499 """The check button is sensitive."""1510 """The check button is sensitive."""
1500 self.assertTrue(self.ui.button.get_sensitive())1511 self.assertTrue(self.ui.button.get_sensitive())
15011512
1513 def test_on_replication_settings_changed(self):
1514 """When settings were changed for this replication, enable it."""
1515 new_val = not self.ui.button.get_active()
1516 self.ui.button.set_active(new_val)
1517
1518 self.ui.on_replication_settings_changed(replication_id=self.ui.id)
1519
1520 self.assertEqual(self.ui.warning_label.get_text(), '')
1521 self.assertEqual(new_val, self.ui.button.get_active())
1522
1523 def test_on_replication_settings_changed_after_error(self):
1524 """Change success after error."""
1525 self.ui.button.set_active(not self.ui.button.get_active())
1526 self.ui.on_replication_settings_change_error(replication_id=self.ui.id)
1527
1528 self.test_on_replication_settings_changed()
1529
1530 def test_on_replication_settings_changed_different_id(self):
1531 """When settings were changed for other rep, nothing changes."""
1532 self.ui.button.set_active(not self.ui.button.get_active())
1533 self.ui.on_replication_settings_changed(replication_id='yadda')
1534
1535 self.assertEqual(self.ui.warning_label.get_text(), '')
1536
1537 def test_on_replication_settings_changed_different_id_after_error(self):
1538 """When settings were changed for other + error, nothing changes."""
1539 self.ui.on_replication_settings_change_error(replication_id=self.ui.id)
1540 self.ui.on_replication_settings_changed(replication_id='yadda')
1541
1542 self.assert_warning_correct(self.ui.warning_label,
1543 self.ui.CHANGE_ERROR)
1544
1545 def test_on_replication_settings_change_error(self):
1546 """When settings were not changed, notify the user.
1547
1548 Also, confirm that old value was restored.
1549
1550 """
1551 old_val = self.ui.button.get_active()
1552 self.ui.button.set_active(not old_val)
1553 self.ui.on_replication_settings_change_error(replication_id=self.ui.id)
1554
1555 self.assert_warning_correct(self.ui.warning_label,
1556 self.ui.CHANGE_ERROR)
1557 self.assertEqual(old_val, self.ui.button.get_active())
1558
1559 def test_on_replication_settings_change_error_after_success(self):
1560 """Change error after success."""
1561 self.ui.button.set_active(not self.ui.button.get_active())
1562 self.ui.on_replication_settings_changed(replication_id=self.ui.id)
1563
1564 self.test_on_replication_settings_change_error()
1565
1566 def test_on_replication_settings_change_error_different_id(self):
1567 """When settings were not changed for other replication, do nothing."""
1568 self.ui.button.set_active(not self.ui.button.get_active())
1569 self.ui.on_replication_settings_change_error(replication_id='yudo')
1570
1571 self.assertEqual(self.ui.warning_label.get_text(), '')
1572
1573
1574class DesktopcouchServiceDisabledAtStartupTestCase(ServiceTestCase):
1575 """The test suite for a desktopcouch service when enabled=False."""
1576
1577 enabled = False
1578
15021579
1503class DesktopcouchServiceWithDependencyTestCase(DesktopcouchServiceTestCase):1580class DesktopcouchServiceWithDependencyTestCase(DesktopcouchServiceTestCase):
1504 """The test suite for a desktopcouch service when it needs a dependency."""1581 """The test suite for a desktopcouch service when it needs a dependency."""
@@ -1532,7 +1609,8 @@
1532 self.ui.dependency.emit('finished')1609 self.ui.dependency.emit('finished')
15331610
1534 self.assertTrue(self.ui.dependency is None)1611 self.assertTrue(self.ui.dependency is None)
1535 self.assertEqual(self.ui.get_children(), [self.ui.button])1612 self.assertEqual(sorted(self.ui.get_children()),
1613 sorted([self.ui.button, self.ui.warning_label]))
15361614
15371615
1538class ServicesTestCase(ControlPanelMixinTestCase):1616class ServicesTestCase(ControlPanelMixinTestCase):
@@ -1562,6 +1640,13 @@
1562 """The install box is None."""1640 """The install box is None."""
1563 self.assertTrue(self.ui.install_box is None)1641 self.assertTrue(self.ui.install_box is None)
15641642
1643 def test_backend_signals(self):
1644 """The proper signals are connected to the backend."""
1645 self.assertEqual(self.ui.backend._signals['ReplicationsInfoReady'],
1646 [self.ui.on_replications_info_ready])
1647 self.assertEqual(self.ui.backend._signals['ReplicationsInfoError'],
1648 [self.ui.on_replications_info_error])
1649
15651650
1566class ServicesFilesTestCase(ServicesTestCase):1651class ServicesFilesTestCase(ServicesTestCase):
1567 """The test suite for the services panel (files section)."""1652 """The test suite for the services panel (files section)."""
@@ -1589,22 +1674,10 @@
1589 self.assertFalse(self.ui.message.active)1674 self.assertFalse(self.ui.message.active)
1590 self.assertEqual(self.ui.message.get_text(), '')1675 self.assertEqual(self.ui.message.get_text(), '')
15911676
1592 def test_replication_service(self):
1593 """Has a replication service."""
1594 self.assertEqual(self.ui.replication_service, None)
1595
1596 def test_has_desktopcouch(self):1677 def test_has_desktopcouch(self):
1597 """Has desktopcouch installed?"""1678 """Has desktopcouch installed?"""
1598 self.assertFalse(self.ui.has_desktopcouch)1679 self.assertFalse(self.ui.has_desktopcouch)
15991680
1600 def test_has_bindwood(self):
1601 """Has bindwood installed?"""
1602 self.assertFalse(self.ui.has_bindwood)
1603
1604 def test_has_evocouch(self):
1605 """Has evocouch installed?"""
1606 self.assertFalse(self.ui.has_evocouch)
1607
1608 def test_install_box_is_hidden(self):1681 def test_install_box_is_hidden(self):
1609 """The install box is not hidden."""1682 """The install box is not hidden."""
1610 self.assertTrue(self.ui.install_box.get_visible())1683 self.assertTrue(self.ui.install_box.get_visible())
@@ -1629,41 +1702,27 @@
16291702
1630 self.assertEqual(self._called, ((self.ui.install_box,), {}))1703 self.assertEqual(self._called, ((self.ui.install_box,), {}))
16311704
1705 def test_load_replications(self):
1706 """The load_replications starts the spinner and calls the backend."""
1707 self.ui.load_replications()
1708
1709 self.assertTrue(self.ui.message.active)
1710 self.assert_backend_called('replications_info', ())
1711
16321712
1633class ServicesWithDesktopcouchTestCase(ServicesTestCase):1713class ServicesWithDesktopcouchTestCase(ServicesTestCase):
1634 """The test suite for the services panel."""1714 """The test suite for the services panel."""
16351715
1636 kwargs = {'replication_exclusion_class': FakedReplication}
1637
1638 def setUp(self):1716 def setUp(self):
1639 super(ServicesWithDesktopcouchTestCase, self).setUp()1717 super(ServicesWithDesktopcouchTestCase, self).setUp()
1640 self.ui.package_manager._installed[self.ui.DESKTOPCOUCH_PKG] = True1718 self.ui.package_manager._installed[self.ui.DESKTOPCOUCH_PKG] = True
1641 self.ui.load()1719 self.ui.on_replications_info_ready(info=FAKE_REPLICATIONS_INFO)
16421720
1643 def test_message(self):1721 def test_message(self):
1644 """Global load message is stopped and proper test is shown."""1722 """Global load message is stopped and proper test is shown."""
1645 self.assertFalse(self.ui.message.active)1723 self.assertFalse(self.ui.message.active)
1646 self.assertEqual(self.ui.message.get_text(), self.ui.CHOOSE_SERVICES)1724 self.assertEqual(self.ui.message.get_text(), self.ui.CHOOSE_SERVICES)
16471725
1648 def test_replication_service(self):
1649 """Has a replication service."""
1650 self.assertIsInstance(self.ui.replication_service, FakedReplication)
1651
1652 def test_no_pairing_record(self):
1653 """The pairing record is not in place."""
1654
1655 def no_pairing_record(*a):
1656 """Fake a ReplicationExclusion with no pairing record."""
1657 raise ValueError("No pairing record for ubuntuone.")
1658
1659 self.ui.replication_exclusion_class = no_pairing_record
1660 self.ui.replication_service = None
1661 self.ui.load()
1662
1663 self.assertEqual(self.ui.replications.get_children(), [])
1664 self.assertFalse(self.ui.message.active)
1665 self.assert_warning_correct(self.ui.message, self.ui.NO_PAIRING_RECORD)
1666
1667 def test_has_desktopcouch(self):1726 def test_has_desktopcouch(self):
1668 """Has desktopcouch installed?"""1727 """Has desktopcouch installed?"""
1669 self.assertTrue(self.ui.has_desktopcouch)1728 self.assertTrue(self.ui.has_desktopcouch)
@@ -1673,79 +1732,67 @@
1673 self.assertTrue(self.ui.replications.get_visible())1732 self.assertTrue(self.ui.replications.get_visible())
16741733
1675 children = self.ui.replications.get_children()1734 children = self.ui.replications.get_children()
1676 self.assertEqual(len(children), 2)1735 self.assertEqual(len(children), len(FAKE_REPLICATIONS_INFO))
1677 for child in children:1736 for expected, child in zip(FAKE_REPLICATIONS_INFO, children):
1678 self.assertIsInstance(child, gui.DesktopcouchService)1737 self.assertIsInstance(child, gui.DesktopcouchService)
16791738 self.assertEqual(expected['replication_id'], child.id)
1680 self.assertTrue(self.ui.bookmarks is children[0])1739 self.assertEqual(expected['name'], child.button.get_label())
1681 self.assertTrue(self.ui.contacts is children[1])1740 self.assertEqual(bool(expected['enabled']),
16821741 child.button.get_active())
1683 def test_replications_after_loading_twice(self):1742
1684 """Has proper child after loading twice."""1743 if expected['dependency']:
1685 self.ui.load()1744 self.assertTrue(child.dependency is not None)
1745 self.assertEqual(expected['dependency'],
1746 child.dependency.package_name)
1747 else:
1748 self.assertTrue(child.dependency is None)
1749
1750 def test_replications_after_getting_info_twice(self):
1751 """Has proper child after getting backend info twice."""
1752 self.ui.on_replications_info_ready(info=FAKE_REPLICATIONS_INFO)
1686 self.test_replications()1753 self.test_replications()
16871754
1688 def test_bookmarks(self):1755
1689 """The bookmarks is correct."""1756class ServicesWithDesktopcouchErrorTestCase(ServicesTestCase):
1690 self.assertEqual(self.ui.bookmarks.service_name, 'bookmarks')1757 """The test suite for the services panel."""
1691 self.assertEqual(self.ui.bookmarks.button.get_label(),1758
1692 self.ui.BOOKMARKS)1759 def setUp(self):
1693 self.assertTrue(self.ui.bookmarks.replication_service is1760 super(ServicesWithDesktopcouchErrorTestCase, self).setUp()
1694 self.ui.replication_service)1761 self.ui.package_manager._installed[self.ui.DESKTOPCOUCH_PKG] = True
16951762
1696 def test_bookmarks_dependency(self):1763 def test_no_pairing_record(self):
1697 """The bookmarks dependency is correct."""1764 """The pairing record is not in place."""
1698 self.assertTrue(self.ui.bookmarks.dependency is not None)1765 error_dict = {'error_type': 'NoPairingRecord'}
1699 self.assertEqual(self.ui.bookmarks.dependency.package_name,1766 self.ui.on_replications_info_error(error_dict)
1700 self.ui.BINDWOOD_PKG)1767
17011768 self.assertEqual(self.ui.replications.get_children(), [])
1702 def test_contacts(self):1769 self.assertFalse(self.ui.message.active)
1703 """The contacts is correct."""1770 self.assert_warning_correct(self.ui.message, self.ui.NO_PAIRING_RECORD)
1704 self.assertEqual(self.ui.contacts.service_name, 'contacts')1771
1705 self.assertEqual(self.ui.contacts.button.get_label(),1772 def test_other_error(self):
1706 self.ui.CONTACTS)1773 """There was an error other than no pairing record."""
1707 self.assertTrue(self.ui.contacts.replication_service is1774 error_dict = {'error_type': 'OtherError'}
1708 self.ui.replication_service)1775 self.ui.on_replications_info_error(error_dict)
17091776
1710 def test_contacts_dependency(self):1777 self.assertEqual(self.ui.replications.get_children(), [])
1711 """The contacts dependency is correct."""1778 self.assertFalse(self.ui.message.active)
1712 self.assertTrue(self.ui.contacts.dependency is not None)1779 self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
1713 self.assertEqual(self.ui.contacts.dependency.package_name,1780
1714 self.ui.EVOCOUCH_PKG)1781 def test_empty_dict(self):
17151782 """Handle empty dicts errors."""
17161783 self.ui.on_replications_info_error(error_dict={})
1717class ServicesWithDCAndBindwoodTestCase(ServicesWithDesktopcouchTestCase):1784
1718 """The test suite for the services panel."""1785 self.assertEqual(self.ui.replications.get_children(), [])
17191786 self.assertFalse(self.ui.message.active)
1720 def setUp(self):1787 self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
1721 super(ServicesWithDCAndBindwoodTestCase, self).setUp()1788
1722 self.ui.package_manager._installed[self.ui.BINDWOOD_PKG] = True1789 def test_error_dict_none(self):
1723 self.ui.load()1790 """HGandle empty dicts errors."""
17241791 self.ui.on_replications_info_error(error_dict=None)
1725 def test_has_bindwood(self):1792
1726 """Has bindwood installed?"""1793 self.assertEqual(self.ui.replications.get_children(), [])
1727 self.assertTrue(self.ui.has_bindwood)1794 self.assertFalse(self.ui.message.active)
17281795 self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
1729 def test_bookmarks_dependency(self):
1730 """The bookmarks dependency is correct."""
1731 self.assertTrue(self.ui.bookmarks.dependency is None)
1732
1733
1734class ServicesWithDCAndEvocouchTestCase(ServicesWithDesktopcouchTestCase):
1735 """The test suite for the services panel."""
1736
1737 def setUp(self):
1738 super(ServicesWithDCAndEvocouchTestCase, self).setUp()
1739 self.ui.package_manager._installed[self.ui.EVOCOUCH_PKG] = True
1740 self.ui.load()
1741
1742 def test_has_evocouch(self):
1743 """Has evocoucg installed?"""
1744 self.assertTrue(self.ui.has_evocouch)
1745
1746 def test_contacts_dependency(self):
1747 """The bookmarks dependency is correct."""
1748 self.assertTrue(self.ui.contacts.dependency is None)
17491796
17501797
1751class ManagementPanelTestCase(ControlPanelMixinTestCase):1798class ManagementPanelTestCase(ControlPanelMixinTestCase):
17521799
=== modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_service.py'
--- ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2010-12-23 18:20:56 +0000
+++ ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2011-01-06 20:36:09 +0000
@@ -84,6 +84,11 @@
84 },84 },
85]85]
8686
87SAMPLE_REPLICATIONS_INFO = [
88 {'replication_id': 'yadda', 'wait for it': 'awesome'},
89 {'replication_id': 'yoda', 'something else': 'awesome'},
90]
91
8792
88class DBusServiceMainTestCase(mocker.MockerTestCase):93class DBusServiceMainTestCase(mocker.MockerTestCase):
89 """Tests for the main function."""94 """Tests for the main function."""
@@ -166,6 +171,19 @@
166 """Configure a given volume."""171 """Configure a given volume."""
167 return self._process(volume_id)172 return self._process(volume_id)
168173
174 def replications_info(self):
175 """Start the replication exclusion service if needed.
176
177 Return the replication info, which is a dictionary of (replication
178 name, enabled).
179
180 """
181 return self._process(SAMPLE_REPLICATIONS_INFO)
182
183 def change_replication_settings(self, replication_id, settings):
184 """Configure a given replication."""
185 return self._process(replication_id)
186
169 def query_bookmark_extension(self):187 def query_bookmark_extension(self):
170 """True if the bookmark extension has been installed."""188 """True if the bookmark extension has been installed."""
171 return self._process(False)189 return self._process(False)
@@ -258,13 +276,13 @@
258 self.assertEqual(expected, result)276 self.assertEqual(expected, result)
259277
260278
261class OperationsTestCase(TestCase):279class BaseTestCase(TestCase):
262 """Test for the DBus service operations."""280 """Base test case for the DBus service."""
263281
264 timeout = 3282 timeout = 3
265283
266 def setUp(self):284 def setUp(self):
267 super(OperationsTestCase, self).setUp()285 super(BaseTestCase, self).setUp()
268 dbus_service.init_mainloop()286 dbus_service.init_mainloop()
269 be = dbus_service.publish_backend(MockBackend())287 be = dbus_service.publish_backend(MockBackend())
270 self.addCleanup(be.remove_from_connection)288 self.addCleanup(be.remove_from_connection)
@@ -279,7 +297,7 @@
279 def tearDown(self):297 def tearDown(self):
280 self.backend = None298 self.backend = None
281 self.deferred = None299 self.deferred = None
282 super(OperationsTestCase, self).tearDown()300 super(BaseTestCase, self).tearDown()
283301
284 def got_error(self, *a):302 def got_error(self, *a):
285 """Some error happened in the DBus call."""303 """Some error happened in the DBus call."""
@@ -322,6 +340,10 @@
322340
323 return self.deferred341 return self.deferred
324342
343
344class OperationsTestCase(BaseTestCase):
345 """Test for the DBus service operations."""
346
325 def test_account_info_returned(self):347 def test_account_info_returned(self):
326 """The account info is successfully returned."""348 """The account info is successfully returned."""
327349
@@ -416,9 +438,9 @@
416 def test_volumes_info(self):438 def test_volumes_info(self):
417 """The volumes info is reported."""439 """The volumes info is reported."""
418440
419 def got_signal(volumes_dict):441 def got_signal(volumes):
420 """The correct info was received."""442 """The correct info was received."""
421 self.assertEqual(volumes_dict, SAMPLE_VOLUMES_INFO)443 self.assertEqual(volumes, SAMPLE_VOLUMES_INFO)
422 self.deferred.callback("success")444 self.deferred.callback("success")
423445
424 args = ("VolumesInfoReady", "VolumesInfoError", got_signal,446 args = ("VolumesInfoReady", "VolumesInfoError", got_signal,
@@ -439,6 +461,32 @@
439 expected_volume_id, {'subscribed': ''})461 expected_volume_id, {'subscribed': ''})
440 return self.assert_correct_method_call(*args)462 return self.assert_correct_method_call(*args)
441463
464 def test_replications_info(self):
465 """The replications info is reported."""
466
467 def got_signal(replications):
468 """The correct info was received."""
469 self.assertEqual(replications, SAMPLE_REPLICATIONS_INFO)
470 self.deferred.callback("success")
471
472 args = ("ReplicationsInfoReady", "ReplicationsInfoError", got_signal,
473 self.backend.replications_info)
474 return self.assert_correct_method_call(*args)
475
476 def test_change_replication_settings(self):
477 """The replication settings are successfully changed."""
478 expected_replication_id = SAMPLE_REPLICATIONS_INFO[0]['replication_id']
479
480 def got_signal(replication_id):
481 """The correct replication was changed."""
482 self.assertEqual(replication_id, expected_replication_id)
483 self.deferred.callback("success")
484
485 args = ("ReplicationSettingsChanged", "ReplicationSettingsChangeError",
486 got_signal, self.backend.change_replication_settings,
487 expected_replication_id, {'enabled': ''})
488 return self.assert_correct_method_call(*args)
489
442 def test_query_bookmarks_extension(self):490 def test_query_bookmarks_extension(self):
443 """The bookmarks extension is queried."""491 """The bookmarks extension is queried."""
444492
@@ -495,7 +543,7 @@
495 error_sig, success_sig, got_error_signal, method, *args)543 error_sig, success_sig, got_error_signal, method, *args)
496544
497545
498class FileSyncTestCase(OperationsTestCase):546class FileSyncTestCase(BaseTestCase):
499 """Test for the DBus service when requesting file sync status."""547 """Test for the DBus service when requesting file sync status."""
500548
501 def assert_correct_status_signal(self, status, sync_signal,549 def assert_correct_status_signal(self, status, sync_signal,
502550
=== modified file 'ubuntuone/controlpanel/logger.py'
--- ubuntuone/controlpanel/logger.py 2010-12-23 18:20:56 +0000
+++ ubuntuone/controlpanel/logger.py 2011-01-06 20:36:09 +0000
@@ -53,10 +53,7 @@
53 logger.addHandler(MAIN_HANDLER)53 logger.addHandler(MAIN_HANDLER)
54 if os.environ.get('DEBUG'):54 if os.environ.get('DEBUG'):
55 debug_handler = logging.StreamHandler(sys.stderr)55 debug_handler = logging.StreamHandler(sys.stderr)
56 if prefix is not None:56 debug_handler.setFormatter(basic_formatter)
57 fmt = prefix + "%(name)s - %(levelname)s\n%(message)s\n"
58 formatter = logging.Formatter(fmt)
59 debug_handler.setFormatter(formatter)
60 logger.addHandler(debug_handler)57 logger.addHandler(debug_handler)
6158
62 return logger59 return logger
6360
=== added file 'ubuntuone/controlpanel/replication_client.py'
--- ubuntuone/controlpanel/replication_client.py 1970-01-01 00:00:00 +0000
+++ ubuntuone/controlpanel/replication_client.py 2011-01-06 20:36:09 +0000
@@ -0,0 +1,115 @@
1# -*- coding: utf-8 -*-
2
3# Authors: Natalia B. Bidart <nataliabidart@canonical.com>
4#
5# Copyright 2010 Canonical Ltd.
6#
7# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published
9# by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranties of
13# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14# PURPOSE. See the GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program. If not, see <http://www.gnu.org/licenses/>.
18
19"""Client to use replication services."""
20
21from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
22
23from ubuntuone.controlpanel.logger import setup_logging
24
25
26logger = setup_logging('replication_client')
27
28BOOKMARKS = 'bookmarks'
29CONTACTS = 'contacts'
30# we should get this list from somewhere else
31REPLICATIONS = set([BOOKMARKS, CONTACTS])
32
33
34class ReplicationError(Exception):
35 """A replication error."""
36
37
38class NoPairingRecord(ReplicationError):
39 """There is no pairing record."""
40
41
42class InvalidIdError(ReplicationError):
43 """The replication id is not valid."""
44
45
46class NotExcludedError(ReplicationError):
47 """The replication can not be replicated since is not excluded."""
48
49
50class AlreadyExcludedError(ReplicationError):
51 """The replication can not be excluded since is already excluded."""
52
53
54def get_replication_proxy(replication_module=None):
55 """Return a proxy to the replication client."""
56 d = Deferred()
57 if replication_module is None:
58 # delay import in case DC is not installed at module import time
59 # Unable to import 'desktopcouch.application.replication_services'
60 # pylint: disable=F0401
61 from desktopcouch.application.replication_services \
62 import ubuntuone as replication_module
63 try:
64 result = replication_module.ReplicationExclusion()
65 except ValueError:
66 d.errback(NoPairingRecord())
67 else:
68 d.callback(result)
69
70 return d
71
72
73@inlineCallbacks
74def get_replications():
75 """Retrieve the list of replications."""
76 yield get_replication_proxy()
77 returnValue(REPLICATIONS)
78
79
80@inlineCallbacks
81def get_exclusions():
82 """Retrieve the list of exclusions."""
83 proxy = yield get_replication_proxy()
84 result = proxy.all_exclusions()
85 returnValue(result)
86
87
88@inlineCallbacks
89def replicate(replication_id):
90 """Remove replication_id from the exclusions list."""
91 replications = yield get_replications()
92 if replication_id not in replications:
93 raise InvalidIdError(replication_id)
94
95 exclusions = yield get_exclusions()
96 if replication_id not in exclusions:
97 raise NotExcludedError(replication_id)
98
99 proxy = yield get_replication_proxy()
100 yield proxy.replicate(replication_id)
101
102
103@inlineCallbacks
104def exclude(replication_id):
105 """Add replication_id to the exclusions list."""
106 replications = yield get_replications()
107 if replication_id not in replications:
108 raise InvalidIdError(replication_id)
109
110 exclusions = yield get_exclusions()
111 if replication_id in exclusions:
112 raise AlreadyExcludedError(replication_id)
113
114 proxy = yield get_replication_proxy()
115 yield proxy.exclude(replication_id)
0116
=== modified file 'ubuntuone/controlpanel/tests/__init__.py'
--- ubuntuone/controlpanel/tests/__init__.py 2010-12-20 16:11:13 +0000
+++ ubuntuone/controlpanel/tests/__init__.py 2011-01-06 20:36:09 +0000
@@ -24,9 +24,157 @@
24TOKEN = {u'consumer_key': u'xQ7xDAz',24TOKEN = {u'consumer_key': u'xQ7xDAz',
25 u'consumer_secret': u'KzCJWCTNbbntwfyCKKjomJDzlgqxLy',25 u'consumer_secret': u'KzCJWCTNbbntwfyCKKjomJDzlgqxLy',
26 u'token_name': u'test',26 u'token_name': u'test',
27 u'token': u'GkInOfSMGwTXAUoVQwLUoPxElEEUdhsLVNTPhxHJDUIeHCPNEo',27 u'token': u'ABCDEF01234-localtoken',
28 u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'}28 u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'}
2929
30SAMPLE_ACCOUNT_JSON = """
31{
32 "username": "andrewpz",
33 "openid": "https://login.launchpad.net/+id/abcdefg",
34 "first_name": "Andrew P.",
35 "last_name": "Zoilo",
36 "couchdb": {
37 "host": "https://couchdb.one.ubuntu.com",
38 "root": "https://couchdb.one.ubuntu.com/u/abc/def/12345",
39 "dbpath": "u/abc/def/12345"
40 },
41 "couchdb_root": "https://couchdb.one.ubuntu.com/u/abc/def/12345",
42 "email": "andrewpz@protocultura.net",%s
43 "nickname": "Andrew P. Zoilo",
44 "id": 12345,
45 "subscription": {
46 "upgrade_available": false,
47 "description": "Paid Plan, 50 GB of storage",
48 "trial": false,
49 "started": "2010-03-24T18:38:38Z",
50 "is_paid": true,
51 "expires": null,
52 "qty": 1,
53 "price": 0.0,
54 "currency": null,
55 "id": 654321,
56 "name": "50 GB"
57 }
58}
59"""
60
61CURRENT_PLAN = "Ubuntu One Basic (2 GB) + 1 x 20-Pack with 20 GB (monthly)"
62SAMPLE_CURRENT_PLAN = '\n "current_plan": "%s",' % CURRENT_PLAN
63
64SAMPLE_ACCOUNT_NO_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % ''
65SAMPLE_ACCOUNT_WITH_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % SAMPLE_CURRENT_PLAN
66
67
68SAMPLE_QUOTA_JSON = """
69{
70 "total": 53687091200,
71 "used": 2350345156
72}
73"""
74
75EXPECTED_ACCOUNT_INFO = {
76 "quota_used": "2350345156",
77 "quota_total": "53687091200",
78 "type": "Paid Plan, 50 GB of storage",
79 "name": "Andrew P. Zoilo",
80 "email": "andrewpz@protocultura.net",
81}
82
83EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN = {
84 "quota_used": "2350345156",
85 "quota_total": "53687091200",
86 "type": CURRENT_PLAN,
87 "name": "Andrew P. Zoilo",
88 "email": "andrewpz@protocultura.net",
89}
90
91SAMPLE_DEVICES_JSON = """
92[
93 {
94 "token": "ABCDEF01234token",
95 "description": "Ubuntu One @ darkstar",
96 "kind": "Computer"
97 },
98 {
99 "token": "ABCDEF01234-localtoken",
100 "description": "Ubuntu One @ localhost",
101 "kind": "Computer"
102 },
103 {
104 "kind": "Phone",
105 "description": "Nokia E65",
106 "id": 1000
107 }
108]
109"""
110
111EXPECTED_DEVICES_INFO = [
112 {
113 "device_id": "ComputerABCDEF01234token",
114 "name": "Ubuntu One @ darkstar",
115 "type": "Computer",
116 "is_local": '',
117 "configurable": '',
118 },
119 {
120 'is_local': 'True',
121 'configurable': 'True',
122 'device_id': 'ComputerABCDEF01234-localtoken',
123 'limit_bandwidth': '',
124 'max_download_speed': '-1',
125 'max_upload_speed': '-1',
126 'name': 'Ubuntu One @ localhost',
127 'type': 'Computer'
128 },
129 {
130 "device_id": "Phone1000",
131 "name": "Nokia E65",
132 "type": "Phone",
133 "configurable": '',
134 "is_local": '',
135 },
136]
137
138SAMPLE_FOLDERS = [
139 {u'generation': u'2', u'node_id': u'341da068-81d8-437a-8f75-5bb9d86455ba',
140 u'path': u'/home/tester/Public', u'subscribed': u'True',
141 u'suggested_path': u'~/Public',
142 u'type': u'UDF', u'volume_id': u'9ea892f8-15fa-4201-bdbf-8de99fa5f588'},
143 {u'generation': u'', u'node_id': u'11fbc86c-0d7a-49f5-ae83-8402caf66c6a',
144 u'path': u'/home/tester/Documents', u'subscribed': u'',
145 u'suggested_path': u'~/Documents',
146 u'type': u'UDF', u'volume_id': u'2db262f5-a151-4c19-969c-bb5ced753c61'},
147 {u'generation': u'24', u'node_id': u'9ee0e130-a7c7-4d76-a5e3-5df506221b48',
148 u'path': u'/home/tester/Pictures/Photos', u'subscribed': u'True',
149 u'suggested_path': u'~/Pictures/Photos',
150 u'type': u'UDF', u'volume_id': u'1deb2874-3d28-46ae-9999-d5f48de9f460'},
151]
152
153SAMPLE_SHARES = [
154 {u'accepted': u'True', u'access_level': u'View',
155 u'free_bytes': u'39892622746', u'generation': u'2704',
156 u'name': u're', u'node_id': u'c483f419-ed28-490a-825d-a8c074e2d795',
157 u'other_username': u'otheruser', u'other_visible_name': u'Other User',
158 u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User',
159 u'type': u'Share', u'volume_id': u'4a1b263b-a2b3-4f66-9e66-4cd18050810d'},
160 {u'accepted': u'True', u'access_level': u'Modify',
161 u'free_bytes': u'39892622746', u'generation': u'2704',
162 u'name': u'do', u'node_id': u'84544ea4-aefe-4f91-9bb9-ed7b0a805baf',
163 u'other_username': u'otheruser', u'other_visible_name': u'Other User',
164 u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User',
165 u'type': u'Share', u'volume_id': u'7d130dfe-98b2-4bd5-8708-9eeba9838ac0'},
166]
167
168SAMPLE_SHARED = [
169 {u'accepted': u'True', u'access_level': u'View',
170 u'free_bytes': u'', u'generation': u'',
171 u'name': u'bar', u'node_id': u'31e47530-9448-4f03-b4dc-4154fdf35225',
172 u'other_username': u'otheruser', u'other_visible_name': u'Other User',
173 u'path': u'/home/tester/Ubuntu One/bar',
174 u'type': u'Shared',
175 u'volume_id': u'79584900-517f-4dff-b2f3-20e8c1e79365'},
176]
177
30178
31class TestCase(BaseTestCase):179class TestCase(BaseTestCase):
32 """Basics for testing."""180 """Basics for testing."""
33181
=== modified file 'ubuntuone/controlpanel/tests/test_backend.py'
--- ubuntuone/controlpanel/tests/test_backend.py 2010-12-23 18:20:56 +0000
+++ ubuntuone/controlpanel/tests/test_backend.py 2011-01-06 20:36:09 +0000
@@ -25,9 +25,9 @@
25from twisted.internet.defer import inlineCallbacks25from twisted.internet.defer import inlineCallbacks
26from ubuntuone.devtools.handlers import MementoHandler26from ubuntuone.devtools.handlers import MementoHandler
2727
28from ubuntuone.controlpanel import backend28from ubuntuone.controlpanel import backend, replication_client
29from ubuntuone.controlpanel.backend import (ACCOUNT_API,29from ubuntuone.controlpanel.backend import (bool_str,
30 DEVICES_API, DEVICE_REMOVE_API, QUOTA_API,30 ACCOUNT_API, DEVICES_API, DEVICE_REMOVE_API, QUOTA_API,
31 FILE_SYNC_DISABLED,31 FILE_SYNC_DISABLED,
32 FILE_SYNC_DISCONNECTED,32 FILE_SYNC_DISCONNECTED,
33 FILE_SYNC_ERROR,33 FILE_SYNC_ERROR,
@@ -37,160 +37,21 @@
37 FILE_SYNC_UNKNOWN,37 FILE_SYNC_UNKNOWN,
38 MSG_KEY, STATUS_KEY,38 MSG_KEY, STATUS_KEY,
39)39)
4040from ubuntuone.controlpanel.tests import (TestCase,
41from ubuntuone.controlpanel.tests import TestCase41 EXPECTED_ACCOUNT_INFO,
42 EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN,
43 EXPECTED_DEVICES_INFO,
44 SAMPLE_ACCOUNT_NO_CURRENT_PLAN,
45 SAMPLE_ACCOUNT_WITH_CURRENT_PLAN,
46 SAMPLE_DEVICES_JSON,
47 SAMPLE_FOLDERS,
48 SAMPLE_QUOTA_JSON,
49 SAMPLE_SHARED,
50 SAMPLE_SHARES,
51 TOKEN,
52)
42from ubuntuone.controlpanel.webclient import WebClientError53from ubuntuone.controlpanel.webclient import WebClientError
4354
44SAMPLE_CREDENTIALS = {"token": "ABC1234DEF"}
45
46SAMPLE_ACCOUNT_JSON = """
47{
48 "username": "andrewpz",
49 "openid": "https://login.launchpad.net/+id/abcdefg",
50 "first_name": "Andrew P.",
51 "last_name": "Zoilo",
52 "couchdb": {
53 "host": "https://couchdb.one.ubuntu.com",
54 "root": "https://couchdb.one.ubuntu.com/u/abc/def/12345",
55 "dbpath": "u/abc/def/12345"
56 },
57 "couchdb_root": "https://couchdb.one.ubuntu.com/u/abc/def/12345",
58 "email": "andrewpz@protocultura.net",%s
59 "nickname": "Andrew P. Zoilo",
60 "id": 12345,
61 "subscription": {
62 "upgrade_available": false,
63 "description": "Paid Plan, 50 GB of storage",
64 "trial": false,
65 "started": "2010-03-24T18:38:38Z",
66 "is_paid": true,
67 "expires": null,
68 "qty": 1,
69 "price": 0.0,
70 "currency": null,
71 "id": 654321,
72 "name": "50 GB"
73 }
74}
75"""
76
77CURRENT_PLAN = "Ubuntu One Basic (2 GB) + 1 x 20-Pack with 20 GB (monthly)"
78SAMPLE_CURRENT_PLAN = '\n "current_plan": "%s",' % CURRENT_PLAN
79
80SAMPLE_ACCOUNT_NO_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % ''
81SAMPLE_ACCOUNT_WITH_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % SAMPLE_CURRENT_PLAN
82
83
84SAMPLE_QUOTA_JSON = """
85{
86 "total": 53687091200,
87 "used": 2350345156
88}
89"""
90
91EXPECTED_ACCOUNT_INFO = {
92 "quota_used": "2350345156",
93 "quota_total": "53687091200",
94 "type": "Paid Plan, 50 GB of storage",
95 "name": "Andrew P. Zoilo",
96 "email": "andrewpz@protocultura.net",
97}
98
99EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN = {
100 "quota_used": "2350345156",
101 "quota_total": "53687091200",
102 "type": CURRENT_PLAN,
103 "name": "Andrew P. Zoilo",
104 "email": "andrewpz@protocultura.net",
105}
106
107SAMPLE_DEVICES_JSON = """
108[
109 {
110 "token": "ABCDEF01234token",
111 "description": "Ubuntu One @ darkstar",
112 "kind": "Computer"
113 },
114 {
115 "token": "ABC1234DEF",
116 "description": "Ubuntu One @ localhost",
117 "kind": "Computer"
118 },
119 {
120 "kind": "Phone",
121 "description": "Nokia E65",
122 "id": 1000
123 }
124]
125"""
126
127EXPECTED_DEVICES_INFO = [
128 {
129 "device_id": "ComputerABCDEF01234token",
130 "name": "Ubuntu One @ darkstar",
131 "type": "Computer",
132 "is_local": '',
133 "configurable": '',
134 },
135 {
136 'is_local': 'True',
137 'configurable': 'True',
138 'device_id': 'ComputerABC1234DEF',
139 'limit_bandwidth': '',
140 'max_download_speed': '-1',
141 'max_upload_speed': '-1',
142 'name': 'Ubuntu One @ localhost',
143 'type': 'Computer'
144 },
145 {
146 "device_id": "Phone1000",
147 "name": "Nokia E65",
148 "type": "Phone",
149 "configurable": '',
150 "is_local": '',
151 },
152]
153
154SAMPLE_FOLDERS = [
155 {u'generation': u'2', u'node_id': u'341da068-81d8-437a-8f75-5bb9d86455ba',
156 u'path': u'/home/tester/Public', u'subscribed': u'True',
157 u'suggested_path': u'~/Public',
158 u'type': u'UDF', u'volume_id': u'9ea892f8-15fa-4201-bdbf-8de99fa5f588'},
159 {u'generation': u'', u'node_id': u'11fbc86c-0d7a-49f5-ae83-8402caf66c6a',
160 u'path': u'/home/tester/Documents', u'subscribed': u'',
161 u'suggested_path': u'~/Documents',
162 u'type': u'UDF', u'volume_id': u'2db262f5-a151-4c19-969c-bb5ced753c61'},
163 {u'generation': u'24', u'node_id': u'9ee0e130-a7c7-4d76-a5e3-5df506221b48',
164 u'path': u'/home/tester/Pictures/Photos', u'subscribed': u'True',
165 u'suggested_path': u'~/Pictures/Photos',
166 u'type': u'UDF', u'volume_id': u'1deb2874-3d28-46ae-9999-d5f48de9f460'},
167]
168
169SAMPLE_SHARES = [
170 {u'accepted': u'True', u'access_level': u'View',
171 u'free_bytes': u'39892622746', u'generation': u'2704',
172 u'name': u're', u'node_id': u'c483f419-ed28-490a-825d-a8c074e2d795',
173 u'other_username': u'otheruser', u'other_visible_name': u'Other User',
174 u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User',
175 u'type': u'Share', u'volume_id': u'4a1b263b-a2b3-4f66-9e66-4cd18050810d'},
176 {u'accepted': u'True', u'access_level': u'Modify',
177 u'free_bytes': u'39892622746', u'generation': u'2704',
178 u'name': u'do', u'node_id': u'84544ea4-aefe-4f91-9bb9-ed7b0a805baf',
179 u'other_username': u'otheruser', u'other_visible_name': u'Other User',
180 u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User',
181 u'type': u'Share', u'volume_id': u'7d130dfe-98b2-4bd5-8708-9eeba9838ac0'},
182]
183
184SAMPLE_SHARED = [
185 {u'accepted': u'True', u'access_level': u'View',
186 u'free_bytes': u'', u'generation': u'',
187 u'name': u'bar', u'node_id': u'31e47530-9448-4f03-b4dc-4154fdf35225',
188 u'other_username': u'otheruser', u'other_visible_name': u'Other User',
189 u'path': u'/home/tester/Ubuntu One/bar',
190 u'type': u'Shared',
191 u'volume_id': u'79584900-517f-4dff-b2f3-20e8c1e79365'},
192]
193
19455
195class MockWebClient(object):56class MockWebClient(object):
196 """A mock webclient."""57 """A mock webclient."""
@@ -213,7 +74,7 @@
213class MockDBusClient(object):74class MockDBusClient(object):
214 """A mock dbus_client module."""75 """A mock dbus_client module."""
21576
216 creds = SAMPLE_CREDENTIALS77 creds = TOKEN
217 throttling = False78 throttling = False
218 limits = {"download": -1, "upload": -1}79 limits = {"download": -1, "upload": -1}
219 file_sync = True80 file_sync = True
@@ -292,6 +153,36 @@
292 return SAMPLE_SHARED153 return SAMPLE_SHARED
293154
294155
156class MockReplicationClient(object):
157 """A mock replication_client module."""
158
159 BOOKMARKS = 'awesome'
160 CONTACTS = 'legendary'
161
162 replications = set([BOOKMARKS, CONTACTS, 'other'])
163 exclusions = set([CONTACTS])
164
165 def get_replications(self):
166 """Grab the list of replications in this machine."""
167 return MockReplicationClient.replications
168
169 def get_exclusions(self):
170 """Grab the list of exclusions in this machine."""
171 return MockReplicationClient.exclusions
172
173 def replicate(self, replication_id):
174 """Remove replication_id from the exclusions list."""
175 if replication_id not in MockReplicationClient.replications:
176 raise replication_client.ReplicationError(replication_id)
177 MockReplicationClient.exclusions.remove(replication_id)
178
179 def exclude(self, replication_id):
180 """Add replication_id to the exclusions list."""
181 if replication_id not in MockReplicationClient.replications:
182 raise replication_client.ReplicationError(replication_id)
183 MockReplicationClient.exclusions.add(replication_id)
184
185
295class BackendBasicTestCase(TestCase):186class BackendBasicTestCase(TestCase):
296 """Simple tests for the backend."""187 """Simple tests for the backend."""
297188
@@ -301,13 +192,14 @@
301 super(BackendBasicTestCase, self).setUp()192 super(BackendBasicTestCase, self).setUp()
302 self.patch(backend, "WebClient", MockWebClient)193 self.patch(backend, "WebClient", MockWebClient)
303 self.patch(backend, "dbus_client", MockDBusClient())194 self.patch(backend, "dbus_client", MockDBusClient())
304 self.local_token = "Computer" + SAMPLE_CREDENTIALS["token"]195 self.patch(backend, "replication_client", MockReplicationClient())
196 self.local_token = "Computer" + TOKEN["token"]
305 self.be = backend.ControlBackend()197 self.be = backend.ControlBackend()
306198
307 self.memento = MementoHandler()199 self.memento = MementoHandler()
308 backend.logger.addHandler(self.memento)200 backend.logger.addHandler(self.memento)
309201
310 MockDBusClient.creds = SAMPLE_CREDENTIALS202 MockDBusClient.creds = TOKEN
311203
312 def test_backend_creation(self):204 def test_backend_creation(self):
313 """The backend instance is successfully created."""205 """The backend instance is successfully created."""
@@ -317,7 +209,7 @@
317 def test_get_token(self):209 def test_get_token(self):
318 """The get_token method returns the right token."""210 """The get_token method returns the right token."""
319 token = yield self.be.get_token()211 token = yield self.be.get_token()
320 self.assertEqual(token, SAMPLE_CREDENTIALS["token"])212 self.assertEqual(token, TOKEN["token"])
321213
322 @inlineCallbacks214 @inlineCallbacks
323 def test_device_is_local(self):215 def test_device_is_local(self):
@@ -388,12 +280,12 @@
388 result = yield self.be.remove_device(device_id)280 result = yield self.be.remove_device(device_id)
389 self.assertEqual(result, device_id)281 self.assertEqual(result, device_id)
390 # credentials were not cleared282 # credentials were not cleared
391 self.assertEqual(MockDBusClient.creds, SAMPLE_CREDENTIALS)283 self.assertEqual(MockDBusClient.creds, TOKEN)
392284
393 @inlineCallbacks285 @inlineCallbacks
394 def test_remove_device_clear_credentials_if_local_device(self):286 def test_remove_device_clear_credentials_if_local_device(self):
395 """The remove_device method clears the credentials if is local."""287 """The remove_device method clears the credentials if is local."""
396 apiurl = DEVICE_REMOVE_API % ('computer', SAMPLE_CREDENTIALS['token'])288 apiurl = DEVICE_REMOVE_API % ('computer', TOKEN['token'])
397 # pylint: disable=E1101289 # pylint: disable=E1101
398 self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON290 self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON
399 yield self.be.remove_device(self.local_token)291 yield self.be.remove_device(self.local_token)
@@ -487,10 +379,10 @@
487 """The volume settings can be changed."""379 """The volume settings can be changed."""
488 fid = '0123-4567'380 fid = '0123-4567'
489381
490 yield self.be.change_volume_settings(fid, {'subscribed': True})382 yield self.be.change_volume_settings(fid, {'subscribed': 'True'})
491 self.assertEqual(MockDBusClient.subscribed_folders, [fid])383 self.assertEqual(MockDBusClient.subscribed_folders, [fid])
492384
493 yield self.be.change_volume_settings(fid, {'subscribed': False})385 yield self.be.change_volume_settings(fid, {'subscribed': ''})
494 self.assertEqual(MockDBusClient.subscribed_folders, [])386 self.assertEqual(MockDBusClient.subscribed_folders, [])
495387
496 @inlineCallbacks388 @inlineCallbacks
@@ -671,3 +563,64 @@
671563
672 self.be.disable_files()564 self.be.disable_files()
673 self.assertFalse(MockDBusClient.file_sync)565 self.assertFalse(MockDBusClient.file_sync)
566
567
568class BackendReplicationsTestCase(BackendBasicTestCase):
569 """Replications tests for the backend."""
570
571 @inlineCallbacks
572 def test_replications_info(self):
573 """The replications_info method exercises its callback."""
574 result = yield self.be.replications_info()
575
576 # replications_info will use exclusions information
577 expected = []
578 for name in MockReplicationClient.replications:
579 enabled = bool_str(name not in MockReplicationClient.exclusions)
580 dependency = ''
581 if name == MockReplicationClient.BOOKMARKS:
582 dependency = backend.BOOKMARKS_PKG
583 elif name == MockReplicationClient.CONTACTS:
584 dependency = backend.CONTACTS_PKG
585
586 item = {'replication_id': name, 'name': name,
587 'enabled': enabled, 'dependency': dependency}
588 expected.append(item)
589 self.assertEqual(sorted(expected), sorted(result))
590
591 @inlineCallbacks
592 def test_change_replication_settings(self):
593 """The replication settings can be changed."""
594 rid = '0123-4567'
595 MockReplicationClient.replications.add(rid)
596 self.addCleanup(lambda: MockReplicationClient.replications.remove(rid))
597
598 yield self.be.change_replication_settings(rid, {'enabled': ''})
599 self.assertIn(rid, MockReplicationClient.exclusions)
600
601 yield self.be.change_replication_settings(rid, {'enabled': 'True'})
602 self.assertNotIn(rid, MockReplicationClient.exclusions)
603
604 @inlineCallbacks
605 def test_change_replication_settings_not_in_replications(self):
606 """The settings can not be changed for an item not in replications."""
607 rid = '0123-4567'
608 assert rid not in MockReplicationClient.replications
609
610 d = self.be.change_replication_settings(rid, {'enabled': 'True'})
611 yield self.assertFailure(d, replication_client.ReplicationError)
612
613 d = self.be.change_replication_settings(rid, {'enabled': ''})
614 yield self.assertFailure(d, replication_client.ReplicationError)
615
616 @inlineCallbacks
617 def test_change_replication_settings_no_setting(self):
618 """The change replication settings does not fail on empty settings."""
619 rid = '0123-4567'
620 MockReplicationClient.replications.add(rid)
621 self.addCleanup(lambda: MockReplicationClient.replications.remove(rid))
622
623 prior = MockReplicationClient.exclusions.copy()
624 yield self.be.change_replication_settings(rid, {})
625
626 self.assertEqual(MockReplicationClient.exclusions, prior)
674627
=== added file 'ubuntuone/controlpanel/tests/test_replication_client.py'
--- ubuntuone/controlpanel/tests/test_replication_client.py 1970-01-01 00:00:00 +0000
+++ ubuntuone/controlpanel/tests/test_replication_client.py 2011-01-06 20:36:09 +0000
@@ -0,0 +1,160 @@
1# -*- coding: utf-8 -*-
2
3# Authors: Natalia B. Bidart <natalia.bidart@canonical.com>
4#
5# Copyright 2010 Canonical Ltd.
6#
7# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published
9# by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranties of
13# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14# PURPOSE. See the GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program. If not, see <http://www.gnu.org/licenses/>.
18
19"""Tests for the DBus service when accessing desktopcouch replications."""
20
21from twisted.internet.defer import inlineCallbacks
22
23from ubuntuone.controlpanel import replication_client
24from ubuntuone.controlpanel.tests import TestCase
25
26EXCLUSIONS = set()
27
28
29class FakedReplication(object):
30 """Faked a DC replication exclusion."""
31
32 def __init__(self):
33 self.all_exclusions = lambda: EXCLUSIONS
34 self.replicate = EXCLUSIONS.remove
35 self.exclude = EXCLUSIONS.add
36
37
38class FakedReplicationModule(object):
39 """Faked a DC replication module."""
40
41 ReplicationExclusion = FakedReplication
42
43
44class ReplicationsTestCase(TestCase):
45 """Test for the replications client methods."""
46
47 def setUp(self):
48 super(ReplicationsTestCase, self).setUp()
49
50 orig_get_proxy = replication_client.get_replication_proxy
51
52 def get_proxy():
53 """Fake the proxy getter."""
54 return orig_get_proxy(replication_module=FakedReplicationModule())
55
56 self.patch(replication_client, 'get_replication_proxy', get_proxy)
57
58 def tearDown(self):
59 EXCLUSIONS.clear()
60 super(ReplicationsTestCase, self).tearDown()
61
62 @inlineCallbacks
63 def test_no_pairing_record(self):
64 """Handle ValueError from replication layer."""
65
66 def no_pairing_record(*args, **kwargs):
67 """Fail with ValueError."""
68 raise ValueError('No pairing record.')
69
70 self.patch(FakedReplicationModule, 'ReplicationExclusion',
71 no_pairing_record)
72
73 yield self.assertFailure(replication_client.get_replications(),
74 replication_client.NoPairingRecord)
75
76 @inlineCallbacks
77 def test_get_replications(self):
78 """Replications are correctly retrieved."""
79 result = yield replication_client.get_replications()
80 self.assertEqual(result, replication_client.REPLICATIONS)
81
82 @inlineCallbacks
83 def test_get_exclusions(self):
84 """Exclusions are correctly retrieved."""
85 replications = yield replication_client.get_replications()
86 for rep in replications:
87 yield replication_client.exclude(rep)
88
89 result = yield replication_client.get_exclusions()
90 self.assertEqual(result, replications)
91
92 @inlineCallbacks
93 def test_replicate(self):
94 """Replicate a service is correct."""
95 replications = yield replication_client.get_replications()
96 rid = list(replications)[0]
97 yield replication_client.exclude(rid)
98
99 yield replication_client.replicate(rid)
100 exclusions = yield replication_client.get_exclusions()
101 self.assertNotIn(rid, exclusions)
102
103 @inlineCallbacks
104 def test_replicate_name_not_in_replications(self):
105 """Replicate a service fails if not in replications."""
106 replications = yield replication_client.get_replications()
107 rid = 'not in replications'
108 assert rid not in replications
109
110 yield self.assertFailure(replication_client.replicate(rid),
111 replication_client.InvalidIdError)
112
113 @inlineCallbacks
114 def test_replicate_name_not_in_exclusions(self):
115 """Replicate a service fails if not in exclusions."""
116 replications = yield replication_client.get_replications()
117 rid = list(replications)[0]
118 assert rid in replications
119
120 exclusions = yield replication_client.get_exclusions()
121 assert rid not in exclusions
122
123 yield self.assertFailure(replication_client.replicate(rid),
124 replication_client.NotExcludedError)
125
126 @inlineCallbacks
127 def test_exclude(self):
128 """Excluding a service is correct."""
129 replications = yield replication_client.get_replications()
130 rid = list(replications)[0]
131 yield replication_client.exclude(rid)
132 yield replication_client.replicate(rid)
133
134 yield replication_client.exclude(rid)
135 exclusions = yield replication_client.get_exclusions()
136 self.assertIn(rid, exclusions)
137
138 @inlineCallbacks
139 def test_exclude_name_not_in_replications(self):
140 """Excluding a service fails if not in replications."""
141 replications = yield replication_client.get_replications()
142 rid = 'not in replications'
143 assert rid not in replications
144
145 yield self.assertFailure(replication_client.exclude(rid),
146 replication_client.InvalidIdError)
147
148 @inlineCallbacks
149 def test_exclude_name_in_exclusions(self):
150 """Excluding a service fails if already on exclusions."""
151 replications = yield replication_client.get_replications()
152 rid = list(replications)[0]
153 assert rid in replications
154
155 yield replication_client.exclude(rid)
156 exclusions = yield replication_client.get_exclusions()
157 assert rid in exclusions
158
159 yield self.assertFailure(replication_client.exclude(rid),
160 replication_client.AlreadyExcludedError)

Subscribers

People subscribed via source and target branches