Merge lp:~nataliabidart/ubuntuone-control-panel/applications-woohoo into lp:ubuntuone-control-panel

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 55
Merged at revision: 42
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/applications-woohoo
Merge into: lp:ubuntuone-control-panel
Diff against target: 1986 lines (+1422/-188)
15 files modified
data/install.ui (+46/-0)
data/management.ui (+7/-7)
data/services.ui (+57/-1)
po/POTFILES.in (+3/-2)
ubuntuone/controlpanel/backend.py (+12/-0)
ubuntuone/controlpanel/dbus_service.py (+47/-0)
ubuntuone/controlpanel/gtk/gui.py (+271/-16)
ubuntuone/controlpanel/gtk/package_manager.py (+60/-0)
ubuntuone/controlpanel/gtk/tests/__init__.py (+150/-0)
ubuntuone/controlpanel/gtk/tests/test_gui.py (+525/-157)
ubuntuone/controlpanel/gtk/tests/test_package_manager.py (+177/-0)
ubuntuone/controlpanel/integrationtests/test_dbus_service.py (+44/-0)
ubuntuone/controlpanel/logger.py (+1/-1)
ubuntuone/controlpanel/tests/__init__.py (+2/-2)
ubuntuone/controlpanel/tests/test_backend.py (+20/-2)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/applications-woohoo
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
John Lenton (community) Approve
Review via email: mp+44623@code.launchpad.net

Commit message

