Merge lp:~nataliabidart/ubuntuone-control-panel/stable-3-0-update-2.99.92 into lp:ubuntuone-control-panel/stable-3-0

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 258
Merged at revision: 257
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/stable-3-0-update-2.99.92
Merge into: lp:ubuntuone-control-panel/stable-3-0
Diff against target: 1335 lines (+334/-437)
19 files modified
ubuntuone/controlpanel/backend.py (+13/-31)
ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py (+0/-94)
ubuntuone/controlpanel/gui/__init__.py (+1/-1)
ubuntuone/controlpanel/gui/qt/controlpanel.py (+3/-4)
ubuntuone/controlpanel/gui/qt/folders.py (+3/-2)
ubuntuone/controlpanel/gui/qt/gui.py (+1/-2)
ubuntuone/controlpanel/gui/qt/main/__init__.py (+2/-1)
ubuntuone/controlpanel/gui/qt/main/tests/test_main.py (+10/-1)
ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+13/-1)
ubuntuone/controlpanel/gui/qt/tests/test_preferences.py (+15/-0)
ubuntuone/controlpanel/gui/qt/tests/test_wizard.py (+12/-1)
ubuntuone/controlpanel/gui/qt/wizard.py (+4/-8)
ubuntuone/controlpanel/sd_client/__init__.py (+10/-13)
ubuntuone/controlpanel/sd_client/linux.py (+0/-46)
ubuntuone/controlpanel/tests/test_backend.py (+66/-105)
ubuntuone/controlpanel/tests/test_sd_client.py (+23/-17)
ubuntuone/controlpanel/utils/__init__.py (+8/-4)
ubuntuone/controlpanel/utils/tests/test_windows.py (+106/-71)
ubuntuone/controlpanel/utils/windows.py (+44/-35)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/stable-3-0-update-2.99.92
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Approve
Review via email: mp+100599@code.launchpad.net

Commit message

- Updating from trunk up to revno 308:

[ Brian Curtin <email address hidden> ]
  - Match test behavior with updated functionality.

