Merge lp:~nataliabidart/ubuntuone-control-panel/computer-to-cloud-page into lp:ubuntuone-control-panel

Proposed by Natalia Bidart
Status: Merged
Approved by: Roberto Alsina
Approved revision: 296
Merged at revision: 290
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/computer-to-cloud-page
Merge into: lp:ubuntuone-control-panel
Prerequisite: lp:~nataliabidart/ubuntuone-control-panel/cloud-to-computer-page
Diff against target: 1319 lines (+848/-56)
16 files modified
data/qt/controlpanel.ui (+7/-2)
data/qt/local_folders.ui (+179/-0)
ubuntuone/controlpanel/gui/__init__.py (+10/-0)
ubuntuone/controlpanel/gui/qt/addfolder.py (+9/-5)
ubuntuone/controlpanel/gui/qt/controlpanel.py (+4/-2)
ubuntuone/controlpanel/gui/qt/folders.py (+298/-1)
ubuntuone/controlpanel/gui/qt/gotoweb.py (+17/-1)
ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py (+16/-7)
ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py (+1/-1)
ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+206/-0)
ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py (+24/-5)
ubuntuone/controlpanel/gui/qt/tests/test_wizard.py (+33/-15)
ubuntuone/controlpanel/gui/qt/wizard.py (+33/-7)
ubuntuone/controlpanel/tests/__init__.py (+9/-0)
ubuntuone/controlpanel/tests/test_login_client.py (+1/-5)
ubuntuone/controlpanel/tests/test_sd_client.py (+1/-5)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/computer-to-cloud-page
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Eric Casteleijn (community) Approve
Review via email: mp+98286@code.launchpad.net

Commit message

