Merge lp:~ralsina/ubuntuone-windows-installer/local-folder-fixes into lp:ubuntuone-windows-installer
- local-folder-fixes
- Merge into trunk
Proposed by
Roberto Alsina
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~ralsina/ubuntuone-windows-installer/local-folder-fixes | ||||
Merge into: | lp:ubuntuone-windows-installer | ||||
Diff against target: |
657 lines (+356/-98) 3 files modified
data/qt/local_folders.ui (+1/-8) ubuntuone_installer/gui/qt/local_folders.py (+142/-57) ubuntuone_installer/gui/qt/tests/test_gui.py (+213/-33) |
||||
To merge this branch: | bzr merge lp:~ralsina/ubuntuone-windows-installer/local-folder-fixes | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu One hackers | Pending | ||
Review via email:
|
Commit message
Implement the "Computer-to-cloud" page of the installer according to spec and discussions.
Description of the change
Implement the "Computer-to-cloud" page of the installer according to spec and discussions.
To post a comment you must log in.
- 47. By Roberto Alsina
-
merged
- 48. By Roberto Alsina
-
added tests for pre/post initializePage
- 49. By Roberto Alsina
-
more tests, lint fixes
- 50. By Roberto Alsina
-
added tests for FolderItem
- 51. By Roberto Alsina
-
added tests for FolderItem
- 52. By Roberto Alsina
-
fixes
- 53. By Roberto Alsina
-
added test for special_folders
- 54. By Roberto Alsina
-
merged trunk
- 55. By Roberto Alsina
-
fixes
- 56. By Roberto Alsina
-
add test for 'no folder selected, no quota used'
- 57. By Roberto Alsina
-
suggested fix for the while True
- 58. By Roberto Alsina
-
Separate tests for local folders
- 59. By Roberto Alsina
-
style fixes
- 60. By Roberto Alsina
-
added two suggested tests
- 61. By Roberto Alsina
-
lint
- 62. By Roberto Alsina
-
solve conflict
- 63. By Roberto Alsina
-
merge conflict fix
- 64. By Roberto Alsina
-
grmbl
- 65. By Roberto Alsina
-
resolve further
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'data/qt/local_folders.ui' | |||
2 | --- data/qt/local_folders.ui 2011-08-08 21:48:11 +0000 | |||
3 | +++ data/qt/local_folders.ui 2011-08-17 00:58:24 +0000 | |||
4 | @@ -97,7 +97,7 @@ | |||
5 | 97 | </spacer> | 97 | </spacer> |
6 | 98 | </item> | 98 | </item> |
7 | 99 | <item> | 99 | <item> |
9 | 100 | <widget class="AddFolderButton" name="add_folder_button"> | 100 | <widget class="QPushButton" name="add_folder_button"> |
10 | 101 | <property name="text"> | 101 | <property name="text"> |
11 | 102 | <string>Add a folder from this computer</string> | 102 | <string>Add a folder from this computer</string> |
12 | 103 | </property> | 103 | </property> |
13 | @@ -188,13 +188,6 @@ | |||
14 | 188 | </item> | 188 | </item> |
15 | 189 | </layout> | 189 | </layout> |
16 | 190 | </widget> | 190 | </widget> |
17 | 191 | <customwidgets> | ||
18 | 192 | <customwidget> | ||
19 | 193 | <class>AddFolderButton</class> | ||
20 | 194 | <extends>QPushButton</extends> | ||
21 | 195 | <header>ubuntuone.controlpanel.gui.qt.addfolder</header> | ||
22 | 196 | </customwidget> | ||
23 | 197 | </customwidgets> | ||
24 | 198 | <resources/> | 191 | <resources/> |
25 | 199 | <connections/> | 192 | <connections/> |
26 | 200 | </ui> | 193 | </ui> |
27 | 201 | 194 | ||
28 | === modified file 'ubuntuone_installer/gui/qt/local_folders.py' | |||
29 | --- ubuntuone_installer/gui/qt/local_folders.py 2011-08-17 00:58:24 +0000 | |||
30 | +++ ubuntuone_installer/gui/qt/local_folders.py 2011-08-17 00:58:24 +0000 | |||
31 | @@ -24,9 +24,14 @@ | |||
32 | 24 | import threading | 24 | import threading |
33 | 25 | import Queue | 25 | import Queue |
34 | 26 | 26 | ||
36 | 27 | from twisted.internet.defer import inlineCallbacks | 27 | from twisted.internet.defer import inlineCallbacks, returnValue |
37 | 28 | from PyQt4 import QtCore, QtGui | 28 | from PyQt4 import QtCore, QtGui |
39 | 29 | from ubuntuone.controlpanel.gui import humanize, sign_url | 29 | from ubuntuone.controlpanel import backend |
40 | 30 | from ubuntuone.controlpanel.gui import ( | ||
41 | 31 | humanize, | ||
42 | 32 | sign_url, | ||
43 | 33 | FOLDER_INVALID_PATH | ||
44 | 34 | ) | ||
45 | 30 | from ubuntuone.platform.credentials import CredentialsManagementTool | 35 | from ubuntuone.platform.credentials import CredentialsManagementTool |
46 | 31 | from ubuntu_sso.qt.gui import SSOWizardPage | 36 | from ubuntu_sso.qt.gui import SSOWizardPage |
47 | 32 | 37 | ||
48 | @@ -49,12 +54,19 @@ | |||
49 | 49 | 54 | ||
50 | 50 | class FolderItem(QtGui.QTreeWidgetItem): | 55 | class FolderItem(QtGui.QTreeWidgetItem): |
51 | 51 | """Class representing a folder in the folder list UI.""" | 56 | """Class representing a folder in the folder list UI.""" |
53 | 52 | def __init__(self, strings, path=None, queue=None): | 57 | def __init__(self, strings, path=None, queue=None, |
54 | 58 | calculate=True, volume_id=None): | ||
55 | 53 | super(FolderItem, self).__init__(strings) | 59 | super(FolderItem, self).__init__(strings) |
59 | 54 | self.thread = CalculateSize(path, queue) | 60 | if calculate: |
60 | 55 | self.thread.start() | 61 | self.thread = CalculateSize(path, queue) |
61 | 56 | self.size = None | 62 | self.thread.start() |
62 | 63 | self.size = None | ||
63 | 64 | else: | ||
64 | 65 | self.thread = CalculateSize(path, queue) | ||
65 | 66 | self.size = 0 | ||
66 | 57 | self.path = path | 67 | self.path = path |
67 | 68 | self.setCheckState(0, QtCore.Qt.Unchecked) | ||
68 | 69 | self.volume_id = volume_id | ||
69 | 58 | 70 | ||
70 | 59 | 71 | ||
71 | 60 | class CalculateSize(threading.Thread): | 72 | class CalculateSize(threading.Thread): |
72 | @@ -88,6 +100,9 @@ | |||
73 | 88 | self.queue = Queue.Queue() | 100 | self.queue = Queue.Queue() |
74 | 89 | self.timer = QtCore.QTimer() | 101 | self.timer = QtCore.QTimer() |
75 | 90 | self.items = {} | 102 | self.items = {} |
76 | 103 | self.folders_info = None | ||
77 | 104 | self.account_info = None | ||
78 | 105 | self.cp_backend = backend.ControlBackend() | ||
79 | 91 | 106 | ||
80 | 92 | # initializePage is inherited | 107 | # initializePage is inherited |
81 | 93 | # pylint: disable=C0103 | 108 | # pylint: disable=C0103 |
82 | @@ -99,16 +114,72 @@ | |||
83 | 99 | self.wizard()._next_id = self.wizard().SYNC_NOW_OR_LATER_PAGE | 114 | self.wizard()._next_id = self.wizard().SYNC_NOW_OR_LATER_PAGE |
84 | 100 | # Start with this invisible | 115 | # Start with this invisible |
85 | 101 | self.ui.offer_frame.setVisible(False) | 116 | self.ui.offer_frame.setVisible(False) |
87 | 102 | if not self.ui.folder_list.topLevelItemCount(): | 117 | # Block until we have server data |
88 | 118 | self.wizard().overlay.show() | ||
89 | 119 | self.get_info() | ||
90 | 120 | |||
91 | 121 | @inlineCallbacks | ||
92 | 122 | def get_info(self): | ||
93 | 123 | """Get information from CP backend and fill folder list.""" | ||
94 | 124 | try: | ||
95 | 125 | volumes_info = yield self.cp_backend.volumes_info() | ||
96 | 126 | self.account_info = yield self.cp_backend.account_info() | ||
97 | 127 | self.folders_info = [] | ||
98 | 128 | for _, _, volumes in volumes_info: | ||
99 | 129 | for volume in volumes: | ||
100 | 130 | if volume[u'type'] == u"UDF": | ||
101 | 131 | self.folders_info.append(volume) | ||
102 | 132 | self.ui.folder_list.clear() | ||
103 | 133 | for folder in self.folders_info: | ||
104 | 134 | item = yield self.add_folder( | ||
105 | 135 | os.path.expanduser(folder['path']), | ||
106 | 136 | validate=False, volume_id=folder['volume_id']) | ||
107 | 137 | if item: | ||
108 | 138 | if folder['subscribed']: | ||
109 | 139 | item.setCheckState(0, QtCore.Qt.Checked) | ||
110 | 140 | item.thread.join() | ||
111 | 141 | item.size = 0 | ||
112 | 103 | for folder_name in self.default_folders(): | 142 | for folder_name in self.default_folders(): |
116 | 104 | self.add_folder(folder_name) | 143 | item = yield self.add_folder(folder_name, validate=True) |
117 | 105 | self.timer.start(2000) | 144 | self.timer.start(2000) |
118 | 106 | self.timer.timeout.connect(self.update_sizes) | 145 | self.timer.timeout.connect(self.update_sizes) |
119 | 146 | self.wizard().overlay.hide() | ||
120 | 147 | self.wizard().currentIdChanged.connect(self.changed_page) | ||
121 | 148 | except: | ||
122 | 149 | logger.exception("Error getting backend info:") | ||
123 | 150 | |||
124 | 151 | @QtCore.pyqtSlot("int") | ||
125 | 152 | @inlineCallbacks | ||
126 | 153 | def changed_page(self, page_id): | ||
127 | 154 | """When moving to next page, create/[un]subscribe UDFs.""" | ||
128 | 155 | self.timer.stop() | ||
129 | 156 | try: | ||
130 | 157 | self.wizard().currentIdChanged.disconnect(self.changed_page) | ||
131 | 158 | except KeyError: | ||
132 | 159 | pass | ||
133 | 160 | if page_id == self.wizard().SYNC_NOW_OR_LATER_PAGE: | ||
134 | 161 | # The page following this one | ||
135 | 162 | self.wizard().overlay.show() | ||
136 | 163 | for path, item in self.items.items(): | ||
137 | 164 | if item.checkState(0) == QtCore.Qt.Checked: | ||
138 | 165 | if item.volume_id: | ||
139 | 166 | yield self.cp_backend.change_volume_settings( | ||
140 | 167 | item.volume_id, | ||
141 | 168 | dict(subscribed=True)) | ||
142 | 169 | else: | ||
143 | 170 | yield self.cp_backend.create_folder(path) | ||
144 | 171 | else: | ||
145 | 172 | if item.volume_id: | ||
146 | 173 | yield self.cp_backend.change_volume_settings( | ||
147 | 174 | item.volume_id, | ||
148 | 175 | dict(subscribed=False)) | ||
149 | 176 | self.wizard().overlay.hide() | ||
150 | 107 | 177 | ||
151 | 108 | def default_folders(self): | 178 | def default_folders(self): |
152 | 109 | """Return a list of the folders to add by default.""" | 179 | """Return a list of the folders to add by default.""" |
153 | 110 | if sys.platform == 'win32': | 180 | if sys.platform == 'win32': |
155 | 111 | # Special Folder "My Documents" | 181 | # XXXX to be replaced by calls to xdg_base_directory's |
156 | 182 | # special_folders | ||
157 | 112 | dll = ctypes.windll.shell32 | 183 | dll = ctypes.windll.shell32 |
158 | 113 | buf = ctypes.create_string_buffer(300) | 184 | buf = ctypes.create_string_buffer(300) |
159 | 114 | dll.SHGetSpecialFolderPathA(None, buf, 5, False) | 185 | dll.SHGetSpecialFolderPathA(None, buf, 5, False) |
160 | @@ -122,61 +193,62 @@ | |||
161 | 122 | result = ['To be implemented'] | 193 | result = ['To be implemented'] |
162 | 123 | return result | 194 | return result |
163 | 124 | 195 | ||
165 | 125 | def add_folder(self, path): | 196 | @inlineCallbacks |
166 | 197 | def add_folder(self, path, validate=True, volume_id=False): | ||
167 | 126 | """Add a folder to the list.""" | 198 | """Add a folder to the list.""" |
168 | 127 | if path in self.items: | 199 | if path in self.items: |
175 | 128 | return None | 200 | returnValue(None) |
176 | 129 | # FIXME: the path should actually be sent to u1cp to verify as valid | 201 | if validate: |
177 | 130 | item = FolderItem([path, "", "remove"], path=path, queue=self.queue) | 202 | is_valid = yield self.cp_backend.validate_path_for_folder(path) |
178 | 131 | self.ui.folder_list.addTopLevelItem(item) | 203 | else: |
179 | 132 | self.items[path] = item | 204 | is_valid = True |
180 | 133 | return item | 205 | if is_valid: |
181 | 206 | item = FolderItem([path, ""], | ||
182 | 207 | path=path, queue=self.queue, volume_id=volume_id) | ||
183 | 208 | self.ui.folder_list.addTopLevelItem(item) | ||
184 | 209 | self.items[path] = item | ||
185 | 210 | returnValue(item) | ||
186 | 211 | returnValue(None) | ||
187 | 134 | 212 | ||
188 | 135 | @inlineCallbacks | ||
189 | 136 | def update_sizes(self): | 213 | def update_sizes(self): |
190 | 137 | """Poll the queue were the threads put the size info.""" | 214 | """Poll the queue were the threads put the size info.""" |
191 | 138 | try: | 215 | try: |
197 | 139 | path, size = self.queue.get(False) | 216 | while True: |
198 | 140 | item = self.items.get(path) | 217 | path, size = self.queue.get(False) |
199 | 141 | if item: | 218 | item = self.items.get(path) |
200 | 142 | item.size = size | 219 | if item: |
201 | 143 | item.setText(1, humanize(size)) | 220 | item.size = size |
202 | 221 | try: | ||
203 | 222 | item.setText(1, humanize(size)) | ||
204 | 223 | except RuntimeError: | ||
205 | 224 | del self.items[path] | ||
206 | 144 | except Queue.Empty: | 225 | except Queue.Empty: |
207 | 145 | pass | 226 | pass |
209 | 146 | total = 0 | 227 | total = long(self.account_info['quota_used']) |
210 | 147 | for path, item in self.items.items(): | 228 | for path, item in self.items.items(): |
211 | 148 | if item.size is None: | 229 | if item.size is None: |
212 | 149 | total = LOCAL_FOLDERS_CALCULATING | 230 | total = LOCAL_FOLDERS_CALCULATING |
213 | 150 | break | 231 | break |
216 | 151 | total += item.size | 232 | if not item.volume_id and item.checkState(0) == QtCore.Qt.Checked: |
217 | 152 | 233 | # Existing UDFs are already accounted for, count if marked. | |
218 | 234 | total += item.size | ||
219 | 153 | if isinstance(total, long): | 235 | if isinstance(total, long): |
221 | 154 | yield self.show_hide_offer(total) | 236 | self.show_hide_offer(total) |
222 | 155 | total = humanize(total) | 237 | total = humanize(total) |
223 | 156 | else: | 238 | else: |
225 | 157 | yield self.show_hide_offer(0) | 239 | self.show_hide_offer(0) |
226 | 158 | self.ui.folder_list.headerItem().setText( | 240 | self.ui.folder_list.headerItem().setText( |
227 | 159 | 1, LOCAL_FOLDERS_SPACE_HEADER % total) | 241 | 1, LOCAL_FOLDERS_SPACE_HEADER % total) |
228 | 160 | 242 | ||
229 | 161 | @inlineCallbacks | ||
230 | 162 | def show_hide_offer(self, cur_size): | 243 | def show_hide_offer(self, cur_size): |
248 | 163 | """Show or hide the offer to buy space according to the total size. | 244 | """Show or hide the offer to buy space according to the total size.""" |
249 | 164 | 245 | quota = self.account_info['quota_total'] | |
250 | 165 | Returns a deferred that is triggered when the update is finished. | 246 | if cur_size > quota: |
251 | 166 | 247 | self.ui.offer_frame.setVisible(True) | |
252 | 167 | """ | 248 | else: |
253 | 168 | # pylint: disable=W0702 | 249 | self.ui.offer_frame.setVisible(False) |
254 | 169 | try: | 250 | self.ui.offer_label.setText(LOCAL_FOLDERS_OFFER_LABEL % |
255 | 170 | user_info = yield self.ui.add_folder_button.backend.account_info() | 251 | {"quota": humanize(quota)}) |
239 | 171 | quota = user_info['quota_total'] | ||
240 | 172 | if cur_size > quota: | ||
241 | 173 | self.ui.offer_frame.setVisible(True) | ||
242 | 174 | else: | ||
243 | 175 | self.ui.offer_frame.setVisible(False) | ||
244 | 176 | self.ui.offer_label.setText(LOCAL_FOLDERS_OFFER_LABEL % | ||
245 | 177 | {"quota": humanize(quota)}) | ||
246 | 178 | except: | ||
247 | 179 | logger.exception('Error while trying to update the quota:') | ||
256 | 180 | 252 | ||
257 | 181 | def stop_threads(self): | 253 | def stop_threads(self): |
258 | 182 | """Stop all pending threads.""" | 254 | """Stop all pending threads.""" |
259 | @@ -185,31 +257,44 @@ | |||
260 | 185 | 257 | ||
261 | 186 | # itemClicked is a Qt signal name. | 258 | # itemClicked is a Qt signal name. |
262 | 187 | # pylint: disable=C0103 | 259 | # pylint: disable=C0103 |
264 | 188 | def on_folder_list_itemClicked(self, item, column): | 260 | def on_folder_list_itemChanged(self, item, column): |
265 | 189 | """Delete folder from the list.""" | 261 | """Delete folder from the list.""" |
272 | 190 | if column == 2: | 262 | if column == 0: |
273 | 191 | del(self.items[item.path]) | 263 | self.update_sizes() |
268 | 192 | item.thread._stop = True | ||
269 | 193 | self.ui.folder_list.takeTopLevelItem( | ||
270 | 194 | self.ui.folder_list.indexOfTopLevelItem(item)) | ||
271 | 195 | self.update_sizes() | ||
274 | 196 | 264 | ||
275 | 197 | @inlineCallbacks | 265 | @inlineCallbacks |
276 | 198 | @QtCore.pyqtSlot() | 266 | @QtCore.pyqtSlot() |
277 | 199 | def on_add_storage_button_clicked(self): | 267 | def on_add_storage_button_clicked(self): |
278 | 200 | """user clicked on the "Add more storage" button.""" | 268 | """user clicked on the "Add more storage" button.""" |
280 | 201 | 269 | # Really want to catch everything | |
281 | 202 | # pylint: disable=W0702 | 270 | # pylint: disable=W0702 |
282 | 203 | try: | 271 | try: |
283 | 204 | credtool = CredentialsManagementTool() | 272 | credtool = CredentialsManagementTool() |
284 | 205 | creds = yield credtool.find_credentials() | 273 | creds = yield credtool.find_credentials() |
285 | 206 | except: | 274 | except: |
287 | 207 | logger.exception('Error while trying to update que quota:') | 275 | logger.exception('Error while trying to get credentials:') |
288 | 208 | creds = {} | 276 | creds = {} |
289 | 209 | |||
290 | 210 | if creds: | 277 | if creds: |
291 | 211 | signed_url = sign_url( | 278 | signed_url = sign_url( |
292 | 212 | "https://one.ubuntu.com/services/#storage_panel", creds) | 279 | "https://one.ubuntu.com/services/#storage_panel", creds) |
293 | 213 | else: | 280 | else: |
294 | 214 | signed_url = "https://one.ubuntu.com/services/#storage_panel" | 281 | signed_url = "https://one.ubuntu.com/services/#storage_panel" |
295 | 215 | QtGui.QDesktopServices.openUrl(QtCore.QUrl(signed_url)) | 282 | QtGui.QDesktopServices.openUrl(QtCore.QUrl(signed_url)) |
296 | 283 | |||
297 | 284 | @inlineCallbacks | ||
298 | 285 | @QtCore.pyqtSlot() | ||
299 | 286 | def on_add_folder_button_clicked(self): | ||
300 | 287 | """user clicked on the "Add Folder" button.""" | ||
301 | 288 | folder = QtGui.QFileDialog.getExistingDirectory(parent=self) | ||
302 | 289 | folder = unicode(folder) | ||
303 | 290 | if folder == '': | ||
304 | 291 | return | ||
305 | 292 | |||
306 | 293 | is_valid = yield self.cp_backend.validate_path_for_folder(folder) | ||
307 | 294 | if not is_valid: | ||
308 | 295 | user_home = os.path.expanduser('~') | ||
309 | 296 | text = FOLDER_INVALID_PATH % {'folder_path': folder, | ||
310 | 297 | 'home_folder': user_home} | ||
311 | 298 | QtGui.QMessageBox.warning(self, '', text, QtGui.QMessageBox.Close) | ||
312 | 299 | return | ||
313 | 300 | yield self.add_folder(folder, validate=False, volume_id=False) | ||
314 | 216 | 301 | ||
315 | === modified file 'ubuntuone_installer/gui/qt/tests/test_gui.py' | |||
316 | --- ubuntuone_installer/gui/qt/tests/test_gui.py 2011-08-17 00:58:24 +0000 | |||
317 | +++ ubuntuone_installer/gui/qt/tests/test_gui.py 2011-08-17 00:58:24 +0000 | |||
318 | @@ -529,15 +529,20 @@ | |||
319 | 529 | 529 | ||
320 | 530 | def __init__(self, *args, **kwargs): | 530 | def __init__(self, *args, **kwargs): |
321 | 531 | """Initialize.""" | 531 | """Initialize.""" |
323 | 532 | self.target = lambda *args: None | 532 | self.target = None |
324 | 533 | 533 | ||
325 | 534 | def connect(self, target): | 534 | def connect(self, target): |
326 | 535 | """Fake connect.""" | 535 | """Fake connect.""" |
327 | 536 | self.target = target | 536 | self.target = target |
328 | 537 | 537 | ||
329 | 538 | def disconnect(self, *args): | ||
330 | 539 | """Fake disconnect.""" | ||
331 | 540 | self.target = None | ||
332 | 541 | |||
333 | 538 | def emit(self, *args): | 542 | def emit(self, *args): |
334 | 539 | """Fake emit.""" | 543 | """Fake emit.""" |
336 | 540 | self.target(*args) | 544 | if self.target: |
337 | 545 | self.target(*args) | ||
338 | 541 | 546 | ||
339 | 542 | 547 | ||
340 | 543 | class FakeMainWindow(object): | 548 | class FakeMainWindow(object): |
341 | @@ -548,7 +553,10 @@ | |||
342 | 548 | registrationSuccess = FakeSignal() | 553 | registrationSuccess = FakeSignal() |
343 | 549 | userCancellation = FakeSignal() | 554 | userCancellation = FakeSignal() |
344 | 550 | shown = False | 555 | shown = False |
345 | 556 | _buttonlayout = None | ||
346 | 551 | SYNC_NOW_OR_LATER_PAGE = 4 | 557 | SYNC_NOW_OR_LATER_PAGE = 4 |
347 | 558 | overlay = FakeOverlay() | ||
348 | 559 | currentIdChanged = FakeSignal() | ||
349 | 552 | 560 | ||
350 | 553 | def show(self): | 561 | def show(self): |
351 | 554 | """Fake method.""" | 562 | """Fake method.""" |
352 | @@ -563,9 +571,71 @@ | |||
353 | 563 | class FakeCPBackend(object): | 571 | class FakeCPBackend(object): |
354 | 564 | """Fake Control Panel backend.""" | 572 | """Fake Control Panel backend.""" |
355 | 565 | 573 | ||
356 | 574 | def __init__(self): | ||
357 | 575 | """Initialize.""" | ||
358 | 576 | self._is_valid = True | ||
359 | 577 | self.volume_setings_changes = [] | ||
360 | 578 | self.folders_created = [] | ||
361 | 579 | |||
362 | 566 | def account_info(self, *args): | 580 | def account_info(self, *args): |
363 | 567 | """Fake account info.""" | 581 | """Fake account info.""" |
365 | 568 | return defer.succeed({"quota_total": 1000}) | 582 | return defer.succeed({ |
366 | 583 | "quota_total": 1000, | ||
367 | 584 | "quota_used": 200, | ||
368 | 585 | }) | ||
369 | 586 | |||
370 | 587 | def volumes_info(self, *args): | ||
371 | 588 | """"Fake volumes info.""" | ||
372 | 589 | return defer.succeed(((None, None, | ||
373 | 590 | [ | ||
374 | 591 | { | ||
375 | 592 | 'type': u'UDF', | ||
376 | 593 | 'path': os.path.expanduser(u'~/xyzzy'), | ||
377 | 594 | 'volume_id': 'asdfgh', | ||
378 | 595 | 'subscribed': True | ||
379 | 596 | }, | ||
380 | 597 | { | ||
381 | 598 | 'type': u'UDF', | ||
382 | 599 | 'path': os.path.expanduser(u'~/zxyzzy'), | ||
383 | 600 | 'volume_id': 'qwerty', | ||
384 | 601 | 'subscribed': False | ||
385 | 602 | }, | ||
386 | 603 | ], | ||
387 | 604 | ),)) | ||
388 | 605 | |||
389 | 606 | def validate_path_for_folder(self, path): | ||
390 | 607 | """Fake folder validation.""" | ||
391 | 608 | return self._is_valid | ||
392 | 609 | |||
393 | 610 | def change_volume_settings(self, *args): | ||
394 | 611 | """Fake change volume settings.""" | ||
395 | 612 | self.volume_setings_changes.append(args) | ||
396 | 613 | |||
397 | 614 | def create_folder(self, *args): | ||
398 | 615 | """Fake folder creation.""" | ||
399 | 616 | self.folders_created.append(args) | ||
400 | 617 | |||
401 | 618 | |||
402 | 619 | class FakeFileDialog(object): | ||
403 | 620 | |||
404 | 621 | """A fake QFileDialog class.""" | ||
405 | 622 | |||
406 | 623 | # pylint: disable=C0103 | ||
407 | 624 | def getExistingDirectory(self, *args, **kwargs): | ||
408 | 625 | """Fake existing folder name.""" | ||
409 | 626 | return u"whatever" | ||
410 | 627 | |||
411 | 628 | |||
412 | 629 | class FakeMessageBox(object): | ||
413 | 630 | |||
414 | 631 | """A fake QMessageBox class.""" | ||
415 | 632 | |||
416 | 633 | Close = 2 | ||
417 | 634 | _warning = None | ||
418 | 635 | |||
419 | 636 | def warning(self, *args, **kwargs): | ||
420 | 637 | """Fake warning.""" | ||
421 | 638 | self._warning = (args, kwargs) | ||
422 | 569 | 639 | ||
423 | 570 | 640 | ||
424 | 571 | class FakeFailingCPBackend(object): | 641 | class FakeFailingCPBackend(object): |
425 | @@ -598,6 +668,7 @@ | |||
426 | 598 | f.write(" " * 737) | 668 | f.write(" " * 737) |
427 | 599 | f.close() | 669 | f.close() |
428 | 600 | self.fake_wizard = FakeMainWindow() | 670 | self.fake_wizard = FakeMainWindow() |
429 | 671 | self.patch(local_folders.backend, "ControlBackend", FakeCPBackend) | ||
430 | 601 | super(LocalFoldersTestCase, self).setUp() | 672 | super(LocalFoldersTestCase, self).setUp() |
431 | 602 | self.patch(self.ui, "wizard", lambda: self.fake_wizard) | 673 | self.patch(self.ui, "wizard", lambda: self.fake_wizard) |
432 | 603 | 674 | ||
433 | @@ -606,6 +677,24 @@ | |||
434 | 606 | shutil.rmtree(self.tmpdir) | 677 | shutil.rmtree(self.tmpdir) |
435 | 607 | BaseTestCase.tearDown(self) | 678 | BaseTestCase.tearDown(self) |
436 | 608 | 679 | ||
437 | 680 | @defer.inlineCallbacks | ||
438 | 681 | def test_subscribed_udf_checked(self): | ||
439 | 682 | """Check that subscribed UDF items are created correctly.""" | ||
440 | 683 | yield self.ui.get_info() | ||
441 | 684 | item = self.ui.items[os.path.expanduser(u'~/xyzzy')] | ||
442 | 685 | self.assertEqual(item.checkState(0), QtCore.Qt.Checked) | ||
443 | 686 | self.assertEqual(item.size, 0) | ||
444 | 687 | self.assertTrue(item.volume_id) | ||
445 | 688 | |||
446 | 689 | @defer.inlineCallbacks | ||
447 | 690 | def test_unsubscribed_udf_checked(self): | ||
448 | 691 | """Check that unsubscribed UDF items are created correctly.""" | ||
449 | 692 | yield self.ui.get_info() | ||
450 | 693 | item = self.ui.items[os.path.expanduser(u'~/zxyzzy')] | ||
451 | 694 | self.assertEqual(item.checkState(0), QtCore.Qt.Unchecked) | ||
452 | 695 | self.assertEqual(item.size, 0) | ||
453 | 696 | self.assertTrue(item.volume_id) | ||
454 | 697 | |||
455 | 609 | def test_size_calculation(self): | 698 | def test_size_calculation(self): |
456 | 610 | """Test the recursive folder size calculation.""" | 699 | """Test the recursive folder size calculation.""" |
457 | 611 | queue = Queue.Queue() | 700 | queue = Queue.Queue() |
458 | @@ -615,38 +704,77 @@ | |||
459 | 615 | self.assertEqual(path, self.tmpdir) | 704 | self.assertEqual(path, self.tmpdir) |
460 | 616 | self.assertEqual(size, 1337) | 705 | self.assertEqual(size, 1337) |
461 | 617 | 706 | ||
462 | 707 | @defer.inlineCallbacks | ||
463 | 618 | def test_item_addition(self): | 708 | def test_item_addition(self): |
468 | 619 | """Add an item (plus the default one), then remove them.""" | 709 | """Add a folder.""" |
469 | 620 | self.ui.add_folder(self.tmpdir) | 710 | self.ui.ui.folder_list.clear() |
470 | 621 | self.assertEqual(4, self.ui.ui.folder_list.topLevelItemCount()) | 711 | yield self.ui.add_folder(self.tmpdir) |
471 | 622 | 712 | self.assertEqual(1, self.ui.ui.folder_list.topLevelItemCount()) | |
472 | 713 | |||
473 | 714 | @defer.inlineCallbacks | ||
474 | 715 | def test_invalid_item_addition(self): | ||
475 | 716 | """Try to add an invalid folder.""" | ||
476 | 717 | self.ui.ui.folder_list.clear() | ||
477 | 718 | self.ui.cp_backend._is_valid = False | ||
478 | 719 | yield self.ui.add_folder(self.tmpdir) | ||
479 | 720 | self.assertEqual(0, self.ui.ui.folder_list.topLevelItemCount()) | ||
480 | 721 | |||
481 | 722 | @defer.inlineCallbacks | ||
482 | 623 | def test_total_size(self): | 723 | def test_total_size(self): |
483 | 624 | """Test that the header reflects the change in item sizes.""" | 724 | """Test that the header reflects the change in item sizes.""" |
495 | 625 | while self.ui.ui.folder_list.topLevelItemCount(): | 725 | yield self.ui.get_info() |
496 | 626 | self.ui.on_folder_list_itemClicked( | 726 | self.ui.ui.folder_list.clear() |
497 | 627 | self.ui.ui.folder_list.topLevelItem(0), 2) | 727 | self.ui.items = {} |
498 | 628 | self.assertEqual(0, self.ui.ui.folder_list.topLevelItemCount()) | 728 | item = yield self.ui.add_folder(self.tmpdir) |
499 | 629 | item = self.ui.add_folder(self.tmpdir) | 729 | item.thread.run() |
500 | 630 | item.size = 1337 | 730 | item.thread.join() |
501 | 631 | item.thread.run() | 731 | item.size = 1337 |
502 | 632 | item.thread.join() | 732 | item.setCheckState(0, QtCore.Qt.Checked) |
503 | 633 | self.ui.update_sizes() | 733 | self.ui.update_sizes() |
504 | 634 | self.assertEqual(unicode(self.ui.ui.folder_list.headerItem().text(1)), | 734 | self.assertEqual(unicode(self.ui.ui.folder_list.headerItem().text(1)), |
505 | 635 | u"Space (1337)") | 735 | u"Space (1.5 KiB)") |
506 | 736 | |||
507 | 737 | @defer.inlineCallbacks | ||
508 | 738 | def test_total_size_unchecked(self): | ||
509 | 739 | """Unchecked items use no space beyond quota_used.""" | ||
510 | 740 | yield self.ui.get_info() | ||
511 | 741 | self.ui.ui.folder_list.clear() | ||
512 | 742 | self.ui.items = {} | ||
513 | 743 | item = yield self.ui.add_folder(self.tmpdir) | ||
514 | 744 | item.thread.run() | ||
515 | 745 | item.thread.join() | ||
516 | 746 | item.size = 1337 | ||
517 | 747 | item.setCheckState(0, QtCore.Qt.Unchecked) | ||
518 | 748 | self.ui.update_sizes() | ||
519 | 749 | self.assertEqual(unicode(self.ui.ui.folder_list.headerItem().text(1)), | ||
520 | 750 | u"Space (200 bytes)") | ||
521 | 751 | |||
522 | 752 | @defer.inlineCallbacks | ||
523 | 753 | def test_total_size_udf(self): | ||
524 | 754 | """UDFs use no space beyond quota_used.""" | ||
525 | 755 | yield self.ui.get_info() | ||
526 | 756 | for _, item in self.ui.items.items(): | ||
527 | 757 | item.thread.join() | ||
528 | 758 | if item.volume_id: | ||
529 | 759 | item.setCheckState(0, QtCore.Qt.Checked) | ||
530 | 760 | else: | ||
531 | 761 | item.setCheckState(0, QtCore.Qt.Unchecked) | ||
532 | 762 | self.ui.update_sizes() | ||
533 | 763 | self.assertEqual(unicode(self.ui.ui.folder_list.headerItem().text(1)), | ||
534 | 764 | u"Space (200 bytes)") | ||
535 | 636 | 765 | ||
536 | 637 | def test_add_twice(self): | 766 | def test_add_twice(self): |
537 | 638 | """Behaviour for adding the same folder twice: | 767 | """Behaviour for adding the same folder twice: |
538 | 639 | 768 | ||
539 | 640 | * It's added only once. | 769 | * It's added only once. |
540 | 641 | """ | 770 | """ |
544 | 642 | while self.ui.ui.folder_list.topLevelItemCount(): | 771 | self.ui.ui.folder_list.clear() |
542 | 643 | self.ui.on_folder_list_itemClicked( | ||
543 | 644 | self.ui.ui.folder_list.topLevelItem(0), 2) | ||
545 | 645 | self.assertEqual(0, self.ui.ui.folder_list.topLevelItemCount()) | 772 | self.assertEqual(0, self.ui.ui.folder_list.topLevelItemCount()) |
546 | 646 | self.ui.add_folder(self.tmpdir) | 773 | self.ui.add_folder(self.tmpdir) |
547 | 647 | self.ui.add_folder(self.tmpdir) | 774 | self.ui.add_folder(self.tmpdir) |
548 | 648 | self.assertEqual(1, self.ui.ui.folder_list.topLevelItemCount()) | 775 | self.assertEqual(1, self.ui.ui.folder_list.topLevelItemCount()) |
549 | 649 | 776 | ||
550 | 777 | @defer.inlineCallbacks | ||
551 | 650 | def test_add_missing_folder(self): | 778 | def test_add_missing_folder(self): |
552 | 651 | """Behaviour for adding a folder that doesn't exist: | 779 | """Behaviour for adding a folder that doesn't exist: |
553 | 652 | 780 | ||
554 | @@ -654,11 +782,11 @@ | |||
555 | 654 | * Has size 0. | 782 | * Has size 0. |
556 | 655 | """ | 783 | """ |
557 | 656 | 784 | ||
561 | 657 | while self.ui.ui.folder_list.topLevelItemCount(): | 785 | yield self.ui.get_info() |
562 | 658 | self.ui.on_folder_list_itemClicked( | 786 | self.ui.ui.folder_list.clear() |
560 | 659 | self.ui.ui.folder_list.topLevelItem(0), 2) | ||
563 | 660 | self.assertEqual(0, self.ui.ui.folder_list.topLevelItemCount()) | 787 | self.assertEqual(0, self.ui.ui.folder_list.topLevelItemCount()) |
565 | 661 | item = self.ui.add_folder(os.path.join("xyzzy", "xyzzy", "xyzzy")) | 788 | item = yield self.ui.add_folder(os.path.join( |
566 | 789 | "xyzzy", "xyzzy", "xyzzy")) | ||
567 | 662 | self.assertEqual(1, self.ui.ui.folder_list.topLevelItemCount()) | 790 | self.assertEqual(1, self.ui.ui.folder_list.topLevelItemCount()) |
568 | 663 | item.thread.run() | 791 | item.thread.run() |
569 | 664 | item.thread.join() | 792 | item.thread.join() |
570 | @@ -672,27 +800,79 @@ | |||
571 | 672 | Push the user over quota, it should be visible. | 800 | Push the user over quota, it should be visible. |
572 | 673 | """ | 801 | """ |
573 | 674 | self.patch(self.ui.ui.offer_frame, "setVisible", self._set_called) | 802 | self.patch(self.ui.ui.offer_frame, "setVisible", self._set_called) |
578 | 675 | while self.ui.ui.folder_list.topLevelItemCount(): | 803 | yield self.ui.get_info() |
579 | 676 | self.ui.on_folder_list_itemClicked( | 804 | self.ui.ui.folder_list.clear() |
576 | 677 | self.ui.ui.folder_list.topLevelItem(0), 2) | ||
577 | 678 | self.assertEqual(0, self.ui.ui.folder_list.topLevelItemCount()) | ||
580 | 679 | self.ui.update_sizes() | 805 | self.ui.update_sizes() |
581 | 680 | self.assertEqual(self._called, ((False,), {})) | 806 | self.assertEqual(self._called, ((False,), {})) |
582 | 681 | self.ui.show_hide_offer(self.ui.quota() + 1) | 807 | self.ui.show_hide_offer(self.ui.quota() + 1) |
583 | 682 | self.assertEqual(self._called, ((True,), {})) | 808 | self.assertEqual(self._called, ((True,), {})) |
584 | 683 | 809 | ||
585 | 810 | def test_add_folder_valid(self): | ||
586 | 811 | """Test behaviour when adding a valid folder via the button.""" | ||
587 | 812 | self.patch(QtGui, "QFileDialog", FakeFileDialog()) | ||
588 | 813 | self.patch(self.ui, "add_folder", self._set_called) | ||
589 | 814 | self.ui.ui.add_folder_button.click() | ||
590 | 815 | self.assertEqual(self._called, | ||
591 | 816 | ((u'whatever',), {'validate': False, 'volume_id': False})) | ||
592 | 817 | |||
593 | 818 | def test_add_folder_invalid(self): | ||
594 | 819 | """Test behaviour when adding an invalid folder via the button.""" | ||
595 | 820 | self.patch(QtGui, "QFileDialog", FakeFileDialog()) | ||
596 | 821 | message_box = FakeMessageBox() | ||
597 | 822 | self.patch(QtGui, "QMessageBox", message_box) | ||
598 | 823 | self.patch(self.ui, "add_folder", self._set_called) | ||
599 | 824 | self.ui.cp_backend._is_valid = False | ||
600 | 825 | self.ui.ui.add_folder_button.click() | ||
601 | 826 | self.assertEqual(self._called, False) | ||
602 | 827 | user_home = os.path.expanduser('~') | ||
603 | 828 | text = local_folders.FOLDER_INVALID_PATH % { | ||
604 | 829 | 'folder_path': "whatever", | ||
605 | 830 | 'home_folder': user_home, | ||
606 | 831 | } | ||
607 | 832 | self.assertEqual(message_box._warning, | ||
608 | 833 | ((self.ui, | ||
609 | 834 | '', text, 2), {})) | ||
610 | 835 | |||
611 | 836 | @defer.inlineCallbacks | ||
612 | 837 | def test_changed_page_existing_udf_behaviour(self): | ||
613 | 838 | """If a UDF is checked, subscribe it, if not, unsubscribe it.""" | ||
614 | 839 | yield self.ui.get_info() | ||
615 | 840 | self.ui.changed_page(self.ui.wizard().SYNC_NOW_OR_LATER_PAGE) | ||
616 | 841 | self.assertEqual(self.ui.cp_backend.volume_setings_changes, | ||
617 | 842 | [('asdfgh', {'subscribed': True}), | ||
618 | 843 | ('qwerty', {'subscribed': False})]) | ||
619 | 844 | |||
620 | 845 | @defer.inlineCallbacks | ||
621 | 846 | def test_changed_page_new_udf_behaviour(self): | ||
622 | 847 | """Create UDFs for non-existing, checked UDFs.""" | ||
623 | 848 | yield self.ui.get_info() | ||
624 | 849 | self.ui.ui.folder_list.clear() | ||
625 | 850 | self.ui.items = {} | ||
626 | 851 | item = yield self.ui.add_folder("whatever") | ||
627 | 852 | item.setCheckState(0, QtCore.Qt.Checked) | ||
628 | 853 | item = yield self.ui.add_folder("whatever2") | ||
629 | 854 | item.setCheckState(0, QtCore.Qt.Unchecked) | ||
630 | 855 | self.ui.changed_page(self.ui.wizard().SYNC_NOW_OR_LATER_PAGE) | ||
631 | 856 | self.assertEqual(self.ui.cp_backend.folders_created, | ||
632 | 857 | [('whatever',)]) | ||
633 | 858 | |||
634 | 684 | def test_exception_on_account_info(self): | 859 | def test_exception_on_account_info(self): |
635 | 685 | """When account_info fails, nothing should happen.""" | 860 | """When account_info fails, nothing should happen.""" |
639 | 686 | self.patch(self.ui.ui.add_folder_button, | 861 | self.patch(self.ui, |
640 | 687 | "backend", FakeFailingCPBackend()) | 862 | "cp_backend", FakeFailingCPBackend()) |
641 | 688 | self.ui.show_hide_offer(1000) | 863 | self.ui.get_info() |
642 | 689 | # Still here | 864 | # Still here |
643 | 690 | 865 | ||
644 | 691 | def test_timer_is_started(self): | 866 | def test_timer_is_started(self): |
646 | 692 | """ When displaying the page, the timer should start.""" | 867 | """When displaying the page, the timer should start.""" |
647 | 693 | self.ui.initializePage() | 868 | self.ui.initializePage() |
648 | 694 | self.assertTrue(self.ui.timer.isActive()) | 869 | self.assertTrue(self.ui.timer.isActive()) |
649 | 695 | 870 | ||
650 | 871 | def test_timer_is_stopped(self): | ||
651 | 872 | """When leaving the page, the timer should stop.""" | ||
652 | 873 | self.ui.changed_page(-1) | ||
653 | 874 | self.assertFalse(self.ui.timer.isActive()) | ||
654 | 875 | |||
655 | 696 | 876 | ||
656 | 697 | class SetupAccountTestCase(BaseTestCase): | 877 | class SetupAccountTestCase(BaseTestCase): |
657 | 698 | """Test the SetupAccountPage code.""" | 878 | """Test the SetupAccountPage code.""" |