* renamed Account to Dashboard
* renamed Applications to Services
* added enable_files/disable_files to backend + dbus service
* added a new dbus signal FileSyncStatusChanged
* desktopcouch replication exclusion layer is acceded directly on the GUI. We should later move that to the backend.
* a new module package_manager was added to the gtk package, since we need to do some deeper work to abstract that manager from the toolkit (since it uses a gtk progress bar)(LP: #673673).
* a new widget InstallPackage that provides package installation.
* widgets FilesService and DesktopcouchService that can be re used as needed.
* ServicesPanel checks for depenencies and creates widget as necessary (LP: #673672).

Description of the change

This is an epic branch, I know, please don't hate me. Think on the good things, we have a full featured control panel!

This branch implements all bits needed to list the services offered and install package dependencies if needed.

What was done:

(see commit message for the full list)

All this is tested with code and was tested IRL.

To run the tests, do:

./run-tests

To test IRL:

* uninstall desktopcouch, xul-ext-bindwood, evolution-couchdb
* on terminal 1: DEBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-backend
* on terminal 2: DEBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-gtk

Got to the Services tab, and install desktopcouch using the same UI, and then install (if you want) the bindwood and evocouch packages.

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

Need to handle the case where the pairing record is not present:

  File "/home/ralsina/canonical/ubuntuone/woo/ubuntuone/controlpanel/logger.py", line 75, in inner
    res = f(*args, **kwargs)
  File "/home/ralsina/canonical/ubuntuone/woo/ubuntuone/controlpanel/gtk/gui.py", line 992, in load_replications
    self.replication_service = self.replication_exclusion_class()
  File "/usr/lib/pymodules/python2.7/desktopcouch/application/replication_services/ubuntuone.py", line 140, in __init__
    raise ValueError("No pairing record for ubuntuone.")
ValueError: No pairing record for ubuntuone.

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

Code to properly handle ValueError is now in place. The lack of a pairing record with Ubuntu One should be handled in the pairing_ubuntuone plugin of desktopcouch, see bug #694495.

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

Just filled bug #696782 to make the DC service starts on the backend.

53. By Natalia Bidart

aptdaemon.defer on natty is just defer.

54. By Natalia Bidart

Proper namespace for defer.

55. By Natalia Bidart

Avoiding lint warnings.

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

Approved.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== renamed file 'data/account.ui' => 'data/dashboard.ui'
=== added file 'data/install.ui'
--- data/install.ui 1970-01-01 00:00:00 +0000
+++ data/install.ui 2011-01-03 14:24:42 +0000
@@ -0,0 +1,46 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<interface>
3 <requires lib="gtk+" version="2.16"/>
4 <!-- interface-naming-policy project-wide -->
5 <object class="GtkVBox" id="itself">
6 <property name="visible">True</property>
7 <property name="border_width">10</property>
8 <property name="spacing">10</property>
9 <child>
10 <object class="GtkLabel" id="install_label">
11 <property name="visible">True</property>
12 <property name="xalign">0</property>
13 <property name="label">label</property>
14 <property name="wrap">True</property>
15 </object>
16 <packing>
17 <property name="expand">False</property>
18 <property name="position">0</property>
19 </packing>
20 </child>
21 <child>
22 <object class="GtkHButtonBox" id="install_button_box">
23 <property name="visible">True</property>
24 <child>
25 <object class="GtkButton" id="install_button">
26 <property name="label">gtk-ok</property>
27 <property name="visible">True</property>
28 <property name="can_focus">True</property>
29 <property name="receives_default">True</property>
30 <property name="use_stock">True</property>
31 <signal name="clicked" handler="on_install_button_clicked"/>
32 </object>
33 <packing>
34 <property name="expand">False</property>
35 <property name="fill">False</property>
36 <property name="position">0</property>
37 </packing>
38 </child>
39 </object>
40 <packing>
41 <property name="expand">False</property>
42 <property name="position">1</property>
43 </packing>
44 </child>
45 </object>
46</interface>
047
=== modified file 'data/management.ui'
--- data/management.ui 2010-12-20 20:23:57 +0000
+++ data/management.ui 2011-01-03 14:24:42 +0000
@@ -64,8 +64,8 @@
64 <property name="visible">True</property>64 <property name="visible">True</property>
65 <property name="layout_style">center</property>65 <property name="layout_style">center</property>
66 <child>66 <child>
67 <object class="GtkRadioButton" id="account_button">67 <object class="GtkRadioButton" id="dashboard_button">
68 <property name="label" translatable="yes">Account</property>68 <property name="label" translatable="yes">Dashboard</property>
69 <property name="visible">True</property>69 <property name="visible">True</property>
70 <property name="can_focus">True</property>70 <property name="can_focus">True</property>
71 <property name="receives_default">False</property>71 <property name="receives_default">False</property>
@@ -85,7 +85,7 @@
85 <property name="can_focus">True</property>85 <property name="can_focus">True</property>
86 <property name="receives_default">False</property>86 <property name="receives_default">False</property>
87 <property name="draw_indicator">False</property>87 <property name="draw_indicator">False</property>
88 <property name="group">account_button</property>88 <property name="group">dashboard_button</property>
89 </object>89 </object>
90 <packing>90 <packing>
91 <property name="expand">False</property>91 <property name="expand">False</property>
@@ -100,7 +100,7 @@
100 <property name="can_focus">True</property>100 <property name="can_focus">True</property>
101 <property name="receives_default">False</property>101 <property name="receives_default">False</property>
102 <property name="draw_indicator">False</property>102 <property name="draw_indicator">False</property>
103 <property name="group">account_button</property>103 <property name="group">dashboard_button</property>
104 </object>104 </object>
105 <packing>105 <packing>
106 <property name="expand">False</property>106 <property name="expand">False</property>
@@ -109,13 +109,13 @@
109 </packing>109 </packing>
110 </child>110 </child>
111 <child>111 <child>
112 <object class="GtkRadioButton" id="applications_button">112 <object class="GtkRadioButton" id="services_button">
113 <property name="label" translatable="yes">Applications</property>113 <property name="label" translatable="yes">Services</property>
114 <property name="visible">True</property>114 <property name="visible">True</property>
115 <property name="can_focus">True</property>115 <property name="can_focus">True</property>
116 <property name="receives_default">False</property>116 <property name="receives_default">False</property>
117 <property name="draw_indicator">False</property>117 <property name="draw_indicator">False</property>
118 <property name="group">account_button</property>118 <property name="group">dashboard_button</property>
119 </object>119 </object>
120 <packing>120 <packing>
121 <property name="expand">False</property>121 <property name="expand">False</property>
122122
=== renamed file 'data/applications.ui' => 'data/services.ui'
--- data/applications.ui 2010-10-21 21:14:24 +0000
+++ data/services.ui 2011-01-03 14:24:42 +0000
@@ -7,7 +7,63 @@
7 <property name="border_width">10</property>7 <property name="border_width">10</property>
8 <property name="spacing">10</property>8 <property name="spacing">10</property>
9 <child>9 <child>
10 <placeholder/>10 <object class="GtkScrolledWindow" id="scrolledwindow1">
11 <property name="visible">True</property>
12 <property name="can_focus">True</property>
13 <property name="hscrollbar_policy">automatic</property>
14 <property name="vscrollbar_policy">automatic</property>
15 <child>
16 <object class="GtkViewport" id="viewport1">
17 <property name="visible">True</property>
18 <property name="resize_mode">queue</property>
19 <property name="shadow_type">none</property>
20 <child>
21 <object class="GtkVBox" id="vbox1">
22 <property name="visible">True</property>
23 <child>
24 <object class="GtkAlignment" id="alignment1">
25 <property name="visible">True</property>
26 <child>
27 <object class="GtkVBox" id="replications">
28 <property name="visible">True</property>
29 <property name="spacing">5</property>
30 <child>
31 <placeholder/>
32 </child>
33 </object>
34 </child>
35 </object>
36 <packing>
37 <property name="expand">False</property>
38 <property name="position">0</property>
39 </packing>
40 </child>
41 <child>
42 <object class="GtkAlignment" id="alignment2">
43 <property name="visible">True</property>
44 <child>
45 <object class="GtkVBox" id="files">
46 <property name="visible">True</property>
47 <property name="spacing">5</property>
48 <child>
49 <placeholder/>
50 </child>
51 </object>
52 </child>
53 </object>
54 <packing>
55 <property name="expand">False</property>
56 <property name="position">1</property>
57 </packing>
58 </child>
59 </object>
60 </child>
61 </object>
62 </child>
63 </object>
64 <packing>
65 <property name="position">0</property>
66 </packing>
11 </child>67 </child>
12 </object>68 </object>
13</interface>69</interface>
1470
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2010-12-22 13:20:20 +0000
+++ po/POTFILES.in 2011-01-03 14:24:42 +0000
@@ -1,6 +1,7 @@
1ubuntuone/controlpanel/gtk/gui.py1ubuntuone/controlpanel/gtk/gui.py
2[type: gettext/glade] data/account.ui2[type: gettext/glade] data/dashboard.ui
3[type: gettext/glade] data/applications.ui3[type: gettext/glade] data/services.ui
4[type: gettext/glade] data/device.ui
4[type: gettext/glade] data/devices.ui5[type: gettext/glade] data/devices.ui
5[type: gettext/glade] data/management.ui6[type: gettext/glade] data/management.ui
6[type: gettext/glade] data/overview.ui7[type: gettext/glade] data/overview.ui
78
=== modified file 'ubuntuone/controlpanel/backend.py'
--- ubuntuone/controlpanel/backend.py 2010-12-18 20:16:18 +0000
+++ ubuntuone/controlpanel/backend.py 2011-01-03 14:24:42 +0000
@@ -273,6 +273,18 @@
273273
274 @log_call(logger.debug)274 @log_call(logger.debug)
275 @inlineCallbacks275 @inlineCallbacks
276 def enable_files(self):
277 """Enable the files service."""
278 yield dbus_client.set_files_sync_enabled(True)
279
280 @log_call(logger.debug)
281 @inlineCallbacks
282 def disable_files(self):
283 """Enable the files service."""
284 yield dbus_client.set_files_sync_enabled(False)
285
286 @log_call(logger.debug)
287 @inlineCallbacks
276 def volumes_info(self):288 def volumes_info(self):
277 """Get the volumes info."""289 """Get the volumes info."""
278 result = yield dbus_client.get_folders()290 result = yield dbus_client.get_folders()
279291
=== modified file 'ubuntuone/controlpanel/dbus_service.py'
--- ubuntuone/controlpanel/dbus_service.py 2010-12-16 21:09:46 +0000
+++ ubuntuone/controlpanel/dbus_service.py 2011-01-03 14:24:42 +0000
@@ -209,6 +209,8 @@
209 else:209 else:
210 self.FileSyncStatusError(error_handler(status_dict))210 self.FileSyncStatusError(error_handler(status_dict))
211211
212 self.FileSyncStatusChanged(status)
213
212 @log_call(logger.debug)214 @log_call(logger.debug)
213 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")215 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
214 def file_sync_status(self):216 def file_sync_status(self):
@@ -242,6 +244,11 @@
242 def FileSyncStatusIdle(self, msg):244 def FileSyncStatusIdle(self, msg):
243 """The file sync service is idle."""245 """The file sync service is idle."""
244246
247 @log_call(logger.debug)
248 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
249 def FileSyncStatusChanged(self, msg):
250 """The file sync service status changed."""
251
245 @log_call(logger.error)252 @log_call(logger.error)
246 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")253 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
247 def FileSyncStatusError(self, error):254 def FileSyncStatusError(self, error):
@@ -251,6 +258,46 @@
251258
252 @log_call(logger.debug)259 @log_call(logger.debug)
253 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")260 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
261 def enable_files(self):
262 """Enable the files service."""
263 d = self.backend.enable_files()
264 d.addCallback(lambda _: self.FilesEnabled())
265 d.addErrback(transform_failure(self.FilesEnableError))
266
267 @log_call(logger.debug)
268 @signal(dbus_interface=DBUS_PREFERENCES_IFACE)
269 def FilesEnabled(self):
270 """The files service is enabled."""
271
272 @log_call(logger.error)
273 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
274 def FilesEnableError(self, error):
275 """Problem enabling the files service."""
276
277 #---
278
279 @log_call(logger.debug)
280 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
281 def disable_files(self):
282 """Disable the files service."""
283 d = self.backend.disable_files()
284 d.addCallback(lambda _: self.FilesDisabled())
285 d.addErrback(transform_failure(self.FilesDisableError))
286
287 @log_call(logger.debug)
288 @signal(dbus_interface=DBUS_PREFERENCES_IFACE)
289 def FilesDisabled(self):
290 """The files service is disabled."""
291
292 @log_call(logger.error)
293 @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
294 def FilesDisableError(self, error):
295 """Problem disabling the files service."""
296
297 #---
298
299 @log_call(logger.debug)
300 @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
254 def volumes_info(self):301 def volumes_info(self):
255 """Find out the volumes info for the logged in user."""302 """Find out the volumes info for the logged in user."""
256 d = self.backend.volumes_info()303 d = self.backend.volumes_info()
257304
=== modified file 'ubuntuone/controlpanel/gtk/gui.py'
--- ubuntuone/controlpanel/gtk/gui.py 2010-12-20 21:55:49 +0000
+++ ubuntuone/controlpanel/gtk/gui.py 2011-01-03 14:24:42 +0000
@@ -46,12 +46,13 @@
46from ubuntuone.controlpanel.gtk.widgets import GreyableBin46from ubuntuone.controlpanel.gtk.widgets import GreyableBin
4747
48from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH,48from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH,
49 DBUS_PREFERENCES_IFACE)49 DBUS_PREFERENCES_IFACE, backend)
50from ubuntuone.controlpanel.backend import (DEVICE_TYPE_PHONE,50from ubuntuone.controlpanel.backend import (DEVICE_TYPE_PHONE,
51 DEVICE_TYPE_COMPUTER, bool_str)51 DEVICE_TYPE_COMPUTER, bool_str)
52from ubuntuone.controlpanel.logger import setup_logging, log_call52from ubuntuone.controlpanel.logger import setup_logging, log_call
53from ubuntuone.controlpanel.utils import get_data_file53from ubuntuone.controlpanel.utils import get_data_file
5454
55from ubuntuone.controlpanel.gtk import package_manager
5556
56logger = setup_logging('gtk.gui')57logger = setup_logging('gtk.gui')
57_ = gettext.gettext58_ = gettext.gettext
@@ -149,7 +150,7 @@
149class ControlPanelWindow(gtk.Window):150class ControlPanelWindow(gtk.Window):
150 """The main window for the Ubuntu One control panel."""151 """The main window for the Ubuntu One control panel."""
151152
152 TITLE = _('My %(app_name)s Account')153 TITLE = _('My %(app_name)s Dashboard')
153154
154 def __init__(self):155 def __init__(self):
155 super(ControlPanelWindow, self).__init__()156 super(ControlPanelWindow, self).__init__()
@@ -410,8 +411,8 @@
410 self.sso_backend.find_credentials(U1_APP_NAME, {})411 self.sso_backend.find_credentials(U1_APP_NAME, {})
411412
412413
413class AccountPanel(UbuntuOneBin, ControlPanelMixin):414class DashboardPanel(UbuntuOneBin, ControlPanelMixin):
414 """The account panel. The user can manage the subscription."""415 """The dashboard panel. The user can manage the subscription."""
415416
416 TITLE = _('Welcome to Ubuntu One!')417 TITLE = _('Welcome to Ubuntu One!')
417 NAME = _('Name')418 NAME = _('Name')
@@ -420,7 +421,7 @@
420421
421 def __init__(self):422 def __init__(self):
422 UbuntuOneBin.__init__(self)423 UbuntuOneBin.__init__(self)
423 ControlPanelMixin.__init__(self, filename='account.ui')424 ControlPanelMixin.__init__(self, filename='dashboard.ui')
424 self.add(self.itself)425 self.add(self.itself)
425 self.show()426 self.show()
426427
@@ -750,26 +751,276 @@
750 self.message.start()751 self.message.start()
751752
752753
753class ApplicationsPanel(UbuntuOneBin, ControlPanelMixin):754class InstallPackage(gtk.VBox, ControlPanelMixin):
754 """The applications panel."""755 """A widget to process the install of a package."""
756
757 INSTALL_PACKAGE = _('You need to install the package <i>%(package_name)s'
758 '</i> in order to enable replication.')
759 INSTALLING = _('The package <i>%(package_name)s</i> is being installed, '
760 'please wait...')
761 FAILED_INSTALL = _('The installation of <i>%(package_name)s</i> failed.')
762 SUCCESS_INSTALL = _('The installation of <i>%(package_name)s</i> '
763 'was successful.')
764
765 def __init__(self, package_name):
766 gtk.VBox.__init__(self)
767 ControlPanelMixin.__init__(self, filename='install.ui')
768 self.add(self.itself)
769
770 self.package_name = package_name
771 self.package_manager = package_manager.PackageManager()
772 self.args = {'package_name': self.package_name}
773 self.transaction = None
774
775 self.progress_bar = None
776 self.install_label.set_markup(self.INSTALL_PACKAGE % self.args)
777
778 self.show()
779
780 @package_manager.inline_callbacks
781 def on_install_button_clicked(self, button):
782 """The install button was clicked."""
783 try:
784 # create the install transaction
785 self.transaction = yield self.package_manager.install(
786 self.package_name)
787
788 # create the progress bar and pack it to the box
789 self.progress_bar = package_manager.PackageManagerProgressBar(
790 self.transaction)
791 self.progress_bar.show()
792
793 self.itself.remove(self.install_button_box)
794 self.itself.pack_start(self.progress_bar)
795
796 self.transaction.connect('finished', self.on_install_finished)
797 self.install_label.set_markup(self.INSTALLING % self.args)
798 yield self.transaction.run()
799 except: # pylint: disable=W0702
800 self._set_warning(self.FAILED_INSTALL % self.args,
801 self.install_label)
802
803 @log_call(logger.info)
804 def on_install_finished(self, transaction, exit_code):
805 """The installation finished."""
806 self.progress_bar.set_sensitive(False)
807
808 if exit_code != package_manager.aptdaemon.enums.EXIT_SUCCESS:
809 if hasattr(transaction, 'error'):
810 logger.error('transaction failed: %r', transaction.error)
811 self._set_warning(self.FAILED_INSTALL % self.args,
812 self.install_label)
813 else:
814 self.install_label.set_markup(self.SUCCESS_INSTALL % self.args)
815 self.emit('finished')
816
817
818class Service(gtk.VBox, ControlPanelMixin):
819 """A service."""
820
821 def __init__(self, name, localized_name, *args, **kwargs):
822 gtk.VBox.__init__(self)
823 ControlPanelMixin.__init__(self)
824 self.service_name = name
825
826 self.button = gtk.CheckButton(label=localized_name)
827 self.pack_start(self.button, expand=False)
828
829 self.show_all()
830
831
832class FilesService(Service):
833 """The file sync service."""
834
835 FILES_SERVICE_NAME = _('Files')
836
837 def __init__(self):
838 Service.__init__(self, name='files',
839 localized_name=self.FILES_SERVICE_NAME)
840
841 self.set_sensitive(False)
842 self.backend.connect_to_signal('FileSyncStatusChanged',
843 self.on_file_sync_status_changed)
844 self.backend.connect_to_signal('FilesEnabled', self.on_files_enabled)
845 self.backend.connect_to_signal('FilesDisabled', self.on_files_disabled)
846 self.backend.file_sync_status()
847
848 @log_call(logger.debug)
849 def on_file_sync_status_changed(self, status):
850 """File sync status changed."""
851 enabled = status != backend.FILE_SYNC_DISABLED
852 self.button.set_active(enabled)
853
854 if not self.is_sensitive():
855 # first time we're getting this event
856 self.button.connect('toggled', self.on_button_toggled)
857 self.set_sensitive(True)
858
859 def on_files_enabled(self):
860 """Files service was enabled."""
861 self.on_file_sync_status_changed('enabled!')
862
863 def on_files_disabled(self):
864 """Files service was disabled."""
865 self.on_file_sync_status_changed(backend.FILE_SYNC_DISABLED)
866
867 @log_call(logger.debug)
868 def on_button_toggled(self, button):
869 """Button was toggled, exclude/replicate the service properly."""
870 logger.info('File sync enabled? %r', self.button.get_active())
871 if self.button.get_active():
872 self.backend.enable_files()
873 else:
874 self.backend.disable_files()
875
876
877class DesktopcouchService(Service):
878 """A desktopcouch service."""
879
880 def __init__(self, name, localized_name,
881 replication_service, dependency=None):
882 Service.__init__(self, name, localized_name)
883 self.replication_service = replication_service
884 enabled = name not in self.replication_service.all_exclusions()
885 self.button.set_active(enabled)
886
887 self.dependency = None
888 if dependency is not None:
889 self.dependency = InstallPackage(dependency)
890 self.dependency.connect('finished', self.on_depedency_finished)
891 self.pack_start(self.dependency, expand=False)
892 self.button.set_sensitive(False)
893
894 self.button.connect('toggled', self.on_button_toggled)
895
896 def on_depedency_finished(self, widget):
897 """The dependency was installed."""
898 self.button.set_sensitive(True)
899 self.remove(self.dependency)
900 self.dependency = None
901
902 @log_call(logger.debug)
903 def on_button_toggled(self, button):
904 """Button was toggled, exclude/replicate the service properly."""
905 logger.info('Starting replication for %r? %r',
906 self.service_name, self.button.get_active())
907 if self.button.get_active():
908 self.replication_service.replicate(self.service_name)
909 else:
910 self.replication_service.exclude(self.service_name)
911
912
913class ServicesPanel(UbuntuOneBin, ControlPanelMixin):
914 """The services panel."""
755915
756 TITLE = _('Ubuntu One services including data sync are enabled for the '916 TITLE = _('Ubuntu One services including data sync are enabled for the '
757 'data types and applications listed below:')917 'data types and services listed below.')
918 CHOOSE_SERVICES = _('Choose services to synchronize with this computer:')
919 DESKTOPCOUCH_PKG = 'desktopcouch'
920 BINDWOOD_PKG = 'xul-ext-bindwood'
921 EVOCOUCH_PKG = 'evolution-couchdb'
922 BOOKMARKS = _('Bookmarks (Firefox)')
923 CONTACTS = _('Contacts (Evolution)')
924 NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.')
758925
759 def __init__(self):926 def __init__(self, replication_exclusion_class=None):
760 UbuntuOneBin.__init__(self)927 UbuntuOneBin.__init__(self)
761 ControlPanelMixin.__init__(self, filename='applications.ui')928 ControlPanelMixin.__init__(self, filename='services.ui')
762 self.add(self.itself)929 self.add(self.itself)
930
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()
937 self.install_box = None
938 self.bookmarks = None
939 self.contacts = None
940
941 self.files.pack_start(FilesService(), expand=False)
942
763 self.show()943 self.show()
764944
945 @log_call(logger.debug)
946 def load(self):
947 """Load info."""
948 if self.install_box is not None:
949 self.itself.remove(self.install_box)
950 self.install_box = None
951
952 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:
963 self.message.set_text('')
964 self.replications.hide()
965
966 self.install_box = InstallPackage(self.DESKTOPCOUCH_PKG)
967 self.install_box.connect('finished', self.load_replications)
968 self.itself.pack_start(self.install_box, expand=False)
969 self.itself.reorder_child(self.install_box, 0)
970 else:
971 self.load_replications()
972
765 self.message.stop()973 self.message.stop()
766 self.message.set_text('Under construction')974
975 @log_call(logger.debug)
976 def load_replications(self, *args):
977 """Load replications info."""
978 self.replications.show()
979
980 if self.install_box is not None:
981 self.itself.remove(self.install_box)
982 self.install_box = None
983
984 self.message.set_text(self.CHOOSE_SERVICES)
985 for child in self.replications.get_children():
986 self.replications.remove(child)
987
988 # Unable to import 'desktopcouch.application.replication_services'
989 # pylint: disable=F0401
990 if self.replication_exclusion_class is None:
991 from desktopcouch.application.replication_services import \
992 ubuntuone as u1rep
993 self.replication_exclusion_class = u1rep.ReplicationExclusion
994
995 if self.replication_service is None:
996 try:
997 self.replication_service = self.replication_exclusion_class()
998 except ValueError:
999 logger.exception('Can not load replications:')
1000 self._set_warning(self.NO_PAIRING_RECORD, self.message)
1001 return
1002
1003 pkg = None
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)
7671018
7681019
769class ManagementPanel(gtk.VBox, ControlPanelMixin):1020class ManagementPanel(gtk.VBox, ControlPanelMixin):
770 """The management panel.1021 """The management panel.
7711022
772 The user can manage account, folders, devices and applications.1023 The user can manage dashboard, folders, devices and services.
7731024
774 """1025 """
7751026
@@ -817,13 +1068,13 @@
817 self.status_label = LabelLoading(LOADING, fg_color=DEFAULT_FG)1068 self.status_label = LabelLoading(LOADING, fg_color=DEFAULT_FG)
818 self.status_box.pack_end(self.status_label, expand=False)1069 self.status_box.pack_end(self.status_label, expand=False)
8191070
820 self.account = AccountPanel()1071 self.dashboard = DashboardPanel()
821 self.folders = FoldersPanel()1072 self.folders = FoldersPanel()
822 self.devices = DevicesPanel()1073 self.devices = DevicesPanel()
823 self.applications = ApplicationsPanel()1074 self.services = ServicesPanel()
8241075
825 cb = lambda button, page_num: self.notebook.set_current_page(page_num)1076 cb = lambda button, page_num: self.notebook.set_current_page(page_num)
826 self.tabs = (u'account', u'folders', u'devices', u'applications')1077 self.tabs = (u'dashboard', u'folders', u'devices', u'services')
827 for page_num, tab in enumerate(self.tabs):1078 for page_num, tab in enumerate(self.tabs):
828 setattr(self, ('%s_page' % tab).upper(), page_num)1079 setattr(self, ('%s_page' % tab).upper(), page_num)
829 button = getattr(self, '%s_button' % tab)1080 button = getattr(self, '%s_button' % tab)
@@ -835,6 +1086,7 @@
8351086
836 self.folders_button.connect('clicked', lambda b: self.folders.load())1087 self.folders_button.connect('clicked', lambda b: self.folders.load())
837 self.devices_button.connect('clicked', lambda b: self.devices.load())1088 self.devices_button.connect('clicked', lambda b: self.devices.load())
1089 self.services_button.connect('clicked', lambda b: self.services.load())
838 self.devices.connect('local-device-removed',1090 self.devices.connect('local-device-removed',
839 lambda widget: self.emit('local-device-removed'))1091 lambda widget: self.emit('local-device-removed'))
8401092
@@ -857,7 +1109,7 @@
857 """Load the account info and file sync status list."""1109 """Load the account info and file sync status list."""
858 self.backend.account_info()1110 self.backend.account_info()
859 self.backend.file_sync_status()1111 self.backend.file_sync_status()
860 self.account_button.clicked()1112 self.dashboard_button.clicked()
8611113
862 @log_call(logger.debug)1114 @log_call(logger.debug)
863 def on_account_info_ready(self, info):1115 def on_account_info_ready(self, info):
@@ -910,3 +1162,6 @@
9101162
911gobject.signal_new('local-device-removed', DevicesPanel,1163gobject.signal_new('local-device-removed', DevicesPanel,
912 gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())1164 gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
1165
1166gobject.signal_new('finished', InstallPackage,
1167 gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
9131168
=== added file 'ubuntuone/controlpanel/gtk/package_manager.py'
--- ubuntuone/controlpanel/gtk/package_manager.py 1970-01-01 00:00:00 +0000
+++ ubuntuone/controlpanel/gtk/package_manager.py 2011-01-03 14:24:42 +0000
@@ -0,0 +1,60 @@
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 manage packages."""
20
21import apt
22import aptdaemon.client
23import aptdaemon.enums
24
25try:
26 # Unable to import 'defer', pylint: disable=F0401,E0611
27 from aptdaemon.defer import inline_callbacks, return_value
28except ImportError:
29 # Unable to import 'defer', pylint: disable=F0401,E0611
30 from defer import inline_callbacks, return_value
31from aptdaemon.gtkwidgets import AptProgressBar
32
33from ubuntuone.controlpanel.logger import setup_logging
34
35
36logger = setup_logging('package_manager')
37
38
39class PackageManagerProgressBar(AptProgressBar):
40 """A progress bar for a transaction."""
41
42
43class PackageManager(object):
44 """Manage packages (check if is installed, install)."""
45
46 def is_installed(self, package_name):
47 """Return whether 'package_name' is installed in this system."""
48 cache = apt.Cache()
49 result = package_name in cache and cache[package_name].is_installed
50 return result
51
52 @inline_callbacks
53 def install(self, package_name):
54 """Install 'package_name' if is not installed in this system."""
55 if self.is_installed(package_name):
56 return_value(aptdaemon.enums.EXIT_SUCCESS)
57
58 client = aptdaemon.client.AptClient()
59 transaction = yield client.install_packages([package_name])
60 return_value(transaction)
061
=== modified file 'ubuntuone/controlpanel/gtk/tests/__init__.py'
--- ubuntuone/controlpanel/gtk/tests/__init__.py 2010-12-02 16:23:03 +0000
+++ ubuntuone/controlpanel/gtk/tests/__init__.py 2011-01-03 14:24:42 +0000
@@ -17,3 +17,153 @@
17# with this program. If not, see <http://www.gnu.org/licenses/>.17# with this program. If not, see <http://www.gnu.org/licenses/>.
1818
19"""The test suite for the GTK UI for the control panel for Ubuntu One."""19"""The test suite for the GTK UI for the control panel for Ubuntu One."""
20
21from collections import defaultdict
22
23from ubuntuone.controlpanel.gtk import gui
24from ubuntuone.controlpanel.gtk.tests.test_package_manager import (
25 FakedTransaction)
26
27
28FAKE_ACCOUNT_INFO = {'type': 'Payed', 'name': 'Test me',
29 'email': 'test.com', 'quota_total': '12345', 'quota_used': '9999'}
30
31FAKE_VOLUMES_INFO = [
32 {'volume_id': '0', 'suggested_path': '~/foo', 'subscribed': ''},
33 {'volume_id': '1', 'suggested_path': '~/bar', 'subscribed': 'True'},
34 {'volume_id': '2', 'suggested_path': '~/baz', 'subscribed': 'True'},
35]
36
37FAKE_DEVICE_INFO = {
38 'device_id': '1258-6854', 'device_name': 'Baz', 'device_type': 'Computer',
39 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True',
40 'max_upload_speed': '1000', 'max_download_speed': '72548',
41}
42
43FAKE_DEVICES_INFO = [
44 {'device_id': '0', 'name': 'Foo', 'type': 'Computer',
45 'is_local': '', 'configurable': ''},
46 {'device_id': '1', 'name': 'Bar', 'type': 'Phone',
47 'is_local': '', 'configurable': ''},
48 {'device_id': '2', 'name': 'Z', 'type': 'Computer',
49 'is_local': '', 'configurable': 'True', 'limit_bandwidth': '',
50 'max_upload_speed': '0', 'max_download_speed': '0'},
51 {'device_id': '1258-6854', 'name': 'Baz', 'type': 'Computer',
52 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True',
53 'max_upload_speed': '1000', 'max_download_speed': '72548'}, # local
54]
55
56
57class FakedObject(object):
58 """Fake an object, record every call."""
59
60 exposed_methods = []
61
62 def __init__(self, *args, **kwargs):
63 self._args = args
64 self._kwargs = kwargs
65 self._called = {}
66 for i in self.exposed_methods:
67 setattr(self, i, self._record_call(i))
68
69 def _record_call(self, func_name):
70 """Store values when calling 'func_name'."""
71
72 def inner(*args, **kwargs):
73 """Fake 'func_name'."""
74 self._called[func_name] = (args, kwargs)
75
76 return inner
77
78
79class FakedNMState(FakedObject):
80 """Fake a NetworkManagerState."""
81
82 exposed_methods = ['find_online_state']
83
84
85class FakedDBusBackend(FakedObject):
86 """Fake a DBus Backend."""
87
88 bus_name = None
89 object_path = None
90 iface = None
91
92 def __init__(self, obj, dbus_interface, *args, **kwargs):
93 if dbus_interface != self.iface:
94 raise TypeError()
95 self._signals = defaultdict(list)
96 super(FakedDBusBackend, self).__init__(*args, **kwargs)
97
98 def connect_to_signal(self, signal, handler):
99 """Bind 'handler' to be callback'd when 'signal' is fired."""
100 self._signals[signal].append(handler)
101
102
103class FakedSSOBackend(FakedDBusBackend):
104 """Fake a SSO Backend, act as a dbus.Interface."""
105
106 bus_name = gui.ubuntu_sso.DBUS_BUS_NAME
107 object_path = gui.ubuntu_sso.DBUS_CREDENTIALS_PATH
108 iface = gui.ubuntu_sso.DBUS_CREDENTIALS_IFACE
109 exposed_methods = ['find_credentials', 'clear_credentials',
110 'login', 'register']
111
112
113class FakedControlPanelBackend(FakedDBusBackend):
114 """Fake a Control Panel Backend, act as a dbus.Interface."""
115
116 bus_name = gui.DBUS_BUS_NAME
117 object_path = gui.DBUS_PREFERENCES_PATH
118 iface = gui.DBUS_PREFERENCES_IFACE
119 exposed_methods = [
120 'account_info', 'devices_info', 'change_device_settings',
121 'volumes_info', 'change_volume_settings', 'file_sync_status',
122 'remove_device', 'enable_files', 'disable_files',
123 ]
124
125
126class FakedSessionBus(object):
127 """Fake a session bus."""
128
129 def get_object(self, bus_name, object_path, introspect=True,
130 follow_name_owner_changes=False, **kwargs):
131 """Return a faked proxy for the given remote object."""
132 return None
133
134
135class FakedInterface(object):
136 """Fake a dbus interface."""
137
138 def __new__(cls, obj, dbus_interface, *args, **kwargs):
139 if dbus_interface == gui.DBUS_PREFERENCES_IFACE:
140 return FakedControlPanelBackend(obj, dbus_interface,
141 *args, **kwargs)
142 if dbus_interface == gui.ubuntu_sso.DBUS_CREDENTIALS_IFACE:
143 return FakedSSOBackend(obj, dbus_interface, *args, **kwargs)
144
145
146class FakedPackageManager(object):
147 """Faked a package manager."""
148
149 def __init__(self):
150 self._installed = {}
151 self.is_installed = lambda package_name: \
152 self._installed.setdefault(package_name, False)
153
154 @gui.package_manager.inline_callbacks
155 def install(self, package_name):
156 """Install 'package_name' if is not installed in this system."""
157 yield
158 self._installed[package_name] = True
159 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
20170
=== modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py'
--- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-20 20:58:39 +0000
+++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-01-03 14:24:42 +0000
@@ -22,136 +22,21 @@
2222
23import logging23import logging
2424
25from collections import defaultdict
26
27from ubuntuone.devtools.handlers import MementoHandler25from ubuntuone.devtools.handlers import MementoHandler
2826
29from ubuntuone.controlpanel.gtk import gui27from ubuntuone.controlpanel.gtk import gui
28from ubuntuone.controlpanel.gtk.tests import (FAKE_ACCOUNT_INFO,
29 FAKE_VOLUMES_INFO, FAKE_DEVICE_INFO, FAKE_DEVICES_INFO,
30 FakedNMState, FakedSSOBackend, FakedSessionBus, FakedInterface,
31 FakedPackageManager, FakedReplication,
32)
30from ubuntuone.controlpanel.tests import TOKEN, TestCase33from ubuntuone.controlpanel.tests import TOKEN, TestCase
3134from ubuntuone.controlpanel.gtk.tests.test_package_manager import (
32# Attribute 'yyy' defined outside __init__35 SUCCESS, FAILURE)
33# pylint: disable=W020136
3437
35# Access to a protected member 'yyy' of a client class38# Attribute 'yyy' defined outside __init__, access to a protected member
36# pylint: disable=W021239# pylint: disable=W0201, W0212
37
38
39FAKE_ACCOUNT_INFO = {'type': 'Payed', 'name': 'Test me',
40 'email': 'test.com', 'quota_total': '12345', 'quota_used': '9999'}
41
42FAKE_VOLUMES_INFO = [
43 {'volume_id': '0', 'suggested_path': '~/foo', 'subscribed': ''},
44 {'volume_id': '1', 'suggested_path': '~/bar', 'subscribed': 'True'},
45 {'volume_id': '2', 'suggested_path': '~/baz', 'subscribed': 'True'},
46]
47
48FAKE_DEVICE_INFO = {
49 'device_id': '1258-6854', 'device_name': 'Baz', 'device_type': 'Computer',
50 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True',
51 'max_upload_speed': '1000', 'max_download_speed': '72548',
52}
53
54FAKE_DEVICES_INFO = [
55 {'device_id': '0', 'name': 'Foo', 'type': 'Computer',
56 'is_local': '', 'configurable': ''},
57 {'device_id': '1', 'name': 'Bar', 'type': 'Phone',
58 'is_local': '', 'configurable': ''},
59 {'device_id': '2', 'name': 'Z', 'type': 'Computer',
60 'is_local': '', 'configurable': 'True', 'limit_bandwidth': '',
61 'max_upload_speed': '0', 'max_download_speed': '0'},
62 {'device_id': '1258-6854', 'name': 'Baz', 'type': 'Computer',
63 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True',
64 'max_upload_speed': '1000', 'max_download_speed': '72548'}, # local
65]
66
67
68class FakedObject(object):
69 """Fake an object, record every call."""
70
71 exposed_methods = []
72
73 def __init__(self, *args, **kwargs):
74 self._args = args
75 self._kwargs = kwargs
76 self._called = {}
77 for i in self.exposed_methods:
78 setattr(self, i, self._record_call(i))
79
80 def _record_call(self, func_name):
81 """Store values when calling 'func_name'."""
82
83 def inner(*args, **kwargs):
84 """Fake 'func_name'."""
85 self._called[func_name] = (args, kwargs)
86
87 return inner
88
89
90class FakedNMState(FakedObject):
91 """Fake a NetworkManagerState."""
92
93 exposed_methods = ['find_online_state']
94
95
96class FakedDBusBackend(FakedObject):
97 """Fake a DBus Backend."""
98
99 bus_name = None
100 object_path = None
101 iface = None
102
103 def __init__(self, obj, dbus_interface, *args, **kwargs):
104 if dbus_interface != self.iface:
105 raise TypeError()
106 self._signals = defaultdict(list)
107 super(FakedDBusBackend, self).__init__(*args, **kwargs)
108
109 def connect_to_signal(self, signal, handler):
110 """Bind 'handler' to be callback'd when 'signal' is fired."""
111 self._signals[signal].append(handler)
112
113
114class FakedSSOBackend(FakedDBusBackend):
115 """Fake a SSO Backend, act as a dbus.Interface."""
116
117 bus_name = gui.ubuntu_sso.DBUS_BUS_NAME
118 object_path = gui.ubuntu_sso.DBUS_CREDENTIALS_PATH
119 iface = gui.ubuntu_sso.DBUS_CREDENTIALS_IFACE
120 exposed_methods = ['find_credentials', 'clear_credentials',
121 'login', 'register']
122
123
124class FakedControlPanelBackend(FakedDBusBackend):
125 """Fake a Control Panel Backend, act as a dbus.Interface."""
126
127 bus_name = gui.DBUS_BUS_NAME
128 object_path = gui.DBUS_PREFERENCES_PATH
129 iface = gui.DBUS_PREFERENCES_IFACE
130 exposed_methods = [
131 'account_info', 'devices_info', 'change_device_settings',
132 'volumes_info', 'change_volume_settings', 'file_sync_status',
133 'remove_device',
134 ]
135
136
137class FakedSessionBus(object):
138 """Fake a session bus."""
139
140 def get_object(self, bus_name, object_path, introspect=True,
141 follow_name_owner_changes=False, **kwargs):
142 """Return a faked proxy for the given remote object."""
143 return None
144
145
146class FakedInterface(object):
147 """Fake a dbus interface."""
148
149 def __new__(cls, obj, dbus_interface, *args, **kwargs):
150 if dbus_interface == gui.DBUS_PREFERENCES_IFACE:
151 return FakedControlPanelBackend(obj, dbus_interface,
152 *args, **kwargs)
153 if dbus_interface == gui.ubuntu_sso.DBUS_CREDENTIALS_IFACE:
154 return FakedSSOBackend(obj, dbus_interface, *args, **kwargs)
15540
15641
157class BaseTestCase(TestCase):42class BaseTestCase(TestCase):
@@ -168,6 +53,7 @@
168 self.patch(gui.dbus, 'SessionBus', FakedSessionBus)53 self.patch(gui.dbus, 'SessionBus', FakedSessionBus)
169 self.patch(gui.dbus, 'Interface', FakedInterface)54 self.patch(gui.dbus, 'Interface', FakedInterface)
170 self.patch(gui.networkstate, 'NetworkManagerState', FakedNMState)55 self.patch(gui.networkstate, 'NetworkManagerState', FakedNMState)
56 self.patch(gui.package_manager, 'PackageManager', FakedPackageManager)
17157
172 if self.klass is not None:58 if self.klass is not None:
173 self.ui = self.klass(**self.kwargs)59 self.ui = self.klass(**self.kwargs)
@@ -339,10 +225,10 @@
339225
340 self.assert_current_tab_correct(self.ui.management)226 self.assert_current_tab_correct(self.ui.management)
341227
342 def test_credentials_found_shows_account_management_panel(self):228 def test_credentials_found_shows_dashboard_management_panel(self):
343 """On 'credentials-found' signal, the management panel is shown.229 """On 'credentials-found' signal, the management panel is shown.
344230
345 If first signal parameter is False, visible tab should be account.231 If first signal parameter is False, visible tab should be dashboard.
346232
347 """233 """
348 self.patch(self.ui.management, 'load', self._set_called)234 self.patch(self.ui.management, 'load', self._set_called)
@@ -350,7 +236,7 @@
350236
351 self.assert_current_tab_correct(self.ui.management)237 self.assert_current_tab_correct(self.ui.management)
352 self.assertEqual(self.ui.management.notebook.get_current_page(),238 self.assertEqual(self.ui.management.notebook.get_current_page(),
353 self.ui.management.ACCOUNT_PAGE)239 self.ui.management.DASHBOARD_PAGE)
354 self.assertEqual(self._called, ((), {}))240 self.assertEqual(self._called, ((), {}))
355241
356 def test_credentials_found_shows_folders_management_panel(self):242 def test_credentials_found_shows_folders_management_panel(self):
@@ -736,11 +622,11 @@
736 messages = ['<small>Test me</small>', 'A <b>little</b> bit more']622 messages = ['<small>Test me</small>', 'A <b>little</b> bit more']
737623
738624
739class AccountTestCase(ControlPanelMixinTestCase):625class DashboardTestCase(ControlPanelMixinTestCase):
740 """The test suite for the account panel."""626 """The test suite for the dashboard panel."""
741627
742 klass = gui.AccountPanel628 klass = gui.DashboardPanel
743 ui_filename = 'account.ui'629 ui_filename = 'dashboard.ui'
744630
745 def assert_account_info_correct(self, info):631 def assert_account_info_correct(self, info):
746 """Check that the displayed account info matches 'info'."""632 """Check that the displayed account info matches 'info'."""
@@ -1087,7 +973,8 @@
1087973
1088 def test_on_limit_bandwidth_toggled(self):974 def test_on_limit_bandwidth_toggled(self):
1089 """When toggling limit_bandwidth, backend is updated."""975 """When toggling limit_bandwidth, backend is updated."""
1090 self.ui.limit_bandwidth.toggled()976 value = not self.ui.limit_bandwidth.get_active()
977 self.ui.limit_bandwidth.set_active(value)
1091 self.assert_device_settings_changed()978 self.assert_device_settings_changed()
1092979
1093 def test_on_max_upload_speed_value_changed(self):980 def test_on_max_upload_speed_value_changed(self):
@@ -1365,11 +1252,294 @@
1365 self.assertEqual(new_devices, old_devices)1252 self.assertEqual(new_devices, old_devices)
13661253
13671254
1368class ApplicationsTestCase(ControlPanelMixinTestCase):1255class InstallPackageTestCase(ControlPanelMixinTestCase):
1369 """The test suite for the applications panel."""1256 """The test suite for the install widget."""
13701257
1371 klass = gui.ApplicationsPanel1258 klass = gui.InstallPackage
1372 ui_filename = 'applications.ui'1259 ui_filename = 'install.ui'
1260 kwargs = {'package_name': 'a test package'}
1261
1262 def test_is_an_box(self):
1263 """Inherits from gtk.VBox."""
1264 self.assertIsInstance(self.ui, gui.gtk.VBox)
1265
1266 def test_inner_widget_is_packed(self):
1267 """The 'itself' vbox is packed into the widget."""
1268 self.assertIn(self.ui.itself, self.ui.get_children())
1269
1270 def test_is_visible(self):
1271 """Is visible."""
1272 self.assertTrue(self.ui.get_visible())
1273
1274 def test_package_name(self):
1275 """The package_name is stored."""
1276 self.assertEqual(self.ui.package_name, self.kwargs['package_name'])
1277
1278 def test_children(self):
1279 """The children is correct."""
1280 children = self.ui.itself.get_children()
1281 self.assertEqual(len(children), 2)
1282 self.assertEqual(self.ui.install_label, children[0])
1283 self.assertIn(self.ui.install_button, children[1].get_children())
1284
1285 def test_install_label(self):
1286 """The install label is correct."""
1287 msg = self.ui.INSTALL_PACKAGE % self.kwargs
1288 self.assertEqual(self.ui.install_label.get_label(), msg)
1289
1290 @gui.package_manager.inline_callbacks
1291 def test_install_button_clicked_shows_progress(self):
1292 """The install button is correct."""
1293 yield self.ui.install_button.clicked()
1294
1295 children = self.ui.itself.get_children()
1296 self.assertEqual(len(children), 2)
1297 self.assertEqual(self.ui.progress_bar, children[1])
1298 self.assertTrue(self.ui.progress_bar.get_visible())
1299 self.assertIsInstance(self.ui.progress_bar,
1300 gui.package_manager.PackageManagerProgressBar)
1301
1302 def test_install_button_clicked_install_label(self):
1303 """The install label is correct."""
1304 yield self.ui.install_button.clicked()
1305
1306 children = self.ui.itself.get_children()
1307 self.assertEqual(len(children), 2)
1308 self.assertEqual(self.ui.install_label, children[0])
1309 msg = self.ui.INSTALLING % self.kwargs
1310 self.assertEqual(self.ui.install_label.get_label(), msg)
1311
1312 @gui.package_manager.inline_callbacks
1313 def test_install_button_clicked_transaction(self):
1314 """The install button transaction is correct."""
1315 yield self.ui.install_button.clicked()
1316
1317 transaction = self.ui.transaction
1318 self.assertTrue(transaction.packages, [self.ui.package_name])
1319 self.assertIn(self.ui.on_install_finished,
1320 transaction._signals['finished'])
1321 self.assertTrue(transaction.was_run)
1322
1323 @gui.package_manager.inline_callbacks
1324 def test_install_button_clicked_fails(self):
1325 """The install button transaction is correct."""
1326
1327 def fail(*args):
1328 """Simulate an error."""
1329 raise Exception(args)
1330
1331 self.patch(self.ui.package_manager, 'install', fail)
1332 yield self.ui.install_button.clicked()
1333
1334 msg = self.ui.FAILED_INSTALL % self.kwargs
1335 self.assert_warning_correct(self.ui.install_label, msg)
1336
1337 @gui.package_manager.inline_callbacks
1338 def test_on_install_finished_success(self):
1339 """The install finished."""
1340 self.ui.connect('finished', self._set_called)
1341 yield self.ui.install_button.clicked()
1342 self.ui.on_install_finished(object(), SUCCESS)
1343
1344 self.assertFalse(self.ui.progress_bar.get_sensitive())
1345 msg = self.ui.SUCCESS_INSTALL % self.kwargs
1346 self.assertEqual(self.ui.install_label.get_label(), msg)
1347 self.assertEqual(self._called, ((self.ui,), {}))
1348
1349 @gui.package_manager.inline_callbacks
1350 def test_on_install_finished_failed(self):
1351 """The install finished."""
1352 yield self.ui.install_button.clicked()
1353 self.ui.on_install_finished(object(), FAILURE)
1354
1355 self.assertFalse(self.ui.progress_bar.get_sensitive())
1356 msg = self.ui.FAILED_INSTALL % self.kwargs
1357 self.assert_warning_correct(self.ui.install_label, msg)
1358
1359
1360class ServiceTestCase(ControlPanelMixinTestCase):
1361 """The test suite for a service."""
1362
1363 klass = gui.Service
1364 name = 'dc_test'
1365 localized_name = u'Qué lindo test!'
1366 kwargs = {'name': 'dc_test', 'localized_name': u'Qué lindo test!'}
1367
1368 def test_is_an_box(self):
1369 """Inherits from gtk.VBox."""
1370 self.assertIsInstance(self.ui, gui.gtk.VBox)
1371
1372 def test_is_visible(self):
1373 """Is visible."""
1374 self.assertTrue(self.ui.get_visible())
1375
1376 def test_check_button_packed(self):
1377 """A check button is packed as only child."""
1378 self.assertIn(self.ui.button, self.ui.get_children())
1379
1380 def test_label(self):
1381 """The label is set."""
1382 self.assertEqual(self.localized_name, self.ui.button.get_label())
1383
1384 def test_service_name(self):
1385 """The service_name is set."""
1386 self.assertEqual(self.name, self.ui.service_name)
1387
1388
1389class FilesServiceTestCase(ServiceTestCase):
1390 """The test suite for the file sync service."""
1391
1392 klass = gui.FilesService
1393 kwargs = {}
1394
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):
1401 """The proper signals are connected to the backend."""
1402 self.assertEqual(self.ui.backend._signals['FileSyncStatusChanged'],
1403 [self.ui.on_file_sync_status_changed])
1404 self.assertEqual(self.ui.backend._signals['FilesEnabled'],
1405 [self.ui.on_files_enabled])
1406 self.assertEqual(self.ui.backend._signals['FilesDisabled'],
1407 [self.ui.on_files_disabled])
1408
1409 def test_file_sync_status_is_requested(self):
1410 """The file sync status is requested to the backend."""
1411 self.assert_backend_called('file_sync_status', ())
1412
1413 def test_is_disabled(self):
1414 """Until file sync status is given, the widget is disabled."""
1415 self.assertFalse(self.ui.get_sensitive())
1416
1417 def test_is_enabled_on_file_sync_status_changed(self):
1418 """When the file sync status is given, the widget is enabled."""
1419 self.ui.on_file_sync_status_changed('something')
1420 self.assertTrue(self.ui.get_sensitive())
1421
1422 def test_active(self):
1423 """Is active when file status is anything but 'file-sync-disabled'."""
1424 self.ui.on_file_sync_status_changed('something not disabled')
1425 self.assertTrue(self.ui.button.get_active())
1426
1427 def test_not_active(self):
1428 """Is not active when status is exactly but 'file-sync-disabled'."""
1429 self.ui.on_file_sync_status_changed(gui.backend.FILE_SYNC_DISABLED)
1430 self.assertFalse(self.ui.button.get_active())
1431
1432 def test_on_button_toggled(self):
1433 """When toggling the button, the file sync service is updated."""
1434 self.ui.on_file_sync_status_changed('something not disabled')
1435 assert self.ui.button.get_active()
1436
1437 self.ui.button.set_active(not self.ui.button.get_active())
1438 self.assert_backend_called('disable_files', ())
1439
1440 self.ui.button.set_active(not self.ui.button.get_active())
1441 self.assert_backend_called('enable_files', ())
1442
1443 def test_on_file_sync_enabled(self):
1444 """When file sync is enabled, the button is active."""
1445 self.ui.on_files_disabled()
1446 assert not self.ui.button.get_active()
1447
1448 self.ui.on_files_enabled()
1449 self.assertTrue(self.ui.button.get_active())
1450
1451 def test_on_file_sync_disabled(self):
1452 """When file sync is disabled, the button is not active."""
1453 self.ui.on_files_enabled()
1454 assert self.ui.button.get_active()
1455
1456 self.ui.on_files_disabled()
1457 self.assertFalse(self.ui.button.get_active())
1458
1459
1460class DesktopcouchServiceTestCase(ServiceTestCase):
1461 """The test suite for a desktopcouch service."""
1462
1463 klass = gui.DesktopcouchService
1464
1465 def setUp(self):
1466 self.replication = FakedReplication()
1467 self.name = self.kwargs['name']
1468 self.kwargs['replication_service'] = self.replication
1469 super(DesktopcouchServiceTestCase, self).setUp()
1470
1471 def test_active(self):
1472 """Is active since replication has an empty database."""
1473 self.assertTrue(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())
1480
1481 def test_on_button_toggled(self):
1482 """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())
1485 self.assertEqual(set([self.name]), self.replication.all_exclusions())
1486
1487 def test_on_button_toggled_twice(self):
1488 """When toggling the button twice, the DC exclude list is updated."""
1489 assert self.ui.button.get_active()
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())
1493
1494 def test_dependency(self):
1495 """The dependency box is None."""
1496 self.assertTrue(self.ui.dependency is None)
1497
1498 def test_button_sensitiveness(self):
1499 """The check button is sensitive."""
1500 self.assertTrue(self.ui.button.get_sensitive())
1501
1502
1503class DesktopcouchServiceWithDependencyTestCase(DesktopcouchServiceTestCase):
1504 """The test suite for a desktopcouch service when it needs a dependency."""
1505
1506 def setUp(self):
1507 self.kwargs['dependency'] = 'a package'
1508 super(DesktopcouchServiceWithDependencyTestCase, self).setUp()
1509
1510 def test_dependency(self):
1511 """The dependency bos is not hidden."""
1512 self.assertIsInstance(self.ui.dependency, gui.InstallPackage)
1513 self.assertEqual(self.ui.dependency.package_name,
1514 self.kwargs['dependency'])
1515
1516 def test_dependency_is_packed(self):
1517 """The dependency is packed in the ui."""
1518 self.assertIn(self.ui.dependency, self.ui.get_children())
1519
1520 def test_button_sensitiveness(self):
1521 """The check button is not sensitive until depedency installed."""
1522 self.assertFalse(self.ui.button.get_sensitive())
1523
1524 def test_button_is_enabled_on_dependency_installed(self):
1525 """The check button is sensitive when depedency is installed."""
1526 self.ui.dependency.emit('finished')
1527
1528 self.assertTrue(self.ui.button.get_sensitive())
1529
1530 def test_install_widget_is_removed_on_dependency_installed(self):
1531 """The install button is removed when depedency is installed."""
1532 self.ui.dependency.emit('finished')
1533
1534 self.assertTrue(self.ui.dependency is None)
1535 self.assertEqual(self.ui.get_children(), [self.ui.button])
1536
1537
1538class ServicesTestCase(ControlPanelMixinTestCase):
1539 """The test suite for the services panel."""
1540
1541 klass = gui.ServicesPanel
1542 ui_filename = 'services.ui'
13731543
1374 def test_is_an_ubuntuone_bin(self):1544 def test_is_an_ubuntuone_bin(self):
1375 """Inherits from UbuntuOneBin."""1545 """Inherits from UbuntuOneBin."""
@@ -1383,6 +1553,200 @@
1383 """Is visible."""1553 """Is visible."""
1384 self.assertTrue(self.ui.get_visible())1554 self.assertTrue(self.ui.get_visible())
13851555
1556 def test_package_manager(self):
1557 """Has a package manager."""
1558 self.assertIsInstance(self.ui.package_manager,
1559 gui.package_manager.PackageManager)
1560
1561 def test_install_box(self):
1562 """The install box is None."""
1563 self.assertTrue(self.ui.install_box is None)
1564
1565
1566class ServicesFilesTestCase(ServicesTestCase):
1567 """The test suite for the services panel (files section)."""
1568
1569 def test_files_is_visible(self):
1570 """Files section is visible."""
1571 self.assertTrue(self.ui.files.get_visible())
1572
1573 def test_files_is_a_file_sync_service(self):
1574 """Files contains a FilesService."""
1575 child, = self.ui.files.get_children()
1576 self.assertIsInstance(child, gui.FilesService)
1577
1578
1579class ServicesWithoutDesktopcouchTestCase(ServicesTestCase):
1580 """The test suite for the services panel when DC is not installed."""
1581
1582 def setUp(self):
1583 super(ServicesWithoutDesktopcouchTestCase, self).setUp()
1584 self.patch(self.ui.package_manager, 'is_installed', lambda *a: False)
1585 self.ui.load()
1586
1587 def test_message(self):
1588 """Global load message is stopped and cleared."""
1589 self.assertFalse(self.ui.message.active)
1590 self.assertEqual(self.ui.message.get_text(), '')
1591
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):
1597 """Has desktopcouch installed?"""
1598 self.assertFalse(self.ui.has_desktopcouch)
1599
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):
1609 """The install box is not hidden."""
1610 self.assertTrue(self.ui.install_box.get_visible())
1611
1612 def test_replications_is_hidden(self):
1613 """The replications section is disabled."""
1614 self.assertFalse(self.ui.replications.get_visible())
1615
1616 def test_install_box(self):
1617 """The install box is enabled."""
1618 self.assertTrue(self.ui.install_box.get_visible())
1619 self.assertIn(self.ui.install_box, self.ui.itself.get_children())
1620 self.assertEqual(self.ui.install_box.package_name,
1621 self.ui.DESKTOPCOUCH_PKG)
1622
1623 def test_install_box_finished_connected(self):
1624 """The install box 'finished' signal is connected."""
1625 self.patch(self.ui, 'load_replications', self._set_called)
1626 self.ui.load() # ensure signal connection uses the new method
1627
1628 self.ui.install_box.emit('finished')
1629
1630 self.assertEqual(self._called, ((self.ui.install_box,), {}))
1631
1632
1633class ServicesWithDesktopcouchTestCase(ServicesTestCase):
1634 """The test suite for the services panel."""
1635
1636 kwargs = {'replication_exclusion_class': FakedReplication}
1637
1638 def setUp(self):
1639 super(ServicesWithDesktopcouchTestCase, self).setUp()
1640 self.ui.package_manager._installed[self.ui.DESKTOPCOUCH_PKG] = True
1641 self.ui.load()
1642
1643 def test_message(self):
1644 """Global load message is stopped and proper test is shown."""
1645 self.assertFalse(self.ui.message.active)
1646 self.assertEqual(self.ui.message.get_text(), self.ui.CHOOSE_SERVICES)
1647
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):
1668 """Has desktopcouch installed?"""
1669 self.assertTrue(self.ui.has_desktopcouch)
1670
1671 def test_replications(self):
1672 """Has proper child for each desktopcouch replication available."""
1673 self.assertTrue(self.ui.replications.get_visible())
1674
1675 children = self.ui.replications.get_children()
1676 self.assertEqual(len(children), 2)
1677 for child in children:
1678 self.assertIsInstance(child, gui.DesktopcouchService)
1679
1680 self.assertTrue(self.ui.bookmarks is children[0])
1681 self.assertTrue(self.ui.contacts is children[1])
1682
1683 def test_replications_after_loading_twice(self):
1684 """Has proper child after loading twice."""
1685 self.ui.load()
1686 self.test_replications()
1687
1688 def test_bookmarks(self):
1689 """The bookmarks is correct."""
1690 self.assertEqual(self.ui.bookmarks.service_name, 'bookmarks')
1691 self.assertEqual(self.ui.bookmarks.button.get_label(),
1692 self.ui.BOOKMARKS)
1693 self.assertTrue(self.ui.bookmarks.replication_service is
1694 self.ui.replication_service)
1695
1696 def test_bookmarks_dependency(self):
1697 """The bookmarks dependency is correct."""
1698 self.assertTrue(self.ui.bookmarks.dependency is not None)
1699 self.assertEqual(self.ui.bookmarks.dependency.package_name,
1700 self.ui.BINDWOOD_PKG)
1701
1702 def test_contacts(self):
1703 """The contacts is correct."""
1704 self.assertEqual(self.ui.contacts.service_name, 'contacts')
1705 self.assertEqual(self.ui.contacts.button.get_label(),
1706 self.ui.CONTACTS)
1707 self.assertTrue(self.ui.contacts.replication_service is
1708 self.ui.replication_service)
1709
1710 def test_contacts_dependency(self):
1711 """The contacts dependency is correct."""
1712 self.assertTrue(self.ui.contacts.dependency is not None)
1713 self.assertEqual(self.ui.contacts.dependency.package_name,
1714 self.ui.EVOCOUCH_PKG)
1715
1716
1717class ServicesWithDCAndBindwoodTestCase(ServicesWithDesktopcouchTestCase):
1718 """The test suite for the services panel."""
1719
1720 def setUp(self):
1721 super(ServicesWithDCAndBindwoodTestCase, self).setUp()
1722 self.ui.package_manager._installed[self.ui.BINDWOOD_PKG] = True
1723 self.ui.load()
1724
1725 def test_has_bindwood(self):
1726 """Has bindwood installed?"""
1727 self.assertTrue(self.ui.has_bindwood)
1728
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)
1749
13861750
1387class ManagementPanelTestCase(ControlPanelMixinTestCase):1751class ManagementPanelTestCase(ControlPanelMixinTestCase):
1388 """The test suite for the management panel."""1752 """The test suite for the management panel."""
@@ -1420,11 +1784,11 @@
1420 """Tabs are not shown."""1784 """Tabs are not shown."""
1421 self.assertFalse(self.ui.notebook.get_show_tabs())1785 self.assertFalse(self.ui.notebook.get_show_tabs())
14221786
1423 def test_default_page_is_account(self):1787 def test_default_page_is_dashboard(self):
1424 """The default page is Account."""1788 """The default page is Dashboard."""
1425 self.assertEqual(self.ui.notebook.get_current_page(),1789 self.assertEqual(self.ui.notebook.get_current_page(),
1426 self.ui.ACCOUNT_PAGE)1790 self.ui.DASHBOARD_PAGE)
1427 self.assertTrue(self.ui.account_button.get_active())1791 self.assertTrue(self.ui.dashboard_button.get_active())
14281792
1429 def test_buttons_set_notebook_pages(self):1793 def test_buttons_set_notebook_pages(self):
1430 """The notebook pages are set when clicking buttons."""1794 """The notebook pages are set when clicking buttons."""
@@ -1449,10 +1813,6 @@
1449 active = getattr(self.ui, '%s_button' % other).get_active()1813 active = getattr(self.ui, '%s_button' % other).get_active()
1450 self.assertFalse(active, msg % (button, other))1814 self.assertFalse(active, msg % (button, other))
14511815
1452
1453class ManagementPanelAccountTestCase(ManagementPanelTestCase):
1454 """The test suite for the management panel (account tab)."""
1455
1456 def test_backend_account_signals(self):1816 def test_backend_account_signals(self):
1457 """The proper signals are connected to the backend."""1817 """The proper signals are connected to the backend."""
1458 self.assertEqual(self.ui.backend._signals['AccountInfoReady'],1818 self.assertEqual(self.ui.backend._signals['AccountInfoReady'],
@@ -1465,11 +1825,11 @@
1465 self.ui.load()1825 self.ui.load()
1466 self.assert_backend_called('account_info', ())1826 self.assert_backend_called('account_info', ())
14671827
1468 def test_account_panel_is_packed(self):1828 def test_dashboard_panel_is_packed(self):
1469 """The account panel is packed."""1829 """The dashboard panel is packed."""
1470 self.assertIsInstance(self.ui.account, gui.AccountPanel)1830 self.assertIsInstance(self.ui.dashboard, gui.DashboardPanel)
1471 actual = self.ui.notebook.get_nth_page(self.ui.ACCOUNT_PAGE)1831 actual = self.ui.notebook.get_nth_page(self.ui.DASHBOARD_PAGE)
1472 self.assertTrue(self.ui.account is actual)1832 self.assertTrue(self.ui.dashboard is actual)
14731833
1474 def test_folders_panel_is_packed(self):1834 def test_folders_panel_is_packed(self):
1475 """The folders panel is packed."""1835 """The folders panel is packed."""
@@ -1483,11 +1843,11 @@
1483 actual = self.ui.notebook.get_nth_page(self.ui.DEVICES_PAGE)1843 actual = self.ui.notebook.get_nth_page(self.ui.DEVICES_PAGE)
1484 self.assertTrue(self.ui.devices is actual)1844 self.assertTrue(self.ui.devices is actual)
14851845
1486 def test_applications_panel_is_packed(self):1846 def test_services_panel_is_packed(self):
1487 """The applications panel is packed."""1847 """The services panel is packed."""
1488 self.assertIsInstance(self.ui.applications, gui.ApplicationsPanel)1848 self.assertIsInstance(self.ui.services, gui.ServicesPanel)
1489 actual = self.ui.notebook.get_nth_page(self.ui.APPLICATIONS_PAGE)1849 actual = self.ui.notebook.get_nth_page(self.ui.SERVICES_PAGE)
1490 self.assertTrue(self.ui.applications is actual)1850 self.assertTrue(self.ui.services is actual)
14911851
1492 def test_entering_folders_tab_loads_content(self):1852 def test_entering_folders_tab_loads_content(self):
1493 """The volumes info is loaded when entering the Folders tab."""1853 """The volumes info is loaded when entering the Folders tab."""
@@ -1505,6 +1865,14 @@
15051865
1506 self.assertEqual(self._called, ((), {}))1866 self.assertEqual(self._called, ((), {}))
15071867
1868 def test_entering_services_tab_loads_content(self):
1869 """The services info is loaded when entering the Devices tab."""
1870 self.patch(self.ui.services, 'load', self._set_called)
1871 # clean backend calls
1872 self.ui.services_button.clicked()
1873
1874 self.assertEqual(self._called, ((), {}))
1875
1508 def test_quota_placeholder_is_loading(self):1876 def test_quota_placeholder_is_loading(self):
1509 """Placeholders for quota label is a Loading widget."""1877 """Placeholders for quota label is a Loading widget."""
1510 self.assertIsInstance(self.ui.quota_label, gui.LabelLoading)1878 self.assertIsInstance(self.ui.quota_label, gui.LabelLoading)
15111879
=== added file 'ubuntuone/controlpanel/gtk/tests/test_package_manager.py'
--- ubuntuone/controlpanel/gtk/tests/test_package_manager.py 1970-01-01 00:00:00 +0000
+++ ubuntuone/controlpanel/gtk/tests/test_package_manager.py 2011-01-03 14:24:42 +0000
@@ -0,0 +1,177 @@
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"""Tests for the package manager service."""
20
21import collections
22
23try:
24 # Unable to import 'defer', pylint: disable=F0401,E0611
25 from aptdaemon import defer
26except ImportError:
27 # Unable to import 'defer', pylint: disable=F0401,E0611
28 import defer
29
30from ubuntuone.controlpanel.gtk import package_manager
31from ubuntuone.controlpanel.tests import TestCase
32
33
34FAKED_CACHE = {}
35SUCCESS = package_manager.aptdaemon.enums.EXIT_SUCCESS
36FAILURE = package_manager.aptdaemon.enums.EXIT_FAILED
37
38
39class FakedPackage(object):
40 """Fake a package."""
41
42 def __init__(self, name=None, is_installed=False):
43 self.name = name
44 self.is_installed = is_installed
45
46
47class FakedTransaction(object):
48 """Fake a transaction."""
49
50 failure = None
51
52 def __init__(self, packages, cache=None):
53 self._signals = collections.defaultdict(list)
54 self._cache = cache
55 self.was_run = False
56 self.packages = packages
57 self.connect = lambda sig, f: self._signals[sig].append(f)
58
59 def run(self):
60 """Run!"""
61 self.was_run = True
62
63 if self._cache is not None:
64 for package in self.packages:
65 FAKED_CACHE[package].is_installed = True
66
67 if self.failure is None:
68 code = SUCCESS
69 else:
70 code = FAILURE
71
72 for listener in self._signals['finished']:
73 listener(self, code)
74
75 d = defer.Deferred()
76 d.callback(code)
77 return d
78
79
80class FakedClient(object):
81 """Fake an apt client."""
82
83 def install_packages(self, packages_names, **kwargs):
84 """Install packages listed in 'package_names'.
85
86 package_names - a list of package names
87 wait - if True run the transaction immediately and return its exit
88 state instead of the transaction itself.
89 reply_handler - callback function. If specified in combination with
90 error_handler the method will be called asynchrounsouly.
91 error_handler - in case of an error the given callback gets the
92 corresponding DBus exception instance
93
94 """
95 if kwargs.get('wait', False):
96 return package_manager.aptdaemon.enums.EXIT_FAILED
97 d = defer.Deferred()
98 d.callback(FakedTransaction(packages_names, cache=FAKED_CACHE))
99 return d
100
101
102class PackageManagerTestCase(TestCase):
103 """Test for the package manager."""
104
105 timeout = 2
106
107 def setUp(self):
108 FAKED_CACHE.clear() # clean cache
109 self.patch(package_manager.apt, 'Cache', lambda: FAKED_CACHE)
110 self.patch(package_manager.aptdaemon.client, 'AptClient', FakedClient)
111 self.obj = package_manager.PackageManager()
112
113 def test_is_installed(self):
114 """Check that a package is installed."""
115 name = 'test'
116 FAKED_CACHE[name] = FakedPackage(name, is_installed=True)
117
118 result = self.obj.is_installed(name)
119
120 self.assertTrue(result, 'must be installed')
121
122 def test_is_not_installed(self):
123 """Check if a package is installed."""
124 name = 'test'
125 FAKED_CACHE[name] = FakedPackage(name) # not installed by default
126
127 result = self.obj.is_installed(name)
128
129 self.assertFalse(result, 'must not be installed')
130
131 def test_is_not_installed_if_key_error(self):
132 """Check if a package is installed when cache raises KeyError."""
133 name = 'test' # is not in the cache
134 result = self.obj.is_installed(name)
135
136 self.assertFalse(result, 'must not be installed')
137
138 def test_progress_bar(self):
139 """The progress bar class is correct."""
140 self.assertIsInstance(package_manager.PackageManagerProgressBar(),
141 package_manager.AptProgressBar)
142
143 @package_manager.inline_callbacks
144 def test_install(self):
145 """Install is correct."""
146 name = 'test'
147 FAKED_CACHE[name] = FakedPackage(name) # not installed by default
148
149 result = yield self.obj.install(name)
150
151 self.assertIsInstance(result, FakedTransaction)
152 self.assertFalse(result.was_run, 'transaction must not be run')
153
154 @package_manager.inline_callbacks
155 def test_transaction_install(self):
156 """Install is correct."""
157 name = 'test'
158 FAKED_CACHE[name] = FakedPackage(name) # not installed by default
159
160 trans = yield self.obj.install(name)
161
162 trans.connect('finished', self._set_called)
163 trans.run()
164
165 self.assertEqual(self._called, ((trans, SUCCESS), {}))
166 self.assertTrue(trans.was_run, 'transaction must be run')
167 self.assertTrue(self.obj.is_installed(name))
168
169 @package_manager.inline_callbacks
170 def test_install_if_installed(self):
171 """Install does nothing is package is already installed."""
172 name = 'test'
173 FAKED_CACHE[name] = FakedPackage(name, is_installed=True)
174
175 result = yield self.obj.install(name)
176
177 self.assertEqual(result, SUCCESS)
0178
=== modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_service.py'
--- ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2010-12-16 21:09:46 +0000
+++ ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2011-01-03 14:24:42 +0000
@@ -150,6 +150,14 @@
150 """Return the status of the file sync service."""150 """Return the status of the file sync service."""
151 return self._process(self.sample_status)151 return self._process(self.sample_status)
152152
153 def enable_files(self):
154 """Enable files service."""
155 return self._process(None)
156
157 def disable_files(self):
158 """Disable files service."""
159 return self._process(None)
160
153 def volumes_info(self):161 def volumes_info(self):
154 """Get the user volumes info."""162 """Get the user volumes info."""
155 return self._process(SAMPLE_VOLUMES_INFO)163 return self._process(SAMPLE_VOLUMES_INFO)
@@ -383,6 +391,28 @@
383 self.backend.remove_device, sample_token)391 self.backend.remove_device, sample_token)
384 return self.assert_correct_method_call(*args)392 return self.assert_correct_method_call(*args)
385393
394 def test_enable_files(self):
395 """Enable files service."""
396
397 def got_signal(*args):
398 """The correct signal was received."""
399 self.deferred.callback("success")
400
401 args = ("FilesEnabled", "FilesEnableError", got_signal,
402 self.backend.enable_files)
403 return self.assert_correct_method_call(*args)
404
405 def test_disable_files(self):
406 """Disable files service."""
407
408 def got_signal():
409 """The correct signal was received."""
410 self.deferred.callback("success")
411
412 args = ("FilesDisabled", "FilesDisableError", got_signal,
413 self.backend.disable_files)
414 return self.assert_correct_method_call(*args)
415
386 def test_volumes_info(self):416 def test_volumes_info(self):
387 """The volumes info is reported."""417 """The volumes info is reported."""
388418
@@ -527,6 +557,20 @@
527 args = (dbus_service.FILE_SYNC_IDLE, "FileSyncStatusIdle")557 args = (dbus_service.FILE_SYNC_IDLE, "FileSyncStatusIdle")
528 return self.assert_correct_status_signal(*args)558 return self.assert_correct_status_signal(*args)
529559
560 def test_file_sync_status_changed(self):
561 """The file sync status is reported every time status changed."""
562 status = (
563 dbus_service.FILE_SYNC_DISABLED,
564 dbus_service.FILE_SYNC_DISCONNECTED,
565 dbus_service.FILE_SYNC_ERROR,
566 dbus_service.FILE_SYNC_IDLE,
567 dbus_service.FILE_SYNC_STARTING,
568 dbus_service.FILE_SYNC_SYNCING,
569 )
570 for arg in status:
571 args = (arg, "FileSyncStatusChanged")
572 return self.assert_correct_status_signal(*args, expected_msg=arg)
573
530 def test_status_changed_handler(self):574 def test_status_changed_handler(self):
531 """The status changed handler is properly set."""575 """The status changed handler is properly set."""
532 be = MockBackend()576 be = MockBackend()
533577
=== modified file 'ubuntuone/controlpanel/logger.py'
--- ubuntuone/controlpanel/logger.py 2010-12-08 22:25:24 +0000
+++ ubuntuone/controlpanel/logger.py 2011-01-03 14:24:42 +0000
@@ -33,7 +33,7 @@
33 LOG_LEVEL = logging.DEBUG33 LOG_LEVEL = logging.DEBUG
34else:34else:
35 # Only log this level and above35 # Only log this level and above
36 LOG_LEVEL = logging.INFO36 LOG_LEVEL = logging.DEBUG # before final release, switch to INFO
3737
38MAIN_HANDLER = RotatingFileHandler(os.path.join(LOGFOLDER, 'controlpanel.log'),38MAIN_HANDLER = RotatingFileHandler(os.path.join(LOGFOLDER, 'controlpanel.log'),
39 maxBytes=1048576,39 maxBytes=1048576,
4040
=== modified file 'ubuntuone/controlpanel/tests/__init__.py'
--- ubuntuone/controlpanel/tests/__init__.py 2010-12-02 16:23:03 +0000
+++ ubuntuone/controlpanel/tests/__init__.py 2011-01-03 14:24:42 +0000
@@ -18,7 +18,7 @@
1818
19"""The test suite for the control panel for Ubuntu One."""19"""The test suite for the control panel for Ubuntu One."""
2020
21from twisted.trial import unittest21from ubuntuone.devtools.testcase import TestCase as BaseTestCase
2222
2323
24TOKEN = {u'consumer_key': u'xQ7xDAz',24TOKEN = {u'consumer_key': u'xQ7xDAz',
@@ -28,7 +28,7 @@
28 u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'}28 u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'}
2929
3030
31class TestCase(unittest.TestCase):31class TestCase(BaseTestCase):
32 """Basics for testing."""32 """Basics for testing."""
3333
34 def setUp(self):34 def setUp(self):
3535
=== modified file 'ubuntuone/controlpanel/tests/test_backend.py'
--- ubuntuone/controlpanel/tests/test_backend.py 2010-12-18 20:16:18 +0000
+++ ubuntuone/controlpanel/tests/test_backend.py 2011-01-03 14:24:42 +0000
@@ -257,11 +257,11 @@
257257
258 def files_sync_enabled(self):258 def files_sync_enabled(self):
259 """Get if file sync service is enabled."""259 """Get if file sync service is enabled."""
260 return self.file_sync260 return MockDBusClient.file_sync
261261
262 def set_files_sync_enabled(self, enabled):262 def set_files_sync_enabled(self, enabled):
263 """Set the file sync service to be 'enabled'."""263 """Set the file sync service to be 'enabled'."""
264 self.file_sync = enabled264 MockDBusClient.file_sync = enabled
265265
266 def get_folders(self):266 def get_folders(self):
267 """Grab list of folders."""267 """Grab list of folders."""
@@ -653,3 +653,21 @@
653 # Access to a protected member _process_file_sync_status653 # Access to a protected member _process_file_sync_status
654 expected_status = self.be._process_file_sync_status(status)654 expected_status = self.be._process_file_sync_status(status)
655 self.assertEqual(self._called, ((expected_status,), {}))655 self.assertEqual(self._called, ((expected_status,), {}))
656
657
658class BackendSyncEnabledTestCase(BackendBasicTestCase):
659 """Syncdaemon enable/disable for the backend."""
660
661 def test_enable_files(self):
662 """Files service is enabled."""
663 self.be.disable_files()
664
665 self.be.enable_files()
666 self.assertTrue(MockDBusClient.file_sync)
667
668 def test_disable_files(self):
669 """Files service is disabled."""
670 self.be.enable_files()
671
672 self.be.disable_files()
673 self.assertFalse(MockDBusClient.file_sync)

Subscribers

People subscribed via source and target branches