- Added 'Computer to cloud' page to the wizard (part of LP: #933697).
- Tweaked the title of the aforementioned page (LP: #888521).

Description of the change

To test IRL, please have nightlies installed and up to date. Then, from this branch, please run:

./setup.py clean build; U1_DEBUG=True PYTHONPATH=. bin/ubuntuone-control-panel-qt

Assuming you already have U1 credentials, go to the devices tab and remove the current device. You will be presented with the initial screen, where you can play with:

From the dependency branch, you will get:

- closing from the button in the right bottom corner, you should get a confirmation dialog
- after login/register you should be presented with a screen to choose cloud folders to sync from your cloud to your desktop
- optionally, you can play with settings clicking on the button at the end of the folder listing

From this branch, you will get an additional page at the end to choose local folders to sync to your cloud. The list will offer some 'suggested' folders, and you can also add a custom folder using the 'add folder' button. Please note that changes will get applied *only* when the finish button is clicked.

Known bugs from this branch:

Bug #959447
Bug #959690

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

Merged trunk in.

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

It all works as expected!

Code looks good, but it does use new style and old style formatting: The problem is that while new style formatting has an awesome syntax, it is reportedly much slower than the old style, *and* it may tie us to a newer Python than we want without good reason.

Old style formatting is not scheduled to ever disappear from the language so I feel we should stick with that, at least until the performance problems are solved, and everyone is on a python that supports it, but maybe just forever.

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

+1 excellent work!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'data/qt/controlpanel.ui'
--- data/qt/controlpanel.ui 2012-03-12 20:29:41 +0000
+++ data/qt/controlpanel.ui 2012-03-19 21:32:18 +0000
@@ -153,9 +153,9 @@
153 </widget>153 </widget>
154 </item>154 </item>
155 <item>155 <item>
156 <widget class="GoToWebButton" name="get_more_space_button">156 <widget class="GetStorageButton" name="get_more_space_button">
157 <property name="text">157 <property name="text">
158 <string notr="true">Get more storage</string>158 <string notr="true">foo bar baz</string>
159 </property>159 </property>
160 <property name="default">160 <property name="default">
161 <bool>true</bool>161 <bool>true</bool>
@@ -362,6 +362,11 @@
362 </widget>362 </widget>
363 <customwidgets>363 <customwidgets>
364 <customwidget>364 <customwidget>
365 <class>GetStorageButton</class>
366 <extends>QPushButton</extends>
367 <header>ubuntuone.controlpanel.gui.qt.gotoweb</header>
368 </customwidget>
369 <customwidget>
365 <class>GoToWebButton</class>370 <class>GoToWebButton</class>
366 <extends>QPushButton</extends>371 <extends>QPushButton</extends>
367 <header>ubuntuone.controlpanel.gui.qt.gotoweb</header>372 <header>ubuntuone.controlpanel.gui.qt.gotoweb</header>
368373
=== added file 'data/qt/local_folders.ui'
--- data/qt/local_folders.ui 1970-01-01 00:00:00 +0000
+++ data/qt/local_folders.ui 2012-03-19 21:32:18 +0000
@@ -0,0 +1,179 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>Form</class>
4 <widget class="QWidget" name="Form">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>274</width>
10 <height>312</height>
11 </rect>
12 </property>
13 <layout class="QVBoxLayout" name="verticalLayout_2">
14 <property name="topMargin">
15 <number>0</number>
16 </property>
17 <item>
18 <widget class="QTreeWidget" name="folders">
19 <property name="verticalScrollBarPolicy">
20 <enum>Qt::ScrollBarAlwaysOn</enum>
21 </property>
22 <property name="alternatingRowColors">
23 <bool>true</bool>
24 </property>
25 <property name="indentation">
26 <number>0</number>
27 </property>
28 <property name="rootIsDecorated">
29 <bool>false</bool>
30 </property>
31 <property name="uniformRowHeights">
32 <bool>true</bool>
33 </property>
34 <property name="allColumnsShowFocus">
35 <bool>true</bool>
36 </property>
37 <attribute name="headerStretchLastSection">
38 <bool>false</bool>
39 </attribute>
40 <column>
41 <property name="text">
42 <string notr="true">Sync these folders on my computer</string>
43 </property>
44 </column>
45 <column>
46 <property name="text">
47 <string notr="true">Space (Total)</string>
48 </property>
49 </column>
50 </widget>
51 </item>
52 <item>
53 <layout class="QHBoxLayout" name="horizontalLayout_3">
54 <item>
55 <spacer name="horizontalSpacer_4">
56 <property name="orientation">
57 <enum>Qt::Horizontal</enum>
58 </property>
59 <property name="sizeHint" stdset="0">
60 <size>
61 <width>40</width>
62 <height>20</height>
63 </size>
64 </property>
65 </spacer>
66 </item>
67 <item>
68 <widget class="AddFolderButton" name="add_folder_button">
69 <property name="text">
70 <string notr="true">Add a folder</string>
71 </property>
72 <property name="default">
73 <bool>true</bool>
74 </property>
75 <property name="DisabledState" stdset="0">
76 <bool>false</bool>
77 </property>
78 </widget>
79 </item>
80 <item>
81 <spacer name="horizontalSpacer_5">
82 <property name="orientation">
83 <enum>Qt::Horizontal</enum>
84 </property>
85 <property name="sizeHint" stdset="0">
86 <size>
87 <width>40</width>
88 <height>20</height>
89 </size>
90 </property>
91 </spacer>
92 </item>
93 </layout>
94 </item>
95 <item>
96 <widget class="QFrame" name="offer_frame">
97 <layout class="QVBoxLayout" name="verticalLayout">
98 <property name="leftMargin">
99 <number>0</number>
100 </property>
101 <property name="rightMargin">
102 <number>0</number>
103 </property>
104 <item>
105 <widget class="QLabel" name="offer_label">
106 <property name="text">
107 <string notr="true">overflow message</string>
108 </property>
109 <property name="wordWrap">
110 <bool>true</bool>
111 </property>
112 </widget>
113 </item>
114 <item>
115 <layout class="QHBoxLayout" name="horizontalLayout">
116 <item>
117 <spacer name="horizontalSpacer">
118 <property name="orientation">
119 <enum>Qt::Horizontal</enum>
120 </property>
121 <property name="sizeHint" stdset="0">
122 <size>
123 <width>40</width>
124 <height>20</height>
125 </size>
126 </property>
127 </spacer>
128 </item>
129 <item>
130 <widget class="GetStorageButton" name="add_storage_button">
131 <property name="sizePolicy">
132 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
133 <horstretch>0</horstretch>
134 <verstretch>0</verstretch>
135 </sizepolicy>
136 </property>
137 <property name="text">
138 <string notr="true">add storage</string>
139 </property>
140 <property name="default">
141 <bool>true</bool>
142 </property>
143 </widget>
144 </item>
145 <item>
146 <spacer name="horizontalSpacer_2">
147 <property name="orientation">
148 <enum>Qt::Horizontal</enum>
149 </property>
150 <property name="sizeHint" stdset="0">
151 <size>
152 <width>40</width>
153 <height>20</height>
154 </size>
155 </property>
156 </spacer>
157 </item>
158 </layout>
159 </item>
160 </layout>
161 </widget>
162 </item>
163 </layout>
164 </widget>
165 <customwidgets>
166 <customwidget>
167 <class>GetStorageButton</class>
168 <extends>QPushButton</extends>
169 <header>ubuntuone.controlpanel.gui.qt.gotoweb</header>
170 </customwidget>
171 <customwidget>
172 <class>AddFolderButton</class>
173 <extends>QPushButton</extends>
174 <header>ubuntuone.controlpanel.gui.qt.addfolder</header>
175 </customwidget>
176 </customwidgets>
177 <resources/>
178 <connections/>
179</ui>
0180
=== modified file 'ubuntuone/controlpanel/gui/__init__.py'
--- ubuntuone/controlpanel/gui/__init__.py 2012-03-16 21:18:43 +0000
+++ ubuntuone/controlpanel/gui/__init__.py 2012-03-19 21:32:18 +0000
@@ -71,6 +71,7 @@
71EDIT_PROFILE_LINK = u'https://login.ubuntu.com/'71EDIT_PROFILE_LINK = u'https://login.ubuntu.com/'
72EDIT_SERVICES_LINK = UBUNTUONE_LINK + u'services'72EDIT_SERVICES_LINK = UBUNTUONE_LINK + u'services'
73FACEBOOK_LINK = u'http://www.facebook.com/ubuntuone/'73FACEBOOK_LINK = u'http://www.facebook.com/ubuntuone/'
74GET_STORAGE_LINK = UBUNTUONE_LINK + u'services/#storage_panel'
74GET_SUPPORT_LINK = UBUNTUONE_LINK + u'support/'75GET_SUPPORT_LINK = UBUNTUONE_LINK + u'support/'
75LEARN_MORE_LINK = UBUNTUONE_LINK76LEARN_MORE_LINK = UBUNTUONE_LINK
76MANAGE_FILES_LINK = UBUNTUONE_LINK + u'files/'77MANAGE_FILES_LINK = UBUNTUONE_LINK + u'files/'
@@ -93,6 +94,9 @@
93CLOUD_TO_COMPUTER_SUBTITLE = _('These are the folders in your cloud. '94CLOUD_TO_COMPUTER_SUBTITLE = _('These are the folders in your cloud. '
94 'Select the ones you want to sync with this computer.')95 'Select the ones you want to sync with this computer.')
95CLOUD_TO_COMPUTER_TITLE = _('Syncing the cloud to your computer')96CLOUD_TO_COMPUTER_TITLE = _('Syncing the cloud to your computer')
97COMPUTER_TO_CLOUD_SUBTITLE = _('Okay! Now it\'s time to choose which folders '
98 'on this computer you would like to sync to the cloud.')
99COMPUTER_TO_CLOUD_TITLE = _('Syncing your computer with the cloud')
96CONNECT_BUTTON_LABEL = _('Connect to Ubuntu One')100CONNECT_BUTTON_LABEL = _('Connect to Ubuntu One')
97CONTACTS = _('Thunderbird plug-in')101CONTACTS = _('Thunderbird plug-in')
98CREDENTIALS_ERROR = _('There was a problem while retrieving the credentials.')102CREDENTIALS_ERROR = _('There was a problem while retrieving the credentials.')
@@ -177,6 +181,12 @@
177INSTALLING = _('Installation of <i>%(package_name)s</i> in progress')181INSTALLING = _('Installation of <i>%(package_name)s</i> in progress')
178LOADING = _('Loading...')182LOADING = _('Loading...')
179LOADING_OVERLAY = _('Getting information, please wait...')183LOADING_OVERLAY = _('Getting information, please wait...')
184LOCAL_FOLDERS_CALCULATING = _('Calculating...')
185LOCAL_FOLDERS_OVERFLOW = _('The folders you have selected to sync take '
186 'over your {quota_total} space. '
187 'You can remove some folders or add some extra storage.')
188LOCAL_FOLDERS_FOLDER_HEADER = _('Sync these folders on my computer')
189LOCAL_FOLDERS_SPACE_HEADER = _('Space {space_total}')
180MAIN_ACCOUNT_TAB = _('Account information')190MAIN_ACCOUNT_TAB = _('Account information')
181MAIN_DEVICES_TAB = _('Devices')191MAIN_DEVICES_TAB = _('Devices')
182MAIN_FOLDERS_TAB = _('Folders')192MAIN_FOLDERS_TAB = _('Folders')
183193
=== modified file 'ubuntuone/controlpanel/gui/qt/addfolder.py'
--- ubuntuone/controlpanel/gui/qt/addfolder.py 2012-03-09 14:16:38 +0000
+++ ubuntuone/controlpanel/gui/qt/addfolder.py 2012-03-19 21:32:18 +0000
@@ -56,6 +56,8 @@
56 def __init__(self, *args, **kwargs):56 def __init__(self, *args, **kwargs):
57 """Initialize the UI of the widget."""57 """Initialize the UI of the widget."""
58 super(AddFolderButton, self).__init__(*args, **kwargs)58 super(AddFolderButton, self).__init__(*args, **kwargs)
59 self.add_folder_func = \
60 lambda *a, **kw: defer.fail(NotImplementedError())
59 self.cloud_folders = []61 self.cloud_folders = []
60 self.clicked.connect(self.on_clicked)62 self.clicked.connect(self.on_clicked)
6163
@@ -70,18 +72,20 @@
70 parent=self, directory=home_dir,72 parent=self, directory=home_dir,
71 options=FILE_CHOOSER_OPTIONS)73 options=FILE_CHOOSER_OPTIONS)
72 folder = unicode(QtCore.QDir.toNativeSeparators(folder))74 folder = unicode(QtCore.QDir.toNativeSeparators(folder))
73 logger.debug('on_add_folder_button_clicked: user requested folder '75 logger.info('on_add_folder_button_clicked: user requested folder '
74 'creation for path %r', folder)76 'creation for path %r.', folder)
75 if folder == '':77 if folder == '':
76 self.folderCreationCanceled.emit()78 self.folderCreationCanceled.emit()
77 return79 defer.returnValue(None)
7880
79 is_valid = yield self.backend.validate_path_for_folder(folder)81 is_valid = yield self.backend.validate_path_for_folder(folder)
80 if not is_valid:82 if not is_valid:
83 logger.error('on_add_folder_button_clicked: user requested to '
84 'create a folder for an invalid path %r.', folder)
81 text = FOLDER_INVALID_PATH % {'folder_path': folder,85 text = FOLDER_INVALID_PATH % {'folder_path': folder,
82 'home_folder': home_dir}86 'home_folder': home_dir}
83 QtGui.QMessageBox.warning(self, '', text, CLOSE)87 QtGui.QMessageBox.warning(self, '', text, CLOSE)
84 return88 defer.returnValue(None)
8589
86 yield self.backend.create_folder(folder_path=folder)90 yield self.add_folder_func(folder_path=folder)
87 self.folderCreated.emit(folder)91 self.folderCreated.emit(folder)
8892
=== modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py'
--- ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-03-16 21:12:20 +0000
+++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-03-19 21:32:18 +0000
@@ -24,10 +24,10 @@
24from ubuntuone.controlpanel.backend import AUTOCONNECT_KEY24from ubuntuone.controlpanel.backend import AUTOCONNECT_KEY
25from ubuntuone.controlpanel.logger import setup_logging, log_call25from ubuntuone.controlpanel.logger import setup_logging, log_call
26from ubuntuone.controlpanel.gui import (26from ubuntuone.controlpanel.gui import (
27 EDIT_SERVICES_LINK,
28 FACEBOOK_LINK,27 FACEBOOK_LINK,
29 GET_HELP_ONLINE,28 GET_HELP_ONLINE,
30 GET_MORE_STORAGE,29 GET_MORE_STORAGE,
30 GET_STORAGE_LINK,
31 GET_SUPPORT_LINK,31 GET_SUPPORT_LINK,
32 GREETING,32 GREETING,
33 humanize,33 humanize,
@@ -63,7 +63,7 @@
63 def _setup(self):63 def _setup(self):
64 """Do some extra setupping for the UI."""64 """Do some extra setupping for the UI."""
65 self.ui.get_more_space_button.setText(GET_MORE_STORAGE)65 self.ui.get_more_space_button.setText(GET_MORE_STORAGE)
66 self.ui.get_more_space_button.uri = EDIT_SERVICES_LINK66 self.ui.get_more_space_button.uri = GET_STORAGE_LINK
6767
68 self.ui.help_button.setText(GET_HELP_ONLINE)68 self.ui.help_button.setText(GET_HELP_ONLINE)
69 self.ui.help_button.uri = GET_SUPPORT_LINK69 self.ui.help_button.uri = GET_SUPPORT_LINK
@@ -100,6 +100,8 @@
100 @log_call(logger.debug)100 @log_call(logger.debug)
101 def on_credentials_found(self):101 def on_credentials_found(self):
102 """Credentials are not found or were removed."""102 """Credentials are not found or were removed."""
103 folders_tab_idx = self.ui.tab_widget.indexOf(self.ui.folders_tab)
104 self.ui.tab_widget.setCurrentIndex(folders_tab_idx)
103 self.ui.switcher.setCurrentWidget(self.ui.management)105 self.ui.switcher.setCurrentWidget(self.ui.management)
104 self.connect_file_sync()106 self.connect_file_sync()
105 self.is_processing = False107 self.is_processing = False
106108
=== modified file 'ubuntuone/controlpanel/gui/qt/folders.py'
--- ubuntuone/controlpanel/gui/qt/folders.py 2012-03-19 13:47:56 +0000
+++ ubuntuone/controlpanel/gui/qt/folders.py 2012-03-19 21:32:18 +0000
@@ -19,10 +19,13 @@
19from __future__ import division19from __future__ import division
2020
21import os21import os
22import Queue
23import threading
2224
23from PyQt4 import QtGui, QtCore25from PyQt4 import QtGui, QtCore
24from twisted.internet import defer26from twisted.internet import defer
2527
28from ubuntuone.controlpanel.utils import default_folders
26from ubuntuone.controlpanel.logger import setup_logging, log_call29from ubuntuone.controlpanel.logger import setup_logging, log_call
27from ubuntuone.controlpanel.gui import (30from ubuntuone.controlpanel.gui import (
28 ALWAYS_SUBSCRIBED,31 ALWAYS_SUBSCRIBED,
@@ -35,6 +38,12 @@
35 FOLDERS_COLUMN_SYNC_LOCALLY,38 FOLDERS_COLUMN_SYNC_LOCALLY,
36 FOLDERS_CONFIRM_MERGE,39 FOLDERS_CONFIRM_MERGE,
37 FOLDERS_MANAGE_LABEL,40 FOLDERS_MANAGE_LABEL,
41 GET_MORE_STORAGE,
42 humanize,
43 LOCAL_FOLDERS_CALCULATING,
44 LOCAL_FOLDERS_FOLDER_HEADER,
45 LOCAL_FOLDERS_OVERFLOW,
46 LOCAL_FOLDERS_SPACE_HEADER,
38 MANAGE_FILES_LINK,47 MANAGE_FILES_LINK,
39 MUSIC_ICON_NAME,48 MUSIC_ICON_NAME,
40 MUSIC_DISPLAY_NAME,49 MUSIC_DISPLAY_NAME,
@@ -48,7 +57,7 @@
48 uri_hook,57 uri_hook,
49)58)
50from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin59from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin
51from ubuntuone.controlpanel.gui.qt.ui import folders_ui60from ubuntuone.controlpanel.gui.qt.ui import folders_ui, local_folders_ui
5261
5362
54logger = setup_logging('qt.folders')63logger = setup_logging('qt.folders')
@@ -57,6 +66,9 @@
57SUBSCRIPTION_COL = 166SUBSCRIPTION_COL = 1
58EXPLORE_COL = 267EXPLORE_COL = 2
5968
69LOCAL_SUBSCRIPTION_COL = 0
70LOCAL_SPACE_COL = 1
71
60CANCEL = QtGui.QMessageBox.Cancel72CANCEL = QtGui.QMessageBox.Cancel
61CHECKED = QtCore.Qt.Checked73CHECKED = QtCore.Qt.Checked
62CLOSE = QtGui.QMessageBox.Close74CLOSE = QtGui.QMessageBox.Close
@@ -102,6 +114,7 @@
102 super(FoldersPanel, self)._setup()114 super(FoldersPanel, self)._setup()
103 self.ui.add_folder_button.folderCreated.connect(self.on_folder_created)115 self.ui.add_folder_button.folderCreated.connect(self.on_folder_created)
104 self.ui.add_folder_button.setText(FOLDERS_BUTTON_ADD_FOLDER)116 self.ui.add_folder_button.setText(FOLDERS_BUTTON_ADD_FOLDER)
117 self.ui.add_folder_button.add_folder_func = self.backend.create_folder
105118
106 self.ui.share_publish_button.setVisible(not self.remote_folders)119 self.ui.share_publish_button.setVisible(not self.remote_folders)
107 self.ui.add_folder_button.setVisible(not self.remote_folders)120 self.ui.add_folder_button.setVisible(not self.remote_folders)
@@ -328,3 +341,287 @@
328 """The Folders Panel that only shows remote cloud folders."""341 """The Folders Panel that only shows remote cloud folders."""
329342
330 remote_folders = True343 remote_folders = True
344
345
346class CalculateSize(threading.Thread):
347 """A thread that calculates, in the background, the size of a folder."""
348
349 def __init__(self, path_name, queue):
350 self.path_name = path_name
351 self.queue = queue
352 self._stop = False
353
354 super(CalculateSize, self).__init__()
355
356 # http://docs.python.org/library/threading.html#threading.Thread.daemon
357 # "A boolean value indicating whether this thread is a daemon thread
358 # (True) or not (False)"
359 self.daemon = True
360
361 def run(self):
362 """Run this thread."""
363 logger.debug('size_calculator: about to calculate size for %r.',
364 self.path_name)
365 try:
366 total_size = 0
367 for dirpath, _, filenames in os.walk(self.path_name):
368 for f in filenames:
369 fp = os.path.join(dirpath, f)
370 if os.path.isfile(fp):
371 total_size += os.path.getsize(fp)
372 if self._stop:
373 logger.warning('size_calculator: stopping due to external '
374 'request.')
375 return
376 except: # pylint: disable=W0702
377 logger.exception('size_calculator: failed for %r:', self.path_name)
378 else:
379 logger.info('size_calculator: added new size %r for %r.',
380 self.path_name, total_size)
381 self.queue.put([self.path_name, total_size])
382
383
384class FolderItem(QtGui.QTreeWidgetItem):
385 """A folder in the local folder list."""
386
387 def __init__(self, values=None, path=None, queue=None, volume_id=None):
388 super(FolderItem, self).__init__(values)
389 self.path = path
390 self.volume_id = volume_id
391 self.thread = None
392 self.size = 0
393
394 state = UNCHECKED
395 if path is not None:
396 if volume_id is not None:
397 state = CHECKED
398 elif queue is not None:
399 # calculate sizes of non-existing folders
400 self.thread = CalculateSize(path, queue)
401 self.thread.start()
402 self.size = None
403
404 self.setCheckState(LOCAL_SUBSCRIPTION_COL, state)
405
406
407class LocalFoldersPanel(UbuntuOneBin):
408 """The panel that only shows local, non-synched folders."""
409
410 changesApplied = QtCore.pyqtSignal()
411 changesCanceled = QtCore.pyqtSignal()
412 logger = logger
413 ui_class = local_folders_ui
414
415 def __init__(self, *args, **kwargs):
416 super(LocalFoldersPanel, self).__init__(*args, **kwargs)
417 self.queue = Queue.Queue()
418 self.timer = QtCore.QTimer()
419 self.items = {}
420 self.user_home = None
421 self.account_info = None
422
423 def _setup(self):
424 """Do some extra setupping for the UI."""
425 super(LocalFoldersPanel, self)._setup()
426 # Start with storage upgrade offer invisible
427 self.ui.offer_frame.setVisible(False)
428
429 headers = self.ui.folders.header()
430 headers.setResizeMode(LOCAL_SUBSCRIPTION_COL, headers.Stretch)
431 headers.setResizeMode(LOCAL_SPACE_COL, headers.Custom)
432 headers.resizeSection(LOCAL_SPACE_COL, 130)
433
434 self.ui.folders.headerItem().setText(LOCAL_SUBSCRIPTION_COL,
435 LOCAL_FOLDERS_FOLDER_HEADER)
436 self._set_space_header()
437
438 self.ui.add_folder_button.folderCreated.connect(self.on_folder_created)
439 self.ui.add_folder_button.setText(FOLDERS_BUTTON_ADD_FOLDER)
440 self.ui.add_folder_button.add_folder_func = self.add_folder
441
442 self.ui.add_storage_button.setText(GET_MORE_STORAGE)
443
444 def _set_space_header(self, total=None):
445 """Set the folders listing 'space' header."""
446 if total is None:
447 total = ''
448 else:
449 try:
450 total = humanize(long(total))
451 except (TypeError, ValueError):
452 pass
453 total = '(%s)' % total
454
455 title = LOCAL_FOLDERS_SPACE_HEADER.format(space_total=total)
456 self.ui.folders.headerItem().setText(LOCAL_SPACE_COL, title)
457
458 def _stop(self):
459 """Stop all pending threads and timers."""
460 self.timer.stop()
461 self.timer = None
462
463 for item in self.items.itervalues():
464 if item.thread is None:
465 logger.warning('LocalFoldersPanel: attempted to stop a thread '
466 'for an item with a None thread.')
467 else:
468 item.thread._stop = True
469
470 # pylint: disable=E0202
471 @defer.inlineCallbacks
472 def load(self):
473 """Load specific tab info."""
474 self.is_processing = True
475 self.account_info = yield self.backend.account_info()
476 self._set_space_header(self.account_info['quota_used'])
477 volumes_info = yield self.backend.volumes_info(with_storage_info=False)
478 yield self.process_info(volumes_info)
479
480 @defer.inlineCallbacks
481 @log_call(logger.debug)
482 def process_info(self, volumes_info):
483 """Load local folders info into the tree view."""
484 try:
485 folders = []
486 for _, _, volumes in volumes_info:
487 for volume in volumes:
488 if (volume[u'type'] == self.backend.FOLDER_TYPE and
489 bool(volume['subscribed'])):
490 folders.append((volume['path'], volume['volume_id']))
491
492 # add local folders only if they are valid
493 self.user_home = yield self.backend.get_home_dir()
494 for folder in default_folders(user_home=self.user_home):
495 is_valid = yield self.backend.validate_path_for_folder(folder)
496 if is_valid:
497 folders.append((folder, None))
498
499 # always clear the items dict first, since clearing the folders
500 # list will trigger "underlying C/C++ object has been deleted"
501 self.items.clear()
502 self.ui.folders.clear()
503
504 # self.timer can be None if self._stop was called before
505 # this piece of code was executed
506 if self.timer is not None:
507 for path, volume_id in folders:
508 self.add_folder(folder_path=path, volume_id=volume_id)
509
510 self.timer.start(2000)
511 self.timer.timeout.connect(self.update_sizes)
512 finally:
513 self.is_processing = False
514
515 @handle_errors(logger=logger)
516 @defer.inlineCallbacks
517 def apply_changes(self):
518 """When moving to next page, create/[un]subscribe UDFs."""
519 self.is_processing = True
520 try:
521 self._stop()
522
523 for path, item in self.items.iteritems():
524 subscribed = item.checkState(LOCAL_SUBSCRIPTION_COL) == CHECKED
525 if item.volume_id is not None:
526 logger.info('apply_changes: change settings for %r to %r.',
527 item.path, dict(subscribed=subscribed))
528 yield self.backend.change_volume_settings(item.volume_id,
529 dict(subscribed=subscribed))
530 else:
531 if subscribed:
532 logger.info('apply_changes: create folder for %r.',
533 path)
534 yield self.backend.create_folder(path)
535 finally:
536 self.is_processing = False
537
538 self.changesApplied.emit()
539
540 @handle_errors(logger=logger)
541 def add_folder(self, folder_path, volume_id=None):
542 """Add a folder to the list."""
543 if folder_path in self.items:
544 logger.warning('LocalFoldersPanel: already have an item for %r.',
545 folder_path)
546
547 if self.user_home is None:
548 logger.warning('LocalFoldersPanel: user home is None! '
549 'paths will not be pretty.')
550 display_name = folder_path
551 else:
552 user_home = self.user_home + os.path.sep
553 display_name = _process_name(folder_path.replace(user_home, ''))
554
555 item = FolderItem([display_name, ""], path=folder_path,
556 queue=self.queue, volume_id=volume_id)
557 self.ui.folders.addTopLevelItem(item)
558
559 if volume_id is None: # new folder
560 self.items[folder_path] = item
561
562 @log_call(logger.info)
563 def on_folder_created(self, new_folder):
564 """User clicked on the "Add Folder" button."""
565 item = self.items.get(unicode(new_folder))
566 if item is not None:
567 item.setCheckState(LOCAL_SUBSCRIPTION_COL, CHECKED)
568 else:
569 logger.warning('LocalFoldersPanel: on_folder_created was called '
570 'for %r which is not tracked in the internal dict',
571 unicode(new_folder))
572
573 @handle_errors(logger=logger)
574 def update_sizes(self):
575 """Poll the queue were the threads put the size info.
576
577 Every item put in this queue will be a non-synched folder. Thus, the
578 item's volume_id will be None, so no need to check that.
579
580 """
581 while True:
582 try:
583 path, size = self.queue.get(block=False)
584 except Queue.Empty:
585 break
586 else:
587 item = self.items.get(path)
588 if item:
589 item.size = size
590 item.setText(LOCAL_SPACE_COL, humanize(size))
591
592 total = long(self.account_info['quota_used'])
593 for path, item in self.items.iteritems():
594 if item.size is None:
595 total = LOCAL_FOLDERS_CALCULATING
596 break
597
598 subscribed = item.checkState(LOCAL_SUBSCRIPTION_COL) == CHECKED
599 if subscribed:
600 total += item.size
601
602 if isinstance(total, long):
603 self.show_hide_offer(total)
604 else:
605 self.show_hide_offer(0)
606
607 self._set_space_header(total)
608
609 def show_hide_offer(self, current_size):
610 """Show or hide the offer to buy space according to the total size."""
611 quota = long(self.account_info['quota_total'])
612 if current_size > quota:
613 msg = LOCAL_FOLDERS_OVERFLOW.format(quota_total=humanize(quota))
614 self.ui.offer_label.setText(msg)
615 self.ui.offer_frame.setVisible(True)
616 else:
617 self.ui.offer_frame.setVisible(False)
618
619 # pylint: disable=C0103
620
621 def on_folders_itemChanged(self, item, column):
622 """Update the size for the chosen row."""
623 if column == LOCAL_SUBSCRIPTION_COL:
624 self.update_sizes()
625 self.items[item.path] = item
626
627 # pylint: enable=C0103
331628
=== modified file 'ubuntuone/controlpanel/gui/qt/gotoweb.py'
--- ubuntuone/controlpanel/gui/qt/gotoweb.py 2012-02-06 15:23:27 +0000
+++ ubuntuone/controlpanel/gui/qt/gotoweb.py 2012-03-19 21:32:18 +0000
@@ -23,16 +23,25 @@
23from twisted.internet import defer23from twisted.internet import defer
2424
25from ubuntuone.controlpanel import cache25from ubuntuone.controlpanel import cache
26from ubuntuone.controlpanel.gui import (
27 GET_MORE_STORAGE,
28 GET_STORAGE_LINK,
29)
26from ubuntuone.controlpanel.gui import qt30from ubuntuone.controlpanel.gui import qt
2731
2832
29class GoToWebButton(cache.Cache, QtGui.QPushButton):33class GoToWebButton(cache.Cache, QtGui.QPushButton):
30 """The GoToWebButton widget"""34 """The GoToWebButton widget"""
3135
36 uri = None
37 legend = None
38
32 def __init__(self, *args, **kwargs):39 def __init__(self, *args, **kwargs):
33 """Initialize the UI of the widget."""40 """Initialize the UI of the widget."""
34 super(GoToWebButton, self).__init__(*args, **kwargs)41 super(GoToWebButton, self).__init__(*args, **kwargs)
35 self.uri = None42 if self.legend is not None:
43 self.setText(self.legend)
44
36 self.setIcon(qt.icon_from_name('external_icon_white'))45 self.setIcon(qt.icon_from_name('external_icon_white'))
37 self.setLayoutDirection(QtCore.Qt.RightToLeft)46 self.setLayoutDirection(QtCore.Qt.RightToLeft)
38 self.clicked.connect(self.on_clicked)47 self.clicked.connect(self.on_clicked)
@@ -45,3 +54,10 @@
45 if self.uri is not None:54 if self.uri is not None:
46 uri = yield self.backend.build_signed_iri(self.uri)55 uri = yield self.backend.build_signed_iri(self.uri)
47 qt.uri_hook(uri)56 qt.uri_hook(uri)
57
58
59class GetStorageButton(GoToWebButton):
60 """A specific GoToWebButton to get more storage."""
61
62 legend = GET_MORE_STORAGE
63 uri = GET_STORAGE_LINK
4864
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py 2012-03-09 13:44:53 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py 2012-03-19 21:32:18 +0000
@@ -18,8 +18,6 @@
1818
19"""Tests for the AddFolderButton widget."""19"""Tests for the AddFolderButton widget."""
2020
21import os
22
23from twisted.internet import defer21from twisted.internet import defer
2422
25from ubuntuone.controlpanel.gui.tests import (23from ubuntuone.controlpanel.gui.tests import (
@@ -48,11 +46,10 @@
48 @defer.inlineCallbacks46 @defer.inlineCallbacks
49 def setUp(self):47 def setUp(self):
50 yield super(AddFolderButtonTestCase, self).setUp()48 yield super(AddFolderButtonTestCase, self).setUp()
49 self.created_folders = []
51 self.patch(FakedFileDialog, 'response', gui.QtCore.QString(''))50 self.patch(FakedFileDialog, 'response', gui.QtCore.QString(''))
52 self.patch(self.ui.backend, 'validate_path_for_folder', lambda p: True)51 self.patch(self.ui.backend, 'validate_path_for_folder', lambda p: True)
53 old_home = os.environ['HOME']52 self.patch(self.ui, 'add_folder_func', self.add_folder)
54 os.environ['HOME'] = USER_HOME
55 self.addCleanup(lambda: os.environ.__setitem__('HOME', old_home))
5653
57 @defer.inlineCallbacks54 @defer.inlineCallbacks
58 def assert_does_nothing(self, method_call):55 def assert_does_nothing(self, method_call):
@@ -73,6 +70,18 @@
73 # the folderCreated signal was not emitted70 # the folderCreated signal was not emitted
74 self.assertEqual(self._called, False)71 self.assertEqual(self._called, False)
7572
73 def add_folder(self, folder_path):
74 """A dummy add folder func."""
75 self.created_folders.append(folder_path)
76 return defer.succeed(None)
77
78 def test_default_add_folder_func(self):
79 """The add_folder_func is used."""
80 folder = set_path_on_file_dialog()
81 yield self.ui.on_clicked()
82
83 self.assertEqual(self.created_folders, [folder])
84
76 @defer.inlineCallbacks85 @defer.inlineCallbacks
77 def test_add_folder_button_clicked_opens_file_chooser(self):86 def test_add_folder_button_clicked_opens_file_chooser(self):
78 """When adding a new folder, the proper file chooser is raised."""87 """When adding a new folder, the proper file chooser is raised."""
@@ -121,7 +130,7 @@
121 self.assertEqual(FakedDialog.args, None)130 self.assertEqual(FakedDialog.args, None)
122 self.assertEqual(FakedDialog.kwargs, None)131 self.assertEqual(FakedDialog.kwargs, None)
123 # backend called132 # backend called
124 self.assert_backend_called('create_folder', folder_path=folder)133 self.assertEqual(self.created_folders, [folder])
125134
126 @defer.inlineCallbacks135 @defer.inlineCallbacks
127 def test_calls_backend(self):136 def test_calls_backend(self):
@@ -133,7 +142,7 @@
133 self.assertEqual(FakedDialog.args, None)142 self.assertEqual(FakedDialog.args, None)
134 self.assertEqual(FakedDialog.kwargs, None)143 self.assertEqual(FakedDialog.kwargs, None)
135 # backend called144 # backend called
136 self.assert_backend_called('create_folder', folder_path=folder)145 self.assertEqual(self.created_folders, [folder])
137146
138 @defer.inlineCallbacks147 @defer.inlineCallbacks
139 def test_emit_folder_created_on_success(self):148 def test_emit_folder_created_on_success(self):
140149
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2012-03-16 18:54:55 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2012-03-19 21:32:18 +0000
@@ -193,7 +193,7 @@
193 def test_get_more_space_button(self):193 def test_get_more_space_button(self):
194 """When clicking the get more GB button, the proper url is opened."""194 """When clicking the get more GB button, the proper url is opened."""
195 self.assert_uri_hook_called(self.ui.ui.get_more_space_button,195 self.assert_uri_hook_called(self.ui.ui.get_more_space_button,
196 gui.EDIT_SERVICES_LINK)196 gui.GET_STORAGE_LINK)
197197
198 def test_help_button(self):198 def test_help_button(self):
199 """When clicking the help button, the proper url is opened."""199 """When clicking the help button, the proper url is opened."""
200200
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-03-19 13:47:56 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-03-19 21:32:18 +0000
@@ -20,11 +20,14 @@
20import logging20import logging
21import operator21import operator
22import os22import os
23import Queue
24import shutil
2325
24from PyQt4 import QtGui26from PyQt4 import QtGui
25from twisted.internet import defer27from twisted.internet import defer
26from ubuntuone.devtools.handlers import MementoHandler28from ubuntuone.devtools.handlers import MementoHandler
2729
30from ubuntuone.controlpanel.tests import helper_fail
28from ubuntuone.controlpanel.gui.tests import (31from ubuntuone.controlpanel.gui.tests import (
29 FAKE_VOLUMES_INFO,32 FAKE_VOLUMES_INFO,
30 FAKE_VOLUMES_MINIMAL_INFO,33 FAKE_VOLUMES_MINIMAL_INFO,
@@ -413,6 +416,8 @@
413416
414 def test_add_folder_button(self):417 def test_add_folder_button(self):
415 """The 'add_folder_button' is visible by default."""418 """The 'add_folder_button' is visible by default."""
419 self.assertEqual(self.ui.ui.add_folder_button.add_folder_func,
420 self.ui.backend.create_folder)
416 self.assertTrue(self.ui.ui.add_folder_button.isVisible())421 self.assertTrue(self.ui.ui.add_folder_button.isVisible())
417422
418 def test_check_settings_button(self):423 def test_check_settings_button(self):
@@ -650,3 +655,204 @@
650655
651 class_ui = gui.RemoteFoldersPanel656 class_ui = gui.RemoteFoldersPanel
652 faked_volumes = volumes_with_music_unsubscribed()657 faked_volumes = volumes_with_music_unsubscribed()
658
659
660class BaseLocalFoldersTestCase(BaseTestCase):
661 """Test suite for the class implementing the LocalFolders feature."""
662
663 @defer.inlineCallbacks
664 def setUp(self):
665 self.path = 'not-existing-dir'
666 self.expected_size = self.build_test_dir(self.path)
667 self.queue = Queue.Queue()
668 yield super(BaseLocalFoldersTestCase, self).setUp()
669
670 def build_test_dir(self, dir_path):
671 """Build a testing directory hierarchy."""
672 assert not os.path.exists(dir_path)
673
674 os.makedirs(dir_path)
675 self.addCleanup(shutil.rmtree, dir_path)
676
677 total_size = 0
678
679 a_file = os.path.join(dir_path, 'test_file')
680 with open(a_file, 'wb') as f:
681 f.write('z' * 1000000)
682
683 total_size += os.path.getsize(a_file)
684
685 inner_dir = os.path.join(dir_path, 'test_dir')
686 os.mkdir(inner_dir)
687
688 other_file = os.path.join(dir_path, 'other_test_file')
689 with open(other_file, 'wb') as f:
690 f.write(' ' * 99999)
691
692 total_size += os.path.getsize(other_file)
693
694 empty_dir = os.path.join(dir_path, 'empty')
695 os.mkdir(empty_dir)
696
697 # add a symlink to confirm those are avoided
698 a_link = os.path.join(dir_path, 'some_link')
699 os.symlink(a_file, a_link)
700
701 return total_size
702
703
704class CalculateSizeTestCase(BaseLocalFoldersTestCase):
705 """Test suite for the CalculateSize thread implementation."""
706
707 @defer.inlineCallbacks
708 def setUp(self):
709 yield super(CalculateSizeTestCase, self).setUp()
710 self.ui = gui.CalculateSize(path_name=self.path, queue=self.queue)
711 self.patch(self.ui, 'start', lambda: None)
712
713 def test_creation(self):
714 """The created instance is correct."""
715 self.assertEqual(self.ui.path_name, self.path)
716 self.assertEqual(self.ui.queue, self.queue)
717 self.assertTrue(self.ui.daemon)
718
719 def test_run(self):
720 """The run() method calculates the size for the given path."""
721 self.ui.run()
722
723 path, size = self.queue.get(block=True, timeout=0.5)
724
725 self.assertEqual(path, self.path)
726 self.assertEqual(size, self.expected_size)
727
728 def test_run_handles_errors(self):
729 """The run() method handles errors."""
730 self.patch(gui.os, 'walk', helper_fail)
731 self.ui.run()
732
733 self.assertRaises(Queue.Empty, self.queue.get, block=True, timeout=0.5)
734
735
736class FakedCalculateSize(object):
737 """A faked CalculateSize thread."""
738
739 def __init__(self, *args, **kwargs):
740 self.started = False
741
742 def start(self):
743 """Fake start."""
744 self.started = True
745
746
747class FolderItemTestCase(BaseLocalFoldersTestCase):
748 """Test suite for the FolderItem widget."""
749
750 @defer.inlineCallbacks
751 def setUp(self):
752 yield super(FolderItemTestCase, self).setUp()
753 self.calculator = FakedCalculateSize(self.path, self.queue)
754 self.patch(gui, 'CalculateSize', lambda *a, **kw: self.calculator)
755 self.values = ['foo', 'bar']
756
757 assert not self.calculator.started
758
759 def test_no_params(self):
760 """The creation with no params uses defaults."""
761 item = gui.FolderItem()
762
763 self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
764 self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
765 self.assertEqual(item.path, None)
766 self.assertEqual(item.volume_id, None)
767 self.assertEqual(item.thread, None)
768 self.assertEqual(item.size, 0)
769 self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
770 gui.UNCHECKED)
771
772 def test_values(self):
773 """The creation with only values."""
774 item = gui.FolderItem(values=self.values)
775
776 self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), self.values[0])
777 self.assertEqual(item.text(gui.LOCAL_SPACE_COL), self.values[1])
778 self.assertEqual(item.path, None)
779 self.assertEqual(item.volume_id, None)
780 self.assertEqual(item.thread, None)
781 self.assertEqual(item.size, 0)
782 self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
783 gui.UNCHECKED)
784
785 def test_path(self):
786 """The creation with only a path."""
787 item = gui.FolderItem(path=self.path)
788
789 self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
790 self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
791 self.assertEqual(item.path, self.path)
792 self.assertEqual(item.volume_id, None)
793 self.assertEqual(item.thread, None)
794 self.assertEqual(item.size, 0)
795 self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
796 gui.UNCHECKED)
797
798 def test_queue(self):
799 """The creation with only a queue."""
800 item = gui.FolderItem(queue=self.queue)
801
802 self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
803 self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
804 self.assertEqual(item.path, None)
805 self.assertEqual(item.volume_id, None)
806 self.assertEqual(item.thread, None)
807 self.assertEqual(item.size, 0)
808 self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
809 gui.UNCHECKED)
810
811 def test_volume_id(self):
812 """The creation with only a volume_id."""
813 item = gui.FolderItem(volume_id='yadda')
814
815 self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
816 self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
817 self.assertEqual(item.path, None)
818 self.assertEqual(item.volume_id, 'yadda')
819 self.assertEqual(item.thread, None)
820 self.assertEqual(item.size, 0)
821 self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
822 gui.UNCHECKED)
823
824 def test_path_and_volume_id(self):
825 """The creation with only a volume_id."""
826 item = gui.FolderItem(path=self.path, volume_id='yadda')
827
828 self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
829 self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
830 self.assertEqual(item.path, self.path)
831 self.assertEqual(item.volume_id, 'yadda')
832 self.assertEqual(item.thread, None)
833 self.assertEqual(item.size, 0)
834 self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
835 gui.CHECKED)
836
837 def test_path_and_queue(self):
838 """The creation with only a volume_id."""
839 item = gui.FolderItem(path=self.path, queue=self.queue)
840
841 self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
842 self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
843 self.assertEqual(item.path, self.path)
844 self.assertEqual(item.volume_id, None)
845 self.assertEqual(item.thread, self.calculator)
846 self.assertEqual(item.size, None)
847 self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
848 gui.UNCHECKED)
849
850 self.assertTrue(self.calculator.started)
851
852
853class LocalFoldersPanelTestCase(UbuntuOneBinTestCase):
854 """Test suite for the LocalFoldersPanel widget."""
855
856 class_ui = gui.LocalFoldersPanel
857
858 # TODO: add the test suite (LP: #959690).
653859
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py 2012-02-06 15:23:27 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py 2012-03-19 21:32:18 +0000
@@ -18,8 +18,6 @@
1818
19"""Tests for the GoToWebButton widget."""19"""Tests for the GoToWebButton widget."""
2020
21from twisted.internet import defer
22
23from ubuntuone.controlpanel.gui import qt21from ubuntuone.controlpanel.gui import qt
24from ubuntuone.controlpanel.gui.qt import gotoweb as gui22from ubuntuone.controlpanel.gui.qt import gotoweb as gui
25from ubuntuone.controlpanel.gui.qt.tests import (23from ubuntuone.controlpanel.gui.qt.tests import (
@@ -32,9 +30,16 @@
3230
33 class_ui = gui.GoToWebButton31 class_ui = gui.GoToWebButton
3432
35 @defer.inlineCallbacks33 def test_uri_default(self):
36 def setUp(self):34 """The uri uses the default."""
37 yield super(GoToWebButtonTestCase, self).setUp()35 self.assertEqual(self.ui.uri, self.class_ui.uri)
36
37 def test_text_default(self):
38 """The text uses the default."""
39 if self.class_ui.legend is not None:
40 self.assertEqual(self.ui.text(), self.class_ui.legend)
41 else:
42 self.assertEqual(self.ui.text(), '')
3843
39 def test_uri_can_be_set(self):44 def test_uri_can_be_set(self):
40 """The uri can be set."""45 """The uri can be set."""
@@ -69,3 +74,17 @@
69 self.ui.click()74 self.ui.click()
7075
71 self.assertEqual(self._called, False)76 self.assertEqual(self._called, False)
77
78
79class GetStorageButtonTestCase(GoToWebButtonTestCase):
80 """The test suite for the GetStorageButton widget."""
81
82 class_ui = gui.GetStorageButton
83
84 def test_uri(self):
85 """The default uri is correct."""
86 self.assertEqual(self.ui.uri, gui.GET_STORAGE_LINK)
87
88 def test_text(self):
89 """The default legend is correct."""
90 self.assertEqual(self.ui.text(), gui.GET_MORE_STORAGE)
7291
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_wizard.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-16 21:15:09 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-19 21:32:18 +0000
@@ -110,10 +110,6 @@
110 panel_class = gui.RemoteFoldersPanel110 panel_class = gui.RemoteFoldersPanel
111 sub_title = gui.CLOUD_TO_COMPUTER_SUBTITLE111 sub_title = gui.CLOUD_TO_COMPUTER_SUBTITLE
112112
113 def test_is_final(self):
114 """The page is not final."""
115 self.assertTrue(self.ui.isFinalPage())
116
117 def test_folder_panel_shows_remote_folders_only(self):113 def test_folder_panel_shows_remote_folders_only(self):
118 """The FolderPanel shows only remote folders."""114 """The FolderPanel shows only remote folders."""
119 self.assertTrue(self.ui.panel.remote_folders)115 self.assertTrue(self.ui.panel.remote_folders)
@@ -126,6 +122,19 @@
126 panel_class = gui.PreferencesPanel122 panel_class = gui.PreferencesPanel
127123
128124
125class ComputerToCloudPageTestCase(UbuntuOnePageTestCase):
126 """Test the ComputerToCloudPage wizard page."""
127
128 class_ui = gui.ComputerToCloudPage
129 main_title = gui.COMPUTER_TO_CLOUD_TITLE
130 panel_class = gui.LocalFoldersPanel
131 sub_title = gui.COMPUTER_TO_CLOUD_SUBTITLE
132
133 def test_is_final(self):
134 """The page is not final."""
135 self.assertTrue(self.ui.isFinalPage())
136
137
129class UbuntuOneWizardTestCase(BaseTestCase):138class UbuntuOneWizardTestCase(BaseTestCase):
130 """Test the UbuntuOneWizard widget."""139 """Test the UbuntuOneWizard widget."""
131140
@@ -266,16 +275,33 @@
266class UbuntuOneWizardCloudToComputerTestCase(UbuntuOneWizardSignInTestCase):275class UbuntuOneWizardCloudToComputerTestCase(UbuntuOneWizardSignInTestCase):
267 """Test the CloudToComputerPage wizard page."""276 """Test the CloudToComputerPage wizard page."""
268277
269 buttons = {'FinishButton':278 buttons = {'NextButton': (None, 'currentIdChanged', (3,))}
270 (None, 'finished', (gui.QtGui.QDialog.Accepted,))}
271 page_name = 'cloud_folders'279 page_name = 'cloud_folders'
272 stage_name = 'folders'280 stage_name = 'folders'
273281
282
283class UbuntuOneWizardSettingsTestCase(UbuntuOneWizardSignInTestCase):
284 """Test the CloudToComputerPage wizard page."""
285
286 buttons = {'BackButton': (None, 'currentIdChanged', (0,))}
287 page_name = 'settings'
288 stage_name = 'folders'
289
290
291class UbuntuOneWizardComputerToCloudTestCase(UbuntuOneWizardSignInTestCase):
292 """Test the CloudToComputerPage wizard page."""
293
294 buttons = {
295 'FinishButton': (None, 'finished', (gui.QtGui.QDialog.Accepted,)),
296 'BackButton': (None, 'currentIdChanged', (0,)),
297 }
298 page_name = 'local_folders'
299 stage_name = 'sync'
300
274 def test_done_rejected(self):301 def test_done_rejected(self):
275 """When the wizard is closed on the final page, emit rejected."""302 """When the wizard is closed on the final page, emit rejected."""
276 self.ui.finished.connect(self._set_called)303 self.ui.finished.connect(self._set_called)
277304
278 self.ui._next_id = self.ui.pages[self.ui.cloud_folders_page]
279 assert self.ui.page(self.ui.currentId()).isFinalPage()305 assert self.ui.page(self.ui.currentId()).isFinalPage()
280 self.ui.done(gui.QtGui.QDialog.Rejected)306 self.ui.done(gui.QtGui.QDialog.Rejected)
281307
@@ -286,14 +312,6 @@
286 # does not apply to this page312 # does not apply to this page
287313
288314
289class UbuntuOneWizardSettingsTestCase(UbuntuOneWizardSignInTestCase):
290 """Test the CloudToComputerPage wizard page."""
291
292 buttons = {'BackButton': (None, 'currentIdChanged', (0,))}
293 page_name = 'settings'
294 stage_name = 'folders'
295
296
297class UbuntuOneWizardLoginTestCase(UbuntuOneWizardTestCase):315class UbuntuOneWizardLoginTestCase(UbuntuOneWizardTestCase):
298 """Test the login through the wizard."""316 """Test the login through the wizard."""
299317
300318
=== modified file 'ubuntuone/controlpanel/gui/qt/wizard.py'
--- ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-16 21:15:09 +0000
+++ ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-19 21:32:18 +0000
@@ -34,10 +34,13 @@
34 ARE_YOU_SURE_YES,34 ARE_YOU_SURE_YES,
35 CLOUD_TO_COMPUTER_SUBTITLE,35 CLOUD_TO_COMPUTER_SUBTITLE,
36 CLOUD_TO_COMPUTER_TITLE,36 CLOUD_TO_COMPUTER_TITLE,
37 COMPUTER_TO_CLOUD_SUBTITLE,
38 COMPUTER_TO_CLOUD_TITLE,
37 UBUNTUONE_LINK,39 UBUNTUONE_LINK,
38)40)
39from ubuntuone.controlpanel.gui.qt.folders import (41from ubuntuone.controlpanel.gui.qt.folders import (
40 RemoteFoldersPanel,42 RemoteFoldersPanel,
43 LocalFoldersPanel,
41)44)
42from ubuntuone.controlpanel.gui.qt.preferences import PreferencesPanel45from ubuntuone.controlpanel.gui.qt.preferences import PreferencesPanel
43from ubuntuone.controlpanel.gui.qt.signin import SignInPanel46from ubuntuone.controlpanel.gui.qt.signin import SignInPanel
@@ -109,7 +112,6 @@
109 main_title = CLOUD_TO_COMPUTER_TITLE112 main_title = CLOUD_TO_COMPUTER_TITLE
110 panel_class = RemoteFoldersPanel113 panel_class = RemoteFoldersPanel
111 sub_title = CLOUD_TO_COMPUTER_SUBTITLE114 sub_title = CLOUD_TO_COMPUTER_SUBTITLE
112 is_final = True
113115
114 def __init__(self, *args, **kwargs):116 def __init__(self, *args, **kwargs):
115 super(CloudToComputerPage, self).__init__(*args, **kwargs)117 super(CloudToComputerPage, self).__init__(*args, **kwargs)
@@ -122,6 +124,15 @@
122 panel_class = PreferencesPanel124 panel_class = PreferencesPanel
123125
124126
127class ComputerToCloudPage(UbuntuOnePage):
128 """The page to choose local folders to sync remotly."""
129
130 is_final = True
131 main_title = COMPUTER_TO_CLOUD_TITLE
132 panel_class = LocalFoldersPanel
133 sub_title = COMPUTER_TO_CLOUD_SUBTITLE
134
135
125class UbuntuOneWizard(cache.Cache, QtGui.QWizard):136class UbuntuOneWizard(cache.Cache, QtGui.QWizard):
126 """The Ubuntu One wizard."""137 """The Ubuntu One wizard."""
127138
@@ -160,6 +171,10 @@
160 self.settings_page = SettingsPage()171 self.settings_page = SettingsPage()
161 self.addPage(self.settings_page)172 self.addPage(self.settings_page)
162173
174 # computer to cloud
175 self.local_folders_page = ComputerToCloudPage()
176 self.addPage(self.local_folders_page)
177
163 self._next_id = self.pages[self.signin_page]178 self._next_id = self.pages[self.signin_page]
164 self.next()179 self.next()
165180
@@ -187,16 +202,22 @@
187 stage = self.side_widget.signin_stage202 stage = self.side_widget.signin_stage
188 self._next_id = self.pages[self.cloud_folders_page]203 self._next_id = self.pages[self.cloud_folders_page]
189 elif page is self.cloud_folders_page:204 elif page is self.cloud_folders_page:
190 button_layout = [self.Stretch, self.FinishButton]205 button_layout = [self.Stretch, self.NextButton]
191 button = self.cloud_folders_page.panel.ui.check_settings_button206 button = self.cloud_folders_page.panel.ui.check_settings_button
192 button_to = self.button(self.FinishButton)207 button_to = self.button(self.NextButton)
193 stage = self.side_widget.folders_stage208 stage = self.side_widget.folders_stage
209 self._next_id = self.pages[self.local_folders_page]
194 elif page is self.settings_page:210 elif page is self.settings_page:
195 button_layout = [self.BackButton, self.Stretch]211 button_layout = [self.Stretch, self.BackButton]
196 button = self.settings_page.panel.ui.apply_changes_button212 button = self.settings_page.panel.ui.apply_changes_button
197 button_to = self.button(self.BackButton)213 button_to = self.button(self.BackButton)
198 stage = self.side_widget.folders_stage214 stage = self.side_widget.folders_stage
199 self._next_id = self.pages[self.cloud_folders_page]215 self._next_id = self.pages[self.cloud_folders_page]
216 elif page is self.local_folders_page:
217 button_layout = [self.Stretch, self.BackButton, self.FinishButton]
218 button = self.local_folders_page.panel.ui.add_folder_button
219 button_to = self.button(self.BackButton)
220 stage = self.side_widget.sync_stage
200 else:221 else:
201 logger.error('UbuntuOneWizard.initializePage was called with an'222 logger.error('UbuntuOneWizard.initializePage was called with an'
202 'unknown page: %r (page_id was %r).', page, page_id)223 'unknown page: %r (page_id was %r).', page, page_id)
@@ -217,7 +238,7 @@
217 """Called clean up 'page_id' just before the user leaves it."""238 """Called clean up 'page_id' just before the user leaves it."""
218 page = self.page(page_id)239 page = self.page(page_id)
219 logger.debug('UbuntuOneWizard.cleanupPage: page is %r.', page)240 logger.debug('UbuntuOneWizard.cleanupPage: page is %r.', page)
220 if page is self.settings_page:241 if page is self.settings_page or page is self.local_folders_page:
221 self.initializePage(self.pages[self.cloud_folders_page])242 self.initializePage(self.pages[self.cloud_folders_page])
222243
223 def nextId(self):244 def nextId(self):
@@ -257,8 +278,13 @@
257278
258 def done(self, result):279 def done(self, result):
259 """The main window is being closed, call any custom callback."""280 """The main window is being closed, call any custom callback."""
260 if (result == QtGui.QDialog.Rejected and281 if result == QtGui.QDialog.Accepted:
261 not self.page(self.currentId()).isFinalPage()):282 parent_done = super(UbuntuOneWizard, self).done
283 f = lambda: parent_done(QtGui.QDialog.Accepted)
284 self.local_folders_page.panel.changesApplied.connect(f)
285 # commit local_folders_page's changes
286 self.local_folders_page.panel.apply_changes()
287 elif not self.page(self.currentId()).isFinalPage():
262 response = self.confirm_dialog.exec_()288 response = self.confirm_dialog.exec_()
263 if response == QtGui.QDialog.Accepted:289 if response == QtGui.QDialog.Accepted:
264 logger.warning('UbuntuOneWizard: user canceled setup.')290 logger.warning('UbuntuOneWizard: user canceled setup.')
265291
=== modified file 'ubuntuone/controlpanel/tests/__init__.py'
--- ubuntuone/controlpanel/tests/__init__.py 2011-11-21 13:32:44 +0000
+++ ubuntuone/controlpanel/tests/__init__.py 2012-03-19 21:32:18 +0000
@@ -334,6 +334,15 @@
334]334]
335335
336336
337class CustomError(Exception):
338 """Custom error for tests."""
339
340
341def helper_fail(*a, **kw):
342 """Helper to raise an exception, usually used when monkey-patching."""
343 raise CustomError((a, kw))
344
345
337class TestCase(BaseTestCase):346class TestCase(BaseTestCase):
338 """Basics for testing."""347 """Basics for testing."""
339348
340349
=== modified file 'ubuntuone/controlpanel/tests/test_login_client.py'
--- ubuntuone/controlpanel/tests/test_login_client.py 2011-10-24 21:48:27 +0000
+++ ubuntuone/controlpanel/tests/test_login_client.py 2012-03-19 21:32:18 +0000
@@ -21,11 +21,7 @@
21from twisted.internet import defer21from twisted.internet import defer
2222
23from ubuntuone.controlpanel import login_client23from ubuntuone.controlpanel import login_client
24from ubuntuone.controlpanel.tests import TestCase, TOKEN24from ubuntuone.controlpanel.tests import CustomError, TestCase, TOKEN
25
26
27class CustomError(Exception):
28 """Custom error for tests."""
2925
3026
31class FakedCredentialsManagementTool(object):27class FakedCredentialsManagementTool(object):
3228
=== modified file 'ubuntuone/controlpanel/tests/test_sd_client.py'
--- ubuntuone/controlpanel/tests/test_sd_client.py 2012-01-26 13:16:17 +0000
+++ ubuntuone/controlpanel/tests/test_sd_client.py 2012-03-19 21:32:18 +0000
@@ -32,7 +32,7 @@
32from ubuntuone.syncdaemon.interaction_interfaces import bool_str32from ubuntuone.syncdaemon.interaction_interfaces import bool_str
3333
34from ubuntuone.controlpanel import sd_client34from ubuntuone.controlpanel import sd_client
35from ubuntuone.controlpanel.tests import TestCase35from ubuntuone.controlpanel.tests import CustomError, TestCase
3636
37# Instance of 'SyncDaemonTool' has no 'foo' member37# Instance of 'SyncDaemonTool' has no 'foo' member
38# pylint: disable=E110138# pylint: disable=E1101
@@ -41,10 +41,6 @@
41SAMPLE_LIMITS = {'upload': 999, 'download': 838}41SAMPLE_LIMITS = {'upload': 999, 'download': 838}
4242
4343
44class CustomError(Exception):
45 """Custom error for tests."""
46
47
48class FakedSyncDaemonTool(object):44class FakedSyncDaemonTool(object):
49 """Fake the SyncDaemonTool."""45 """Fake the SyncDaemonTool."""
5046

Subscribers

People subscribed via source and target branches