Merge lp:~nataliabidart/ubuntuone-control-panel/stable-3-0-update-2.99.92 into lp:ubuntuone-control-panel/stable-3-0
- stable-3-0-update-2.99.92
- Merge into stable-3-0
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 | ||||||||||||||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alejandro J. Cura (community) | Approve | ||
Review via email:
|
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).
Description of the change
- 258. By Natalia Bidart
-
Attaching bug numbers.
Preview Diff
1 | === modified file 'ubuntuone/controlpanel/backend.py' |
2 | --- ubuntuone/controlpanel/backend.py 2012-03-01 22:05:51 +0000 |
3 | +++ ubuntuone/controlpanel/backend.py 2012-04-03 13:15:23 +0000 |
4 | @@ -1,9 +1,6 @@ |
5 | # -*- coding: utf-8 -*- |
6 | - |
7 | -# Authors: Alejandro J. Cura <alecu@canonical.com> |
8 | -# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
9 | # |
10 | -# Copyright 2010 Canonical Ltd. |
11 | +# Copyright 2010-2012 Canonical Ltd. |
12 | # |
13 | # This program is free software: you can redistribute it and/or modify it |
14 | # under the terms of the GNU General Public License version 3, as published |
15 | @@ -26,11 +23,7 @@ |
16 | from functools import wraps |
17 | |
18 | from twisted.internet.defer import inlineCallbacks, returnValue |
19 | -# No name 'is_link' in module 'ubuntuone.platform' |
20 | -# pylint: disable=E0611, F0401 |
21 | -from ubuntuone.platform import is_link |
22 | from ubuntuone.platform.credentials import CredentialsManagementTool |
23 | -# pylint: enable=E0611, F0401 |
24 | |
25 | from ubuntuone.controlpanel import sd_client, replication_client |
26 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
27 | @@ -599,7 +592,7 @@ |
28 | |
29 | @log_call(logger.debug) |
30 | @inlineCallbacks |
31 | - def volumes_info(self, with_storage_info=True): |
32 | + def volumes_info(self, with_storage_info=True, refresh=False): |
33 | """Get the volumes info.""" |
34 | self._volumes = {} |
35 | |
36 | @@ -612,6 +605,9 @@ |
37 | else: |
38 | free_bytes = account['quota_total'] - account['quota_used'] |
39 | |
40 | + if refresh: |
41 | + yield self.sd_client.refresh_volumes() |
42 | + |
43 | root_dir = yield self.sd_client.get_root_dir() |
44 | shares_dir = yield self.sd_client.get_shares_dir() |
45 | shares_dir_link = yield self.sd_client.get_shares_dir_link() |
46 | @@ -717,28 +713,9 @@ |
47 | yield self.sd_client.create_folder(path=folder_path) |
48 | |
49 | @log_call(logger.debug) |
50 | - @inlineCallbacks |
51 | def validate_path_for_folder(self, folder_path): |
52 | """Validate 'folder_path' for folder creation.""" |
53 | - user_home = yield self.get_home_dir() |
54 | - folder_path = append_path_sep(folder_path) |
55 | - |
56 | - # handle folder_path not within '~' or links |
57 | - # XXX is_link expects bytes, see bug #824252 |
58 | - if not folder_path.startswith(user_home) or is_link( |
59 | - folder_path.encode('utf-8')): |
60 | - returnValue(False) |
61 | - |
62 | - # handle folder_path nested with a existing cloud folder |
63 | - volumes = yield self.volumes_info(with_storage_info=False) |
64 | - for _, _, data in volumes: |
65 | - for volume in data: |
66 | - cloud_folder = append_path_sep(volume['path']) |
67 | - if (folder_path.startswith(cloud_folder) or |
68 | - cloud_folder.startswith(folder_path)): |
69 | - returnValue(False) |
70 | - |
71 | - returnValue(True) |
72 | + return self.sd_client.validate_path(folder_path) |
73 | |
74 | @log_call(logger.debug) |
75 | @inlineCallbacks |
76 | @@ -787,8 +764,13 @@ |
77 | result[name] = bool(value) |
78 | |
79 | limits = yield self.sd_client.get_throttling_limits() |
80 | - result[DOWNLOAD_KEY] = limits['download'] |
81 | - result[UPLOAD_KEY] = limits['upload'] |
82 | + limits_enabled = yield self.sd_client.bandwidth_throttling_enabled() |
83 | + if limits_enabled: |
84 | + result[DOWNLOAD_KEY] = limits['download'] |
85 | + result[UPLOAD_KEY] = limits['upload'] |
86 | + else: |
87 | + result[DOWNLOAD_KEY] = -1 |
88 | + result[UPLOAD_KEY] = -1 |
89 | |
90 | returnValue(result) |
91 | |
92 | |
93 | === removed file 'ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py' |
94 | --- ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py 2011-10-24 21:48:27 +0000 |
95 | +++ ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py 1970-01-01 00:00:00 +0000 |
96 | @@ -1,94 +0,0 @@ |
97 | -# -*- coding: utf-8 -*- |
98 | - |
99 | -# Authors: Alejandro J. Cura <alecu@canonical.com> |
100 | -# Authors: Natalia B. Bidart <natalia.bidart@canonical.com> |
101 | -# |
102 | -# Copyright 2010 Canonical Ltd. |
103 | -# |
104 | -# This program is free software: you can redistribute it and/or modify it |
105 | -# under the terms of the GNU General Public License version 3, as published |
106 | -# by the Free Software Foundation. |
107 | -# |
108 | -# This program is distributed in the hope that it will be useful, but |
109 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
110 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
111 | -# PURPOSE. See the GNU General Public License for more details. |
112 | -# |
113 | -# You should have received a copy of the GNU General Public License along |
114 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
115 | - |
116 | -"""Tests for the DBus service when accessing SyncDaemon.""" |
117 | - |
118 | -import dbus |
119 | - |
120 | -from twisted.internet import defer |
121 | - |
122 | -from ubuntuone.platform.linux import dbus_interface as sd_dbus_iface |
123 | - |
124 | -from ubuntuone.controlpanel import sd_client |
125 | -from ubuntuone.controlpanel.dbustests import DBusClientTestCase |
126 | - |
127 | - |
128 | -SD_DBUS_IFACE_NAME = sd_dbus_iface.DBUS_IFACE_NAME |
129 | -SD_DBUS_IFACE_STATUS_NAME = sd_dbus_iface.DBUS_IFACE_STATUS_NAME |
130 | - |
131 | -# pylint, you have to go to decorator's school |
132 | -# pylint: disable=C0322 |
133 | - |
134 | -# Access to a protected member of a client class |
135 | -# pylint: disable=W0212 |
136 | - |
137 | - |
138 | -class StatusMockDBusSyncDaemon(dbus.service.Object): |
139 | - """A mock object that mimicks syncdaemon regarding the Status iface.""" |
140 | - |
141 | - state_dict = { |
142 | - 'name': 'TEST', |
143 | - 'description': 'Some test state, nothing else.', |
144 | - 'is_error': '', |
145 | - 'is_connected': 'True', |
146 | - 'is_online': '', |
147 | - 'queues': 'GORGEOUS', |
148 | - 'connection': '', |
149 | - } |
150 | - |
151 | - def _get_current_state(self): |
152 | - """Get the current status of the system.""" |
153 | - return self.state_dict |
154 | - |
155 | - @dbus.service.method(SD_DBUS_IFACE_STATUS_NAME, |
156 | - in_signature='', out_signature='a{ss}') |
157 | - def current_status(self): |
158 | - """Return the current faked status of the system.""" |
159 | - return self._get_current_state() |
160 | - |
161 | - # pylint: disable=C0103 |
162 | - # Invalid name "StatusChanged" |
163 | - |
164 | - @dbus.service.signal(SD_DBUS_IFACE_STATUS_NAME) |
165 | - def StatusChanged(self, status): |
166 | - """Fire a signal to notify that the status of the system changed.""" |
167 | - |
168 | - def emit_status_changed(self, state=None): |
169 | - """Emit StatusChanged.""" |
170 | - self.StatusChanged(self._get_current_state()) |
171 | - |
172 | - |
173 | -class StatusTestCase(DBusClientTestCase): |
174 | - """Test for the status dbus client methods.""" |
175 | - |
176 | - @defer.inlineCallbacks |
177 | - def setUp(self): |
178 | - yield super(StatusTestCase, self).setUp() |
179 | - self.register_mockserver(SD_DBUS_IFACE_NAME, |
180 | - "/status", StatusMockDBusSyncDaemon) |
181 | - |
182 | - def test_set_status_changed_handler(self): |
183 | - """A proper callback can be connected to StatusChanged signal.""" |
184 | - client = sd_client.SyncDaemonClient() |
185 | - _, sig = client.set_status_changed_handler(self._set_called) |
186 | - |
187 | - self.assertEqual(sig._handler, self._set_called) |
188 | - self.assertEqual(sig._member, 'StatusChanged') |
189 | - self.assertEqual(sig._path, '/status') |
190 | - self.assertEqual(sig._interface, SD_DBUS_IFACE_STATUS_NAME) |
191 | |
192 | === modified file 'ubuntuone/controlpanel/gui/__init__.py' |
193 | --- ubuntuone/controlpanel/gui/__init__.py 2012-03-20 15:11:56 +0000 |
194 | +++ ubuntuone/controlpanel/gui/__init__.py 2012-04-03 13:15:23 +0000 |
195 | @@ -204,7 +204,7 @@ |
196 | MAIN_PREFERENCES_TAB = _('Settings') |
197 | MAIN_WINDOW_TITLE = _('%(app_name)s Control Panel') |
198 | MUSIC_DISPLAY_NAME = _('Purchased Music') |
199 | -MUSIC_REAL_PATH = '.ubuntuone/Purchased from Ubuntu One' |
200 | +MUSIC_REAL_PATH = u'.ubuntuone/Purchased from Ubuntu One' |
201 | MY_FOLDERS = _('My folders') |
202 | NAME_NOT_SET = _('[unknown user name]') |
203 | NETWORK_OFFLINE = _('An internet connection is required to join or sign ' |
204 | |
205 | === modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py' |
206 | --- ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-03-21 19:17:21 +0000 |
207 | +++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-04-03 13:15:23 +0000 |
208 | @@ -1,5 +1,5 @@ |
209 | # -*- coding: utf-8 -*- |
210 | - |
211 | +# |
212 | # Copyright 2012 Canonical Ltd. |
213 | # |
214 | # This program is free software: you can redistribute it and/or modify it |
215 | @@ -174,7 +174,6 @@ |
216 | @log_call(logger.info) |
217 | def start_from_license(self): |
218 | """Use the license page as first page.""" |
219 | - # license |
220 | - self.ui.wizard.setStartId(self.ui.wizard.pages[ |
221 | - self.ui.wizard.license_page]) |
222 | + license_id = self.ui.wizard.pages[self.ui.wizard.license_page] |
223 | + self.ui.wizard.setStartId(license_id) |
224 | self.ui.wizard.restart() |
225 | |
226 | === modified file 'ubuntuone/controlpanel/gui/qt/folders.py' |
227 | --- ubuntuone/controlpanel/gui/qt/folders.py 2012-03-20 13:05:20 +0000 |
228 | +++ ubuntuone/controlpanel/gui/qt/folders.py 2012-04-03 13:15:23 +0000 |
229 | @@ -123,7 +123,7 @@ |
230 | self.ui.folders.headerItem().setText(FOLDER_NAME_COL, |
231 | FOLDERS_COLUMN_NAME) |
232 | self.ui.folders.headerItem().setText(SUBSCRIPTION_COL, |
233 | - FOLDERS_COLUMN_SYNC_LOCALLY) |
234 | + ALWAYS_SUBSCRIBED) |
235 | self.ui.folders.headerItem().setText(EXPLORE_COL, |
236 | FOLDERS_COLUMN_EXPLORE) |
237 | headers = self.ui.folders.header() |
238 | @@ -153,7 +153,8 @@ |
239 | def load(self): |
240 | """Load specific tab info.""" |
241 | self.is_processing = True |
242 | - info = yield self.backend.volumes_info(with_storage_info=False) |
243 | + info = yield self.backend.volumes_info(with_storage_info=False, |
244 | + refresh=self.remote_folders) |
245 | self.process_info(info) |
246 | |
247 | @handle_errors(logger=logger) |
248 | |
249 | === modified file 'ubuntuone/controlpanel/gui/qt/gui.py' |
250 | --- ubuntuone/controlpanel/gui/qt/gui.py 2012-03-26 13:23:57 +0000 |
251 | +++ ubuntuone/controlpanel/gui/qt/gui.py 2012-04-03 13:15:23 +0000 |
252 | @@ -43,8 +43,7 @@ |
253 | self.ui.setupUi(self) |
254 | self.close_callback = close_callback |
255 | self._setup() |
256 | - self.quit_action = QtGui.QAction(self, |
257 | - triggered=self.close) |
258 | + self.quit_action = QtGui.QAction(self, triggered=self.close) |
259 | self.quit_action.setShortcuts(["Ctrl+q", "Ctrl+w"]) |
260 | self.addAction(self.quit_action) |
261 | self.installer = installer |
262 | |
263 | === modified file 'ubuntuone/controlpanel/gui/qt/main/__init__.py' |
264 | --- ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-03-27 13:41:19 +0000 |
265 | +++ ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-04-03 13:15:23 +0000 |
266 | @@ -98,7 +98,8 @@ |
267 | icon, window = start(lambda: source.main_quit(app), |
268 | minimized=minimized, with_icon=with_icon, |
269 | installer=installer) |
270 | - window.switch_to(switch_to) |
271 | + if window: |
272 | + window.switch_to(switch_to) |
273 | # pylint: enable=W0612 |
274 | if icon: |
275 | app.new_instance.connect(icon.restore_window) |
276 | |
277 | === modified file 'ubuntuone/controlpanel/gui/qt/main/tests/test_main.py' |
278 | --- ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-03-27 13:49:49 +0000 |
279 | +++ ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-04-03 13:15:23 +0000 |
280 | @@ -96,8 +96,11 @@ |
281 | self.window = None |
282 | |
283 | def __call__(self, *args, **kwargs): |
284 | + if kwargs.get('minimized', False): |
285 | + self.window = None |
286 | + else: |
287 | + self.window = FakeMainWindow() |
288 | self.args = (args, kwargs) |
289 | - self.window = FakeMainWindow() |
290 | return None, self.window |
291 | |
292 | |
293 | @@ -166,6 +169,12 @@ |
294 | self.assertEqual(self.start.args[1], |
295 | {'minimized': False, 'with_icon': False, 'installer': True}) |
296 | |
297 | + def test_minimized_with_icon_options(self): |
298 | + """Ensure you can be minimized and with icon at the same time.""" |
299 | + main.main([sys.argv[0], "--minimized", "--with-icon"]) |
300 | + self.assertEqual(self.start.args[1], |
301 | + {'minimized': True, 'with_icon': True, 'installer': False}) |
302 | + |
303 | def test_translator(self): |
304 | """Ensure the Qt translator is loaded.""" |
305 | main.main([sys.argv[0]]) |
306 | |
307 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py' |
308 | --- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-03-23 14:04:57 +0000 |
309 | +++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-04-03 13:15:23 +0000 |
310 | @@ -187,7 +187,8 @@ |
311 | |
312 | def test_info_is_requested_on_load(self): |
313 | """The volumes info is requested to the backend.""" |
314 | - self.assert_backend_called('volumes_info', with_storage_info=False) |
315 | + self.assert_backend_called('volumes_info', with_storage_info=False, |
316 | + refresh=self.ui.remote_folders) |
317 | |
318 | def test_process_info(self): |
319 | """The volumes info is processed when ready.""" |
320 | @@ -409,6 +410,17 @@ |
321 | item) |
322 | it += 1 |
323 | |
324 | + def test_tree_view_header(self): |
325 | + """The tree view header is hidden but the text is correct.""" |
326 | + self.assertFalse(self.ui.ui.folders.header().isVisible()) |
327 | + |
328 | + name = self.ui.ui.folders.headerItem().text(gui.FOLDER_NAME_COL) |
329 | + self.assertEqual(name, gui.FOLDERS_COLUMN_NAME) |
330 | + name = self.ui.ui.folders.headerItem().text(gui.SUBSCRIPTION_COL) |
331 | + self.assertEqual(name, gui.ALWAYS_SUBSCRIBED) |
332 | + name = self.ui.ui.folders.headerItem().text(gui.EXPLORE_COL) |
333 | + self.assertEqual(name, gui.FOLDERS_COLUMN_EXPLORE) |
334 | + |
335 | def test_share_publish_button(self): |
336 | """When clicking the share/publish button, the proper url is opened.""" |
337 | self.assertTrue(self.ui.ui.share_publish_button.isVisible()) |
338 | |
339 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_preferences.py' |
340 | --- ubuntuone/controlpanel/gui/qt/tests/test_preferences.py 2012-03-01 22:05:51 +0000 |
341 | +++ ubuntuone/controlpanel/gui/qt/tests/test_preferences.py 2012-04-03 13:15:23 +0000 |
342 | @@ -76,6 +76,21 @@ |
343 | |
344 | self.assertFalse(self.ui.is_processing) |
345 | |
346 | + def test_process_info_limit_bandwidth(self): |
347 | + """The ui is not processing when contents are load.""" |
348 | + self.ui.process_info(SAMPLE_SETTINGS) |
349 | + |
350 | + self.assertTrue(self.ui.ui.limit_uploads_checkbox.isChecked()) |
351 | + self.assertTrue(self.ui.ui.limit_downloads_checkbox.isChecked()) |
352 | + |
353 | + settings = SAMPLE_SETTINGS.copy() |
354 | + settings[gui.backend.DOWNLOAD_KEY] = -1 |
355 | + settings[gui.backend.UPLOAD_KEY] = -1 |
356 | + self.ui.process_info(settings) |
357 | + |
358 | + self.assertFalse(self.ui.ui.limit_uploads_checkbox.isChecked()) |
359 | + self.assertFalse(self.ui.ui.limit_downloads_checkbox.isChecked()) |
360 | + |
361 | def test_info_is_requested_on_load(self): |
362 | """The info is requested to the backend.""" |
363 | self.assert_backend_called('file_sync_settings_info') |
364 | |
365 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_wizard.py' |
366 | --- ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-26 21:00:47 +0000 |
367 | +++ ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-04-03 13:15:23 +0000 |
368 | @@ -157,6 +157,7 @@ |
369 | @defer.inlineCallbacks |
370 | def setUp(self): |
371 | yield super(UbuntuOneWizardTestCase, self).setUp() |
372 | + self.patch(gui.utils, 'uninstall_application', self.fail) |
373 | self.patch(self.ui.confirm_dialog, 'exec_', |
374 | lambda: self.confirm_response) |
375 | |
376 | @@ -260,7 +261,7 @@ |
377 | button.click() |
378 | |
379 | self.assertEqual(self._called, (signal_args, {}), |
380 | - msg % (name, signal, signal_args)) |
381 | + msg % (name, signal, signal_args)) |
382 | self._called = False |
383 | |
384 | def test_done_rejected(self): |
385 | @@ -336,6 +337,16 @@ |
386 | page_name = 'license' |
387 | stage_name = 'install' |
388 | |
389 | + @defer.inlineCallbacks |
390 | + def setUp(self): |
391 | + yield super(UbuntuOneWizardLicensePage, self).setUp() |
392 | + self.patch(gui.utils, 'uninstall_application', self._set_called) |
393 | + |
394 | + def test_done_rejected(self): |
395 | + """When the wizard is closed on the final page, emit rejected.""" |
396 | + super(UbuntuOneWizardLicensePage, self).test_done_rejected() |
397 | + self.assertEqual(self._called, ((), {})) |
398 | + |
399 | |
400 | class UbuntuOneWizardLoginTestCase(UbuntuOneWizardTestCase): |
401 | """Test the login through the wizard.""" |
402 | |
403 | === modified file 'ubuntuone/controlpanel/gui/qt/wizard.py' |
404 | --- ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-26 20:15:58 +0000 |
405 | +++ ubuntuone/controlpanel/gui/qt/wizard.py 2012-04-03 13:15:23 +0000 |
406 | @@ -23,7 +23,7 @@ |
407 | from ubuntu_sso.qt.sso_wizard_page import BaseWizardPage |
408 | from ubuntu_sso.utils.ui import CLOSE_AND_SETUP_LATER |
409 | |
410 | -from ubuntuone.controlpanel import cache |
411 | +from ubuntuone.controlpanel import cache, utils |
412 | from ubuntuone.controlpanel.logger import log_call, setup_logging |
413 | from ubuntuone.controlpanel.gui import ( |
414 | APP_NAME, |
415 | @@ -342,13 +342,9 @@ |
416 | response = self.confirm_dialog.exec_() |
417 | if response == QtGui.QDialog.Accepted: |
418 | logger.warning('UbuntuOneWizard: user canceled setup.') |
419 | - self.rejected.emit() |
420 | - elif (self.currentId() == self.pages[self.license_page]): |
421 | - response = self.confirm_dialog.exec_() |
422 | - if response == QtGui.QDialog.Accepted: |
423 | - logger.warning('UbuntuOneWizard: user wants to uninstall.') |
424 | - # TODO: needs implementation in this project |
425 | - ##qt.utils.uninstall_application() |
426 | + if self.currentId() == self.pages[self.license_page]: |
427 | + logger.warning('UbuntuOneWizard: user wants to uninstall.') |
428 | + utils.uninstall_application() |
429 | self.rejected.emit() |
430 | else: |
431 | super(UbuntuOneWizard, self).done(result) |
432 | |
433 | === modified file 'ubuntuone/controlpanel/sd_client/__init__.py' |
434 | --- ubuntuone/controlpanel/sd_client/__init__.py 2012-01-18 14:06:35 +0000 |
435 | +++ ubuntuone/controlpanel/sd_client/__init__.py 2012-04-03 13:15:23 +0000 |
436 | @@ -1,8 +1,6 @@ |
437 | # -*- coding: utf-8 -*- |
438 | - |
439 | -# Authors: Natalia B Bidart <natalia.bidart@canonical.com> |
440 | # |
441 | -# Copyright 2011 Canonical Ltd. |
442 | +# Copyright 2012 Canonical Ltd. |
443 | # |
444 | # This program is free software: you can redistribute it and/or modify it |
445 | # under the terms of the GNU General Public License version 3, as published |
446 | @@ -18,7 +16,6 @@ |
447 | |
448 | """The syncdaemon client.""" |
449 | |
450 | -import sys |
451 | import warnings |
452 | |
453 | # pylint: disable=E0611 |
454 | @@ -35,7 +32,6 @@ |
455 | |
456 | def __init__(self): |
457 | """Get a proxy for the SyncDaemonTool.""" |
458 | - self.status_changed_handler = None |
459 | self.proxy = tools.SyncDaemonTool() |
460 | |
461 | def get_throttling_limits(self): |
462 | @@ -128,6 +124,10 @@ |
463 | """Retrieve the folders information from syncdaemon.""" |
464 | return self.proxy.get_folders() |
465 | |
466 | + def validate_path(self, path): |
467 | + """Validates 'path' to create a new folder through syncdaemon.""" |
468 | + return self.proxy.validate_path(path) |
469 | + |
470 | def create_folder(self, path): |
471 | """Create a new folder through syncdaemon.""" |
472 | return self.proxy.create_folder(path) |
473 | @@ -200,11 +200,8 @@ |
474 | |
475 | def set_status_changed_handler(self, handler): |
476 | """Set the status handler function.""" |
477 | - self.status_changed_handler = handler |
478 | - if sys.platform.startswith("linux"): |
479 | - # pylint: disable=W0404 |
480 | - from ubuntuone.controlpanel.sd_client import linux |
481 | - result = linux.set_status_changed_handler(handler) |
482 | - else: |
483 | - result = self.proxy.set_status_changed_handler(handler) |
484 | - return result |
485 | + return self.proxy.connect_signal('StatusChanged', handler) |
486 | + |
487 | + def refresh_volumes(self): |
488 | + """Refresh the volumes information from syncdaemon.""" |
489 | + return self.proxy.refresh_volumes() |
490 | |
491 | === removed file 'ubuntuone/controlpanel/sd_client/linux.py' |
492 | --- ubuntuone/controlpanel/sd_client/linux.py 2011-06-29 17:59:07 +0000 |
493 | +++ ubuntuone/controlpanel/sd_client/linux.py 1970-01-01 00:00:00 +0000 |
494 | @@ -1,46 +0,0 @@ |
495 | -# -*- coding: utf-8 -*- |
496 | - |
497 | -# Authors: Alejandro J. Cura <alecu@canonical.com> |
498 | -# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
499 | -# |
500 | -# Copyright 2010 Canonical Ltd. |
501 | -# |
502 | -# This program is free software: you can redistribute it and/or modify it |
503 | -# under the terms of the GNU General Public License version 3, as published |
504 | -# by the Free Software Foundation. |
505 | -# |
506 | -# This program is distributed in the hope that it will be useful, but |
507 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
508 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
509 | -# PURPOSE. See the GNU General Public License for more details. |
510 | -# |
511 | -# You should have received a copy of the GNU General Public License along |
512 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
513 | - |
514 | -"""Client to use other DBus services.""" |
515 | - |
516 | -import dbus.service |
517 | - |
518 | -from ubuntuone.controlpanel.logger import setup_logging |
519 | - |
520 | - |
521 | -logger = setup_logging('sd_client') |
522 | - |
523 | - |
524 | -def get_syncdaemon_proxy(object_path, dbus_interface): |
525 | - """Get a DBus proxy for syncdaemon at 'object_path':'dbus_interface'.""" |
526 | - logger.debug('get_syncdaemon_proxy: object_path %r, dbus_interface %r', |
527 | - object_path, dbus_interface) |
528 | - bus = dbus.SessionBus() |
529 | - obj = bus.get_object(bus_name='com.ubuntuone.SyncDaemon', |
530 | - object_path=object_path, |
531 | - follow_name_owner_changes=True) |
532 | - proxy = dbus.Interface(object=obj, dbus_interface=dbus_interface) |
533 | - return proxy |
534 | - |
535 | - |
536 | -def set_status_changed_handler(handler): |
537 | - """Connect 'handler' with syncdaemon's StatusChanged signal.""" |
538 | - proxy = get_syncdaemon_proxy('/status', 'com.ubuntuone.SyncDaemon.Status') |
539 | - sig = proxy.connect_to_signal('StatusChanged', handler) |
540 | - return proxy, sig |
541 | |
542 | === modified file 'ubuntuone/controlpanel/tests/test_backend.py' |
543 | --- ubuntuone/controlpanel/tests/test_backend.py 2012-02-06 15:23:27 +0000 |
544 | +++ ubuntuone/controlpanel/tests/test_backend.py 2012-04-03 13:15:23 +0000 |
545 | @@ -1,9 +1,6 @@ |
546 | # -*- coding: utf-8 -*- |
547 | - |
548 | -# Authors: Alejandro J. Cura <alecu@canonical.com> |
549 | -# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
550 | # |
551 | -# Copyright 2010 Canonical Ltd. |
552 | +# Copyright 2010-2012 Canonical Ltd. |
553 | # |
554 | # This program is free software: you can redistribute it and/or modify it |
555 | # under the terms of the GNU General Public License version 3, as published |
556 | @@ -169,6 +166,7 @@ |
557 | self.actions = [] |
558 | self.shares = [] |
559 | self.folders = [] |
560 | + self.volumes_refreshed = False |
561 | |
562 | def get_throttling_limits(self): |
563 | """Return the sample speed limits.""" |
564 | @@ -291,6 +289,10 @@ |
565 | """Unsubcribe from 'volume_id'.""" |
566 | self.subscribed_folders.remove(volume_id) |
567 | |
568 | + def validate_path(self, path): |
569 | + """Validate a path for folder creation.""" |
570 | + return path != USER_HOME |
571 | + |
572 | def create_folder(self, path): |
573 | """Grab list of folders.""" |
574 | self.folders.append(path) |
575 | @@ -319,6 +321,11 @@ |
576 | """Grab list of shared (shares from the user to others).""" |
577 | return SAMPLE_SHARED |
578 | |
579 | + def refresh_volumes(self): |
580 | + """Refresh the volume list.""" |
581 | + self.volumes_refreshed = True |
582 | + return defer.succeed(None) |
583 | + |
584 | |
585 | class MockReplicationClient(CallRecorder): |
586 | """A mock replication_client module.""" |
587 | @@ -860,10 +867,19 @@ |
588 | u'display_name': display_name, |
589 | } |
590 | |
591 | + self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES) |
592 | + self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS) |
593 | + |
594 | @inlineCallbacks |
595 | - def expected_volumes(self, sample_shares, sample_folders, |
596 | + def expected_volumes(self, sample_shares=None, sample_folders=None, |
597 | with_storage_info=True): |
598 | """Get shares and group by sharing user, get folders and free space.""" |
599 | + if sample_shares is None: |
600 | + sample_shares = self.be.sd_client.shares |
601 | + |
602 | + if sample_folders is None: |
603 | + sample_folders = self.be.sd_client.folders |
604 | + |
605 | free_bytes = self.be.FREE_BYTES_NOT_AVAILABLE |
606 | if with_storage_info: |
607 | try: |
608 | @@ -926,11 +942,19 @@ |
609 | @inlineCallbacks |
610 | def test_volumes_info(self): |
611 | """The volumes_info method exercises its callback.""" |
612 | - self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES) |
613 | - self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS) |
614 | - |
615 | - expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS) |
616 | + expected = yield self.expected_volumes() |
617 | result = yield self.be.volumes_info() |
618 | + |
619 | + self.assertEqual(result, expected) |
620 | + self.assertFalse(self.be.sd_client.volumes_refreshed) |
621 | + |
622 | + @inlineCallbacks |
623 | + def test_volumes_info_can_refresh_volumes(self): |
624 | + """The volumes_info can be refreshed.""" |
625 | + expected = yield self.expected_volumes() |
626 | + result = yield self.be.volumes_info(refresh=True) |
627 | + |
628 | + self.assertTrue(self.be.sd_client.volumes_refreshed) |
629 | self.assertEqual(result, expected) |
630 | |
631 | @inlineCallbacks |
632 | @@ -943,7 +967,6 @@ |
633 | path = path[len(USER_HOME) + 1:] |
634 | item[u'path'] = os.path.join(root_path, path) |
635 | self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES) |
636 | - self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS) |
637 | |
638 | yield self.be.volumes_info() |
639 | for item in SAMPLE_FOLDERS: |
640 | @@ -956,11 +979,7 @@ |
641 | @inlineCallbacks |
642 | def test_volumes_info_without_storage_info(self): |
643 | """The volumes_info method exercises its callback.""" |
644 | - self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES) |
645 | - self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS) |
646 | - |
647 | - expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS, |
648 | - with_storage_info=False) |
649 | + expected = yield self.expected_volumes(with_storage_info=False) |
650 | result = yield self.be.volumes_info(with_storage_info=False) |
651 | self.assertEqual(result, expected) |
652 | |
653 | @@ -971,11 +990,8 @@ |
654 | @inlineCallbacks |
655 | def test_volumes_are_cached(self): |
656 | """The volume list is cached.""" |
657 | - self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES) |
658 | - self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS) |
659 | - |
660 | expected = {} |
661 | - info = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS) |
662 | + info = yield self.expected_volumes() |
663 | for _, _, data in info: |
664 | for volume in data: |
665 | sid = volume['volume_id'] |
666 | @@ -1024,10 +1040,8 @@ |
667 | ] |
668 | |
669 | self.patch(self.be.sd_client, 'shares', read_only_shares) |
670 | - self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS) |
671 | |
672 | - expected = yield self.expected_volumes(read_only_shares, |
673 | - SAMPLE_FOLDERS) |
674 | + expected = yield self.expected_volumes() |
675 | result = yield self.be.volumes_info() |
676 | self.assertEqual(result, expected) |
677 | |
678 | @@ -1035,10 +1049,8 @@ |
679 | def test_volumes_info_no_quota_for_root(self): |
680 | """The volumes_info returns info even if quota call fails.""" |
681 | self.be.wc.failure = 500 |
682 | - self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES) |
683 | - self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS) |
684 | |
685 | - expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS) |
686 | + expected = yield self.expected_volumes() |
687 | result = yield self.be.volumes_info() |
688 | |
689 | self.assertEqual(len(result), len(expected)) |
690 | @@ -1126,29 +1138,15 @@ |
691 | @inlineCallbacks |
692 | def test_create_folder(self): |
693 | """New folders can be created.""" |
694 | + self.patch(self.be.sd_client, 'folders', []) |
695 | + |
696 | folder_path = os.path.join(USER_HOME, 'Test Me') |
697 | yield self.be.create_folder(folder_path=folder_path) |
698 | |
699 | self.assertEqual(self.be.sd_client.folders, [folder_path]) |
700 | |
701 | - |
702 | -class BackendValidatePathForFolderTestCase(BackendBasicTestCase): |
703 | - """Test for the path validator when creating folders.""" |
704 | - |
705 | - msg = '%r must not be a valid path for creating a folder.' |
706 | - |
707 | - @defer.inlineCallbacks |
708 | - def setUp(self): |
709 | - yield super(BackendValidatePathForFolderTestCase, self).setUp() |
710 | - old_home = os.environ['HOME'] |
711 | - os.environ['HOME'] = USER_HOME |
712 | - self.addCleanup(os.environ.__setitem__, 'HOME', old_home) |
713 | - self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES) |
714 | - self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS) |
715 | - self.patch(backend, 'is_link', lambda p: False) |
716 | - |
717 | - @defer.inlineCallbacks |
718 | - def test_valid_if_folder_inside_home(self): |
719 | + @defer.inlineCallbacks |
720 | + def test_validate_path_for_folder(self): |
721 | """Test proper validation of path for creating folders.""" |
722 | folder_path = os.path.join(USER_HOME, 'Test Me') |
723 | |
724 | @@ -1157,68 +1155,11 @@ |
725 | '%r must be a valid path for creating a folder.' % folder_path) |
726 | |
727 | @defer.inlineCallbacks |
728 | - def test_valid_if_folder_shares_a_prefix_with_an_udf(self): |
729 | - """Test proper validation of path for creating folders. |
730 | - |
731 | - If the user chooses a folder with the same prefix as an UDF, but |
732 | - outside every UDF, the path is valid. |
733 | - |
734 | - """ |
735 | - tricky_path = ROOT_PATH |
736 | - assert not tricky_path.endswith(os.path.sep) |
737 | - tricky_path += ' Suffix' |
738 | - assert tricky_path.startswith(ROOT_PATH) |
739 | - |
740 | - result = yield self.be.validate_path_for_folder(tricky_path) |
741 | - self.assertTrue(result, |
742 | - '%r must be a valid path for creating a folder.' % tricky_path) |
743 | - |
744 | - @inlineCallbacks |
745 | - def test_path_not_valid_if_outside_home(self): |
746 | - """A folder outside ~ is not valid.""" |
747 | - outside_home = os.path.abspath(os.path.join(USER_HOME, os.path.pardir)) |
748 | - |
749 | - result = yield self.be.validate_path_for_folder(outside_home) |
750 | - self.assertFalse(result, self.msg % outside_home) |
751 | - |
752 | - @defer.inlineCallbacks |
753 | - def test_not_valid_if_folder_inside_root(self): |
754 | - """A folder inside the root is not valid.""" |
755 | - root_path = ROOT_PATH |
756 | - # create a valid path inside the root |
757 | - inside_root = os.path.abspath(os.path.join(root_path, 'test')) |
758 | - |
759 | - result = yield self.be.validate_path_for_folder(inside_root) |
760 | - self.assertFalse(result, self.msg % inside_root) |
761 | - |
762 | - @defer.inlineCallbacks |
763 | - def test_not_valid_if_folder_inside_an_udf(self): |
764 | - """A folder inside an UDF is not valid.""" |
765 | - udf_path = SAMPLE_FOLDERS[-1]['path'] |
766 | - # create a valid path inside an existing UDF |
767 | - inside_udf = os.path.abspath(os.path.join(udf_path, 'test')) |
768 | - |
769 | - result = yield self.be.validate_path_for_folder(inside_udf) |
770 | - self.assertFalse(result, self.msg % inside_udf) |
771 | - |
772 | - @defer.inlineCallbacks |
773 | - def test_not_valid_if_folder_is_parent_of_an_udf(self): |
774 | - """A folder parent of an UDF is not valid.""" |
775 | - udf_path = SAMPLE_FOLDERS[-1]['path'] |
776 | - # create a valid path that is parent from an existing UDF |
777 | - udf_parent = os.path.abspath(os.path.join(udf_path, os.path.pardir)) |
778 | - |
779 | - result = yield self.be.validate_path_for_folder(udf_parent) |
780 | - self.assertFalse(result, self.msg % udf_parent) |
781 | - |
782 | - @defer.inlineCallbacks |
783 | - def test_not_valid_if_folder_is_link(self): |
784 | - """A link path is not valid.""" |
785 | - self.patch(backend, 'is_link', lambda p: True) |
786 | - path_link = os.path.join(USER_HOME, 'Test Me') |
787 | - |
788 | - result = yield self.be.validate_path_for_folder(path_link) |
789 | - self.assertFalse(result, self.msg % path_link) |
790 | + def test_validate_path_for_folder_invalid(self): |
791 | + """Test proper validation of path for creating folders.""" |
792 | + result = yield self.be.validate_path_for_folder(USER_HOME) |
793 | + self.assertFalse(result, |
794 | + '%r must not be a valid path for creating a folder.' % USER_HOME) |
795 | |
796 | |
797 | class BackendSyncStatusTestCase(BackendBasicTestCase): |
798 | @@ -1598,6 +1539,7 @@ |
799 | @inlineCallbacks |
800 | def test_file_sync_settings_info(self): |
801 | """The settings_info method exercises its callback.""" |
802 | + self.patch(self.be.sd_client, "throttling", True) |
803 | self.be.sd_client.limits = {"download": 1000, "upload": 100} |
804 | expected = { |
805 | backend.AUTOCONNECT_KEY: self.be.sd_client.autoconnect, |
806 | @@ -1614,6 +1556,25 @@ |
807 | self.assertEqual(expected, result) |
808 | |
809 | @inlineCallbacks |
810 | + def test_file_sync_settings_info_with_limit(self): |
811 | + """The settings_info method exercises its callback.""" |
812 | + self.patch(self.be.sd_client, "throttling", False) |
813 | + self.be.sd_client.limits = {"download": 987456, "upload": 125698} |
814 | + expected = { |
815 | + backend.AUTOCONNECT_KEY: self.be.sd_client.autoconnect, |
816 | + backend.SHOW_ALL_NOTIFICATIONS_KEY: |
817 | + self.be.sd_client.show_all_notifications, |
818 | + backend.SHARE_AUTOSUBSCRIBE_KEY: |
819 | + self.be.sd_client.share_autosubscribe, |
820 | + backend.UDF_AUTOSUBSCRIBE_KEY: |
821 | + self.be.sd_client.udf_autosubscribe, |
822 | + backend.DOWNLOAD_KEY: -1, |
823 | + backend.UPLOAD_KEY: -1, |
824 | + } |
825 | + result = yield self.be.file_sync_settings_info() |
826 | + self.assertEqual(expected, result) |
827 | + |
828 | + @inlineCallbacks |
829 | def test_change_file_sync_setting_autoconnect(self): |
830 | """The settings can be changed for autoconnect.""" |
831 | yield self.assert_boolean_setting_is_correct(backend.AUTOCONNECT_KEY) |
832 | |
833 | === modified file 'ubuntuone/controlpanel/tests/test_sd_client.py' |
834 | --- ubuntuone/controlpanel/tests/test_sd_client.py 2012-03-19 17:16:43 +0000 |
835 | +++ ubuntuone/controlpanel/tests/test_sd_client.py 2012-04-03 13:15:23 +0000 |
836 | @@ -1,9 +1,6 @@ |
837 | # -*- coding: utf-8 -*- |
838 | - |
839 | -# Authors: Alejandro J. Cura <alecu@canonical.com> |
840 | -# Authors: Natalia B. Bidart <natalia.bidart@canonical.com> |
841 | # |
842 | -# Copyright 2010 Canonical Ltd. |
843 | +# Copyright 2010-2012 Canonical Ltd. |
844 | # |
845 | # This program is free software: you can redistribute it and/or modify it |
846 | # under the terms of the GNU General Public License version 3, as published |
847 | @@ -24,11 +21,7 @@ |
848 | |
849 | from twisted.internet import defer |
850 | from twisted.internet.defer import inlineCallbacks, returnValue |
851 | -from ubuntuone.devtools.testcases import skipIfNotOS |
852 | -# No name 'tools' in module 'ubuntuone.platform' |
853 | -# pylint: disable=E0611 |
854 | from ubuntuone.platform import tools |
855 | -# pylint: enable=E0611 |
856 | from ubuntuone.syncdaemon.interaction_interfaces import bool_str |
857 | |
858 | from ubuntuone.controlpanel import sd_client |
859 | @@ -164,6 +157,10 @@ |
860 | """Get the list of the shares "shared"/created/offered.""" |
861 | self.called['list_shared'] = None |
862 | |
863 | + def validate_path(self, path): |
864 | + """Validate a path for folder creation.""" |
865 | + return path not in [f['path'] for f in self.folders.itervalues()] |
866 | + |
867 | def create_folder(self, path): |
868 | """Create a user defined folder in the specified path.""" |
869 | if path == '': # simulate an error |
870 | @@ -183,14 +180,12 @@ |
871 | @inlineCallbacks |
872 | def subscribe_folder(self, folder_id): |
873 | """Subscribe to a user defined folder given its id.""" |
874 | - yield self._set_folder_attr(folder_id, |
875 | - u'subscribed', True) |
876 | + yield self._set_folder_attr(folder_id, u'subscribed', True) |
877 | |
878 | @inlineCallbacks |
879 | def unsubscribe_folder(self, folder_id): |
880 | """Unsubscribe from a user defined folder given its id.""" |
881 | - yield self._set_folder_attr(folder_id, |
882 | - u'subscribed', False) |
883 | + yield self._set_folder_attr(folder_id, u'subscribed', False) |
884 | |
885 | @inlineCallbacks |
886 | def get_folders(self): |
887 | @@ -315,9 +310,9 @@ |
888 | """Return the shares link directory.""" |
889 | return self.shares_dir_link |
890 | |
891 | - def set_status_changed_handler(self, handler): |
892 | + def connect_signal(self, signal_name, handler): |
893 | """Set the handler for the status changed signal.""" |
894 | - self.called['status_changed_handler'] = handler |
895 | + self.called[signal_name] = handler |
896 | |
897 | |
898 | class BaseTestCase(TestCase): |
899 | @@ -519,6 +514,19 @@ |
900 | yield self.assertFailure(self.sd.get_folders(), CustomError) |
901 | |
902 | @inlineCallbacks |
903 | + def test_validate_path(self): |
904 | + """Check if a folder path is valid.""" |
905 | + path = '~/bar/baz' |
906 | + result = yield self.sd.validate_path(path) |
907 | + |
908 | + self.assertTrue(result) |
909 | + |
910 | + yield self.sd.create_folder(path) |
911 | + result = yield self.sd.validate_path(path) |
912 | + |
913 | + self.assertFalse(result) |
914 | + |
915 | + @inlineCallbacks |
916 | def test_create_folder(self): |
917 | """Create a new folder.""" |
918 | path = '~/bar/baz' |
919 | @@ -694,14 +702,12 @@ |
920 | |
921 | self.assertEqual(self.sd.proxy.called['quit'], None) |
922 | |
923 | - @skipIfNotOS('win32', 'The tested function is only defined on windows') |
924 | @inlineCallbacks |
925 | def test_set_status_changed_handler(self): |
926 | """Connect a handler to the status changed signal.""" |
927 | sample_handler = object() |
928 | yield self.sd.set_status_changed_handler(sample_handler) |
929 | - self.assertEqual(sample_handler, |
930 | - self.sd.proxy.called['status_changed_handler']) |
931 | + self.assertEqual(sample_handler, self.sd.proxy.called['StatusChanged']) |
932 | |
933 | |
934 | class BasicTestCase(BaseTestCase): |
935 | |
936 | === modified file 'ubuntuone/controlpanel/utils/__init__.py' |
937 | --- ubuntuone/controlpanel/utils/__init__.py 2012-03-22 23:28:19 +0000 |
938 | +++ ubuntuone/controlpanel/utils/__init__.py 2012-04-03 13:15:23 +0000 |
939 | @@ -32,19 +32,23 @@ |
940 | # ignore issues with the name of the method |
941 | # pylint: disable=C0103 |
942 | |
943 | +no_op = lambda *args, **kwargs: None |
944 | + |
945 | # import the platform dependent code. |
946 | if sys.platform == 'win32': |
947 | from ubuntuone.controlpanel.utils import windows |
948 | + add_to_autostart = windows.add_to_autostart |
949 | are_updates_present = windows.are_updates_present |
950 | default_folders = windows.default_folders |
951 | perform_update = windows.perform_update |
952 | - add_to_autostart = windows.add_to_autostart |
953 | + uninstall_application = windows.uninstall_application |
954 | else: |
955 | from ubuntuone.controlpanel.utils import linux |
956 | - are_updates_present = lambda *args, **kwargs: False |
957 | + add_to_autostart = no_op |
958 | + are_updates_present = no_op |
959 | default_folders = linux.default_folders |
960 | - perform_update = lambda *args, **kwargs: None |
961 | - add_to_autostart = lambda *args, **kwargs: None |
962 | + perform_update = no_op |
963 | + uninstall_application = no_op |
964 | |
965 | # pylint: enable=C0103 |
966 | |
967 | |
968 | === modified file 'ubuntuone/controlpanel/utils/tests/test_windows.py' |
969 | --- ubuntuone/controlpanel/utils/tests/test_windows.py 2012-03-23 21:20:18 +0000 |
970 | +++ ubuntuone/controlpanel/utils/tests/test_windows.py 2012-04-03 13:15:23 +0000 |
971 | @@ -1,7 +1,6 @@ |
972 | # -*- coding: utf-8 -*- |
973 | - |
974 | # |
975 | -# Copyright 2011 Canonical Ltd. |
976 | +# Copyright 2011-2012 Canonical Ltd. |
977 | # |
978 | # This program is free software: you can redistribute it and/or modify it |
979 | # under the terms of the GNU General Public License version 3, as published |
980 | @@ -28,6 +27,34 @@ |
981 | # let me use protected methods |
982 | # pylint: disable=W0212 |
983 | |
984 | +SOME_EXE_NAME = 'foo.exe' |
985 | + |
986 | + |
987 | +class FrozenTestCase(TestCase): |
988 | + """A base test case for handling frozen/not frozen systems.""" |
989 | + |
990 | + frozen = True |
991 | + executable = 'path/to/current/exe/ubuntuone/dist/executable.exe' |
992 | + |
993 | + @defer.inlineCallbacks |
994 | + def setUp(self): |
995 | + """Set the different tests.""" |
996 | + yield super(FrozenTestCase, self).setUp() |
997 | + |
998 | + for attr_name in ('frozen', 'executable'): |
999 | + value_not_set = object() |
1000 | + value = getattr(utils.windows.sys, attr_name, value_not_set) |
1001 | + |
1002 | + if self.frozen is not None: |
1003 | + setattr(utils.windows.sys, attr_name, getattr(self, attr_name)) |
1004 | + elif self.frozen is None and value is not value_not_set: |
1005 | + delattr(utils.windows.sys, attr_name) |
1006 | + |
1007 | + if self.frozen is not None and value is value_not_set: |
1008 | + self.addCleanup(delattr, utils.windows.sys, attr_name) |
1009 | + elif value is not value_not_set: |
1010 | + self.addCleanup(setattr, utils.windows.sys, attr_name, value) |
1011 | + |
1012 | |
1013 | class AutoupdaterTestCase(TestCase): |
1014 | """Test the code that is used for the auto update process.""" |
1015 | @@ -36,7 +63,9 @@ |
1016 | def setUp(self): |
1017 | """Prepare for the diff tests.""" |
1018 | yield super(AutoupdaterTestCase, self).setUp() |
1019 | - self.auto_update_path = r'path\to\exe' |
1020 | + self._base_path = r'path\to\exe' |
1021 | + self.auto_update_path = os.path.join(self._base_path, |
1022 | + utils.windows.AUTOUPDATE_EXE_NAME) |
1023 | self.return_from_call = 0 |
1024 | self.command = None |
1025 | self.args = [] |
1026 | @@ -48,8 +77,8 @@ |
1027 | return self.return_from_call |
1028 | |
1029 | self.patch(utils.windows, 'getProcessValue', fake_execute_process) |
1030 | - self.patch(utils.windows, '_get_update_path', |
1031 | - lambda: self.auto_update_path) |
1032 | + self.patch(utils.windows, 'get_exe_path', |
1033 | + lambda exe_name: os.path.join(self._base_path, exe_name)) |
1034 | |
1035 | @defer.inlineCallbacks |
1036 | def test_are_updates_present_true(self): |
1037 | @@ -81,11 +110,9 @@ |
1038 | """Test the method that performs the update.""" |
1039 | self.patch(utils.windows.win32api, 'ShellExecute', self._set_called) |
1040 | utils.perform_update() |
1041 | - self.assertIn(self.auto_update_path, self._called[0][2]) |
1042 | - self.assertEqual('runas', self._called[0][1]) |
1043 | - self.assertEqual('--unattendedmodeui none', self._called[0][3]) |
1044 | - self.assertEqual('', self._called[0][4]) |
1045 | - self.assertEqual(0, self._called[0][5]) |
1046 | + args = (None, 'runas', self.auto_update_path, |
1047 | + '--unattendedmodeui none', '', 0) |
1048 | + self.assertEqual(self._called, (args, {})) |
1049 | |
1050 | |
1051 | class FakeOpenKey(object): |
1052 | @@ -164,7 +191,8 @@ |
1053 | [((self.registry.OpenKey, 'Ubuntu One', 0, 1, |
1054 | '"%s\\ubuntuone-syncdaemon.exe"' % path), {}), |
1055 | ((self.registry.OpenKey, 'Ubuntu One Icon', 0, 1, |
1056 | - '"%s\\ubuntuone-control-panel-qt.exe" --minimized' % path), |
1057 | + '"%s\\ubuntuone-control-panel-qt.exe" ' |
1058 | + '--minimized --with-icon' % path), |
1059 | {})]) |
1060 | |
1061 | def test_not_added_if_not_frozen(self): |
1062 | @@ -192,7 +220,7 @@ |
1063 | for name in names: |
1064 | name = getattr(utils.windows.shellcon, 'CSIDL_%s' % name) |
1065 | folder = utils.windows.shell.SHGetFolderPath(0, name, None, 0) |
1066 | - expected.append(folder.encode('utf8')) |
1067 | + expected.append(folder) |
1068 | |
1069 | self.assertEqual(sorted(folders), sorted(expected)) |
1070 | |
1071 | @@ -216,15 +244,14 @@ |
1072 | self.test_special_folders(names=('PERSONAL', 'MYMUSIC', 'MYPICTURES')) |
1073 | |
1074 | |
1075 | -class GetPathTestCase(TestCase): |
1076 | - """Test the code that is used for the auto update process.""" |
1077 | +class GetExePathTestCase(FrozenTestCase): |
1078 | + """Test the path calculator when sys is frozen.""" |
1079 | |
1080 | @defer.inlineCallbacks |
1081 | def setUp(self): |
1082 | """Set the different tests.""" |
1083 | - yield super(GetPathTestCase, self).setUp() |
1084 | + yield super(GetExePathTestCase, self).setUp() |
1085 | self.called = [] |
1086 | - self.exec_path = 'path/to/current/exe' |
1087 | self.exists = True |
1088 | |
1089 | def fake_abspath(path): |
1090 | @@ -247,62 +274,70 @@ |
1091 | self.patch(utils.windows.os.path, 'dirname', fake_dirname) |
1092 | self.patch(utils.windows.os.path, 'exists', fake_exists) |
1093 | |
1094 | - def _delete_frozen_state(self): |
1095 | - """Delete the frozen state.""" |
1096 | - del utils.windows.sys.frozen |
1097 | - del utils.windows.sys.executable |
1098 | - |
1099 | - def test_get_auto_update_path_frozen(self): |
1100 | - """Test the method used to get the autoupdate.""" |
1101 | - # patch the diff parts of sys so that we get fake paths |
1102 | - is_frozen = hasattr(utils.windows.sys, 'frozen') |
1103 | - if not is_frozen: |
1104 | - utils.windows.sys.frozen = True |
1105 | - utils.windows.sys.executable = self.exec_path |
1106 | - self.addCleanup(self._delete_frozen_state) |
1107 | - |
1108 | - # called method and assert that we have the correct result |
1109 | - path = utils.windows._get_update_path() |
1110 | - self.assertEqual(os.path.join(self.exec_path, |
1111 | - utils.windows.AUTOUPDATE_EXE_NAME), path) |
1112 | - self.assertTrue('os.path.abspath' in self.called) |
1113 | - self.assertTrue('os.path.dirname' in self.called) |
1114 | - self.assertTrue('os.path.exists' in self.called) |
1115 | - |
1116 | - def _reset_frozen_state(self, old_frozen, old_exec_path): |
1117 | - """Reset the frozen state.""" |
1118 | - utils.windows.sys.frozen = old_frozen |
1119 | - utils.windows.sys.executable = old_exec_path |
1120 | - |
1121 | - def _reset__file__(self, path): |
1122 | - """Reset the value of __file__.""" |
1123 | - utils.windows.__file__ = path |
1124 | - |
1125 | - def test_get_auto_update_path_not_frozen(self): |
1126 | - """Test the method used to get the autoupdate.""" |
1127 | - is_frozen = hasattr(utils.windows.sys, 'frozen') |
1128 | - if is_frozen: |
1129 | - old_frozen = utils.windows.sys.frozen |
1130 | - old_exec_path = utils.windows.sys.executable |
1131 | - del utils.windows.sys.frozen |
1132 | - del utils.windows.sys.executable |
1133 | - self.addCleanup(self._reset_frozen_state, old_frozen, |
1134 | - old_exec_path) |
1135 | - # set a fake __file__ for the module |
1136 | - old_file = utils.windows.__file__ |
1137 | - utils.windows.__file__ = self.exec_path |
1138 | - self.addCleanup(self._reset__file__, old_file) |
1139 | - |
1140 | - path = utils.windows._get_update_path() |
1141 | - self.assertEqual(os.path.join(self.exec_path, |
1142 | - utils.windows.AUTOUPDATE_EXE_NAME), path) |
1143 | - self.assertEqual(2, self.called.count('os.path.dirname')) |
1144 | - self.assertTrue('os.path.exists' in self.called) |
1145 | - |
1146 | - def test_get_auto_update_path_not_present(self): |
1147 | + def test_get_exe_path(self): |
1148 | + """Test the method used to get the autoupdate.""" |
1149 | + path = utils.windows.get_exe_path(exe_name=SOME_EXE_NAME) |
1150 | + |
1151 | + self.assertEqual(os.path.join(self.executable, SOME_EXE_NAME), path) |
1152 | + |
1153 | + expected = ['os.path.abspath', 'os.path.dirname', 'os.path.dirname', |
1154 | + 'os.path.exists'] |
1155 | + self.assertEqual(expected, self.called) |
1156 | + |
1157 | + def test_get_exe_path_not_present(self): |
1158 | """Test the method used to get the autoupdate.""" |
1159 | self.exists = False |
1160 | |
1161 | # called method and assert that we have the correct result |
1162 | - path = utils.windows._get_update_path() |
1163 | - self.assertEqual(None, path) |
1164 | + path = utils.windows.get_exe_path(exe_name=SOME_EXE_NAME) |
1165 | + self.assertTrue(path is None) |
1166 | + |
1167 | + |
1168 | +class GetExePathNotFrozenTestCase(GetExePathTestCase): |
1169 | + """Test the path calculator when sys is not frozen.""" |
1170 | + |
1171 | + frozen = None |
1172 | + |
1173 | + def test_get_exe_path(self): |
1174 | + """Test the method used to get the autoupdate.""" |
1175 | + self.patch(utils.windows, '__file__', self.executable) |
1176 | + |
1177 | + path = utils.windows.get_exe_path(exe_name=SOME_EXE_NAME) |
1178 | + self.assertEqual(os.path.join(self.executable, SOME_EXE_NAME), path) |
1179 | + |
1180 | + expected = ['os.path.dirname', 'os.path.dirname', 'os.path.dirname', |
1181 | + 'os.path.exists'] |
1182 | + self.assertEqual(expected, self.called) |
1183 | + |
1184 | + |
1185 | +class UninstallApplicationTestCase(FrozenTestCase): |
1186 | + """Test the uninstall_application helper when sys is frozen.""" |
1187 | + |
1188 | + @defer.inlineCallbacks |
1189 | + def setUp(self): |
1190 | + yield super(UninstallApplicationTestCase, self).setUp() |
1191 | + self.patch(utils.windows.win32api, "ShellExecute", self._set_called) |
1192 | + self.patch(os.path, "exists", lambda path: True) |
1193 | + |
1194 | + def test_uninstall(self): |
1195 | + """The uninstaller is run.""" |
1196 | + utils.uninstall_application() |
1197 | + |
1198 | + exe_name = utils.windows.UNINSTALL_EXE_NAME |
1199 | + uninstall_path = utils.windows.get_exe_path(exe_name=exe_name) |
1200 | + self.assertEqual(self._called, |
1201 | + ((None, '', uninstall_path, '--mode win32', '', 0), {})) |
1202 | + |
1203 | + def test_uninstall_exe_not_present(self): |
1204 | + """The uninstaller is not run if not available.""" |
1205 | + self.patch(os.path, "exists", lambda path: False) |
1206 | + |
1207 | + utils.uninstall_application() |
1208 | + |
1209 | + self.assertFalse(self._called) |
1210 | + |
1211 | + |
1212 | +class UninstallApplicationNotFrozenTestCase(UninstallApplicationTestCase): |
1213 | + """Test the uninstall_application helper when sys is not frozen.""" |
1214 | + |
1215 | + frozen = None |
1216 | |
1217 | === modified file 'ubuntuone/controlpanel/utils/windows.py' |
1218 | --- ubuntuone/controlpanel/utils/windows.py 2012-03-23 21:48:53 +0000 |
1219 | +++ ubuntuone/controlpanel/utils/windows.py 2012-04-03 13:15:23 +0000 |
1220 | @@ -1,7 +1,6 @@ |
1221 | # -*- coding: utf-8 -*- |
1222 | - |
1223 | # |
1224 | -# Copyright 2011 Canonical Ltd. |
1225 | +# Copyright 2011-2012 Canonical Ltd. |
1226 | # |
1227 | # This program is free software: you can redistribute it and/or modify it |
1228 | # under the terms of the GNU General Public License version 3, as published |
1229 | @@ -35,19 +34,41 @@ |
1230 | logger = setup_logging('utils.windows') |
1231 | AUTOUPDATE_EXE_NAME = 'autoupdate-windows.exe' |
1232 | AUTORUN_KEY = r"Software\Microsoft\Windows\CurrentVersion\Run" |
1233 | - |
1234 | - |
1235 | -def _get_update_path(): |
1236 | +UNINSTALL_EXE_NAME = 'uninstall.exe' |
1237 | + |
1238 | + |
1239 | +def get_exe_path(exe_name): |
1240 | """Return the path in which the autoupdate command is found.""" |
1241 | - if hasattr(sys, 'frozen'): |
1242 | + if getattr(sys, 'frozen', False): |
1243 | exec_path = os.path.abspath(sys.executable) |
1244 | else: |
1245 | exec_path = os.path.dirname(__file__) |
1246 | - folder = os.path.dirname(exec_path) |
1247 | - update_path = os.path.join(folder, AUTOUPDATE_EXE_NAME) |
1248 | - if os.path.exists(update_path): |
1249 | - return update_path |
1250 | - return None |
1251 | + |
1252 | + result = None |
1253 | + folder = os.path.dirname(os.path.dirname(exec_path)) |
1254 | + exe_path = os.path.join(folder, exe_name) |
1255 | + if os.path.exists(exe_path): |
1256 | + result = exe_path |
1257 | + |
1258 | + return result |
1259 | + |
1260 | + |
1261 | +def add_to_autostart(): |
1262 | + """Add syncdaemon to the session's autostart.""" |
1263 | + if getattr(sys, "frozen", False): |
1264 | + sd_path = '"%s"' % os.path.join(os.path.dirname( |
1265 | + os.path.abspath(sys.executable)), |
1266 | + "ubuntuone-syncdaemon.exe") |
1267 | + u1cp_path = '"%s"' % os.path.join(os.path.dirname( |
1268 | + os.path.abspath(sys.executable)), |
1269 | + "ubuntuone-control-panel-qt.exe") |
1270 | + |
1271 | + with _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, AUTORUN_KEY, |
1272 | + 0, _winreg.KEY_ALL_ACCESS) as key: |
1273 | + # pylint: disable=E0602 |
1274 | + _winreg.SetValueEx(key, "Ubuntu One", 0, _winreg.REG_SZ, sd_path) |
1275 | + _winreg.SetValueEx(key, "Ubuntu One Icon", 0, _winreg.REG_SZ, |
1276 | + u1cp_path + " --minimized --with-icon") |
1277 | |
1278 | |
1279 | @defer.inlineCallbacks |
1280 | @@ -55,7 +76,7 @@ |
1281 | """Return if there are updates for Ubuntu One.""" |
1282 | result = False |
1283 | retcode = None |
1284 | - update_path = _get_update_path() |
1285 | + update_path = get_exe_path(exe_name=AUTOUPDATE_EXE_NAME) |
1286 | if update_path is not None: |
1287 | # If there is an update present we will get 0, non-zero otherwise |
1288 | retcode = yield getProcessValue(update_path, args=('--mode', |
1289 | @@ -74,7 +95,7 @@ |
1290 | # but the latter does not support XP |
1291 | # (Minimum supported client: Windows Vista) |
1292 | get_path = lambda name: shell.SHGetFolderPath( |
1293 | - 0, getattr(shellcon, name), None, 0).encode('utf8') |
1294 | + 0, getattr(shellcon, name), None, 0) |
1295 | |
1296 | folders = [] |
1297 | # More information on these constants at |
1298 | @@ -92,27 +113,15 @@ |
1299 | |
1300 | def perform_update(): |
1301 | """Spawn the autoupdate process and call the stop function.""" |
1302 | - update_path = _get_update_path() |
1303 | + update_path = get_exe_path(exe_name=AUTOUPDATE_EXE_NAME) |
1304 | if update_path is not None: |
1305 | # lets call the updater with the commands that are required, |
1306 | - win32api.ShellExecute(None, 'runas', |
1307 | - update_path, |
1308 | - '--unattendedmodeui none', '', 0) |
1309 | - |
1310 | - |
1311 | -def add_to_autostart(): |
1312 | - """Add syncdaemon to the session's autostart.""" |
1313 | - if getattr(sys, "frozen", False): |
1314 | - sd_path = '"%s"' % os.path.join(os.path.dirname( |
1315 | - os.path.abspath(sys.executable)), |
1316 | - "ubuntuone-syncdaemon.exe") |
1317 | - u1cp_path = '"%s"' % os.path.join(os.path.dirname( |
1318 | - os.path.abspath(sys.executable)), |
1319 | - "ubuntuone-control-panel-qt.exe") |
1320 | - |
1321 | - with _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, AUTORUN_KEY, |
1322 | - 0, _winreg.KEY_ALL_ACCESS) as key: |
1323 | - # pylint: disable=E0602 |
1324 | - _winreg.SetValueEx(key, "Ubuntu One", 0, _winreg.REG_SZ, sd_path) |
1325 | - _winreg.SetValueEx(key, "Ubuntu One Icon", 0, _winreg.REG_SZ, |
1326 | - u1cp_path + " --minimized --with-icon") |
1327 | + win32api.ShellExecute(None, 'runas', update_path, |
1328 | + '--unattendedmodeui none', '', 0) |
1329 | + |
1330 | + |
1331 | +def uninstall_application(): |
1332 | + """Uninstall Ubuntu One.""" |
1333 | + uninstall_path = get_exe_path(exe_name=UNINSTALL_EXE_NAME) |
1334 | + if uninstall_path is not None: |
1335 | + win32api.ShellExecute(None, '', uninstall_path, '--mode win32', '', 0) |
Approving