Merge lp:~dobey/ubuntu/natty/ubuntuone-control-panel/release-0-8-3 into lp:ubuntu/natty/ubuntuone-control-panel
- Natty (11.04)
- release-0-8-3
- Merge into natty
Proposed by
dobey
Status: | Merged |
---|---|
Merged at revision: | 12 |
Proposed branch: | lp:~dobey/ubuntu/natty/ubuntuone-control-panel/release-0-8-3 |
Merge into: | lp:ubuntu/natty/ubuntuone-control-panel |
Diff against target: |
2036 lines (+930/-264) 19 files modified
PKG-INFO (+23/-1) data/device.ui (+1/-1) data/management.ui (+3/-29) data/overview.ui (+235/-68) data/volumes.ui (+22/-19) debian/changelog (+17/-0) debian/python-ubuntuone-control-panel.install (+4/-1) debian/ubuntuone-control-panel-gtk.install (+2/-1) setup.py (+2/-1) ubuntuone-control-panel-gtk.desktop.in (+1/-0) ubuntuone.controlpanel.pth (+2/-0) ubuntuone/controlpanel/backend.py (+56/-8) ubuntuone/controlpanel/dbus_client.py (+43/-2) ubuntuone/controlpanel/gtk/gui.py (+60/-61) ubuntuone/controlpanel/gtk/tests/__init__.py (+2/-2) ubuntuone/controlpanel/gtk/tests/test_gui.py (+47/-41) ubuntuone/controlpanel/integrationtests/test_dbus_client_sd.py (+184/-4) ubuntuone/controlpanel/tests/__init__.py (+79/-3) ubuntuone/controlpanel/tests/test_backend.py (+147/-22) |
To merge this branch: | bzr merge lp:~dobey/ubuntu/natty/ubuntuone-control-panel/release-0-8-3 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ken VanDine | Approve | ||
Review via email: mp+49491@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'PKG-INFO' |
2 | --- PKG-INFO 2011-02-04 20:02:28 +0000 |
3 | +++ PKG-INFO 2011-02-12 03:22:55 +0000 |
4 | @@ -1,6 +1,6 @@ |
5 | Metadata-Version: 1.1 |
6 | Name: ubuntuone-control-panel |
7 | -Version: 0.8.2 |
8 | +Version: 0.8.3 |
9 | Summary: Ubuntu One Control Panel |
10 | Home-page: https://launchpad.net/ubuntuone-control-panel |
11 | Author: Natalia Bidart |
12 | @@ -8,4 +8,26 @@ |
13 | License: GPL v3 |
14 | Description: Application to manage a Ubuntu One account. Provides aDBus service to query/modify all the Ubuntu One bits. |
15 | Platform: UNKNOWN |
16 | +Requires: apt |
17 | +Requires: aptdaemon |
18 | +Requires: dbus |
19 | +Requires: defer |
20 | +Requires: desktopcouch.application.replication_services |
21 | +Requires: gi.repository |
22 | +Requires: gobject |
23 | +Requires: gtk |
24 | +Requires: mocker |
25 | +Requires: oauth |
26 | +Requires: pango |
27 | +Requires: simplejson |
28 | +Requires: twisted.application |
29 | +Requires: twisted.internet |
30 | +Requires: twisted.python.failure |
31 | +Requires: twisted.web |
32 | +Requires: ubuntu_sso |
33 | +Requires: ubuntuone.clientdefs |
34 | +Requires: ubuntuone.devtools.handlers |
35 | +Requires: ubuntuone.devtools.testcase |
36 | +Requires: ubuntuone.logger |
37 | +Requires: ubuntuone.platform.linux |
38 | Provides: ubuntuone.controlpanel |
39 | |
40 | === added file 'data/contacts.png' |
41 | Binary files data/contacts.png 1970-01-01 00:00:00 +0000 and data/contacts.png 2011-02-12 03:22:55 +0000 differ |
42 | === modified file 'data/device.ui' |
43 | --- data/device.ui 2011-01-25 19:08:59 +0000 |
44 | +++ data/device.ui 2011-02-12 03:22:55 +0000 |
45 | @@ -154,7 +154,7 @@ |
46 | <child> |
47 | <object class="GtkVButtonBox" id="vbuttonbox1"> |
48 | <property name="visible">True</property> |
49 | - <property name="layout_style">center</property> |
50 | + <property name="layout_style">start</property> |
51 | <child> |
52 | <object class="GtkButton" id="remove"> |
53 | <property name="label">gtk-remove</property> |
54 | |
55 | === added file 'data/files.png' |
56 | Binary files data/files.png 1970-01-01 00:00:00 +0000 and data/files.png 2011-02-12 03:22:55 +0000 differ |
57 | === modified file 'data/management.ui' |
58 | --- data/management.ui 2011-01-25 19:08:59 +0000 |
59 | +++ data/management.ui 2011-02-12 03:22:55 +0000 |
60 | @@ -20,25 +20,14 @@ |
61 | <property name="border_width">10</property> |
62 | <property name="spacing">10</property> |
63 | <child> |
64 | - <object class="GtkHBox" id="quota_box"> |
65 | + <object class="GtkVBox" id="quota_box"> |
66 | <property name="visible">True</property> |
67 | - <property name="can_focus">False</property> |
68 | + <property name="spacing">5</property> |
69 | <child> |
70 | - <object class="GtkAlignment" id="alignment1"> |
71 | + <object class="GtkProgressBar" id="quota_progressbar"> |
72 | <property name="visible">True</property> |
73 | - <property name="can_focus">False</property> |
74 | - <property name="xscale">0</property> |
75 | - <property name="yscale">0</property> |
76 | - <child> |
77 | - <object class="GtkProgressBar" id="quota_progressbar"> |
78 | - <property name="visible">True</property> |
79 | - <property name="can_focus">False</property> |
80 | - </object> |
81 | - </child> |
82 | </object> |
83 | <packing> |
84 | - <property name="expand">True</property> |
85 | - <property name="fill">True</property> |
86 | <property name="position">0</property> |
87 | </packing> |
88 | </child> |
89 | @@ -117,7 +106,6 @@ |
90 | <property name="label" translatable="yes">Shares</property> |
91 | <property name="can_focus">True</property> |
92 | <property name="receives_default">False</property> |
93 | - <property name="use_action_appearance">False</property> |
94 | <property name="draw_indicator">False</property> |
95 | <property name="group">dashboard_button</property> |
96 | </object> |
97 | @@ -133,7 +121,6 @@ |
98 | <property name="visible">True</property> |
99 | <property name="can_focus">True</property> |
100 | <property name="receives_default">False</property> |
101 | - <property name="use_action_appearance">False</property> |
102 | <property name="draw_indicator">False</property> |
103 | <property name="group">dashboard_button</property> |
104 | </object> |
105 | @@ -148,7 +135,6 @@ |
106 | <property name="label" translatable="yes">Services</property> |
107 | <property name="can_focus">True</property> |
108 | <property name="receives_default">False</property> |
109 | - <property name="use_action_appearance">False</property> |
110 | <property name="draw_indicator">False</property> |
111 | <property name="group">dashboard_button</property> |
112 | </object> |
113 | @@ -161,44 +147,36 @@ |
114 | </object> |
115 | <packing> |
116 | <property name="expand">False</property> |
117 | - <property name="fill">True</property> |
118 | <property name="position">1</property> |
119 | </packing> |
120 | </child> |
121 | <child> |
122 | <object class="GtkHSeparator" id="hseparator2"> |
123 | <property name="visible">True</property> |
124 | - <property name="can_focus">False</property> |
125 | </object> |
126 | <packing> |
127 | - <property name="expand">True</property> |
128 | - <property name="fill">True</property> |
129 | <property name="position">2</property> |
130 | </packing> |
131 | </child> |
132 | </object> |
133 | <packing> |
134 | <property name="expand">False</property> |
135 | - <property name="fill">True</property> |
136 | <property name="position">0</property> |
137 | </packing> |
138 | </child> |
139 | <child> |
140 | <object class="GtkImage" id="image1"> |
141 | <property name="visible">True</property> |
142 | - <property name="can_focus">False</property> |
143 | <property name="pixbuf">banner.png</property> |
144 | </object> |
145 | <packing> |
146 | <property name="expand">False</property> |
147 | - <property name="fill">True</property> |
148 | <property name="position">1</property> |
149 | </packing> |
150 | </child> |
151 | </object> |
152 | <packing> |
153 | <property name="expand">False</property> |
154 | - <property name="fill">True</property> |
155 | <property name="position">1</property> |
156 | </packing> |
157 | </child> |
158 | @@ -207,21 +185,17 @@ |
159 | </object> |
160 | <packing> |
161 | <property name="expand">False</property> |
162 | - <property name="fill">True</property> |
163 | <property name="position">0</property> |
164 | </packing> |
165 | </child> |
166 | <child> |
167 | <object class="GtkNotebook" id="notebook"> |
168 | <property name="visible">True</property> |
169 | - <property name="can_focus">False</property> |
170 | <property name="show_tabs">False</property> |
171 | <property name="show_border">False</property> |
172 | <property name="homogeneous">True</property> |
173 | </object> |
174 | <packing> |
175 | - <property name="expand">True</property> |
176 | - <property name="fill">True</property> |
177 | <property name="position">1</property> |
178 | </packing> |
179 | </child> |
180 | |
181 | === added file 'data/music.png' |
182 | Binary files data/music.png 1970-01-01 00:00:00 +0000 and data/music.png 2011-02-12 03:22:55 +0000 differ |
183 | === added file 'data/notes.png' |
184 | Binary files data/notes.png 1970-01-01 00:00:00 +0000 and data/notes.png 2011-02-12 03:22:55 +0000 differ |
185 | === modified file 'data/overview.ui' |
186 | --- data/overview.ui 2011-01-25 19:08:59 +0000 |
187 | +++ data/overview.ui 2011-02-12 03:22:55 +0000 |
188 | @@ -4,74 +4,263 @@ |
189 | <!-- interface-naming-policy project-wide --> |
190 | <object class="GtkVBox" id="itself"> |
191 | <property name="visible">True</property> |
192 | + <property name="can_focus">False</property> |
193 | <child> |
194 | - <object class="GtkImage" id="image"> |
195 | + <object class="GtkEventBox" id="header"> |
196 | <property name="visible">True</property> |
197 | - <property name="pixbuf">overview.png</property> |
198 | + <property name="can_focus">False</property> |
199 | + <child> |
200 | + <object class="GtkImage" id="image"> |
201 | + <property name="visible">True</property> |
202 | + <property name="can_focus">False</property> |
203 | + <property name="pixbuf">overview.png</property> |
204 | + </object> |
205 | + </child> |
206 | </object> |
207 | <packing> |
208 | <property name="expand">False</property> |
209 | + <property name="fill">True</property> |
210 | <property name="position">0</property> |
211 | </packing> |
212 | </child> |
213 | <child> |
214 | - <object class="GtkAlignment" id="alignment1"> |
215 | + <object class="GtkScrolledWindow" id="scrolledwindow1"> |
216 | <property name="visible">True</property> |
217 | - <property name="yscale">0</property> |
218 | + <property name="can_focus">True</property> |
219 | + <property name="hscrollbar_policy">automatic</property> |
220 | + <property name="vscrollbar_policy">automatic</property> |
221 | <child> |
222 | - <object class="GtkHBox" id="hbox1"> |
223 | + <object class="GtkViewport" id="viewport1"> |
224 | <property name="visible">True</property> |
225 | - <property name="border_width">20</property> |
226 | - <property name="spacing">5</property> |
227 | + <property name="can_focus">False</property> |
228 | + <property name="shadow_type">none</property> |
229 | <child> |
230 | - <object class="GtkVBox" id="messages"> |
231 | + <object class="GtkHBox" id="hbox1"> |
232 | <property name="visible">True</property> |
233 | - <property name="spacing">10</property> |
234 | + <property name="can_focus">False</property> |
235 | + <property name="border_width">20</property> |
236 | + <property name="spacing">5</property> |
237 | <child> |
238 | - <placeholder/> |
239 | + <object class="GtkTable" id="table1"> |
240 | + <property name="visible">True</property> |
241 | + <property name="can_focus">False</property> |
242 | + <property name="n_rows">4</property> |
243 | + <property name="n_columns">2</property> |
244 | + <property name="column_spacing">10</property> |
245 | + <property name="row_spacing">10</property> |
246 | + <child> |
247 | + <object class="GtkImage" id="image1"> |
248 | + <property name="visible">True</property> |
249 | + <property name="can_focus">False</property> |
250 | + <property name="pixbuf">files.png</property> |
251 | + </object> |
252 | + <packing> |
253 | + <property name="x_options">GTK_FILL</property> |
254 | + <property name="y_options">GTK_FILL</property> |
255 | + </packing> |
256 | + </child> |
257 | + <child> |
258 | + <object class="GtkImage" id="image2"> |
259 | + <property name="visible">True</property> |
260 | + <property name="can_focus">False</property> |
261 | + <property name="pixbuf">contacts.png</property> |
262 | + </object> |
263 | + <packing> |
264 | + <property name="top_attach">1</property> |
265 | + <property name="bottom_attach">2</property> |
266 | + <property name="x_options">GTK_FILL</property> |
267 | + <property name="y_options">GTK_FILL</property> |
268 | + </packing> |
269 | + </child> |
270 | + <child> |
271 | + <object class="GtkImage" id="image3"> |
272 | + <property name="visible">True</property> |
273 | + <property name="can_focus">False</property> |
274 | + <property name="pixbuf">music.png</property> |
275 | + </object> |
276 | + <packing> |
277 | + <property name="top_attach">2</property> |
278 | + <property name="bottom_attach">3</property> |
279 | + <property name="x_options">GTK_FILL</property> |
280 | + <property name="y_options">GTK_FILL</property> |
281 | + </packing> |
282 | + </child> |
283 | + <child> |
284 | + <object class="GtkImage" id="image4"> |
285 | + <property name="visible">True</property> |
286 | + <property name="can_focus">False</property> |
287 | + <property name="pixbuf">notes.png</property> |
288 | + </object> |
289 | + <packing> |
290 | + <property name="top_attach">3</property> |
291 | + <property name="bottom_attach">4</property> |
292 | + <property name="x_options">GTK_FILL</property> |
293 | + <property name="y_options">GTK_FILL</property> |
294 | + </packing> |
295 | + </child> |
296 | + <child> |
297 | + <object class="GtkLabel" id="label3"> |
298 | + <property name="visible">True</property> |
299 | + <property name="can_focus">False</property> |
300 | + <property name="xalign">0</property> |
301 | + <property name="label" translatable="yes">Files Anywhere |
302 | +<span foreground="#909090">Backup and access your files from Windows, Ubuntu, or Mobile</span></property> |
303 | + <property name="use_markup">True</property> |
304 | + <property name="wrap">True</property> |
305 | + </object> |
306 | + <packing> |
307 | + <property name="left_attach">1</property> |
308 | + <property name="right_attach">2</property> |
309 | + </packing> |
310 | + </child> |
311 | + <child> |
312 | + <object class="GtkLabel" id="label4"> |
313 | + <property name="visible">True</property> |
314 | + <property name="can_focus">False</property> |
315 | + <property name="xalign">0</property> |
316 | + <property name="label" translatable="yes">Keep connected |
317 | +<span foreground="#909090">Unify your contacts accress Desktop, Mobile and Web</span></property> |
318 | + <property name="use_markup">True</property> |
319 | + <property name="wrap">True</property> |
320 | + </object> |
321 | + <packing> |
322 | + <property name="left_attach">1</property> |
323 | + <property name="right_attach">2</property> |
324 | + <property name="top_attach">1</property> |
325 | + <property name="bottom_attach">2</property> |
326 | + </packing> |
327 | + </child> |
328 | + <child> |
329 | + <object class="GtkLabel" id="label5"> |
330 | + <property name="visible">True</property> |
331 | + <property name="can_focus">False</property> |
332 | + <property name="xalign">0</property> |
333 | + <property name="label" translatable="yes">Rock Out |
334 | +<span foreground="#909090">Your library at your fingertips with Android, iPhone, and AirPlay |
335 | +Plus the Ubuntu One Music store to grow your collection</span></property> |
336 | + <property name="use_markup">True</property> |
337 | + <property name="wrap">True</property> |
338 | + </object> |
339 | + <packing> |
340 | + <property name="left_attach">1</property> |
341 | + <property name="right_attach">2</property> |
342 | + <property name="top_attach">2</property> |
343 | + <property name="bottom_attach">3</property> |
344 | + </packing> |
345 | + </child> |
346 | + <child> |
347 | + <object class="GtkLabel" id="label6"> |
348 | + <property name="visible">True</property> |
349 | + <property name="can_focus">False</property> |
350 | + <property name="xalign">0</property> |
351 | + <property name="label" translatable="yes">Stay Productive |
352 | +<span foreground="#909090">Keep your Firefox bookmarks and Tomboy notes synced</span></property> |
353 | + <property name="use_markup">True</property> |
354 | + <property name="wrap">True</property> |
355 | + </object> |
356 | + <packing> |
357 | + <property name="left_attach">1</property> |
358 | + <property name="right_attach">2</property> |
359 | + <property name="top_attach">3</property> |
360 | + <property name="bottom_attach">4</property> |
361 | + </packing> |
362 | + </child> |
363 | + </object> |
364 | + <packing> |
365 | + <property name="expand">True</property> |
366 | + <property name="fill">True</property> |
367 | + <property name="position">0</property> |
368 | + </packing> |
369 | </child> |
370 | - </object> |
371 | - <packing> |
372 | - <property name="position">0</property> |
373 | - </packing> |
374 | - </child> |
375 | - <child> |
376 | - <object class="GtkAlignment" id="alignment2"> |
377 | - <property name="visible">True</property> |
378 | - <property name="xscale">0</property> |
379 | - <property name="yscale">0</property> |
380 | <child> |
381 | - <object class="GtkVBox" id="vbox2"> |
382 | + <object class="GtkVBox" id="vbox1"> |
383 | <property name="visible">True</property> |
384 | - <property name="spacing">10</property> |
385 | - <child> |
386 | - <object class="GtkButton" id="join_now_button"> |
387 | - <property name="visible">True</property> |
388 | - <property name="can_focus">True</property> |
389 | - <property name="can_default">True</property> |
390 | - <property name="receives_default">True</property> |
391 | - <signal name="clicked" handler="on_join_now_button_clicked"/> |
392 | + <property name="can_focus">False</property> |
393 | + <child> |
394 | + <object class="GtkLabel" id="warning_label"> |
395 | + <property name="visible">True</property> |
396 | + <property name="can_focus">False</property> |
397 | + <property name="label">A warning label that can be long. Possible really long, let's see how it behaves.</property> |
398 | + <property name="wrap">True</property> |
399 | + </object> |
400 | + <packing> |
401 | + <property name="expand">False</property> |
402 | + <property name="fill">True</property> |
403 | + <property name="position">0</property> |
404 | + </packing> |
405 | + </child> |
406 | + <child> |
407 | + <object class="GtkAlignment" id="alignment2"> |
408 | + <property name="visible">True</property> |
409 | + <property name="can_focus">False</property> |
410 | + <property name="xscale">0</property> |
411 | + <property name="yscale">0</property> |
412 | <child> |
413 | - <object class="GtkVBox" id="vbox1"> |
414 | + <object class="GtkVBox" id="vbox2"> |
415 | <property name="visible">True</property> |
416 | - <property name="spacing">5</property> |
417 | + <property name="can_focus">False</property> |
418 | + <property name="spacing">10</property> |
419 | <child> |
420 | - <object class="GtkLabel" id="label1"> |
421 | + <object class="GtkButton" id="join_now_button"> |
422 | <property name="visible">True</property> |
423 | - <property name="label" translatable="yes"><span font_size="xx-large">Join now</span></property> |
424 | - <property name="use_markup">True</property> |
425 | + <property name="can_focus">True</property> |
426 | + <property name="can_default">True</property> |
427 | + <property name="receives_default">True</property> |
428 | + <property name="use_action_appearance">False</property> |
429 | + <signal name="clicked" handler="on_join_now_button_clicked" swapped="no"/> |
430 | + <child> |
431 | + <object class="GtkVBox" id="vbox3"> |
432 | + <property name="visible">True</property> |
433 | + <property name="can_focus">False</property> |
434 | + <property name="spacing">5</property> |
435 | + <child> |
436 | + <object class="GtkLabel" id="label1"> |
437 | + <property name="visible">True</property> |
438 | + <property name="can_focus">False</property> |
439 | + <property name="label" translatable="yes"><span font_size="xx-large">Join now</span></property> |
440 | + <property name="use_markup">True</property> |
441 | + </object> |
442 | + <packing> |
443 | + <property name="expand">True</property> |
444 | + <property name="fill">True</property> |
445 | + <property name="position">0</property> |
446 | + </packing> |
447 | + </child> |
448 | + <child> |
449 | + <object class="GtkLabel" id="label2"> |
450 | + <property name="visible">True</property> |
451 | + <property name="can_focus">False</property> |
452 | + <property name="label" translatable="yes"><span foreground="#909090">2GB of free storage</span></property> |
453 | + <property name="use_markup">True</property> |
454 | + </object> |
455 | + <packing> |
456 | + <property name="expand">True</property> |
457 | + <property name="fill">True</property> |
458 | + <property name="position">1</property> |
459 | + </packing> |
460 | + </child> |
461 | + </object> |
462 | + </child> |
463 | </object> |
464 | <packing> |
465 | + <property name="expand">False</property> |
466 | + <property name="fill">True</property> |
467 | <property name="position">0</property> |
468 | </packing> |
469 | </child> |
470 | <child> |
471 | - <object class="GtkLabel" id="label2"> |
472 | + <object class="GtkLinkButton" id="connect_button"> |
473 | + <property name="label" translatable="yes">I already have an account!</property> |
474 | <property name="visible">True</property> |
475 | - <property name="label" translatable="yes"><span foreground="grey">2GB of free storage</span></property> |
476 | - <property name="use_markup">True</property> |
477 | + <property name="can_focus">True</property> |
478 | + <property name="receives_default">True</property> |
479 | + <property name="use_action_appearance">False</property> |
480 | + <property name="relief">none</property> |
481 | + <signal name="clicked" handler="on_connect_button_clicked" swapped="no"/> |
482 | </object> |
483 | <packing> |
484 | + <property name="expand">False</property> |
485 | + <property name="fill">False</property> |
486 | <property name="position">1</property> |
487 | </packing> |
488 | </child> |
489 | @@ -79,50 +268,28 @@ |
490 | </child> |
491 | </object> |
492 | <packing> |
493 | - <property name="expand">False</property> |
494 | - <property name="position">0</property> |
495 | - </packing> |
496 | - </child> |
497 | - <child> |
498 | - <object class="GtkLinkButton" id="connect_button"> |
499 | - <property name="label" translatable="yes">I already have an account!</property> |
500 | - <property name="visible">True</property> |
501 | - <property name="can_focus">True</property> |
502 | - <property name="receives_default">True</property> |
503 | - <property name="relief">none</property> |
504 | - <signal name="clicked" handler="on_connect_button_clicked"/> |
505 | - </object> |
506 | - <packing> |
507 | - <property name="expand">False</property> |
508 | - <property name="fill">False</property> |
509 | + <property name="expand">True</property> |
510 | + <property name="fill">True</property> |
511 | <property name="position">1</property> |
512 | </packing> |
513 | </child> |
514 | </object> |
515 | + <packing> |
516 | + <property name="expand">False</property> |
517 | + <property name="fill">True</property> |
518 | + <property name="position">1</property> |
519 | + </packing> |
520 | </child> |
521 | </object> |
522 | - <packing> |
523 | - <property name="expand">False</property> |
524 | - <property name="position">1</property> |
525 | - </packing> |
526 | </child> |
527 | </object> |
528 | </child> |
529 | </object> |
530 | <packing> |
531 | + <property name="expand">True</property> |
532 | + <property name="fill">True</property> |
533 | <property name="position">1</property> |
534 | </packing> |
535 | </child> |
536 | - <child> |
537 | - <object class="GtkLabel" id="warning_label"> |
538 | - <property name="visible">True</property> |
539 | - <property name="wrap">True</property> |
540 | - <property name="ellipsize">end</property> |
541 | - </object> |
542 | - <packing> |
543 | - <property name="expand">False</property> |
544 | - <property name="position">2</property> |
545 | - </packing> |
546 | - </child> |
547 | </object> |
548 | </interface> |
549 | |
550 | === modified file 'data/volumes.ui' |
551 | --- data/volumes.ui 2011-01-25 19:08:59 +0000 |
552 | +++ data/volumes.ui 2011-02-12 03:22:55 +0000 |
553 | @@ -2,6 +2,26 @@ |
554 | <interface> |
555 | <requires lib="gtk+" version="2.22"/> |
556 | <!-- interface-naming-policy project-wide --> |
557 | + <object class="GtkTreeStore" id="volumes_store"> |
558 | + <columns> |
559 | + <!-- column-name description --> |
560 | + <column type="gchararray"/> |
561 | + <!-- column-name subscribed --> |
562 | + <column type="gboolean"/> |
563 | + <!-- column-name icon-name --> |
564 | + <column type="gchararray"/> |
565 | + <!-- column-name subscribed-visible --> |
566 | + <column type="gboolean"/> |
567 | + <!-- column-name subscribed-sensitive --> |
568 | + <column type="gboolean"/> |
569 | + <!-- column-name icon-size --> |
570 | + <column type="gint"/> |
571 | + <!-- column-name identifier --> |
572 | + <column type="gchararray"/> |
573 | + <!-- column-name path --> |
574 | + <column type="gchararray"/> |
575 | + </columns> |
576 | + </object> |
577 | <object class="GtkAlignment" id="itself"> |
578 | <property name="visible">True</property> |
579 | <property name="can_focus">False</property> |
580 | @@ -18,6 +38,7 @@ |
581 | <property name="model">volumes_store</property> |
582 | <property name="rules_hint">True</property> |
583 | <property name="tooltip_column">0</property> |
584 | + <signal name="row-activated" handler="on_volumes_view_row_activated" swapped="no"/> |
585 | <child> |
586 | <object class="GtkTreeViewColumn" id="treeviewcolumn2"> |
587 | <property name="resizable">True</property> |
588 | @@ -46,7 +67,7 @@ |
589 | <child> |
590 | <object class="GtkTreeViewColumn" id="treeviewcolumn3"> |
591 | <property name="sizing">autosize</property> |
592 | - <property name="title">On this device?</property> |
593 | + <property name="title" translatable="yes">Sync locally?</property> |
594 | <child> |
595 | <object class="GtkCellRendererToggle" id="cellrenderertoggle1"> |
596 | <property name="indicator_size">15</property> |
597 | @@ -73,22 +94,4 @@ |
598 | </object> |
599 | </child> |
600 | </object> |
601 | - <object class="GtkTreeStore" id="volumes_store"> |
602 | - <columns> |
603 | - <!-- column-name description --> |
604 | - <column type="gchararray"/> |
605 | - <!-- column-name subscribed --> |
606 | - <column type="gboolean"/> |
607 | - <!-- column-name icon-name --> |
608 | - <column type="gchararray"/> |
609 | - <!-- column-name subscribed-visible --> |
610 | - <column type="gboolean"/> |
611 | - <!-- column-name subscribed-sensitive --> |
612 | - <column type="gboolean"/> |
613 | - <!-- column-name icon-size --> |
614 | - <column type="gint"/> |
615 | - <!-- column-name identifier --> |
616 | - <column type="gchararray"/> |
617 | - </columns> |
618 | - </object> |
619 | </interface> |
620 | |
621 | === modified file 'debian/changelog' |
622 | --- debian/changelog 2011-02-07 07:55:07 +0000 |
623 | +++ debian/changelog 2011-02-12 03:22:55 +0000 |
624 | @@ -1,3 +1,20 @@ |
625 | +ubuntuone-control-panel (0.8.3-0ubuntu1) natty; urgency=low |
626 | + |
627 | + * New upstream release. |
628 | + - Support share subscription in Folders tab (LP: #714583) |
629 | + - Use more concise text on overview page (LP: #715732) |
630 | + - Set widget names to style properly (LP: #716678) |
631 | + - Place usage bar label on top (LP: #715713) |
632 | + - Inconsistent placement of 'remove' button (LP: #715804) |
633 | + - Message text should say 'your personal cloud' (LP: #715858) |
634 | + - Shares to me path looks awful (LP: #716431) |
635 | + - Clicking a folder should open it (LP: #716499) |
636 | + - Subtext in 'join now' button is hard to read (LP: #716504) |
637 | + - "On this device?" string is inconsistent with its action (LP: #717159) |
638 | + - Devices don't necessarily have a sync service (LP: #717230) |
639 | + |
640 | + -- Rodney Dawes <rodney.dawes@ubuntu.com> Fri, 11 Feb 2011 22:07:23 -0500 |
641 | + |
642 | ubuntuone-control-panel (0.8.2-0ubuntu1) natty; urgency=low |
643 | |
644 | * New upstream release: |
645 | |
646 | === modified file 'debian/python-ubuntuone-control-panel.install' |
647 | --- debian/python-ubuntuone-control-panel.install 2010-12-06 12:27:11 +0000 |
648 | +++ debian/python-ubuntuone-control-panel.install 2011-02-12 03:22:55 +0000 |
649 | @@ -1,1 +1,4 @@ |
650 | -debian/tmp/usr/lib/python2.*/*-packages/ubuntuone/controlpanel/*.py |
651 | +debian/tmp/usr/lib/python2.*/*-packages/*.pth |
652 | +debian/tmp/usr/lib/python2.*/*-packages/*/ubuntuone/__init__.py |
653 | +debian/tmp/usr/lib/python2.*/*-packages/*/ubuntuone/controlpanel/*.py |
654 | + |
655 | |
656 | === modified file 'debian/ubuntuone-control-panel-gtk.install' |
657 | --- debian/ubuntuone-control-panel-gtk.install 2011-01-27 22:13:48 +0000 |
658 | +++ debian/ubuntuone-control-panel-gtk.install 2011-02-12 03:22:55 +0000 |
659 | @@ -4,4 +4,5 @@ |
660 | debian/tmp/usr/share/ubuntuone-control-panel/*.ui |
661 | debian/tmp/usr/share/ubuntuone-control-panel/*.png |
662 | debian/tmp/usr/share/man/man1/ubuntuone-control-panel-gtk.* |
663 | -debian/tmp/usr/lib/python2.*/*-packages/ubuntuone/controlpanel/gtk/*.py |
664 | +debian/tmp/usr/lib/python2.*/*-packages/*/ubuntuone/controlpanel/gtk |
665 | + |
666 | |
667 | === modified file 'setup.py' |
668 | --- setup.py 2011-02-04 20:02:28 +0000 |
669 | +++ setup.py 2011-02-12 03:22:55 +0000 |
670 | @@ -76,7 +76,7 @@ |
671 | |
672 | DistUtilsExtra.auto.setup( |
673 | name='ubuntuone-control-panel', |
674 | - version='0.8.2', |
675 | + version='0.8.3', |
676 | license='GPL v3', |
677 | author='Natalia Bidart', |
678 | author_email='natalia.bidart@canonical.com', |
679 | @@ -85,6 +85,7 @@ |
680 | 'DBus service to query/modify all the Ubuntu One bits.', |
681 | url='https://launchpad.net/ubuntuone-control-panel', |
682 | packages=['ubuntuone.controlpanel', 'ubuntuone.controlpanel.gtk'], |
683 | + extra_path='ubuntuone-control-panel', |
684 | data_files=[ |
685 | ('lib/ubuntuone-control-panel', |
686 | ['bin/ubuntuone-control-panel-backend']), |
687 | |
688 | === modified file 'ubuntuone-control-panel-gtk.desktop.in' |
689 | --- ubuntuone-control-panel-gtk.desktop.in 2011-01-07 20:07:39 +0000 |
690 | +++ ubuntuone-control-panel-gtk.desktop.in 2011-02-12 03:22:55 +0000 |
691 | @@ -8,6 +8,7 @@ |
692 | StartupNotify=true |
693 | Categories=GNOME;GTK;Settings; |
694 | X-Ayatana-Desktop-Shortcuts=U1 |
695 | +X-Ayatana-Appmenu-Show-Stubs=False |
696 | |
697 | [U1 Shortcut Group] |
698 | Name=Ubuntu One |
699 | |
700 | === added file 'ubuntuone.controlpanel.pth' |
701 | --- ubuntuone.controlpanel.pth 1970-01-01 00:00:00 +0000 |
702 | +++ ubuntuone.controlpanel.pth 2011-02-12 03:22:55 +0000 |
703 | @@ -0,0 +1,2 @@ |
704 | +ubuntuone-control-panel |
705 | + |
706 | |
707 | === modified file 'ubuntuone/controlpanel/backend.py' |
708 | --- ubuntuone/controlpanel/backend.py 2011-01-25 19:08:59 +0000 |
709 | +++ ubuntuone/controlpanel/backend.py 2011-02-12 03:22:55 +0000 |
710 | @@ -19,6 +19,8 @@ |
711 | |
712 | """A backend for the Ubuntu One Control Panel.""" |
713 | |
714 | +from collections import defaultdict |
715 | + |
716 | from twisted.internet.defer import inlineCallbacks, returnValue |
717 | |
718 | from ubuntuone.controlpanel import dbus_client |
719 | @@ -62,12 +64,19 @@ |
720 | class ControlBackend(object): |
721 | """The control panel backend.""" |
722 | |
723 | + ROOT_TYPE = u'ROOT' |
724 | + FOLDER_TYPE = u'UDF' |
725 | + SHARE_TYPE = u'SHARE' |
726 | + NAME_NOT_SET = u'ENAMENOTSET' |
727 | + |
728 | def __init__(self): |
729 | """Initialize the webclient.""" |
730 | self.wc = WebClient(dbus_client.get_credentials) |
731 | self._status_changed_handler = None |
732 | self.status_changed_handler = lambda *a: None |
733 | |
734 | + self._volumes = {} # cache last known volume info |
735 | + |
736 | def _process_file_sync_status(self, status): |
737 | """Process raw file sync status into custom format. |
738 | |
739 | @@ -328,17 +337,54 @@ |
740 | @inlineCallbacks |
741 | def volumes_info(self): |
742 | """Get the volumes info.""" |
743 | + self._volumes = {} |
744 | + |
745 | account = yield self.account_info() |
746 | root_dir = yield dbus_client.get_root_dir() |
747 | + shares_dir = yield dbus_client.get_shares_dir() |
748 | + shares_dir_link = yield dbus_client.get_shares_dir_link() |
749 | folders = yield dbus_client.get_folders() |
750 | + shares = yield dbus_client.get_shares() |
751 | |
752 | free_bytes = int(account['quota_total']) - int(account['quota_used']) |
753 | root_volume = {u'volume_id': u'', u'path': root_dir, |
754 | - u'subscribed': 'True', u'type': u'ROOT'} |
755 | + u'subscribed': 'True', u'type': self.ROOT_TYPE} |
756 | + self._volumes[u''] = root_volume |
757 | + |
758 | + # group shares by the offering user |
759 | + shares_result = defaultdict(list) |
760 | + for share in shares: |
761 | + share[u'type'] = self.SHARE_TYPE |
762 | + |
763 | + vid = share['volume_id'] |
764 | + assert vid not in self._volumes |
765 | + self._volumes[vid] = share |
766 | + |
767 | + if not bool(share['accepted']): |
768 | + continue |
769 | + |
770 | + nicer_path = share[u'path'].replace(shares_dir, shares_dir_link) |
771 | + share[u'path'] = nicer_path |
772 | + |
773 | + username = share['other_visible_name'] |
774 | + if not username: |
775 | + username = u'%s (%s)' % (share['other_username'], |
776 | + self.NAME_NOT_SET) |
777 | + |
778 | + shares_result[username].append(share) |
779 | + |
780 | + for folder in folders: |
781 | + folder[u'type'] = self.FOLDER_TYPE |
782 | + |
783 | + vid = folder['volume_id'] |
784 | + assert vid not in self._volumes |
785 | + self._volumes[vid] = folder |
786 | |
787 | result = [(u'', unicode(free_bytes), [root_volume] + folders)] |
788 | |
789 | - # later, we may request shares info as well |
790 | + for other_username, shares in shares_result.iteritems(): |
791 | + result.append((other_username, shares[0][u'free_bytes'], shares)) |
792 | + |
793 | returnValue(result) |
794 | |
795 | @log_call(logger.debug) |
796 | @@ -359,16 +405,18 @@ |
797 | @inlineCallbacks |
798 | def subscribe_volume(self, volume_id): |
799 | """Subscribe to 'volume_id'.""" |
800 | - # when dealing with shares, we need to check if 'volume_id' |
801 | - # is a folder or a share |
802 | - yield dbus_client.subscribe_folder(volume_id) |
803 | + if self._volumes[volume_id][u'type'] == self.FOLDER_TYPE: |
804 | + yield dbus_client.subscribe_folder(volume_id) |
805 | + elif self._volumes[volume_id][u'type'] == self.SHARE_TYPE: |
806 | + yield dbus_client.subscribe_share(volume_id) |
807 | |
808 | @inlineCallbacks |
809 | def unsubscribe_volume(self, volume_id): |
810 | """Unsubscribe from 'volume_id'.""" |
811 | - # when dealing with shares, we need to check if 'volume_id' |
812 | - # is a folder or a share |
813 | - yield dbus_client.unsubscribe_folder(volume_id) |
814 | + if self._volumes[volume_id][u'type'] == self.FOLDER_TYPE: |
815 | + yield dbus_client.unsubscribe_folder(volume_id) |
816 | + elif self._volumes[volume_id][u'type'] == self.SHARE_TYPE: |
817 | + yield dbus_client.unsubscribe_share(volume_id) |
818 | |
819 | @log_call(logger.debug) |
820 | @inlineCallbacks |
821 | |
822 | === modified file 'ubuntuone/controlpanel/dbus_client.py' |
823 | --- ubuntuone/controlpanel/dbus_client.py 2011-01-25 19:08:59 +0000 |
824 | +++ ubuntuone/controlpanel/dbus_client.py 2011-02-12 03:22:55 +0000 |
825 | @@ -221,6 +221,22 @@ |
826 | return d |
827 | |
828 | |
829 | +def get_shares_dir(): |
830 | + """Retrieve the shares information from syncdaemon.""" |
831 | + d = defer.Deferred() |
832 | + proxy = get_syncdaemon_proxy("/", sd_dbus_iface.DBUS_IFACE_SYNC_NAME) |
833 | + proxy.get_sharesdir(reply_handler=d.callback, error_handler=d.errback) |
834 | + return d |
835 | + |
836 | + |
837 | +def get_shares_dir_link(): |
838 | + """Retrieve the shares information from syncdaemon.""" |
839 | + d = defer.Deferred() |
840 | + proxy = get_syncdaemon_proxy("/", sd_dbus_iface.DBUS_IFACE_SYNC_NAME) |
841 | + proxy.get_sharesdir_link(reply_handler=d.callback, error_handler=d.errback) |
842 | + return d |
843 | + |
844 | + |
845 | def get_folders(): |
846 | """Retrieve the folders information from syncdaemon.""" |
847 | d = defer.Deferred() |
848 | @@ -256,7 +272,7 @@ |
849 | proxy = get_folders_syncdaemon_proxy() |
850 | |
851 | sig = proxy.connect_to_signal('FolderSubscribed', d.callback) |
852 | - cb = lambda folder_info, error: d.errback(VolumesError(folder_info, error)) |
853 | + cb = lambda info, error: d.errback(VolumesError(info['id'], error)) |
854 | esig = proxy.connect_to_signal('FolderSubscribeError', cb) |
855 | |
856 | proxy.subscribe(folder_id, reply_handler=no_op, error_handler=no_op) |
857 | @@ -277,7 +293,7 @@ |
858 | proxy = get_folders_syncdaemon_proxy() |
859 | |
860 | sig = proxy.connect_to_signal('FolderUnSubscribed', d.callback) |
861 | - cb = lambda folder_info, error: d.errback(VolumesError(folder_info, error)) |
862 | + cb = lambda info, error: d.errback(VolumesError(info['id'], error)) |
863 | esig = proxy.connect_to_signal('FolderUnSubscribeError', cb) |
864 | |
865 | proxy.unsubscribe(folder_id, reply_handler=no_op, error_handler=no_op) |
866 | @@ -292,6 +308,31 @@ |
867 | return d |
868 | |
869 | |
870 | +@defer.inlineCallbacks |
871 | +def get_shares(): |
872 | + """Retrieve the shares information from syncdaemon.""" |
873 | + result = yield SyncDaemonTool(bus=dbus.SessionBus()).get_shares() |
874 | + defer.returnValue(result) |
875 | + |
876 | + |
877 | +@defer.inlineCallbacks |
878 | +def subscribe_share(share_id): |
879 | + """Subscribe to 'share_id'.""" |
880 | + try: |
881 | + yield SyncDaemonTool(bus=dbus.SessionBus()).subscribe_share(share_id) |
882 | + except Exception, e: |
883 | + raise VolumesError(share_id, e) |
884 | + |
885 | + |
886 | +@defer.inlineCallbacks |
887 | +def unsubscribe_share(share_id): |
888 | + """Unsubscribe 'share_id'.""" |
889 | + try: |
890 | + yield SyncDaemonTool(bus=dbus.SessionBus()).unsubscribe_share(share_id) |
891 | + except Exception, e: |
892 | + raise VolumesError(share_id, e) |
893 | + |
894 | + |
895 | def get_current_status(): |
896 | """Retrieve the current status from syncdaemon.""" |
897 | d = defer.Deferred() |
898 | |
899 | === modified file 'ubuntuone/controlpanel/gtk/gui.py' |
900 | --- ubuntuone/controlpanel/gtk/gui.py 2011-02-04 20:02:28 +0000 |
901 | +++ ubuntuone/controlpanel/gtk/gui.py 2011-02-12 03:22:55 +0000 |
902 | @@ -61,24 +61,13 @@ |
903 | |
904 | # To be replaced by values from the theme or Ubuntu One' specific (LP: #673663) |
905 | ORANGE = '#c95724' |
906 | +ERROR_COLOR = 'red' |
907 | LOADING = _('Loading...') |
908 | -OVERVIEW_MSGS = [ |
909 | - _('Backup and access your files from <b>any</b> computer (Ubuntu & ' |
910 | - 'Windows), mobile device (Android) or the web.'), |
911 | - _('A <b>portable address book</b> that unifies your most important ' |
912 | - 'contact sources: mobile phones, social networks, email clients and ' |
913 | - 'services.'), |
914 | - _('A <b>personal music library</b> that can be streamed to Android, ' |
915 | - 'iPhone and AirPlay-enabled devices.'), |
916 | - _('Add to that music library with a <b>Music Store</b> that automatically ' |
917 | - 'delivers to your personal cloud and connected devices.'), |
918 | - _('Keep your <b>Firefox bookmarks</b> and <b>Tomboy notes</b> in sync ' |
919 | - 'across computers (Ubuntu & Windows).'), |
920 | -] |
921 | VALUE_ERROR = _('Value could not be retrieved.') |
922 | -WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE |
923 | +WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ERROR_COLOR |
924 | KILOBYTES = 1024 |
925 | NO_OP = lambda *a, **kw: None |
926 | +FILE_URI_PREFIX = 'file://' |
927 | |
928 | |
929 | def error_handler(*args, **kwargs): |
930 | @@ -109,7 +98,7 @@ |
931 | @log_call(logger.debug) |
932 | def uri_hook(button, uri, *args, **kwargs): |
933 | """Open an URI or do nothing if URI is not an URL.""" |
934 | - if uri.startswith('http'): |
935 | + if uri.startswith('http') or uri.startswith(FILE_URI_PREFIX): |
936 | gtk.show_uri(None, uri, gtk.gdk.CURRENT_TIME) |
937 | |
938 | |
939 | @@ -222,30 +211,27 @@ |
940 | |
941 | CREDENTIALS_ERROR = _('There was a problem while retrieving the ' |
942 | 'credentials.') |
943 | - AUTHORIZATION_DENIED = _('The authentication was cancelled. Please either ' |
944 | - 'click join or connect to continue.') |
945 | + AUTHORIZATION_DENIED = _('The authentication was cancelled.') |
946 | NETWORK_OFFLINE = _('An internet connection is required to join or sign ' |
947 | 'in to %(app_name)s.') |
948 | NETWORK_UNKNOWN = _('The internet connection state couldn\'t be ' |
949 | 'discovered. Maybe NetworkManager is not running?') |
950 | |
951 | CONNECT = _('Connect to Ubuntu One') |
952 | - BULLET = '<span foreground="%s">✔</span>' % ORANGE |
953 | |
954 | - def __init__(self, main_window, messages=None): |
955 | + def __init__(self, main_window): |
956 | GreyableBin.__init__(self) |
957 | ControlPanelMixin.__init__(self, filename='overview.ui') |
958 | self.add(self.itself) |
959 | self.warning_label.set_text('') |
960 | self.warning_label.set_property('xalign', 0.5) |
961 | - self.connect('size-allocate', on_size_allocate, self.warning_label) |
962 | + |
963 | + self.header.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(DEFAULT_BG)) |
964 | |
965 | self.connect_button.set_uri(self.CONNECT) |
966 | |
967 | self.main_window = main_window |
968 | self._credentials_are_new = False |
969 | - self._messages = messages |
970 | - self._build_messages() |
971 | self.show() |
972 | |
973 | bus = dbus.SessionBus() |
974 | @@ -274,21 +260,6 @@ |
975 | self.network_manager_state = networkstate.NetworkManagerState(**kw) |
976 | self.network_manager_state.find_online_state() |
977 | |
978 | - def _build_messages(self): |
979 | - """List messages in a itemized list.""" |
980 | - if self._messages is None: |
981 | - self._messages = OVERVIEW_MSGS |
982 | - |
983 | - for msg in self._messages: |
984 | - label = gtk.Label() |
985 | - label.set_markup(self.BULLET + ' ' + msg) |
986 | - label.set_property('xalign', 0) |
987 | - label.set_property('wrap', True) |
988 | - self.messages.connect('size-allocate', on_size_allocate, label) |
989 | - |
990 | - self.messages.pack_start(label) |
991 | - self.messages.show_all() |
992 | - |
993 | def _set_warning(self, message, label=None): |
994 | """Set 'message' as global warning.""" |
995 | ControlPanelMixin._set_warning(self, message, |
996 | @@ -419,18 +390,22 @@ |
997 | class VolumesPanel(UbuntuOneBin, ControlPanelMixin): |
998 | """The volumes panel.""" |
999 | |
1000 | - TITLE = _('Select the folders from your cloud that you want synchronized ' |
1001 | - 'in this device.') |
1002 | + TITLE = _('Select the folders from your personal cloud that you want ' |
1003 | + 'synchronized in this device.') |
1004 | MY_FOLDERS = _('My folders') |
1005 | ALWAYS_SUBSCRIBED = _('Always in sync!') |
1006 | FREE_SPACE = _('%(free_space)s available storage') |
1007 | + NO_VOLUMES = _('No folders to show.') |
1008 | + NAME_NOT_SET = _('[unknown user name]') |
1009 | + |
1010 | + MAX_COLS = 8 |
1011 | + |
1012 | CONTACT_ICON_NAME = 'system-users' |
1013 | FOLDER_ICON_NAME = 'folder' |
1014 | SHARE_ICON_NAME = 'folder-remote' |
1015 | ROW_HEADER = '<span font_size="large"><b>%s</b></span> ' \ |
1016 | '<span foreground="grey">%s</span>' |
1017 | ROOT = '%s - <span foreground="%s" font_size="small">%s</span>' |
1018 | - NO_VOLUMES = _('No folders to show.') |
1019 | |
1020 | def __init__(self, main_window=None): |
1021 | UbuntuOneBin.__init__(self) |
1022 | @@ -438,6 +413,11 @@ |
1023 | self.add(self.itself) |
1024 | self.show_all() |
1025 | |
1026 | + # name, subscribed, icon name, show toggle, sensitive, icon size, |
1027 | + # id, path |
1028 | + self._empty_row = ('', False, '', False, False, gtk.ICON_SIZE_MENU, |
1029 | + None, None) |
1030 | + |
1031 | self.backend.connect_to_signal('VolumesInfoReady', |
1032 | self.on_volumes_info_ready) |
1033 | self.backend.connect_to_signal('VolumesInfoError', |
1034 | @@ -462,14 +442,16 @@ |
1035 | else: |
1036 | self.on_success() |
1037 | |
1038 | - # pylint: disable=W0612 |
1039 | - # name, subscribed, icon name, show toggle, sensitive, icon size, id |
1040 | - empty_row = ('', False, '', False, False, gtk.ICON_SIZE_MENU, None) |
1041 | - |
1042 | for name, free_bytes, volumes in info: |
1043 | + if backend.ControlBackend.NAME_NOT_SET in name: |
1044 | + name = self.NAME_NOT_SET |
1045 | + |
1046 | if name: |
1047 | name = name + "'s" |
1048 | icon_name = self.SHARE_ICON_NAME |
1049 | + |
1050 | + # we already added user folders, let's add an empty row |
1051 | + treeiter = self.volumes_store.append(None, self._empty_row) |
1052 | else: |
1053 | name = self.MY_FOLDERS |
1054 | icon_name = self.FOLDER_ICON_NAME |
1055 | @@ -477,30 +459,33 @@ |
1056 | free_bytes_args = {'free_space': self.humanize(int(free_bytes))} |
1057 | row = (self.ROW_HEADER % (name, self.FREE_SPACE % free_bytes_args), |
1058 | True, self.CONTACT_ICON_NAME, False, False, |
1059 | - gtk.ICON_SIZE_LARGE_TOOLBAR, None) |
1060 | + gtk.ICON_SIZE_LARGE_TOOLBAR, None, None) |
1061 | treeiter = self.volumes_store.append(None, row) |
1062 | |
1063 | volumes.sort(key=operator.itemgetter('path')) |
1064 | for volume in volumes: |
1065 | sensitive = True |
1066 | - path = self._process_path(volume['path']) |
1067 | - if volume['type'] == u'ROOT': |
1068 | + name = self._process_path(volume[u'path']) |
1069 | + |
1070 | + is_root = volume[u'type'] == backend.ControlBackend.ROOT_TYPE |
1071 | + is_share = volume[u'type'] == backend.ControlBackend.SHARE_TYPE |
1072 | + |
1073 | + if is_root: |
1074 | sensitive = False |
1075 | - path = self.ROOT % (path, ORANGE, self.ALWAYS_SUBSCRIBED) |
1076 | - |
1077 | - row = (path, bool(volume['subscribed']), icon_name, True, |
1078 | - sensitive, gtk.ICON_SIZE_MENU, volume['volume_id']) |
1079 | - |
1080 | - if volume['type'] == u'ROOT': # root should go first! |
1081 | + name = self.ROOT % (name, ORANGE, self.ALWAYS_SUBSCRIBED) |
1082 | + elif is_share: |
1083 | + name = volume[u'name'] |
1084 | + |
1085 | + row = (name, bool(volume[u'subscribed']), icon_name, True, |
1086 | + sensitive, gtk.ICON_SIZE_MENU, volume['volume_id'], |
1087 | + volume[u'path']) |
1088 | + |
1089 | + if is_root: # root should go first! |
1090 | self.volumes_store.prepend(treeiter, row) |
1091 | else: |
1092 | self.volumes_store.append(treeiter, row) |
1093 | |
1094 | - # When we display shares info, we'll need to smartly add |
1095 | - # an empty row to the tree view to separate volume groups |
1096 | - #treeiter = self.volumes_store.append(None, empty_row) |
1097 | - |
1098 | - self.volumes_view.expand_row(0, True) |
1099 | + self.volumes_view.expand_all() |
1100 | self.volumes_view.show_all() |
1101 | |
1102 | self.is_processing = False |
1103 | @@ -534,6 +519,12 @@ |
1104 | |
1105 | self.is_processing = True |
1106 | |
1107 | + def on_volumes_view_row_activated(self, widget, path, *args, **kwargs): |
1108 | + """The user double clicked on a row.""" |
1109 | + treeiter = self.volumes_store.get_iter(path) |
1110 | + volume_path = self.volumes_store.get_value(treeiter, 7) |
1111 | + uri_hook(None, FILE_URI_PREFIX + volume_path) |
1112 | + |
1113 | def load(self): |
1114 | """Load the volume list.""" |
1115 | self.backend.volumes_info(reply_handler=NO_OP, |
1116 | @@ -739,7 +730,8 @@ |
1117 | gobject.TYPE_NONE, ()), |
1118 | } |
1119 | |
1120 | - TITLE = _('The devices synced with your personal cloud are listed below.') |
1121 | + TITLE = _('The devices connected with your personal cloud are listed ' |
1122 | + 'below.') |
1123 | NO_DEVICES = _('No devices to show.') |
1124 | CONFIRM_REMOVE = _('Are you sure you want to remove this device ' |
1125 | 'from Ubuntu One?') |
1126 | @@ -1270,6 +1262,8 @@ |
1127 | } |
1128 | |
1129 | QUOTA_LABEL = _('%(used)s used of %(total)s (%(percentage).1f%%)') |
1130 | + DASHBOARD_BUTTON_NAME = 'Account' |
1131 | + DEVICES_BUTTON_NAME = 'Devices' |
1132 | |
1133 | def __init__(self, main_window=None): |
1134 | gtk.VBox.__init__(self) |
1135 | @@ -1287,6 +1281,7 @@ |
1136 | self.quota_progressbar.set_sensitive(False) |
1137 | self.quota_label = LabelLoading(LOADING, fg_color=DEFAULT_FG) |
1138 | self.quota_box.pack_start(self.quota_label, expand=False) |
1139 | + self.quota_box.reorder_child(self.quota_label, 0) |
1140 | |
1141 | self.status_label = FileSyncStatus() |
1142 | self.status_box.pack_end(self.status_label, expand=False) |
1143 | @@ -1309,9 +1304,13 @@ |
1144 | gtk.gdk.Color(DEFAULT_FG)) |
1145 | self.notebook.insert_page(getattr(self, tab), position=page_num) |
1146 | |
1147 | + self.dashboard_button.set_name(self.DASHBOARD_BUTTON_NAME) |
1148 | + |
1149 | self.volumes_button.connect('clicked', lambda b: self.volumes.load()) |
1150 | + self.services_button.connect('clicked', lambda b: self.services.load()) |
1151 | + |
1152 | + self.devices_button.set_name(self.DEVICES_BUTTON_NAME) |
1153 | self.devices_button.connect('clicked', lambda b: self.devices.load()) |
1154 | - self.services_button.connect('clicked', lambda b: self.services.load()) |
1155 | self.devices.connect('local-device-removed', |
1156 | lambda widget: self.emit('local-device-removed')) |
1157 | |
1158 | @@ -1397,7 +1396,7 @@ |
1159 | class ControlPanelWindow(gtk.Window): |
1160 | """The main window for the Ubuntu One control panel.""" |
1161 | |
1162 | - TITLE = _('%(app_name)s Dashboard') |
1163 | + TITLE = _('%(app_name)s Control Panel') |
1164 | |
1165 | def __init__(self, switch_to=None): |
1166 | super(ControlPanelWindow, self).__init__() |
1167 | |
1168 | === modified file 'ubuntuone/controlpanel/gtk/tests/__init__.py' |
1169 | --- ubuntuone/controlpanel/gtk/tests/__init__.py 2011-01-25 19:08:59 +0000 |
1170 | +++ ubuntuone/controlpanel/gtk/tests/__init__.py 2011-02-12 03:22:55 +0000 |
1171 | @@ -45,11 +45,11 @@ |
1172 | ] |
1173 | |
1174 | FAKE_SHARES_INFO = [ |
1175 | - {u'volume_id': u'1234', |
1176 | + {u'volume_id': u'1234', u'name': u'do', |
1177 | u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User', |
1178 | u'subscribed': u'', u'type': u'SHARE'}, |
1179 | |
1180 | - {u'volume_id': u'5678', |
1181 | + {u'volume_id': u'5678', u'name': u're', |
1182 | u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User', |
1183 | u'subscribed': u'True', u'type': u'SHARE'}, |
1184 | ] |
1185 | |
1186 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py' |
1187 | --- ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-01-25 19:08:59 +0000 |
1188 | +++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-02-12 03:22:55 +0000 |
1189 | @@ -27,7 +27,7 @@ |
1190 | from ubuntuone.controlpanel.gtk import gui |
1191 | from ubuntuone.controlpanel.gtk.tests import (FAKE_ACCOUNT_INFO, |
1192 | FAKE_DEVICE_INFO, FAKE_DEVICES_INFO, |
1193 | - FAKE_VOLUMES_INFO, FAKE_REPLICATIONS_INFO, USER_HOME, |
1194 | + FAKE_VOLUMES_INFO, FAKE_REPLICATIONS_INFO, ROOT, USER_HOME, |
1195 | FakedNMState, FakedSSOBackend, FakedSessionBus, FakedInterface, |
1196 | FakedPackageManager, FakedConfirmDialog, |
1197 | ) |
1198 | @@ -82,21 +82,6 @@ |
1199 | pb = gui.gtk.gdk.pixbuf_new_from_file(gui.get_data_file(filename)) |
1200 | self.assertEqual(image.get_pixbuf().get_pixels(), pb.get_pixels()) |
1201 | |
1202 | - def assert_messages_equal(self, widget, msgs): |
1203 | - """Check that 'widget' has all the entries in 'msgs'.""" |
1204 | - children = list(reversed(widget.get_children())) |
1205 | - self.assertEqual(len(children), len(msgs)) |
1206 | - for label, msg in zip(reversed(children), msgs): |
1207 | - full_msg = self.ui.BULLET + ' ' + msg |
1208 | - self.assertTrue(label.get_visible()) |
1209 | - self.assertEqual(label.get_property('xalign'), 0) |
1210 | - self.assertEqual(label.get_property('wrap'), True) |
1211 | - self.assertEqual(label.get_label(), full_msg) |
1212 | - |
1213 | - expected = gui.gtk.Label() |
1214 | - expected.set_markup(full_msg) |
1215 | - self.assertEqual(label.get_text(), expected.get_text()) |
1216 | - |
1217 | def assert_backend_called(self, method_name, args, backend=None): |
1218 | """Check that the control panel backend 'method_name' was called.""" |
1219 | if backend is None: |
1220 | @@ -392,7 +377,7 @@ |
1221 | """The test suite for the overview panel.""" |
1222 | |
1223 | klass = gui.OverviewPanel |
1224 | - kwargs = {'messages': None, 'main_window': gui.gtk.Window()} |
1225 | + kwargs = {'main_window': gui.gtk.Window()} |
1226 | ui_filename = 'overview.ui' |
1227 | |
1228 | def test_is_a_greyable_bin(self): |
1229 | @@ -561,10 +546,7 @@ |
1230 | class OverwiewPanelNoCredsTestCase(OverwiewPanelTestCase): |
1231 | """The test suite for the overview panel when no credentials are found.""" |
1232 | |
1233 | - messages = None |
1234 | - |
1235 | def setUp(self): |
1236 | - self.kwargs['messages'] = self.messages |
1237 | super(OverwiewPanelNoCredsTestCase, self).setUp() |
1238 | self.ui.on_credentials_not_found(gui.U1_APP_NAME) |
1239 | |
1240 | @@ -581,10 +563,6 @@ |
1241 | """There is an image attribute and is correct.""" |
1242 | self.assert_image_equal(self.ui.image, 'overview.png') |
1243 | |
1244 | - def test_messages(self): |
1245 | - """If no messages are passed, the default list is used.""" |
1246 | - self.assert_messages_equal(self.ui.messages, gui.OVERVIEW_MSGS) |
1247 | - |
1248 | def test_join_now_is_default_widget(self): |
1249 | """The join now button is the default widget.""" |
1250 | self.assertTrue(self.ui.join_now_button.get_property('can_default')) |
1251 | @@ -683,18 +661,6 @@ |
1252 | self.assertTrue(self.ui.connect_button.is_sensitive()) |
1253 | |
1254 | |
1255 | -class OverwiewPanelMessagesTestCase(OverwiewPanelTestCase): |
1256 | - """The test suite for the overview panel when messages are set.""" |
1257 | - |
1258 | - messages = ['Test me', 'A little bit more'] |
1259 | - |
1260 | - |
1261 | -class OverwiewPanelMarkupMessagesTestCase(OverwiewPanelTestCase): |
1262 | - """The test suite for the overview panel when markup messages are set.""" |
1263 | - |
1264 | - messages = ['<small>Test me</small>', 'A <b>little</b> bit more'] |
1265 | - |
1266 | - |
1267 | class DashboardTestCase(ControlPanelMixinTestCase): |
1268 | """The test suite for the dashboard panel.""" |
1269 | |
1270 | @@ -835,7 +801,8 @@ |
1271 | """The volumes info is processed when ready.""" |
1272 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
1273 | |
1274 | - self.assertEqual(len(FAKE_VOLUMES_INFO), len(self.ui.volumes_store)) |
1275 | + self.assertEqual(len(FAKE_VOLUMES_INFO) + 1, # count the empty row |
1276 | + len(self.ui.volumes_store)) |
1277 | treeiter = self.ui.volumes_store.get_iter_root() |
1278 | for name, free_bytes, volumes in FAKE_VOLUMES_INFO: |
1279 | name = "%s's" % name if name else self.ui.MY_FOLDERS |
1280 | @@ -843,7 +810,8 @@ |
1281 | header = (name, self.ui.FREE_SPACE % {'free_space': free_bytes}) |
1282 | |
1283 | # check parent row |
1284 | - row = self.ui.volumes_store.get(treeiter, *xrange(7)) |
1285 | + row = self.ui.volumes_store.get(treeiter, |
1286 | + *xrange(self.ui.MAX_COLS)) |
1287 | |
1288 | self.assertEqual(row[0], self.ui.ROW_HEADER % header) |
1289 | self.assertTrue(row[1], 'parent will always be subscribed') |
1290 | @@ -852,6 +820,7 @@ |
1291 | self.assertFalse(row[4], 'toggle should be non sensitive.') |
1292 | self.assertEqual(row[5], gui.gtk.ICON_SIZE_LARGE_TOOLBAR) |
1293 | self.assertEqual(row[6], None) |
1294 | + self.assertEqual(row[7], None) |
1295 | |
1296 | # check children |
1297 | self.assertEqual(len(volumes), |
1298 | @@ -860,7 +829,8 @@ |
1299 | |
1300 | sorted_vols = sorted(volumes, key=gui.operator.itemgetter('path')) |
1301 | for volume in sorted_vols: |
1302 | - row = self.ui.volumes_store.get(childiter, *xrange(7)) |
1303 | + row = self.ui.volumes_store.get(childiter, |
1304 | + *xrange(self.ui.MAX_COLS)) |
1305 | |
1306 | sensitive = True |
1307 | path = volume['path'].replace(USER_HOME + '/', '') |
1308 | @@ -868,6 +838,8 @@ |
1309 | sensitive = False |
1310 | path = self.ui.ROOT % (path, gui.ORANGE, |
1311 | self.ui.ALWAYS_SUBSCRIBED) |
1312 | + elif volume['type'] == 'SHARE': |
1313 | + path = volume['name'] |
1314 | |
1315 | self.assertEqual(row[0], path) |
1316 | self.assertEqual(row[1], bool(volume['subscribed'])) |
1317 | @@ -879,17 +851,28 @@ |
1318 | self.assertEqual(row[4], sensitive) |
1319 | self.assertEqual(row[5], gui.gtk.ICON_SIZE_MENU) |
1320 | self.assertEqual(row[6], volume['volume_id']) |
1321 | + self.assertEqual(row[7], volume['path']) |
1322 | |
1323 | childiter = self.ui.volumes_store.iter_next(childiter) |
1324 | |
1325 | treeiter = self.ui.volumes_store.iter_next(treeiter) |
1326 | |
1327 | + if treeiter is not None: |
1328 | + # skip the empty row |
1329 | + row = self.ui.volumes_store.get(treeiter, |
1330 | + *xrange(self.ui.MAX_COLS)) |
1331 | + self.assertEqual(row, self.ui._empty_row) |
1332 | + |
1333 | + # grab next non-empty row |
1334 | + treeiter = self.ui.volumes_store.iter_next(treeiter) |
1335 | + |
1336 | def test_on_volumes_info_ready_clears_the_list(self): |
1337 | """The old volumes info is cleared before updated.""" |
1338 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
1339 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
1340 | |
1341 | - self.assertEqual(len(self.ui.volumes_store), len(FAKE_VOLUMES_INFO)) |
1342 | + self.assertEqual(len(FAKE_VOLUMES_INFO) + 1, |
1343 | + len(self.ui.volumes_store)) |
1344 | |
1345 | def test_on_volumes_info_ready_with_no_volumes(self): |
1346 | """When there are no volumes, a notification is shown.""" |
1347 | @@ -917,7 +900,9 @@ |
1348 | """Clicking on 'subscribed' updates the folder subscription.""" |
1349 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
1350 | |
1351 | - for parent, (_, _, volumes) in enumerate(FAKE_VOLUMES_INFO): |
1352 | + real_rows = len(FAKE_VOLUMES_INFO) |
1353 | + data = zip(range(real_rows)[::2], FAKE_VOLUMES_INFO) # skip emtpy rows |
1354 | + for parent, (_, _, volumes) in data: |
1355 | |
1356 | sorted_vols = sorted(volumes, key=gui.operator.itemgetter('path')) |
1357 | for child, volume in enumerate(sorted_vols): |
1358 | @@ -964,6 +949,17 @@ |
1359 | # reload folders list to sanitize the info in volumes_store |
1360 | self.assertTrue(self._called, ((), {})) |
1361 | |
1362 | + def test_clicking_on_row_opens_folder(self): |
1363 | + """The folder activated is opened.""" |
1364 | + self.patch(gui, 'uri_hook', self._set_called) |
1365 | + self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
1366 | + |
1367 | + self.ui.volumes_view.row_activated('0:0', |
1368 | + self.ui.volumes_view.get_column(0)) |
1369 | + |
1370 | + self.assertEqual(self._called, |
1371 | + ((None, gui.FILE_URI_PREFIX + ROOT['path']), {})) |
1372 | + |
1373 | |
1374 | class DeviceTestCase(ControlPanelMixinTestCase): |
1375 | """The test suite for the device widget.""" |
1376 | @@ -2358,3 +2354,13 @@ |
1377 | self.ui.devices.emit('local-device-removed') |
1378 | |
1379 | self.assertEqual(self._called, ((self.ui,), {})) |
1380 | + |
1381 | + def test_dashboard_button_name(self): |
1382 | + """The dashboard_button widget has the proper name.""" |
1383 | + self.assertEqual(self.ui.dashboard_button.get_name(), |
1384 | + self.ui.DASHBOARD_BUTTON_NAME) |
1385 | + |
1386 | + def test_devices_button_name(self): |
1387 | + """The devices_button widget has the proper name.""" |
1388 | + self.assertEqual(self.ui.devices_button.get_name(), |
1389 | + self.ui.DEVICES_BUTTON_NAME) |
1390 | |
1391 | === modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_client_sd.py' |
1392 | --- ubuntuone/controlpanel/integrationtests/test_dbus_client_sd.py 2011-01-25 19:08:59 +0000 |
1393 | +++ ubuntuone/controlpanel/integrationtests/test_dbus_client_sd.py 2011-02-12 03:22:55 +0000 |
1394 | @@ -19,9 +19,11 @@ |
1395 | |
1396 | """Tests for the DBus service when accessing SyncDaemon.""" |
1397 | |
1398 | +import uuid |
1399 | + |
1400 | import dbus |
1401 | |
1402 | -from twisted.internet.defer import inlineCallbacks |
1403 | +from twisted.internet.defer import inlineCallbacks, returnValue |
1404 | |
1405 | from ubuntuone.controlpanel import dbus_client |
1406 | from ubuntuone.controlpanel.dbus_client import sd_dbus_iface |
1407 | @@ -34,6 +36,8 @@ |
1408 | "download": "838", |
1409 | } |
1410 | |
1411 | +SHARES = {} |
1412 | + |
1413 | # pylint, you have to go to decorator's school |
1414 | # pylint: disable=C0322 |
1415 | |
1416 | @@ -302,7 +306,7 @@ |
1417 | |
1418 | |
1419 | class FoldersTestCase(DBusClientTestCase): |
1420 | - """Test for the volumes dbus client methods.""" |
1421 | + """Test for the shares dbus client methods.""" |
1422 | |
1423 | def setUp(self): |
1424 | super(FoldersTestCase, self).setUp() |
1425 | @@ -379,7 +383,7 @@ |
1426 | try: |
1427 | yield dbus_client.subscribe_folder(fid) |
1428 | except dbus_client.VolumesError, e: |
1429 | - self.assertEqual(e[0], {'id': fid}) |
1430 | + self.assertEqual(e[0], fid) |
1431 | else: |
1432 | self.fail('dbus_client.subscribe_folder should be errbacking') |
1433 | |
1434 | @@ -405,11 +409,159 @@ |
1435 | try: |
1436 | yield dbus_client.unsubscribe_folder(fid) |
1437 | except dbus_client.VolumesError, e: |
1438 | - self.assertEqual(e[0], {'id': fid}) |
1439 | + self.assertEqual(e[0], fid) |
1440 | else: |
1441 | self.fail('dbus_client.unsubscribe_folder should be errbacking') |
1442 | |
1443 | |
1444 | +class FakedSyncDaemonTool(object): |
1445 | + """Fake the FakedSyncDaemonTool.""" |
1446 | + |
1447 | + def __init__(self, bus): |
1448 | + """New instance.""" |
1449 | + |
1450 | + def _set_share_attr(self, share_id, attr, value): |
1451 | + """Set values to shares.""" |
1452 | + if share_id not in SHARES: |
1453 | + raise dbus.DBusException('share_id not in SHARES.') |
1454 | + |
1455 | + value = sd_dbus_iface.bool_str(value) |
1456 | + SHARES[share_id][attr] = value |
1457 | + return share_id |
1458 | + |
1459 | + @inlineCallbacks |
1460 | + def accept_share(self, share_id): |
1461 | + """Accept the share with id: share_id.""" |
1462 | + yield self._set_share_attr(share_id, u'accepted', True) |
1463 | + |
1464 | + @inlineCallbacks |
1465 | + def reject_share(self, share_id): |
1466 | + """Reject the share with id: share_id.""" |
1467 | + yield self._set_share_attr(share_id, u'accepted', False) |
1468 | + |
1469 | + @inlineCallbacks |
1470 | + def subscribe_share(self, share_id): |
1471 | + """Subscribe to a share given its id.""" |
1472 | + yield self._set_share_attr(share_id, u'subscribed', True) |
1473 | + |
1474 | + @inlineCallbacks |
1475 | + def unsubscribe_share(self, share_id): |
1476 | + """Unsubscribe from a share given its id.""" |
1477 | + yield self._set_share_attr(share_id, u'subscribed', False) |
1478 | + |
1479 | + @inlineCallbacks |
1480 | + def get_shares(self): |
1481 | + """Get the list of shares (accepted or not).""" |
1482 | + result = yield SHARES.values() |
1483 | + returnValue(sorted(result)) |
1484 | + |
1485 | + @inlineCallbacks |
1486 | + def refresh_shares(self): |
1487 | + """Call refresh_shares method via DBus.""" |
1488 | + yield |
1489 | + |
1490 | + @classmethod |
1491 | + def create_share(cls, name, accepted=False, subscribed=False): |
1492 | + """Add a new share (fake).""" |
1493 | + sid = unicode(uuid.uuid4()) |
1494 | + path = u'/home/tester/.hidden/shares/%s from The Othr User' % name |
1495 | + share = { |
1496 | + u'name': name, u'subscribed': sd_dbus_iface.bool_str(subscribed), |
1497 | + u'accepted': sd_dbus_iface.bool_str(accepted), |
1498 | + u'generation': u'36', u'type': u'Share', |
1499 | + u'node_id': u'ca3a1cec-09d2-485e-9685-1a5180bd6441', |
1500 | + u'volume_id': sid, u'access_level': u'View', |
1501 | + u'other_username': u'https://login.ubuntu.com/+id/nHRnYmz', |
1502 | + u'other_visible_name': u'The Other User', |
1503 | + u'free_bytes': u'2146703403', u'path': path, |
1504 | + } |
1505 | + SHARES[sid] = share |
1506 | + return share |
1507 | + |
1508 | + |
1509 | +class SharesTestCase(DBusClientTestCase): |
1510 | + """Test for the shares dbus client methods.""" |
1511 | + |
1512 | + def setUp(self): |
1513 | + super(SharesTestCase, self).setUp() |
1514 | + self.patch(dbus_client, 'SyncDaemonTool', FakedSyncDaemonTool) |
1515 | + SHARES.clear() |
1516 | + |
1517 | + @inlineCallbacks |
1518 | + def test_get_shares(self): |
1519 | + """Retrieve shares info list.""" |
1520 | + share1 = FakedSyncDaemonTool.create_share(name='first, test me!') |
1521 | + share2 = FakedSyncDaemonTool.create_share(name='and test me more!', |
1522 | + accepted=True) |
1523 | + share3 = FakedSyncDaemonTool.create_share(name='last but not least', |
1524 | + accepted=True, subscribed=True) |
1525 | + |
1526 | + result = yield dbus_client.get_shares() |
1527 | + |
1528 | + self.assertEqual(result, sorted([share1, share2, share3])) |
1529 | + |
1530 | + @inlineCallbacks |
1531 | + def test_get_shares_error(self): |
1532 | + """Handle error when retrieving current syncdaemon status.""" |
1533 | + self.patch(FakedSyncDaemonTool, 'get_shares', self.fail) |
1534 | + try: |
1535 | + yield dbus_client.get_shares() |
1536 | + except: # pylint: disable=W0702 |
1537 | + pass # test passes! |
1538 | + else: |
1539 | + self.fail('dbus_client.get_shares should be errbacking') |
1540 | + |
1541 | + @inlineCallbacks |
1542 | + def test_subscribe_share(self): |
1543 | + """Subscribe to a share.""" |
1544 | + share = FakedSyncDaemonTool.create_share(name='to be subscribed', |
1545 | + accepted=True, subscribed=False) |
1546 | + sid = share['volume_id'] |
1547 | + |
1548 | + yield dbus_client.subscribe_share(sid) |
1549 | + |
1550 | + result = yield dbus_client.get_shares() |
1551 | + expected, = filter(lambda share: share['volume_id'] == sid, result) |
1552 | + self.assertEqual(expected['subscribed'], 'True') |
1553 | + |
1554 | + @inlineCallbacks |
1555 | + def test_subscribe_share_error(self): |
1556 | + """Subscribe to a share.""" |
1557 | + sid = u'does not exist' |
1558 | + try: |
1559 | + yield dbus_client.subscribe_share(sid) |
1560 | + except dbus_client.VolumesError, e: |
1561 | + self.assertEqual(e[0], sid) |
1562 | + else: |
1563 | + self.fail('dbus_client.subscribe_share should be errbacking') |
1564 | + |
1565 | + @inlineCallbacks |
1566 | + def test_unsubscribe_share(self): |
1567 | + """Unsubscribe to a share.""" |
1568 | + share = FakedSyncDaemonTool.create_share(name='to be unsubscribed', |
1569 | + accepted=True, subscribed=True) |
1570 | + sid = share['volume_id'] |
1571 | + yield dbus_client.subscribe_share(sid) |
1572 | + # share is subscribed |
1573 | + |
1574 | + yield dbus_client.unsubscribe_share(sid) |
1575 | + |
1576 | + result = yield dbus_client.get_shares() |
1577 | + expected, = filter(lambda share: share['volume_id'] == sid, result) |
1578 | + self.assertEqual(expected['subscribed'], '') |
1579 | + |
1580 | + @inlineCallbacks |
1581 | + def test_unsubscribe_share_error(self): |
1582 | + """Unsubscribe to a share.""" |
1583 | + sid = u'does not exist' |
1584 | + try: |
1585 | + yield dbus_client.unsubscribe_share(sid) |
1586 | + except dbus_client.VolumesError, e: |
1587 | + self.assertEqual(e[0], sid) |
1588 | + else: |
1589 | + self.fail('dbus_client.unsubscribe_share should be errbacking') |
1590 | + |
1591 | + |
1592 | class StatusMockDBusSyncDaemon(dbus.service.Object): |
1593 | """A mock object that mimicks syncdaemon regarding the Status iface.""" |
1594 | |
1595 | @@ -549,6 +701,8 @@ |
1596 | """A mock object that mimicks syncdaemon.""" |
1597 | |
1598 | ROOT_DIR = u'/yadda/yoda/Test me' |
1599 | + SHARES_DIR = u'/yadda/yoda/.hidden/ugly/dir/name/shares' |
1600 | + SHARES_DIR_LINK = u'/yadda/yoda/Test me/Shared With Me' |
1601 | |
1602 | @dbus.service.method(sd_dbus_iface.DBUS_IFACE_SYNC_NAME, |
1603 | in_signature='', out_signature='s') |
1604 | @@ -556,6 +710,18 @@ |
1605 | """Return the root dir/mount point.""" |
1606 | return self.ROOT_DIR |
1607 | |
1608 | + @dbus.service.method(sd_dbus_iface.DBUS_IFACE_SYNC_NAME, |
1609 | + in_signature='', out_signature='s') |
1610 | + def get_sharesdir(self): |
1611 | + """Return the shares dir/mount point.""" |
1612 | + return self.SHARES_DIR |
1613 | + |
1614 | + @dbus.service.method(sd_dbus_iface.DBUS_IFACE_SYNC_NAME, |
1615 | + in_signature='', out_signature='s') |
1616 | + def get_sharesdir_link(self): |
1617 | + """Return the root dir/mount point.""" |
1618 | + return self.SHARES_DIR_LINK |
1619 | + |
1620 | |
1621 | class BasicTestCase(DBusClientTestCase): |
1622 | """Test for the basic dbus client methods.""" |
1623 | @@ -571,3 +737,17 @@ |
1624 | root = yield dbus_client.get_root_dir() |
1625 | |
1626 | self.assertEqual(MockDBusSyncDaemon.ROOT_DIR, root) |
1627 | + |
1628 | + @inlineCallbacks |
1629 | + def test_get_shares_dir(self): |
1630 | + """Retrieve current syncdaemon shares dir.""" |
1631 | + result = yield dbus_client.get_shares_dir() |
1632 | + |
1633 | + self.assertEqual(MockDBusSyncDaemon.SHARES_DIR, result) |
1634 | + |
1635 | + @inlineCallbacks |
1636 | + def test_get_shares_dir_link(self): |
1637 | + """Retrieve current syncdaemon shares dir.""" |
1638 | + result = yield dbus_client.get_shares_dir_link() |
1639 | + |
1640 | + self.assertEqual(MockDBusSyncDaemon.SHARES_DIR_LINK, result) |
1641 | |
1642 | === modified file 'ubuntuone/controlpanel/tests/__init__.py' |
1643 | --- ubuntuone/controlpanel/tests/__init__.py 2011-01-25 19:08:59 +0000 |
1644 | +++ ubuntuone/controlpanel/tests/__init__.py 2011-02-12 03:22:55 +0000 |
1645 | @@ -161,8 +161,14 @@ |
1646 | u'volume_id': u'1deb2874-3d28-46ae-9999-d5f48de9f460'}, |
1647 | ] |
1648 | |
1649 | +ROOT_PATH = u'/home/tester/Ubuntu One' |
1650 | +SHARES_PATH = u'/home/tester/.local/share/ubuntuone/shares' |
1651 | +SHARES_PATH_LINK = ROOT_PATH + u'/Shared With Me' |
1652 | + |
1653 | SAMPLE_SHARES = [ |
1654 | + |
1655 | {u'accepted': u'True', |
1656 | + u'subscribed': u'True', |
1657 | u'access_level': u'View', |
1658 | u'free_bytes': u'39892622746', |
1659 | u'generation': u'2704', |
1660 | @@ -170,11 +176,12 @@ |
1661 | u'node_id': u'c483f419-ed28-490a-825d-a8c074e2d795', |
1662 | u'other_username': u'otheruser', |
1663 | u'other_visible_name': u'Other User', |
1664 | - u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User', |
1665 | + u'path': SHARES_PATH + u'/re from Other User', |
1666 | u'type': u'Share', |
1667 | u'volume_id': u'4a1b263b-a2b3-4f66-9e66-4cd18050810d'}, |
1668 | |
1669 | {u'accepted': u'True', |
1670 | + u'subscribed': u'True', |
1671 | u'access_level': u'Modify', |
1672 | u'free_bytes': u'39892622746', |
1673 | u'generation': u'2704', |
1674 | @@ -182,9 +189,78 @@ |
1675 | u'node_id': u'84544ea4-aefe-4f91-9bb9-ed7b0a805baf', |
1676 | u'other_username': u'otheruser', |
1677 | u'other_visible_name': u'Other User', |
1678 | - u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User', |
1679 | + u'path': SHARES_PATH + u'/do from Other User', |
1680 | u'type': u'Share', |
1681 | u'volume_id': u'7d130dfe-98b2-4bd5-8708-9eeba9838ac0'}, |
1682 | + |
1683 | + {u'name': u'yadda', |
1684 | + u'subscribed': u'True', |
1685 | + u'generation': u'36', |
1686 | + u'other_username': u'https://login.ubuntu.com/+id/nHRnYmz', |
1687 | + u'other_visible_name': u'', |
1688 | + u'access_level': u'View', |
1689 | + u'node_id': u'ca3a1cec-09d2-485e-9685-1a5180bd6441', |
1690 | + u'volume_id': u'f1f1741f-ba49-46ef-b682-816c97e6e3d6', |
1691 | + u'free_bytes': u'2146703403', |
1692 | + u'path': SHARES_PATH + u'/yadda from ', |
1693 | + u'accepted': u'True', |
1694 | + u'type': u'Share'}, |
1695 | + |
1696 | + {u'name': u'images', |
1697 | + u'subscribed': u'True', |
1698 | + u'generation': u'36', |
1699 | + u'other_username': |
1700 | + u'https://login.ubuntu.com/+id/nHRnYmz', |
1701 | + u'other_visible_name': u'', |
1702 | + u'access_level': u'Modify', |
1703 | + u'node_id': |
1704 | + u'b932f0d3-6998-423f-9225-7683a4adbd6f', |
1705 | + u'volume_id': |
1706 | + u'a73f889d-ffd3-4447-b351-f0d8130d1e1a', |
1707 | + u'free_bytes': u'2146703403', |
1708 | + u'path': SHARES_PATH + u'/images from ', |
1709 | + u'accepted': u'True', |
1710 | + u'type': u'Share'}, |
1711 | + |
1712 | + {u'name': u'read-only', |
1713 | + u'subscribed': u'True', |
1714 | + u'generation': u'315', |
1715 | + u'other_username': u'sharing-user', |
1716 | + u'other_visible_name': u'A Sharing User', |
1717 | + u'access_level': u'View', |
1718 | + u'node_id': u'fd944823-e4c2-45a4-8c95-05997698bdbc', |
1719 | + u'volume_id': |
1720 | + u'64b43c96-6c7c-4135-994f-03a1a3774512', |
1721 | + u'free_bytes': u'108786811673', |
1722 | + u'path': SHARES_PATH + u'/read-only from A Sharing User', |
1723 | + u'accepted': u'True', |
1724 | + u'type': u'Share'}, |
1725 | + |
1726 | + {u'name': u'read-write', |
1727 | + u'subscribed': u'True', |
1728 | + u'generation': u'314', |
1729 | + u'other_username': u'sharing-user', |
1730 | + u'other_visible_name': u'A Sharing User', |
1731 | + u'access_level': u'Modify', |
1732 | + u'node_id': u'e95662f7-9979-4745-8c21-8edaf893f143', |
1733 | + u'volume_id': u'8896e8f8-57a3-4bf9-8558-fc54b7a3a777', |
1734 | + u'free_bytes': u'108786811673', |
1735 | + u'path': SHARES_PATH + u'/read-write from A Sharing User', |
1736 | + u'accepted': u'True', |
1737 | + u'type': u'Share'}, |
1738 | + |
1739 | + {u'accepted': u'', |
1740 | + u'access_level': u'View', |
1741 | + u'free_bytes': u'', |
1742 | + u'generation': u'', |
1743 | + u'name': u'unaccepted', |
1744 | + u'node_id': u'67b61c92-855c-49d8-8e09-6d201d15c999', |
1745 | + u'other_username': u'bad guy', |
1746 | + u'other_visible_name': u'', |
1747 | + u'path': SHARES_PATH + u'/unaccepted from ', |
1748 | + u'subscribed': u'', |
1749 | + u'type': u'Share', |
1750 | + u'volume_id': u'19963c95-c684-48db-8668-ebe6d820d5c3'}, |
1751 | ] |
1752 | |
1753 | SAMPLE_SHARED = [ |
1754 | @@ -196,7 +272,7 @@ |
1755 | u'node_id': u'31e47530-9448-4f03-b4dc-4154fdf35225', |
1756 | u'other_username': u'otheruser', |
1757 | u'other_visible_name': u'Other User', |
1758 | - u'path': u'/home/tester/Ubuntu One/bar', |
1759 | + u'path': ROOT_PATH + u'/bar', |
1760 | u'type': u'Shared', |
1761 | u'volume_id': u'79584900-517f-4dff-b2f3-20e8c1e79365'}, |
1762 | ] |
1763 | |
1764 | === modified file 'ubuntuone/controlpanel/tests/test_backend.py' |
1765 | --- ubuntuone/controlpanel/tests/test_backend.py 2011-01-25 19:08:59 +0000 |
1766 | +++ ubuntuone/controlpanel/tests/test_backend.py 2011-02-12 03:22:55 +0000 |
1767 | @@ -19,6 +19,8 @@ |
1768 | |
1769 | """Tests for the control panel backend.""" |
1770 | |
1771 | +from collections import defaultdict |
1772 | + |
1773 | import simplejson |
1774 | |
1775 | from twisted.internet import defer |
1776 | @@ -42,6 +44,7 @@ |
1777 | EXPECTED_ACCOUNT_INFO, |
1778 | EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN, |
1779 | EXPECTED_DEVICES_INFO, |
1780 | + ROOT_PATH, |
1781 | SAMPLE_ACCOUNT_NO_CURRENT_PLAN, |
1782 | SAMPLE_ACCOUNT_WITH_CURRENT_PLAN, |
1783 | SAMPLE_DEVICES_JSON, |
1784 | @@ -49,6 +52,8 @@ |
1785 | SAMPLE_QUOTA_JSON, |
1786 | SAMPLE_SHARED, |
1787 | SAMPLE_SHARES, |
1788 | + SHARES_PATH, |
1789 | + SHARES_PATH_LINK, |
1790 | TOKEN, |
1791 | ) |
1792 | from ubuntuone.controlpanel.webclient import WebClientError |
1793 | @@ -86,8 +91,8 @@ |
1794 | } |
1795 | status_changed_handler = None |
1796 | subscribed_folders = [] |
1797 | + subscribed_shares = [] |
1798 | actions = [] |
1799 | - root_dir = u'/home/tester/Something/Pepe Mosquito' |
1800 | |
1801 | def get_credentials(self): |
1802 | """Return the mock credentials.""" |
1803 | @@ -145,7 +150,15 @@ |
1804 | |
1805 | def get_root_dir(self): |
1806 | """Grab the root dir.""" |
1807 | - return self.root_dir |
1808 | + return ROOT_PATH |
1809 | + |
1810 | + def get_shares_dir(self): |
1811 | + """Grab the shares dir.""" |
1812 | + return SHARES_PATH |
1813 | + |
1814 | + def get_shares_dir_link(self): |
1815 | + """Grab the shares dir.""" |
1816 | + return SHARES_PATH_LINK |
1817 | |
1818 | def get_folders(self): |
1819 | """Grab list of folders.""" |
1820 | @@ -153,11 +166,23 @@ |
1821 | |
1822 | def subscribe_folder(self, volume_id): |
1823 | """Subcribe to 'volume_id'.""" |
1824 | - self.subscribed_folders.append(volume_id) |
1825 | + MockDBusClient.subscribed_folders.append(volume_id) |
1826 | |
1827 | def unsubscribe_folder(self, volume_id): |
1828 | """Unsubcribe from 'volume_id'.""" |
1829 | - self.subscribed_folders.remove(volume_id) |
1830 | + MockDBusClient.subscribed_folders.remove(volume_id) |
1831 | + |
1832 | + def get_shares(self): |
1833 | + """Grab list of shares.""" |
1834 | + return SAMPLE_SHARES |
1835 | + |
1836 | + def subscribe_share(self, volume_id): |
1837 | + """Subcribe to 'volume_id'.""" |
1838 | + MockDBusClient.subscribed_shares.append(volume_id) |
1839 | + |
1840 | + def unsubscribe_share(self, volume_id): |
1841 | + """Unsubcribe from 'volume_id'.""" |
1842 | + MockDBusClient.subscribed_shares.remove(volume_id) |
1843 | |
1844 | def get_current_status(self): |
1845 | """Grab syncdaemon status.""" |
1846 | @@ -167,10 +192,6 @@ |
1847 | """Connect a handler for tracking syncdaemon status changes.""" |
1848 | self.status_changed_handler = handler |
1849 | |
1850 | - def get_shares(self): |
1851 | - """Grab list of shares (shares from others to the user).""" |
1852 | - return SAMPLE_SHARES |
1853 | - |
1854 | def get_shared(self): |
1855 | """Grab list of shared (shares from the user to others).""" |
1856 | return SAMPLE_SHARED |
1857 | @@ -370,40 +391,106 @@ |
1858 | class BackendVolumesTestCase(BackendBasicTestCase): |
1859 | """Volumes tests for the backend.""" |
1860 | |
1861 | + def setUp(self): |
1862 | + super(BackendVolumesTestCase, self).setUp() |
1863 | + # fake quota info and calculate free bytes |
1864 | + # pylint: disable=E1101 |
1865 | + self.be.wc.results[ACCOUNT_API] = SAMPLE_ACCOUNT_NO_CURRENT_PLAN |
1866 | + self.be.wc.results[QUOTA_API] = SAMPLE_QUOTA_JSON |
1867 | + |
1868 | @inlineCallbacks |
1869 | def test_volumes_info(self): |
1870 | """The volumes_info method exercises its callback.""" |
1871 | - # fake quota info and calculate free bytes |
1872 | - # pylint: disable=E1101 |
1873 | - self.be.wc.results[ACCOUNT_API] = SAMPLE_ACCOUNT_NO_CURRENT_PLAN |
1874 | - self.be.wc.results[QUOTA_API] = SAMPLE_QUOTA_JSON |
1875 | result = yield self.be.account_info() |
1876 | free_bytes = int(result['quota_total']) - int(result['quota_used']) |
1877 | |
1878 | # get root dir info |
1879 | - root_dir = MockDBusClient.root_dir |
1880 | - root_volume = {u'volume_id': u'', u'path': root_dir, |
1881 | - u'subscribed': 'True', u'type': u'ROOT'} |
1882 | + root_volume = {u'volume_id': u'', u'path': ROOT_PATH, |
1883 | + u'subscribed': 'True', u'type': self.be.ROOT_TYPE} |
1884 | + |
1885 | + # get shares and group by sharing user |
1886 | + shares = defaultdict(list) |
1887 | + for share in SAMPLE_SHARES: |
1888 | + # filter out non accepted values |
1889 | + if not bool(share['accepted']): |
1890 | + continue |
1891 | + |
1892 | + share = share.copy() |
1893 | + |
1894 | + nicer_path = share[u'path'].replace(SHARES_PATH, SHARES_PATH_LINK) |
1895 | + share[u'path'] = nicer_path |
1896 | + share[u'type'] = self.be.SHARE_TYPE |
1897 | + |
1898 | + username = share['other_visible_name'] |
1899 | + if not username: |
1900 | + username = share['other_username'] + \ |
1901 | + ' (%s)' % self.be.NAME_NOT_SET |
1902 | + |
1903 | + shares[username].append(share) |
1904 | + |
1905 | + folders = [] |
1906 | + for folder in SAMPLE_FOLDERS: |
1907 | + folder = folder.copy() |
1908 | + folder[u'type'] = self.be.FOLDER_TYPE |
1909 | + folders.append(folder) |
1910 | + |
1911 | + expected = [(u'', unicode(free_bytes), [root_volume] + folders)] |
1912 | + for other_user, data in shares.iteritems(): |
1913 | + expected.append((other_user, data[0][u'free_bytes'], data)) |
1914 | |
1915 | result = yield self.be.volumes_info() |
1916 | - |
1917 | - expected = [('', unicode(free_bytes), [root_volume] + SAMPLE_FOLDERS)] |
1918 | - # later, we should unify folders and shares info |
1919 | self.assertEqual(result, expected) |
1920 | |
1921 | - @inlineCallbacks |
1922 | - def test_subscribe_volume(self): |
1923 | + # pylint: disable=W0212 |
1924 | + |
1925 | + def test_cached_volumes_are_initially_empty(self): |
1926 | + """The cached volume list is empty.""" |
1927 | + self.assertEqual(self.be._volumes, {}) |
1928 | + |
1929 | + @inlineCallbacks |
1930 | + def test_volumes_are_cached(self): |
1931 | + """The volume list is cached.""" |
1932 | + # get root dir info |
1933 | + root_volume = {u'volume_id': u'', u'path': ROOT_PATH, |
1934 | + u'subscribed': 'True', u'type': self.be.ROOT_TYPE} |
1935 | + expected = {u'': root_volume} |
1936 | + for volume in SAMPLE_SHARES + SAMPLE_FOLDERS: |
1937 | + if volume[u'type'] == u'UDF': |
1938 | + volume[u'type'] = self.be.FOLDER_TYPE |
1939 | + else: |
1940 | + volume[u'type'] = self.be.SHARE_TYPE |
1941 | + |
1942 | + sid = volume['volume_id'] |
1943 | + assert sid not in expected |
1944 | + expected[sid] = volume |
1945 | + |
1946 | + _ = yield self.be.volumes_info() |
1947 | + |
1948 | + self.assertEqual(self.be._volumes, expected) |
1949 | + |
1950 | + @inlineCallbacks |
1951 | + def test_cached_volumes_are_updated_with_volume_info(self): |
1952 | + """The cached volume list is updated.""" |
1953 | + yield self.test_volumes_are_cached() |
1954 | + yield self.test_volumes_are_cached() |
1955 | + |
1956 | + @inlineCallbacks |
1957 | + def test_subscribe_volume_folder(self): |
1958 | """The subscribe_volume method subscribes a folder.""" |
1959 | fid = '0123-4567' |
1960 | + self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE} |
1961 | + |
1962 | yield self.be.subscribe_volume(volume_id=fid) |
1963 | self.addCleanup(lambda: MockDBusClient.subscribed_folders.remove(fid)) |
1964 | |
1965 | self.assertEqual(MockDBusClient.subscribed_folders, [fid]) |
1966 | |
1967 | @inlineCallbacks |
1968 | - def test_unsubscribe_volume(self): |
1969 | + def test_unsubscribe_volume_folder(self): |
1970 | """The unsubscribe_volume method unsubscribes a folder.""" |
1971 | fid = '0123-4567' |
1972 | + self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE} |
1973 | + |
1974 | yield self.be.subscribe_volume(volume_id=fid) |
1975 | self.assertEqual(MockDBusClient.subscribed_folders, [fid]) |
1976 | |
1977 | @@ -412,9 +499,34 @@ |
1978 | self.assertEqual(MockDBusClient.subscribed_folders, []) |
1979 | |
1980 | @inlineCallbacks |
1981 | - def test_change_volume_settings(self): |
1982 | + def test_subscribe_volume_share(self): |
1983 | + """The subscribe_volume method subscribes a share.""" |
1984 | + sid = '0123-4567' |
1985 | + self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE} |
1986 | + |
1987 | + yield self.be.subscribe_volume(volume_id=sid) |
1988 | + self.addCleanup(lambda: MockDBusClient.subscribed_shares.remove(sid)) |
1989 | + |
1990 | + self.assertEqual(MockDBusClient.subscribed_shares, [sid]) |
1991 | + |
1992 | + @inlineCallbacks |
1993 | + def test_unsubscribe_volume_share(self): |
1994 | + """The unsubscribe_volume method unsubscribes a share.""" |
1995 | + sid = '0123-4567' |
1996 | + self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE} |
1997 | + |
1998 | + yield self.be.subscribe_volume(volume_id=sid) |
1999 | + self.assertEqual(MockDBusClient.subscribed_shares, [sid]) |
2000 | + |
2001 | + yield self.be.unsubscribe_volume(volume_id=sid) |
2002 | + |
2003 | + self.assertEqual(MockDBusClient.subscribed_shares, []) |
2004 | + |
2005 | + @inlineCallbacks |
2006 | + def test_change_volume_settings_folder(self): |
2007 | """The volume settings can be changed.""" |
2008 | fid = '0123-4567' |
2009 | + self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE} |
2010 | |
2011 | yield self.be.change_volume_settings(fid, {'subscribed': 'True'}) |
2012 | self.assertEqual(MockDBusClient.subscribed_folders, [fid]) |
2013 | @@ -423,10 +535,23 @@ |
2014 | self.assertEqual(MockDBusClient.subscribed_folders, []) |
2015 | |
2016 | @inlineCallbacks |
2017 | + def test_change_volume_settings_share(self): |
2018 | + """The volume settings can be changed.""" |
2019 | + sid = '0123-4567' |
2020 | + self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE} |
2021 | + |
2022 | + yield self.be.change_volume_settings(sid, {'subscribed': 'True'}) |
2023 | + self.assertEqual(MockDBusClient.subscribed_shares, [sid]) |
2024 | + |
2025 | + yield self.be.change_volume_settings(sid, {'subscribed': ''}) |
2026 | + self.assertEqual(MockDBusClient.subscribed_shares, []) |
2027 | + |
2028 | + @inlineCallbacks |
2029 | def test_change_volume_settings_no_setting(self): |
2030 | """The change volume settings does not fail on empty settings.""" |
2031 | yield self.be.change_volume_settings('test', {}) |
2032 | self.assertEqual(MockDBusClient.subscribed_folders, []) |
2033 | + self.assertEqual(MockDBusClient.subscribed_shares, []) |
2034 | |
2035 | |
2036 | class BackendSyncStatusTestCase(BackendBasicTestCase): |
Looks good