[ Diego Sarmentero <email address hidden> ]
  - Removing encoding to return unicode (LP: #966513).
  - Using limit_bandwidth attribute to properly process the info dict
    for preferences (LP: #944256).

[ Natalia B. Bidart <email address hidden> ]
  - When user rejects the License, also uninstall the application
    when in Windows (LP: #968327).
  - Remove custom path validation and use the one provided by syncdaemon
    (LP: #824252).
  - Make use of the new feature from syncdaemon where 'refresh_volumes'
    returns a deferred that gets fired when the server info is ready
    (LP: #851810).
  - Ensured that Folders' tree view has proper texts in the columns to
    have proper column widths (LP: #965175).

[ Roberto Alsina <email address hidden> ]
   - Fixed the combination of --minimized --with-icon options
    (LP: #968369).

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

Attaching bug numbers.

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

Approving

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'ubuntuone/controlpanel/backend.py'
--- ubuntuone/controlpanel/backend.py 2012-03-01 22:05:51 +0000
+++ ubuntuone/controlpanel/backend.py 2012-04-03 13:15:23 +0000
@@ -1,9 +1,6 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2
3# Authors: Alejandro J. Cura <alecu@canonical.com>
4# Authors: Natalia B. Bidart <nataliabidart@canonical.com>
5#2#
6# Copyright 2010 Canonical Ltd.3# Copyright 2010-2012 Canonical Ltd.
7#4#
8# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -26,11 +23,7 @@
26from functools import wraps23from functools import wraps
2724
28from twisted.internet.defer import inlineCallbacks, returnValue25from twisted.internet.defer import inlineCallbacks, returnValue
29# No name 'is_link' in module 'ubuntuone.platform'
30# pylint: disable=E0611, F0401
31from ubuntuone.platform import is_link
32from ubuntuone.platform.credentials import CredentialsManagementTool26from ubuntuone.platform.credentials import CredentialsManagementTool
33# pylint: enable=E0611, F0401
3427
35from ubuntuone.controlpanel import sd_client, replication_client28from ubuntuone.controlpanel import sd_client, replication_client
36from ubuntuone.controlpanel.logger import setup_logging, log_call29from ubuntuone.controlpanel.logger import setup_logging, log_call
@@ -599,7 +592,7 @@
599592
600 @log_call(logger.debug)593 @log_call(logger.debug)
601 @inlineCallbacks594 @inlineCallbacks
602 def volumes_info(self, with_storage_info=True):595 def volumes_info(self, with_storage_info=True, refresh=False):
603 """Get the volumes info."""596 """Get the volumes info."""
604 self._volumes = {}597 self._volumes = {}
605598
@@ -612,6 +605,9 @@
612 else:605 else:
613 free_bytes = account['quota_total'] - account['quota_used']606 free_bytes = account['quota_total'] - account['quota_used']
614607
608 if refresh:
609 yield self.sd_client.refresh_volumes()
610
615 root_dir = yield self.sd_client.get_root_dir()611 root_dir = yield self.sd_client.get_root_dir()
616 shares_dir = yield self.sd_client.get_shares_dir()612 shares_dir = yield self.sd_client.get_shares_dir()
617 shares_dir_link = yield self.sd_client.get_shares_dir_link()613 shares_dir_link = yield self.sd_client.get_shares_dir_link()
@@ -717,28 +713,9 @@
717 yield self.sd_client.create_folder(path=folder_path)713 yield self.sd_client.create_folder(path=folder_path)
718714
719 @log_call(logger.debug)715 @log_call(logger.debug)
720 @inlineCallbacks
721 def validate_path_for_folder(self, folder_path):716 def validate_path_for_folder(self, folder_path):
722 """Validate 'folder_path' for folder creation."""717 """Validate 'folder_path' for folder creation."""
723 user_home = yield self.get_home_dir()718 return self.sd_client.validate_path(folder_path)
724 folder_path = append_path_sep(folder_path)
725
726 # handle folder_path not within '~' or links
727 # XXX is_link expects bytes, see bug #824252
728 if not folder_path.startswith(user_home) or is_link(
729 folder_path.encode('utf-8')):
730 returnValue(False)
731
732 # handle folder_path nested with a existing cloud folder
733 volumes = yield self.volumes_info(with_storage_info=False)
734 for _, _, data in volumes:
735 for volume in data:
736 cloud_folder = append_path_sep(volume['path'])
737 if (folder_path.startswith(cloud_folder) or
738 cloud_folder.startswith(folder_path)):
739 returnValue(False)
740
741 returnValue(True)
742719
743 @log_call(logger.debug)720 @log_call(logger.debug)
744 @inlineCallbacks721 @inlineCallbacks
@@ -787,8 +764,13 @@
787 result[name] = bool(value)764 result[name] = bool(value)
788765
789 limits = yield self.sd_client.get_throttling_limits()766 limits = yield self.sd_client.get_throttling_limits()
790 result[DOWNLOAD_KEY] = limits['download']767 limits_enabled = yield self.sd_client.bandwidth_throttling_enabled()
791 result[UPLOAD_KEY] = limits['upload']768 if limits_enabled:
769 result[DOWNLOAD_KEY] = limits['download']
770 result[UPLOAD_KEY] = limits['upload']
771 else:
772 result[DOWNLOAD_KEY] = -1
773 result[UPLOAD_KEY] = -1
792774
793 returnValue(result)775 returnValue(result)
794776
795777
=== removed file 'ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py'
--- ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py 2011-10-24 21:48:27 +0000
+++ ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py 1970-01-01 00:00:00 +0000
@@ -1,94 +0,0 @@
1# -*- coding: utf-8 -*-
2
3# Authors: Alejandro J. Cura <alecu@canonical.com>
4# Authors: Natalia B. Bidart <natalia.bidart@canonical.com>
5#
6# Copyright 2010 Canonical Ltd.
7#
8# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published
10# by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranties of
14# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15# PURPOSE. See the GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program. If not, see <http://www.gnu.org/licenses/>.
19
20"""Tests for the DBus service when accessing SyncDaemon."""
21
22import dbus
23
24from twisted.internet import defer
25
26from ubuntuone.platform.linux import dbus_interface as sd_dbus_iface
27
28from ubuntuone.controlpanel import sd_client
29from ubuntuone.controlpanel.dbustests import DBusClientTestCase
30
31
32SD_DBUS_IFACE_NAME = sd_dbus_iface.DBUS_IFACE_NAME
33SD_DBUS_IFACE_STATUS_NAME = sd_dbus_iface.DBUS_IFACE_STATUS_NAME
34
35# pylint, you have to go to decorator's school
36# pylint: disable=C0322
37
38# Access to a protected member of a client class
39# pylint: disable=W0212
40
41
42class StatusMockDBusSyncDaemon(dbus.service.Object):
43 """A mock object that mimicks syncdaemon regarding the Status iface."""
44
45 state_dict = {
46 'name': 'TEST',
47 'description': 'Some test state, nothing else.',
48 'is_error': '',
49 'is_connected': 'True',
50 'is_online': '',
51 'queues': 'GORGEOUS',
52 'connection': '',
53 }
54
55 def _get_current_state(self):
56 """Get the current status of the system."""
57 return self.state_dict
58
59 @dbus.service.method(SD_DBUS_IFACE_STATUS_NAME,
60 in_signature='', out_signature='a{ss}')
61 def current_status(self):
62 """Return the current faked status of the system."""
63 return self._get_current_state()
64
65 # pylint: disable=C0103
66 # Invalid name "StatusChanged"
67
68 @dbus.service.signal(SD_DBUS_IFACE_STATUS_NAME)
69 def StatusChanged(self, status):
70 """Fire a signal to notify that the status of the system changed."""
71
72 def emit_status_changed(self, state=None):
73 """Emit StatusChanged."""
74 self.StatusChanged(self._get_current_state())
75
76
77class StatusTestCase(DBusClientTestCase):
78 """Test for the status dbus client methods."""
79
80 @defer.inlineCallbacks
81 def setUp(self):
82 yield super(StatusTestCase, self).setUp()
83 self.register_mockserver(SD_DBUS_IFACE_NAME,
84 "/status", StatusMockDBusSyncDaemon)
85
86 def test_set_status_changed_handler(self):
87 """A proper callback can be connected to StatusChanged signal."""
88 client = sd_client.SyncDaemonClient()
89 _, sig = client.set_status_changed_handler(self._set_called)
90
91 self.assertEqual(sig._handler, self._set_called)
92 self.assertEqual(sig._member, 'StatusChanged')
93 self.assertEqual(sig._path, '/status')
94 self.assertEqual(sig._interface, SD_DBUS_IFACE_STATUS_NAME)
950
=== modified file 'ubuntuone/controlpanel/gui/__init__.py'
--- ubuntuone/controlpanel/gui/__init__.py 2012-03-20 15:11:56 +0000
+++ ubuntuone/controlpanel/gui/__init__.py 2012-04-03 13:15:23 +0000
@@ -204,7 +204,7 @@
204MAIN_PREFERENCES_TAB = _('Settings')204MAIN_PREFERENCES_TAB = _('Settings')
205MAIN_WINDOW_TITLE = _('%(app_name)s Control Panel')205MAIN_WINDOW_TITLE = _('%(app_name)s Control Panel')
206MUSIC_DISPLAY_NAME = _('Purchased Music')206MUSIC_DISPLAY_NAME = _('Purchased Music')
207MUSIC_REAL_PATH = '.ubuntuone/Purchased from Ubuntu One'207MUSIC_REAL_PATH = u'.ubuntuone/Purchased from Ubuntu One'
208MY_FOLDERS = _('My folders')208MY_FOLDERS = _('My folders')
209NAME_NOT_SET = _('[unknown user name]')209NAME_NOT_SET = _('[unknown user name]')
210NETWORK_OFFLINE = _('An internet connection is required to join or sign '210NETWORK_OFFLINE = _('An internet connection is required to join or sign '
211211
=== modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py'
--- ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-03-21 19:17:21 +0000
+++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-04-03 13:15:23 +0000
@@ -1,5 +1,5 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
22#
3# Copyright 2012 Canonical Ltd.3# Copyright 2012 Canonical Ltd.
4#4#
5# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
@@ -174,7 +174,6 @@
174 @log_call(logger.info)174 @log_call(logger.info)
175 def start_from_license(self):175 def start_from_license(self):
176 """Use the license page as first page."""176 """Use the license page as first page."""
177 # license177 license_id = self.ui.wizard.pages[self.ui.wizard.license_page]
178 self.ui.wizard.setStartId(self.ui.wizard.pages[178 self.ui.wizard.setStartId(license_id)
179 self.ui.wizard.license_page])
180 self.ui.wizard.restart()179 self.ui.wizard.restart()
181180
=== modified file 'ubuntuone/controlpanel/gui/qt/folders.py'
--- ubuntuone/controlpanel/gui/qt/folders.py 2012-03-20 13:05:20 +0000
+++ ubuntuone/controlpanel/gui/qt/folders.py 2012-04-03 13:15:23 +0000
@@ -123,7 +123,7 @@
123 self.ui.folders.headerItem().setText(FOLDER_NAME_COL,123 self.ui.folders.headerItem().setText(FOLDER_NAME_COL,
124 FOLDERS_COLUMN_NAME)124 FOLDERS_COLUMN_NAME)
125 self.ui.folders.headerItem().setText(SUBSCRIPTION_COL,125 self.ui.folders.headerItem().setText(SUBSCRIPTION_COL,
126 FOLDERS_COLUMN_SYNC_LOCALLY)126 ALWAYS_SUBSCRIBED)
127 self.ui.folders.headerItem().setText(EXPLORE_COL,127 self.ui.folders.headerItem().setText(EXPLORE_COL,
128 FOLDERS_COLUMN_EXPLORE)128 FOLDERS_COLUMN_EXPLORE)
129 headers = self.ui.folders.header()129 headers = self.ui.folders.header()
@@ -153,7 +153,8 @@
153 def load(self):153 def load(self):
154 """Load specific tab info."""154 """Load specific tab info."""
155 self.is_processing = True155 self.is_processing = True
156 info = yield self.backend.volumes_info(with_storage_info=False)156 info = yield self.backend.volumes_info(with_storage_info=False,
157 refresh=self.remote_folders)
157 self.process_info(info)158 self.process_info(info)
158159
159 @handle_errors(logger=logger)160 @handle_errors(logger=logger)
160161
=== modified file 'ubuntuone/controlpanel/gui/qt/gui.py'
--- ubuntuone/controlpanel/gui/qt/gui.py 2012-03-26 13:23:57 +0000
+++ ubuntuone/controlpanel/gui/qt/gui.py 2012-04-03 13:15:23 +0000
@@ -43,8 +43,7 @@
43 self.ui.setupUi(self)43 self.ui.setupUi(self)
44 self.close_callback = close_callback44 self.close_callback = close_callback
45 self._setup()45 self._setup()
46 self.quit_action = QtGui.QAction(self,46 self.quit_action = QtGui.QAction(self, triggered=self.close)
47 triggered=self.close)
48 self.quit_action.setShortcuts(["Ctrl+q", "Ctrl+w"])47 self.quit_action.setShortcuts(["Ctrl+q", "Ctrl+w"])
49 self.addAction(self.quit_action)48 self.addAction(self.quit_action)
50 self.installer = installer49 self.installer = installer
5150
=== modified file 'ubuntuone/controlpanel/gui/qt/main/__init__.py'
--- ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-03-27 13:41:19 +0000
+++ ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-04-03 13:15:23 +0000
@@ -98,7 +98,8 @@
98 icon, window = start(lambda: source.main_quit(app),98 icon, window = start(lambda: source.main_quit(app),
99 minimized=minimized, with_icon=with_icon,99 minimized=minimized, with_icon=with_icon,
100 installer=installer)100 installer=installer)
101 window.switch_to(switch_to)101 if window:
102 window.switch_to(switch_to)
102 # pylint: enable=W0612103 # pylint: enable=W0612
103 if icon:104 if icon:
104 app.new_instance.connect(icon.restore_window)105 app.new_instance.connect(icon.restore_window)
105106
=== modified file 'ubuntuone/controlpanel/gui/qt/main/tests/test_main.py'
--- ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-03-27 13:49:49 +0000
+++ ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-04-03 13:15:23 +0000
@@ -96,8 +96,11 @@
96 self.window = None96 self.window = None
9797
98 def __call__(self, *args, **kwargs):98 def __call__(self, *args, **kwargs):
99 if kwargs.get('minimized', False):
100 self.window = None
101 else:
102 self.window = FakeMainWindow()
99 self.args = (args, kwargs)103 self.args = (args, kwargs)
100 self.window = FakeMainWindow()
101 return None, self.window104 return None, self.window
102105
103106
@@ -166,6 +169,12 @@
166 self.assertEqual(self.start.args[1],169 self.assertEqual(self.start.args[1],
167 {'minimized': False, 'with_icon': False, 'installer': True})170 {'minimized': False, 'with_icon': False, 'installer': True})
168171
172 def test_minimized_with_icon_options(self):
173 """Ensure you can be minimized and with icon at the same time."""
174 main.main([sys.argv[0], "--minimized", "--with-icon"])
175 self.assertEqual(self.start.args[1],
176 {'minimized': True, 'with_icon': True, 'installer': False})
177
169 def test_translator(self):178 def test_translator(self):
170 """Ensure the Qt translator is loaded."""179 """Ensure the Qt translator is loaded."""
171 main.main([sys.argv[0]])180 main.main([sys.argv[0]])
172181
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-03-23 14:04:57 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-04-03 13:15:23 +0000
@@ -187,7 +187,8 @@
187187
188 def test_info_is_requested_on_load(self):188 def test_info_is_requested_on_load(self):
189 """The volumes info is requested to the backend."""189 """The volumes info is requested to the backend."""
190 self.assert_backend_called('volumes_info', with_storage_info=False)190 self.assert_backend_called('volumes_info', with_storage_info=False,
191 refresh=self.ui.remote_folders)
191192
192 def test_process_info(self):193 def test_process_info(self):
193 """The volumes info is processed when ready."""194 """The volumes info is processed when ready."""
@@ -409,6 +410,17 @@
409 item)410 item)
410 it += 1411 it += 1
411412
413 def test_tree_view_header(self):
414 """The tree view header is hidden but the text is correct."""
415 self.assertFalse(self.ui.ui.folders.header().isVisible())
416
417 name = self.ui.ui.folders.headerItem().text(gui.FOLDER_NAME_COL)
418 self.assertEqual(name, gui.FOLDERS_COLUMN_NAME)
419 name = self.ui.ui.folders.headerItem().text(gui.SUBSCRIPTION_COL)
420 self.assertEqual(name, gui.ALWAYS_SUBSCRIBED)
421 name = self.ui.ui.folders.headerItem().text(gui.EXPLORE_COL)
422 self.assertEqual(name, gui.FOLDERS_COLUMN_EXPLORE)
423
412 def test_share_publish_button(self):424 def test_share_publish_button(self):
413 """When clicking the share/publish button, the proper url is opened."""425 """When clicking the share/publish button, the proper url is opened."""
414 self.assertTrue(self.ui.ui.share_publish_button.isVisible())426 self.assertTrue(self.ui.ui.share_publish_button.isVisible())
415427
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_preferences.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_preferences.py 2012-03-01 22:05:51 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_preferences.py 2012-04-03 13:15:23 +0000
@@ -76,6 +76,21 @@
7676
77 self.assertFalse(self.ui.is_processing)77 self.assertFalse(self.ui.is_processing)
7878
79 def test_process_info_limit_bandwidth(self):
80 """The ui is not processing when contents are load."""
81 self.ui.process_info(SAMPLE_SETTINGS)
82
83 self.assertTrue(self.ui.ui.limit_uploads_checkbox.isChecked())
84 self.assertTrue(self.ui.ui.limit_downloads_checkbox.isChecked())
85
86 settings = SAMPLE_SETTINGS.copy()
87 settings[gui.backend.DOWNLOAD_KEY] = -1
88 settings[gui.backend.UPLOAD_KEY] = -1
89 self.ui.process_info(settings)
90
91 self.assertFalse(self.ui.ui.limit_uploads_checkbox.isChecked())
92 self.assertFalse(self.ui.ui.limit_downloads_checkbox.isChecked())
93
79 def test_info_is_requested_on_load(self):94 def test_info_is_requested_on_load(self):
80 """The info is requested to the backend."""95 """The info is requested to the backend."""
81 self.assert_backend_called('file_sync_settings_info')96 self.assert_backend_called('file_sync_settings_info')
8297
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_wizard.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-26 21:00:47 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-04-03 13:15:23 +0000
@@ -157,6 +157,7 @@
157 @defer.inlineCallbacks157 @defer.inlineCallbacks
158 def setUp(self):158 def setUp(self):
159 yield super(UbuntuOneWizardTestCase, self).setUp()159 yield super(UbuntuOneWizardTestCase, self).setUp()
160 self.patch(gui.utils, 'uninstall_application', self.fail)
160 self.patch(self.ui.confirm_dialog, 'exec_',161 self.patch(self.ui.confirm_dialog, 'exec_',
161 lambda: self.confirm_response)162 lambda: self.confirm_response)
162163
@@ -260,7 +261,7 @@
260 button.click()261 button.click()
261262
262 self.assertEqual(self._called, (signal_args, {}),263 self.assertEqual(self._called, (signal_args, {}),
263 msg % (name, signal, signal_args))264 msg % (name, signal, signal_args))
264 self._called = False265 self._called = False
265266
266 def test_done_rejected(self):267 def test_done_rejected(self):
@@ -336,6 +337,16 @@
336 page_name = 'license'337 page_name = 'license'
337 stage_name = 'install'338 stage_name = 'install'
338339
340 @defer.inlineCallbacks
341 def setUp(self):
342 yield super(UbuntuOneWizardLicensePage, self).setUp()
343 self.patch(gui.utils, 'uninstall_application', self._set_called)
344
345 def test_done_rejected(self):
346 """When the wizard is closed on the final page, emit rejected."""
347 super(UbuntuOneWizardLicensePage, self).test_done_rejected()
348 self.assertEqual(self._called, ((), {}))
349
339350
340class UbuntuOneWizardLoginTestCase(UbuntuOneWizardTestCase):351class UbuntuOneWizardLoginTestCase(UbuntuOneWizardTestCase):
341 """Test the login through the wizard."""352 """Test the login through the wizard."""
342353
=== modified file 'ubuntuone/controlpanel/gui/qt/wizard.py'
--- ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-26 20:15:58 +0000
+++ ubuntuone/controlpanel/gui/qt/wizard.py 2012-04-03 13:15:23 +0000
@@ -23,7 +23,7 @@
23from ubuntu_sso.qt.sso_wizard_page import BaseWizardPage23from ubuntu_sso.qt.sso_wizard_page import BaseWizardPage
24from ubuntu_sso.utils.ui import CLOSE_AND_SETUP_LATER24from ubuntu_sso.utils.ui import CLOSE_AND_SETUP_LATER
2525
26from ubuntuone.controlpanel import cache26from ubuntuone.controlpanel import cache, utils
27from ubuntuone.controlpanel.logger import log_call, setup_logging27from ubuntuone.controlpanel.logger import log_call, setup_logging
28from ubuntuone.controlpanel.gui import (28from ubuntuone.controlpanel.gui import (
29 APP_NAME,29 APP_NAME,
@@ -342,13 +342,9 @@
342 response = self.confirm_dialog.exec_()342 response = self.confirm_dialog.exec_()
343 if response == QtGui.QDialog.Accepted:343 if response == QtGui.QDialog.Accepted:
344 logger.warning('UbuntuOneWizard: user canceled setup.')344 logger.warning('UbuntuOneWizard: user canceled setup.')
345 self.rejected.emit()345 if self.currentId() == self.pages[self.license_page]:
346 elif (self.currentId() == self.pages[self.license_page]):346 logger.warning('UbuntuOneWizard: user wants to uninstall.')
347 response = self.confirm_dialog.exec_()347 utils.uninstall_application()
348 if response == QtGui.QDialog.Accepted:
349 logger.warning('UbuntuOneWizard: user wants to uninstall.')
350 # TODO: needs implementation in this project
351 ##qt.utils.uninstall_application()
352 self.rejected.emit()348 self.rejected.emit()
353 else:349 else:
354 super(UbuntuOneWizard, self).done(result)350 super(UbuntuOneWizard, self).done(result)
355351
=== modified file 'ubuntuone/controlpanel/sd_client/__init__.py'
--- ubuntuone/controlpanel/sd_client/__init__.py 2012-01-18 14:06:35 +0000
+++ ubuntuone/controlpanel/sd_client/__init__.py 2012-04-03 13:15:23 +0000
@@ -1,8 +1,6 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2
3# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
4#2#
5# Copyright 2011 Canonical Ltd.3# Copyright 2012 Canonical Ltd.
6#4#
7# This program is free software: you can redistribute it and/or modify it5# 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 published6# under the terms of the GNU General Public License version 3, as published
@@ -18,7 +16,6 @@
1816
19"""The syncdaemon client."""17"""The syncdaemon client."""
2018
21import sys
22import warnings19import warnings
2320
24# pylint: disable=E061121# pylint: disable=E0611
@@ -35,7 +32,6 @@
3532
36 def __init__(self):33 def __init__(self):
37 """Get a proxy for the SyncDaemonTool."""34 """Get a proxy for the SyncDaemonTool."""
38 self.status_changed_handler = None
39 self.proxy = tools.SyncDaemonTool()35 self.proxy = tools.SyncDaemonTool()
4036
41 def get_throttling_limits(self):37 def get_throttling_limits(self):
@@ -128,6 +124,10 @@
128 """Retrieve the folders information from syncdaemon."""124 """Retrieve the folders information from syncdaemon."""
129 return self.proxy.get_folders()125 return self.proxy.get_folders()
130126
127 def validate_path(self, path):
128 """Validates 'path' to create a new folder through syncdaemon."""
129 return self.proxy.validate_path(path)
130
131 def create_folder(self, path):131 def create_folder(self, path):
132 """Create a new folder through syncdaemon."""132 """Create a new folder through syncdaemon."""
133 return self.proxy.create_folder(path)133 return self.proxy.create_folder(path)
@@ -200,11 +200,8 @@
200200
201 def set_status_changed_handler(self, handler):201 def set_status_changed_handler(self, handler):
202 """Set the status handler function."""202 """Set the status handler function."""
203 self.status_changed_handler = handler203 return self.proxy.connect_signal('StatusChanged', handler)
204 if sys.platform.startswith("linux"):204
205 # pylint: disable=W0404205 def refresh_volumes(self):
206 from ubuntuone.controlpanel.sd_client import linux206 """Refresh the volumes information from syncdaemon."""
207 result = linux.set_status_changed_handler(handler)207 return self.proxy.refresh_volumes()
208 else:
209 result = self.proxy.set_status_changed_handler(handler)
210 return result
211208
=== removed file 'ubuntuone/controlpanel/sd_client/linux.py'
--- ubuntuone/controlpanel/sd_client/linux.py 2011-06-29 17:59:07 +0000
+++ ubuntuone/controlpanel/sd_client/linux.py 1970-01-01 00:00:00 +0000
@@ -1,46 +0,0 @@
1# -*- coding: utf-8 -*-
2
3# Authors: Alejandro J. Cura <alecu@canonical.com>
4# Authors: Natalia B. Bidart <nataliabidart@canonical.com>
5#
6# Copyright 2010 Canonical Ltd.
7#
8# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published
10# by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranties of
14# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15# PURPOSE. See the GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program. If not, see <http://www.gnu.org/licenses/>.
19
20"""Client to use other DBus services."""
21
22import dbus.service
23
24from ubuntuone.controlpanel.logger import setup_logging
25
26
27logger = setup_logging('sd_client')
28
29
30def get_syncdaemon_proxy(object_path, dbus_interface):
31 """Get a DBus proxy for syncdaemon at 'object_path':'dbus_interface'."""
32 logger.debug('get_syncdaemon_proxy: object_path %r, dbus_interface %r',
33 object_path, dbus_interface)
34 bus = dbus.SessionBus()
35 obj = bus.get_object(bus_name='com.ubuntuone.SyncDaemon',
36 object_path=object_path,
37 follow_name_owner_changes=True)
38 proxy = dbus.Interface(object=obj, dbus_interface=dbus_interface)
39 return proxy
40
41
42def set_status_changed_handler(handler):
43 """Connect 'handler' with syncdaemon's StatusChanged signal."""
44 proxy = get_syncdaemon_proxy('/status', 'com.ubuntuone.SyncDaemon.Status')
45 sig = proxy.connect_to_signal('StatusChanged', handler)
46 return proxy, sig
470
=== modified file 'ubuntuone/controlpanel/tests/test_backend.py'
--- ubuntuone/controlpanel/tests/test_backend.py 2012-02-06 15:23:27 +0000
+++ ubuntuone/controlpanel/tests/test_backend.py 2012-04-03 13:15:23 +0000
@@ -1,9 +1,6 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2
3# Authors: Alejandro J. Cura <alecu@canonical.com>
4# Authors: Natalia B. Bidart <nataliabidart@canonical.com>
5#2#
6# Copyright 2010 Canonical Ltd.3# Copyright 2010-2012 Canonical Ltd.
7#4#
8# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -169,6 +166,7 @@
169 self.actions = []166 self.actions = []
170 self.shares = []167 self.shares = []
171 self.folders = []168 self.folders = []
169 self.volumes_refreshed = False
172170
173 def get_throttling_limits(self):171 def get_throttling_limits(self):
174 """Return the sample speed limits."""172 """Return the sample speed limits."""
@@ -291,6 +289,10 @@
291 """Unsubcribe from 'volume_id'."""289 """Unsubcribe from 'volume_id'."""
292 self.subscribed_folders.remove(volume_id)290 self.subscribed_folders.remove(volume_id)
293291
292 def validate_path(self, path):
293 """Validate a path for folder creation."""
294 return path != USER_HOME
295
294 def create_folder(self, path):296 def create_folder(self, path):
295 """Grab list of folders."""297 """Grab list of folders."""
296 self.folders.append(path)298 self.folders.append(path)
@@ -319,6 +321,11 @@
319 """Grab list of shared (shares from the user to others)."""321 """Grab list of shared (shares from the user to others)."""
320 return SAMPLE_SHARED322 return SAMPLE_SHARED
321323
324 def refresh_volumes(self):
325 """Refresh the volume list."""
326 self.volumes_refreshed = True
327 return defer.succeed(None)
328
322329
323class MockReplicationClient(CallRecorder):330class MockReplicationClient(CallRecorder):
324 """A mock replication_client module."""331 """A mock replication_client module."""
@@ -860,10 +867,19 @@
860 u'display_name': display_name,867 u'display_name': display_name,
861 }868 }
862869
870 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
871 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
872
863 @inlineCallbacks873 @inlineCallbacks
864 def expected_volumes(self, sample_shares, sample_folders,874 def expected_volumes(self, sample_shares=None, sample_folders=None,
865 with_storage_info=True):875 with_storage_info=True):
866 """Get shares and group by sharing user, get folders and free space."""876 """Get shares and group by sharing user, get folders and free space."""
877 if sample_shares is None:
878 sample_shares = self.be.sd_client.shares
879
880 if sample_folders is None:
881 sample_folders = self.be.sd_client.folders
882
867 free_bytes = self.be.FREE_BYTES_NOT_AVAILABLE883 free_bytes = self.be.FREE_BYTES_NOT_AVAILABLE
868 if with_storage_info:884 if with_storage_info:
869 try:885 try:
@@ -926,11 +942,19 @@
926 @inlineCallbacks942 @inlineCallbacks
927 def test_volumes_info(self):943 def test_volumes_info(self):
928 """The volumes_info method exercises its callback."""944 """The volumes_info method exercises its callback."""
929 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)945 expected = yield self.expected_volumes()
930 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
931
932 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)
933 result = yield self.be.volumes_info()946 result = yield self.be.volumes_info()
947
948 self.assertEqual(result, expected)
949 self.assertFalse(self.be.sd_client.volumes_refreshed)
950
951 @inlineCallbacks
952 def test_volumes_info_can_refresh_volumes(self):
953 """The volumes_info can be refreshed."""
954 expected = yield self.expected_volumes()
955 result = yield self.be.volumes_info(refresh=True)
956
957 self.assertTrue(self.be.sd_client.volumes_refreshed)
934 self.assertEqual(result, expected)958 self.assertEqual(result, expected)
935959
936 @inlineCallbacks960 @inlineCallbacks
@@ -943,7 +967,6 @@
943 path = path[len(USER_HOME) + 1:]967 path = path[len(USER_HOME) + 1:]
944 item[u'path'] = os.path.join(root_path, path)968 item[u'path'] = os.path.join(root_path, path)
945 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)969 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
946 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
947970
948 yield self.be.volumes_info()971 yield self.be.volumes_info()
949 for item in SAMPLE_FOLDERS:972 for item in SAMPLE_FOLDERS:
@@ -956,11 +979,7 @@
956 @inlineCallbacks979 @inlineCallbacks
957 def test_volumes_info_without_storage_info(self):980 def test_volumes_info_without_storage_info(self):
958 """The volumes_info method exercises its callback."""981 """The volumes_info method exercises its callback."""
959 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)982 expected = yield self.expected_volumes(with_storage_info=False)
960 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
961
962 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS,
963 with_storage_info=False)
964 result = yield self.be.volumes_info(with_storage_info=False)983 result = yield self.be.volumes_info(with_storage_info=False)
965 self.assertEqual(result, expected)984 self.assertEqual(result, expected)
966985
@@ -971,11 +990,8 @@
971 @inlineCallbacks990 @inlineCallbacks
972 def test_volumes_are_cached(self):991 def test_volumes_are_cached(self):
973 """The volume list is cached."""992 """The volume list is cached."""
974 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
975 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
976
977 expected = {}993 expected = {}
978 info = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)994 info = yield self.expected_volumes()
979 for _, _, data in info:995 for _, _, data in info:
980 for volume in data:996 for volume in data:
981 sid = volume['volume_id']997 sid = volume['volume_id']
@@ -1024,10 +1040,8 @@
1024 ]1040 ]
10251041
1026 self.patch(self.be.sd_client, 'shares', read_only_shares)1042 self.patch(self.be.sd_client, 'shares', read_only_shares)
1027 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
10281043
1029 expected = yield self.expected_volumes(read_only_shares,1044 expected = yield self.expected_volumes()
1030 SAMPLE_FOLDERS)
1031 result = yield self.be.volumes_info()1045 result = yield self.be.volumes_info()
1032 self.assertEqual(result, expected)1046 self.assertEqual(result, expected)
10331047
@@ -1035,10 +1049,8 @@
1035 def test_volumes_info_no_quota_for_root(self):1049 def test_volumes_info_no_quota_for_root(self):
1036 """The volumes_info returns info even if quota call fails."""1050 """The volumes_info returns info even if quota call fails."""
1037 self.be.wc.failure = 5001051 self.be.wc.failure = 500
1038 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
1039 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
10401052
1041 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)1053 expected = yield self.expected_volumes()
1042 result = yield self.be.volumes_info()1054 result = yield self.be.volumes_info()
10431055
1044 self.assertEqual(len(result), len(expected))1056 self.assertEqual(len(result), len(expected))
@@ -1126,29 +1138,15 @@
1126 @inlineCallbacks1138 @inlineCallbacks
1127 def test_create_folder(self):1139 def test_create_folder(self):
1128 """New folders can be created."""1140 """New folders can be created."""
1141 self.patch(self.be.sd_client, 'folders', [])
1142
1129 folder_path = os.path.join(USER_HOME, 'Test Me')1143 folder_path = os.path.join(USER_HOME, 'Test Me')
1130 yield self.be.create_folder(folder_path=folder_path)1144 yield self.be.create_folder(folder_path=folder_path)
11311145
1132 self.assertEqual(self.be.sd_client.folders, [folder_path])1146 self.assertEqual(self.be.sd_client.folders, [folder_path])
11331147
11341148 @defer.inlineCallbacks
1135class BackendValidatePathForFolderTestCase(BackendBasicTestCase):1149 def test_validate_path_for_folder(self):
1136 """Test for the path validator when creating folders."""
1137
1138 msg = '%r must not be a valid path for creating a folder.'
1139
1140 @defer.inlineCallbacks
1141 def setUp(self):
1142 yield super(BackendValidatePathForFolderTestCase, self).setUp()
1143 old_home = os.environ['HOME']
1144 os.environ['HOME'] = USER_HOME
1145 self.addCleanup(os.environ.__setitem__, 'HOME', old_home)
1146 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
1147 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
1148 self.patch(backend, 'is_link', lambda p: False)
1149
1150 @defer.inlineCallbacks
1151 def test_valid_if_folder_inside_home(self):
1152 """Test proper validation of path for creating folders."""1150 """Test proper validation of path for creating folders."""
1153 folder_path = os.path.join(USER_HOME, 'Test Me')1151 folder_path = os.path.join(USER_HOME, 'Test Me')
11541152
@@ -1157,68 +1155,11 @@
1157 '%r must be a valid path for creating a folder.' % folder_path)1155 '%r must be a valid path for creating a folder.' % folder_path)
11581156
1159 @defer.inlineCallbacks1157 @defer.inlineCallbacks
1160 def test_valid_if_folder_shares_a_prefix_with_an_udf(self):1158 def test_validate_path_for_folder_invalid(self):
1161 """Test proper validation of path for creating folders.1159 """Test proper validation of path for creating folders."""
11621160 result = yield self.be.validate_path_for_folder(USER_HOME)
1163 If the user chooses a folder with the same prefix as an UDF, but1161 self.assertFalse(result,
1164 outside every UDF, the path is valid.1162 '%r must not be a valid path for creating a folder.' % USER_HOME)
1165
1166 """
1167 tricky_path = ROOT_PATH
1168 assert not tricky_path.endswith(os.path.sep)
1169 tricky_path += ' Suffix'
1170 assert tricky_path.startswith(ROOT_PATH)
1171
1172 result = yield self.be.validate_path_for_folder(tricky_path)
1173 self.assertTrue(result,
1174 '%r must be a valid path for creating a folder.' % tricky_path)
1175
1176 @inlineCallbacks
1177 def test_path_not_valid_if_outside_home(self):
1178 """A folder outside ~ is not valid."""
1179 outside_home = os.path.abspath(os.path.join(USER_HOME, os.path.pardir))
1180
1181 result = yield self.be.validate_path_for_folder(outside_home)
1182 self.assertFalse(result, self.msg % outside_home)
1183
1184 @defer.inlineCallbacks
1185 def test_not_valid_if_folder_inside_root(self):
1186 """A folder inside the root is not valid."""
1187 root_path = ROOT_PATH
1188 # create a valid path inside the root
1189 inside_root = os.path.abspath(os.path.join(root_path, 'test'))
1190
1191 result = yield self.be.validate_path_for_folder(inside_root)
1192 self.assertFalse(result, self.msg % inside_root)
1193
1194 @defer.inlineCallbacks
1195 def test_not_valid_if_folder_inside_an_udf(self):
1196 """A folder inside an UDF is not valid."""
1197 udf_path = SAMPLE_FOLDERS[-1]['path']
1198 # create a valid path inside an existing UDF
1199 inside_udf = os.path.abspath(os.path.join(udf_path, 'test'))
1200
1201 result = yield self.be.validate_path_for_folder(inside_udf)
1202 self.assertFalse(result, self.msg % inside_udf)
1203
1204 @defer.inlineCallbacks
1205 def test_not_valid_if_folder_is_parent_of_an_udf(self):
1206 """A folder parent of an UDF is not valid."""
1207 udf_path = SAMPLE_FOLDERS[-1]['path']
1208 # create a valid path that is parent from an existing UDF
1209 udf_parent = os.path.abspath(os.path.join(udf_path, os.path.pardir))
1210
1211 result = yield self.be.validate_path_for_folder(udf_parent)
1212 self.assertFalse(result, self.msg % udf_parent)
1213
1214 @defer.inlineCallbacks
1215 def test_not_valid_if_folder_is_link(self):
1216 """A link path is not valid."""
1217 self.patch(backend, 'is_link', lambda p: True)
1218 path_link = os.path.join(USER_HOME, 'Test Me')
1219
1220 result = yield self.be.validate_path_for_folder(path_link)
1221 self.assertFalse(result, self.msg % path_link)
12221163
12231164
1224class BackendSyncStatusTestCase(BackendBasicTestCase):1165class BackendSyncStatusTestCase(BackendBasicTestCase):
@@ -1598,6 +1539,7 @@
1598 @inlineCallbacks1539 @inlineCallbacks
1599 def test_file_sync_settings_info(self):1540 def test_file_sync_settings_info(self):
1600 """The settings_info method exercises its callback."""1541 """The settings_info method exercises its callback."""
1542 self.patch(self.be.sd_client, "throttling", True)
1601 self.be.sd_client.limits = {"download": 1000, "upload": 100}1543 self.be.sd_client.limits = {"download": 1000, "upload": 100}
1602 expected = {1544 expected = {
1603 backend.AUTOCONNECT_KEY: self.be.sd_client.autoconnect,1545 backend.AUTOCONNECT_KEY: self.be.sd_client.autoconnect,
@@ -1614,6 +1556,25 @@
1614 self.assertEqual(expected, result)1556 self.assertEqual(expected, result)
16151557
1616 @inlineCallbacks1558 @inlineCallbacks
1559 def test_file_sync_settings_info_with_limit(self):
1560 """The settings_info method exercises its callback."""
1561 self.patch(self.be.sd_client, "throttling", False)
1562 self.be.sd_client.limits = {"download": 987456, "upload": 125698}
1563 expected = {
1564 backend.AUTOCONNECT_KEY: self.be.sd_client.autoconnect,
1565 backend.SHOW_ALL_NOTIFICATIONS_KEY:
1566 self.be.sd_client.show_all_notifications,
1567 backend.SHARE_AUTOSUBSCRIBE_KEY:
1568 self.be.sd_client.share_autosubscribe,
1569 backend.UDF_AUTOSUBSCRIBE_KEY:
1570 self.be.sd_client.udf_autosubscribe,
1571 backend.DOWNLOAD_KEY: -1,
1572 backend.UPLOAD_KEY: -1,
1573 }
1574 result = yield self.be.file_sync_settings_info()
1575 self.assertEqual(expected, result)
1576
1577 @inlineCallbacks
1617 def test_change_file_sync_setting_autoconnect(self):1578 def test_change_file_sync_setting_autoconnect(self):
1618 """The settings can be changed for autoconnect."""1579 """The settings can be changed for autoconnect."""
1619 yield self.assert_boolean_setting_is_correct(backend.AUTOCONNECT_KEY)1580 yield self.assert_boolean_setting_is_correct(backend.AUTOCONNECT_KEY)
16201581
=== modified file 'ubuntuone/controlpanel/tests/test_sd_client.py'
--- ubuntuone/controlpanel/tests/test_sd_client.py 2012-03-19 17:16:43 +0000
+++ ubuntuone/controlpanel/tests/test_sd_client.py 2012-04-03 13:15:23 +0000
@@ -1,9 +1,6 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2
3# Authors: Alejandro J. Cura <alecu@canonical.com>
4# Authors: Natalia B. Bidart <natalia.bidart@canonical.com>
5#2#
6# Copyright 2010 Canonical Ltd.3# Copyright 2010-2012 Canonical Ltd.
7#4#
8# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -24,11 +21,7 @@
2421
25from twisted.internet import defer22from twisted.internet import defer
26from twisted.internet.defer import inlineCallbacks, returnValue23from twisted.internet.defer import inlineCallbacks, returnValue
27from ubuntuone.devtools.testcases import skipIfNotOS
28# No name 'tools' in module 'ubuntuone.platform'
29# pylint: disable=E0611
30from ubuntuone.platform import tools24from ubuntuone.platform import tools
31# pylint: enable=E0611
32from ubuntuone.syncdaemon.interaction_interfaces import bool_str25from ubuntuone.syncdaemon.interaction_interfaces import bool_str
3326
34from ubuntuone.controlpanel import sd_client27from ubuntuone.controlpanel import sd_client
@@ -164,6 +157,10 @@
164 """Get the list of the shares "shared"/created/offered."""157 """Get the list of the shares "shared"/created/offered."""
165 self.called['list_shared'] = None158 self.called['list_shared'] = None
166159
160 def validate_path(self, path):
161 """Validate a path for folder creation."""
162 return path not in [f['path'] for f in self.folders.itervalues()]
163
167 def create_folder(self, path):164 def create_folder(self, path):
168 """Create a user defined folder in the specified path."""165 """Create a user defined folder in the specified path."""
169 if path == '': # simulate an error166 if path == '': # simulate an error
@@ -183,14 +180,12 @@
183 @inlineCallbacks180 @inlineCallbacks
184 def subscribe_folder(self, folder_id):181 def subscribe_folder(self, folder_id):
185 """Subscribe to a user defined folder given its id."""182 """Subscribe to a user defined folder given its id."""
186 yield self._set_folder_attr(folder_id,183 yield self._set_folder_attr(folder_id, u'subscribed', True)
187 u'subscribed', True)
188184
189 @inlineCallbacks185 @inlineCallbacks
190 def unsubscribe_folder(self, folder_id):186 def unsubscribe_folder(self, folder_id):
191 """Unsubscribe from a user defined folder given its id."""187 """Unsubscribe from a user defined folder given its id."""
192 yield self._set_folder_attr(folder_id,188 yield self._set_folder_attr(folder_id, u'subscribed', False)
193 u'subscribed', False)
194189
195 @inlineCallbacks190 @inlineCallbacks
196 def get_folders(self):191 def get_folders(self):
@@ -315,9 +310,9 @@
315 """Return the shares link directory."""310 """Return the shares link directory."""
316 return self.shares_dir_link311 return self.shares_dir_link
317312
318 def set_status_changed_handler(self, handler):313 def connect_signal(self, signal_name, handler):
319 """Set the handler for the status changed signal."""314 """Set the handler for the status changed signal."""
320 self.called['status_changed_handler'] = handler315 self.called[signal_name] = handler
321316
322317
323class BaseTestCase(TestCase):318class BaseTestCase(TestCase):
@@ -519,6 +514,19 @@
519 yield self.assertFailure(self.sd.get_folders(), CustomError)514 yield self.assertFailure(self.sd.get_folders(), CustomError)
520515
521 @inlineCallbacks516 @inlineCallbacks
517 def test_validate_path(self):
518 """Check if a folder path is valid."""
519 path = '~/bar/baz'
520 result = yield self.sd.validate_path(path)
521
522 self.assertTrue(result)
523
524 yield self.sd.create_folder(path)
525 result = yield self.sd.validate_path(path)
526
527 self.assertFalse(result)
528
529 @inlineCallbacks
522 def test_create_folder(self):530 def test_create_folder(self):
523 """Create a new folder."""531 """Create a new folder."""
524 path = '~/bar/baz'532 path = '~/bar/baz'
@@ -694,14 +702,12 @@
694702
695 self.assertEqual(self.sd.proxy.called['quit'], None)703 self.assertEqual(self.sd.proxy.called['quit'], None)
696704
697 @skipIfNotOS('win32', 'The tested function is only defined on windows')
698 @inlineCallbacks705 @inlineCallbacks
699 def test_set_status_changed_handler(self):706 def test_set_status_changed_handler(self):
700 """Connect a handler to the status changed signal."""707 """Connect a handler to the status changed signal."""
701 sample_handler = object()708 sample_handler = object()
702 yield self.sd.set_status_changed_handler(sample_handler)709 yield self.sd.set_status_changed_handler(sample_handler)
703 self.assertEqual(sample_handler,710 self.assertEqual(sample_handler, self.sd.proxy.called['StatusChanged'])
704 self.sd.proxy.called['status_changed_handler'])
705711
706712
707class BasicTestCase(BaseTestCase):713class BasicTestCase(BaseTestCase):
708714
=== modified file 'ubuntuone/controlpanel/utils/__init__.py'
--- ubuntuone/controlpanel/utils/__init__.py 2012-03-22 23:28:19 +0000
+++ ubuntuone/controlpanel/utils/__init__.py 2012-04-03 13:15:23 +0000
@@ -32,19 +32,23 @@
32# ignore issues with the name of the method32# ignore issues with the name of the method
33# pylint: disable=C010333# pylint: disable=C0103
3434
35no_op = lambda *args, **kwargs: None
36
35# import the platform dependent code.37# import the platform dependent code.
36if sys.platform == 'win32':38if sys.platform == 'win32':
37 from ubuntuone.controlpanel.utils import windows39 from ubuntuone.controlpanel.utils import windows
40 add_to_autostart = windows.add_to_autostart
38 are_updates_present = windows.are_updates_present41 are_updates_present = windows.are_updates_present
39 default_folders = windows.default_folders42 default_folders = windows.default_folders
40 perform_update = windows.perform_update43 perform_update = windows.perform_update
41 add_to_autostart = windows.add_to_autostart44 uninstall_application = windows.uninstall_application
42else:45else:
43 from ubuntuone.controlpanel.utils import linux46 from ubuntuone.controlpanel.utils import linux
44 are_updates_present = lambda *args, **kwargs: False47 add_to_autostart = no_op
48 are_updates_present = no_op
45 default_folders = linux.default_folders49 default_folders = linux.default_folders
46 perform_update = lambda *args, **kwargs: None50 perform_update = no_op
47 add_to_autostart = lambda *args, **kwargs: None51 uninstall_application = no_op
4852
49# pylint: enable=C010353# pylint: enable=C0103
5054
5155
=== modified file 'ubuntuone/controlpanel/utils/tests/test_windows.py'
--- ubuntuone/controlpanel/utils/tests/test_windows.py 2012-03-23 21:20:18 +0000
+++ ubuntuone/controlpanel/utils/tests/test_windows.py 2012-04-03 13:15:23 +0000
@@ -1,7 +1,6 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2
3#2#
4# Copyright 2011 Canonical Ltd.3# Copyright 2011-2012 Canonical Ltd.
5#4#
6# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -28,6 +27,34 @@
28# let me use protected methods27# let me use protected methods
29# pylint: disable=W021228# pylint: disable=W0212
3029
30SOME_EXE_NAME = 'foo.exe'
31
32
33class FrozenTestCase(TestCase):
34 """A base test case for handling frozen/not frozen systems."""
35
36 frozen = True
37 executable = 'path/to/current/exe/ubuntuone/dist/executable.exe'
38
39 @defer.inlineCallbacks
40 def setUp(self):
41 """Set the different tests."""
42 yield super(FrozenTestCase, self).setUp()
43
44 for attr_name in ('frozen', 'executable'):
45 value_not_set = object()
46 value = getattr(utils.windows.sys, attr_name, value_not_set)
47
48 if self.frozen is not None:
49 setattr(utils.windows.sys, attr_name, getattr(self, attr_name))
50 elif self.frozen is None and value is not value_not_set:
51 delattr(utils.windows.sys, attr_name)
52
53 if self.frozen is not None and value is value_not_set:
54 self.addCleanup(delattr, utils.windows.sys, attr_name)
55 elif value is not value_not_set:
56 self.addCleanup(setattr, utils.windows.sys, attr_name, value)
57
3158
32class AutoupdaterTestCase(TestCase):59class AutoupdaterTestCase(TestCase):
33 """Test the code that is used for the auto update process."""60 """Test the code that is used for the auto update process."""
@@ -36,7 +63,9 @@
36 def setUp(self):63 def setUp(self):
37 """Prepare for the diff tests."""64 """Prepare for the diff tests."""
38 yield super(AutoupdaterTestCase, self).setUp()65 yield super(AutoupdaterTestCase, self).setUp()
39 self.auto_update_path = r'path\to\exe'66 self._base_path = r'path\to\exe'
67 self.auto_update_path = os.path.join(self._base_path,
68 utils.windows.AUTOUPDATE_EXE_NAME)
40 self.return_from_call = 069 self.return_from_call = 0
41 self.command = None70 self.command = None
42 self.args = []71 self.args = []
@@ -48,8 +77,8 @@
48 return self.return_from_call77 return self.return_from_call
4978
50 self.patch(utils.windows, 'getProcessValue', fake_execute_process)79 self.patch(utils.windows, 'getProcessValue', fake_execute_process)
51 self.patch(utils.windows, '_get_update_path',80 self.patch(utils.windows, 'get_exe_path',
52 lambda: self.auto_update_path)81 lambda exe_name: os.path.join(self._base_path, exe_name))
5382
54 @defer.inlineCallbacks83 @defer.inlineCallbacks
55 def test_are_updates_present_true(self):84 def test_are_updates_present_true(self):
@@ -81,11 +110,9 @@
81 """Test the method that performs the update."""110 """Test the method that performs the update."""
82 self.patch(utils.windows.win32api, 'ShellExecute', self._set_called)111 self.patch(utils.windows.win32api, 'ShellExecute', self._set_called)
83 utils.perform_update()112 utils.perform_update()
84 self.assertIn(self.auto_update_path, self._called[0][2])113 args = (None, 'runas', self.auto_update_path,
85 self.assertEqual('runas', self._called[0][1])114 '--unattendedmodeui none', '', 0)
86 self.assertEqual('--unattendedmodeui none', self._called[0][3])115 self.assertEqual(self._called, (args, {}))
87 self.assertEqual('', self._called[0][4])
88 self.assertEqual(0, self._called[0][5])
89116
90117
91class FakeOpenKey(object):118class FakeOpenKey(object):
@@ -164,7 +191,8 @@
164 [((self.registry.OpenKey, 'Ubuntu One', 0, 1,191 [((self.registry.OpenKey, 'Ubuntu One', 0, 1,
165 '"%s\\ubuntuone-syncdaemon.exe"' % path), {}),192 '"%s\\ubuntuone-syncdaemon.exe"' % path), {}),
166 ((self.registry.OpenKey, 'Ubuntu One Icon', 0, 1,193 ((self.registry.OpenKey, 'Ubuntu One Icon', 0, 1,
167 '"%s\\ubuntuone-control-panel-qt.exe" --minimized' % path),194 '"%s\\ubuntuone-control-panel-qt.exe" '
195 '--minimized --with-icon' % path),
168 {})])196 {})])
169197
170 def test_not_added_if_not_frozen(self):198 def test_not_added_if_not_frozen(self):
@@ -192,7 +220,7 @@
192 for name in names:220 for name in names:
193 name = getattr(utils.windows.shellcon, 'CSIDL_%s' % name)221 name = getattr(utils.windows.shellcon, 'CSIDL_%s' % name)
194 folder = utils.windows.shell.SHGetFolderPath(0, name, None, 0)222 folder = utils.windows.shell.SHGetFolderPath(0, name, None, 0)
195 expected.append(folder.encode('utf8'))223 expected.append(folder)
196224
197 self.assertEqual(sorted(folders), sorted(expected))225 self.assertEqual(sorted(folders), sorted(expected))
198226
@@ -216,15 +244,14 @@
216 self.test_special_folders(names=('PERSONAL', 'MYMUSIC', 'MYPICTURES'))244 self.test_special_folders(names=('PERSONAL', 'MYMUSIC', 'MYPICTURES'))
217245
218246
219class GetPathTestCase(TestCase):247class GetExePathTestCase(FrozenTestCase):
220 """Test the code that is used for the auto update process."""248 """Test the path calculator when sys is frozen."""
221249
222 @defer.inlineCallbacks250 @defer.inlineCallbacks
223 def setUp(self):251 def setUp(self):
224 """Set the different tests."""252 """Set the different tests."""
225 yield super(GetPathTestCase, self).setUp()253 yield super(GetExePathTestCase, self).setUp()
226 self.called = []254 self.called = []
227 self.exec_path = 'path/to/current/exe'
228 self.exists = True255 self.exists = True
229256
230 def fake_abspath(path):257 def fake_abspath(path):
@@ -247,62 +274,70 @@
247 self.patch(utils.windows.os.path, 'dirname', fake_dirname)274 self.patch(utils.windows.os.path, 'dirname', fake_dirname)
248 self.patch(utils.windows.os.path, 'exists', fake_exists)275 self.patch(utils.windows.os.path, 'exists', fake_exists)
249276
250 def _delete_frozen_state(self):277 def test_get_exe_path(self):
251 """Delete the frozen state."""278 """Test the method used to get the autoupdate."""
252 del utils.windows.sys.frozen279 path = utils.windows.get_exe_path(exe_name=SOME_EXE_NAME)
253 del utils.windows.sys.executable280
254281 self.assertEqual(os.path.join(self.executable, SOME_EXE_NAME), path)
255 def test_get_auto_update_path_frozen(self):282
256 """Test the method used to get the autoupdate."""283 expected = ['os.path.abspath', 'os.path.dirname', 'os.path.dirname',
257 # patch the diff parts of sys so that we get fake paths284 'os.path.exists']
258 is_frozen = hasattr(utils.windows.sys, 'frozen')285 self.assertEqual(expected, self.called)
259 if not is_frozen:286
260 utils.windows.sys.frozen = True287 def test_get_exe_path_not_present(self):
261 utils.windows.sys.executable = self.exec_path
262 self.addCleanup(self._delete_frozen_state)
263
264 # called method and assert that we have the correct result
265 path = utils.windows._get_update_path()
266 self.assertEqual(os.path.join(self.exec_path,
267 utils.windows.AUTOUPDATE_EXE_NAME), path)
268 self.assertTrue('os.path.abspath' in self.called)
269 self.assertTrue('os.path.dirname' in self.called)
270 self.assertTrue('os.path.exists' in self.called)
271
272 def _reset_frozen_state(self, old_frozen, old_exec_path):
273 """Reset the frozen state."""
274 utils.windows.sys.frozen = old_frozen
275 utils.windows.sys.executable = old_exec_path
276
277 def _reset__file__(self, path):
278 """Reset the value of __file__."""
279 utils.windows.__file__ = path
280
281 def test_get_auto_update_path_not_frozen(self):
282 """Test the method used to get the autoupdate."""
283 is_frozen = hasattr(utils.windows.sys, 'frozen')
284 if is_frozen:
285 old_frozen = utils.windows.sys.frozen
286 old_exec_path = utils.windows.sys.executable
287 del utils.windows.sys.frozen
288 del utils.windows.sys.executable
289 self.addCleanup(self._reset_frozen_state, old_frozen,
290 old_exec_path)
291 # set a fake __file__ for the module
292 old_file = utils.windows.__file__
293 utils.windows.__file__ = self.exec_path
294 self.addCleanup(self._reset__file__, old_file)
295
296 path = utils.windows._get_update_path()
297 self.assertEqual(os.path.join(self.exec_path,
298 utils.windows.AUTOUPDATE_EXE_NAME), path)
299 self.assertEqual(2, self.called.count('os.path.dirname'))
300 self.assertTrue('os.path.exists' in self.called)
301
302 def test_get_auto_update_path_not_present(self):
303 """Test the method used to get the autoupdate."""288 """Test the method used to get the autoupdate."""
304 self.exists = False289 self.exists = False
305290
306 # called method and assert that we have the correct result291 # called method and assert that we have the correct result
307 path = utils.windows._get_update_path()292 path = utils.windows.get_exe_path(exe_name=SOME_EXE_NAME)
308 self.assertEqual(None, path)293 self.assertTrue(path is None)
294
295
296class GetExePathNotFrozenTestCase(GetExePathTestCase):
297 """Test the path calculator when sys is not frozen."""
298
299 frozen = None
300
301 def test_get_exe_path(self):
302 """Test the method used to get the autoupdate."""
303 self.patch(utils.windows, '__file__', self.executable)
304
305 path = utils.windows.get_exe_path(exe_name=SOME_EXE_NAME)
306 self.assertEqual(os.path.join(self.executable, SOME_EXE_NAME), path)
307
308 expected = ['os.path.dirname', 'os.path.dirname', 'os.path.dirname',
309 'os.path.exists']
310 self.assertEqual(expected, self.called)
311
312
313class UninstallApplicationTestCase(FrozenTestCase):
314 """Test the uninstall_application helper when sys is frozen."""
315
316 @defer.inlineCallbacks
317 def setUp(self):
318 yield super(UninstallApplicationTestCase, self).setUp()
319 self.patch(utils.windows.win32api, "ShellExecute", self._set_called)
320 self.patch(os.path, "exists", lambda path: True)
321
322 def test_uninstall(self):
323 """The uninstaller is run."""
324 utils.uninstall_application()
325
326 exe_name = utils.windows.UNINSTALL_EXE_NAME
327 uninstall_path = utils.windows.get_exe_path(exe_name=exe_name)
328 self.assertEqual(self._called,
329 ((None, '', uninstall_path, '--mode win32', '', 0), {}))
330
331 def test_uninstall_exe_not_present(self):
332 """The uninstaller is not run if not available."""
333 self.patch(os.path, "exists", lambda path: False)
334
335 utils.uninstall_application()
336
337 self.assertFalse(self._called)
338
339
340class UninstallApplicationNotFrozenTestCase(UninstallApplicationTestCase):
341 """Test the uninstall_application helper when sys is not frozen."""
342
343 frozen = None
309344
=== modified file 'ubuntuone/controlpanel/utils/windows.py'
--- ubuntuone/controlpanel/utils/windows.py 2012-03-23 21:48:53 +0000
+++ ubuntuone/controlpanel/utils/windows.py 2012-04-03 13:15:23 +0000
@@ -1,7 +1,6 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2
3#2#
4# Copyright 2011 Canonical Ltd.3# Copyright 2011-2012 Canonical Ltd.
5#4#
6# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -35,19 +34,41 @@
35logger = setup_logging('utils.windows')34logger = setup_logging('utils.windows')
36AUTOUPDATE_EXE_NAME = 'autoupdate-windows.exe'35AUTOUPDATE_EXE_NAME = 'autoupdate-windows.exe'
37AUTORUN_KEY = r"Software\Microsoft\Windows\CurrentVersion\Run"36AUTORUN_KEY = r"Software\Microsoft\Windows\CurrentVersion\Run"
3837UNINSTALL_EXE_NAME = 'uninstall.exe'
3938
40def _get_update_path():39
40def get_exe_path(exe_name):
41 """Return the path in which the autoupdate command is found."""41 """Return the path in which the autoupdate command is found."""
42 if hasattr(sys, 'frozen'):42 if getattr(sys, 'frozen', False):
43 exec_path = os.path.abspath(sys.executable)43 exec_path = os.path.abspath(sys.executable)
44 else:44 else:
45 exec_path = os.path.dirname(__file__)45 exec_path = os.path.dirname(__file__)
46 folder = os.path.dirname(exec_path)46
47 update_path = os.path.join(folder, AUTOUPDATE_EXE_NAME)47 result = None
48 if os.path.exists(update_path):48 folder = os.path.dirname(os.path.dirname(exec_path))
49 return update_path49 exe_path = os.path.join(folder, exe_name)
50 return None50 if os.path.exists(exe_path):
51 result = exe_path
52
53 return result
54
55
56def add_to_autostart():
57 """Add syncdaemon to the session's autostart."""
58 if getattr(sys, "frozen", False):
59 sd_path = '"%s"' % os.path.join(os.path.dirname(
60 os.path.abspath(sys.executable)),
61 "ubuntuone-syncdaemon.exe")
62 u1cp_path = '"%s"' % os.path.join(os.path.dirname(
63 os.path.abspath(sys.executable)),
64 "ubuntuone-control-panel-qt.exe")
65
66 with _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, AUTORUN_KEY,
67 0, _winreg.KEY_ALL_ACCESS) as key:
68 # pylint: disable=E0602
69 _winreg.SetValueEx(key, "Ubuntu One", 0, _winreg.REG_SZ, sd_path)
70 _winreg.SetValueEx(key, "Ubuntu One Icon", 0, _winreg.REG_SZ,
71 u1cp_path + " --minimized --with-icon")
5172
5273
53@defer.inlineCallbacks74@defer.inlineCallbacks
@@ -55,7 +76,7 @@
55 """Return if there are updates for Ubuntu One."""76 """Return if there are updates for Ubuntu One."""
56 result = False77 result = False
57 retcode = None78 retcode = None
58 update_path = _get_update_path()79 update_path = get_exe_path(exe_name=AUTOUPDATE_EXE_NAME)
59 if update_path is not None:80 if update_path is not None:
60 # If there is an update present we will get 0, non-zero otherwise81 # If there is an update present we will get 0, non-zero otherwise
61 retcode = yield getProcessValue(update_path, args=('--mode',82 retcode = yield getProcessValue(update_path, args=('--mode',
@@ -74,7 +95,7 @@
74 # but the latter does not support XP95 # but the latter does not support XP
75 # (Minimum supported client: Windows Vista)96 # (Minimum supported client: Windows Vista)
76 get_path = lambda name: shell.SHGetFolderPath(97 get_path = lambda name: shell.SHGetFolderPath(
77 0, getattr(shellcon, name), None, 0).encode('utf8')98 0, getattr(shellcon, name), None, 0)
7899
79 folders = []100 folders = []
80 # More information on these constants at101 # More information on these constants at
@@ -92,27 +113,15 @@
92113
93def perform_update():114def perform_update():
94 """Spawn the autoupdate process and call the stop function."""115 """Spawn the autoupdate process and call the stop function."""
95 update_path = _get_update_path()116 update_path = get_exe_path(exe_name=AUTOUPDATE_EXE_NAME)
96 if update_path is not None:117 if update_path is not None:
97 # lets call the updater with the commands that are required,118 # lets call the updater with the commands that are required,
98 win32api.ShellExecute(None, 'runas',119 win32api.ShellExecute(None, 'runas', update_path,
99 update_path,120 '--unattendedmodeui none', '', 0)
100 '--unattendedmodeui none', '', 0)121
101122
102123def uninstall_application():
103def add_to_autostart():124 """Uninstall Ubuntu One."""
104 """Add syncdaemon to the session's autostart."""125 uninstall_path = get_exe_path(exe_name=UNINSTALL_EXE_NAME)
105 if getattr(sys, "frozen", False):126 if uninstall_path is not None:
106 sd_path = '"%s"' % os.path.join(os.path.dirname(127 win32api.ShellExecute(None, '', uninstall_path, '--mode win32', '', 0)
107 os.path.abspath(sys.executable)),
108 "ubuntuone-syncdaemon.exe")
109 u1cp_path = '"%s"' % os.path.join(os.path.dirname(
110 os.path.abspath(sys.executable)),
111 "ubuntuone-control-panel-qt.exe")
112
113 with _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, AUTORUN_KEY,
114 0, _winreg.KEY_ALL_ACCESS) as key:
115 # pylint: disable=E0602
116 _winreg.SetValueEx(key, "Ubuntu One", 0, _winreg.REG_SZ, sd_path)
117 _winreg.SetValueEx(key, "Ubuntu One Icon", 0, _winreg.REG_SZ,
118 u1cp_path + " --minimized --with-icon")

Subscribers

People subscribed via source and target branches