Merge lp:~nataliabidart/ubuntuone-control-panel/devices into lp:ubuntuone-control-panel
- devices
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Natalia Bidart | ||||||||
Approved revision: | 46 | ||||||||
Merged at revision: | 36 | ||||||||
Proposed branch: | lp:~nataliabidart/ubuntuone-control-panel/devices | ||||||||
Merge into: | lp:ubuntuone-control-panel | ||||||||
Prerequisite: | lp:~nataliabidart/ubuntuone-control-panel/more-logging-and-ids | ||||||||
Diff against target: |
927 lines (+711/-22) 5 files modified
data/device.ui (+201/-0) data/devices.ui (+31/-1) data/folders.ui (+1/-1) ubuntuone/controlpanel/gtk/gui.py (+172/-9) ubuntuone/controlpanel/gtk/tests/test_gui.py (+306/-11) |
||||||||
To merge this branch: | bzr merge lp:~nataliabidart/ubuntuone-control-panel/devices | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Roman Yepishev (community) | fieldtest | Approve | |
Review via email: mp+43975@code.launchpad.net |
Description of the change
To run the tests, do:
./run-tests
To test IRL, open 3 terminals pointing to this branch, and run:
terminal 1: DEBUG=True PYTHONPATH=. ./bin/ubuntuone
terminal 2: DEBUG=True PYTHONPATH=. ./bin/ubuntuone
and play with the 3rd tab. Note: the 'Remove' device button so far does nothing. Throttling settings are properly updated, you can check by looking the ~/.config/
Martin Albisetti (beuno) wrote : | # |
Roman Yepishev (rye) wrote : | # |
When an attempt is made to change max upload/download speed or enable/disable bandwidth usage limits some kind of flickering occurs which is not present in ubuntuone-
This can be seen here - http://
- 45. By Natalia Bidart
-
Merged trunk in.
- 46. By Natalia Bidart
-
UI is not disabled while changing setting for a device.
Roman Yepishev (rye) wrote : | # |
Yep, no flickering occurs.
Roberto Alsina (ralsina) wrote : | # |
Approved, fieldtested on narwhal.
Preview Diff
1 | === added file 'data/device.ui' |
2 | --- data/device.ui 1970-01-01 00:00:00 +0000 |
3 | +++ data/device.ui 2010-12-17 17:27:34 +0000 |
4 | @@ -0,0 +1,201 @@ |
5 | +<?xml version="1.0" encoding="UTF-8"?> |
6 | +<interface> |
7 | + <requires lib="gtk+" version="2.16"/> |
8 | + <!-- interface-naming-policy project-wide --> |
9 | + <object class="GtkVBox" id="itself"> |
10 | + <property name="visible">True</property> |
11 | + <property name="spacing">5</property> |
12 | + <child> |
13 | + <object class="GtkHBox" id="hbox1"> |
14 | + <property name="visible">True</property> |
15 | + <property name="spacing">15</property> |
16 | + <child> |
17 | + <object class="GtkVBox" id="vbox1"> |
18 | + <property name="visible">True</property> |
19 | + <property name="spacing">5</property> |
20 | + <child> |
21 | + <object class="GtkHBox" id="hbox2"> |
22 | + <property name="visible">True</property> |
23 | + <child> |
24 | + <object class="GtkImage" id="device_type"> |
25 | + <property name="visible">True</property> |
26 | + <property name="icon_name">computer</property> |
27 | + </object> |
28 | + <packing> |
29 | + <property name="expand">False</property> |
30 | + <property name="position">0</property> |
31 | + </packing> |
32 | + </child> |
33 | + <child> |
34 | + <object class="GtkLabel" id="device_name"> |
35 | + <property name="visible">True</property> |
36 | + <property name="xalign">0</property> |
37 | + <property name="xpad">5</property> |
38 | + <property name="label">My Laptop</property> |
39 | + </object> |
40 | + <packing> |
41 | + <property name="position">1</property> |
42 | + </packing> |
43 | + </child> |
44 | + <child> |
45 | + <object class="GtkLabel" id="device_id"> |
46 | + <property name="label" translatable="yes">device id (hidden)</property> |
47 | + </object> |
48 | + <packing> |
49 | + <property name="expand">False</property> |
50 | + <property name="position">2</property> |
51 | + </packing> |
52 | + </child> |
53 | + <child> |
54 | + <object class="GtkLabel" id="date_added"> |
55 | + <property name="label">30.02.09</property> |
56 | + </object> |
57 | + <packing> |
58 | + <property name="expand">False</property> |
59 | + <property name="position">3</property> |
60 | + </packing> |
61 | + </child> |
62 | + </object> |
63 | + <packing> |
64 | + <property name="position">0</property> |
65 | + </packing> |
66 | + </child> |
67 | + <child> |
68 | + <object class="GtkTable" id="throttling"> |
69 | + <property name="visible">True</property> |
70 | + <property name="n_rows">3</property> |
71 | + <property name="n_columns">2</property> |
72 | + <property name="column_spacing">3</property> |
73 | + <property name="row_spacing">3</property> |
74 | + <child> |
75 | + <object class="GtkCheckButton" id="limit_bandwidth"> |
76 | + <property name="label" translatable="yes">Limit bandwidth usage</property> |
77 | + <property name="visible">True</property> |
78 | + <property name="can_focus">True</property> |
79 | + <property name="receives_default">False</property> |
80 | + <property name="draw_indicator">True</property> |
81 | + <signal name="toggled" handler="on_limit_bandwidth_toggled"/> |
82 | + </object> |
83 | + </child> |
84 | + <child> |
85 | + <object class="GtkLabel" id="max_upload_speed_label"> |
86 | + <property name="visible">True</property> |
87 | + <property name="xalign">1</property> |
88 | + <property name="xpad">5</property> |
89 | + <property name="label" translatable="yes">Max upload speed (KiB/s)</property> |
90 | + </object> |
91 | + <packing> |
92 | + <property name="top_attach">1</property> |
93 | + <property name="bottom_attach">2</property> |
94 | + </packing> |
95 | + </child> |
96 | + <child> |
97 | + <object class="GtkLabel" id="max_download_speed_label"> |
98 | + <property name="visible">True</property> |
99 | + <property name="xalign">1</property> |
100 | + <property name="xpad">5</property> |
101 | + <property name="label" translatable="yes">Max download speed (KiB/s)</property> |
102 | + </object> |
103 | + <packing> |
104 | + <property name="top_attach">2</property> |
105 | + <property name="bottom_attach">3</property> |
106 | + </packing> |
107 | + </child> |
108 | + <child> |
109 | + <object class="GtkSpinButton" id="max_upload_speed"> |
110 | + <property name="visible">True</property> |
111 | + <property name="can_focus">True</property> |
112 | + <property name="invisible_char">•</property> |
113 | + <property name="activates_default">True</property> |
114 | + <property name="adjustment">adjustment1</property> |
115 | + <signal name="value_changed" handler="on_max_upload_speed_value_changed"/> |
116 | + </object> |
117 | + <packing> |
118 | + <property name="left_attach">1</property> |
119 | + <property name="right_attach">2</property> |
120 | + <property name="top_attach">1</property> |
121 | + <property name="bottom_attach">2</property> |
122 | + <property name="x_options">GTK_FILL</property> |
123 | + <property name="y_options">GTK_FILL</property> |
124 | + </packing> |
125 | + </child> |
126 | + <child> |
127 | + <object class="GtkSpinButton" id="max_download_speed"> |
128 | + <property name="visible">True</property> |
129 | + <property name="can_focus">True</property> |
130 | + <property name="invisible_char">•</property> |
131 | + <property name="activates_default">True</property> |
132 | + <property name="adjustment">adjustment2</property> |
133 | + <signal name="value_changed" handler="on_max_download_speed_value_changed"/> |
134 | + </object> |
135 | + <packing> |
136 | + <property name="left_attach">1</property> |
137 | + <property name="right_attach">2</property> |
138 | + <property name="top_attach">2</property> |
139 | + <property name="bottom_attach">3</property> |
140 | + </packing> |
141 | + </child> |
142 | + <child> |
143 | + <placeholder/> |
144 | + </child> |
145 | + </object> |
146 | + <packing> |
147 | + <property name="expand">False</property> |
148 | + <property name="position">1</property> |
149 | + </packing> |
150 | + </child> |
151 | + </object> |
152 | + <packing> |
153 | + <property name="expand">False</property> |
154 | + <property name="position">0</property> |
155 | + </packing> |
156 | + </child> |
157 | + <child> |
158 | + <object class="GtkVButtonBox" id="vbuttonbox1"> |
159 | + <property name="visible">True</property> |
160 | + <property name="layout_style">start</property> |
161 | + <child> |
162 | + <object class="GtkButton" id="remove"> |
163 | + <property name="label">gtk-remove</property> |
164 | + <property name="visible">True</property> |
165 | + <property name="can_focus">True</property> |
166 | + <property name="receives_default">True</property> |
167 | + <property name="use_stock">True</property> |
168 | + </object> |
169 | + <packing> |
170 | + <property name="expand">False</property> |
171 | + <property name="fill">False</property> |
172 | + <property name="position">0</property> |
173 | + </packing> |
174 | + </child> |
175 | + </object> |
176 | + <packing> |
177 | + <property name="expand">False</property> |
178 | + <property name="pack_type">end</property> |
179 | + <property name="position">1</property> |
180 | + </packing> |
181 | + </child> |
182 | + </object> |
183 | + <packing> |
184 | + <property name="expand">False</property> |
185 | + <property name="position">0</property> |
186 | + </packing> |
187 | + </child> |
188 | + <child> |
189 | + <object class="GtkLabel" id="warning_label"> |
190 | + <property name="visible">True</property> |
191 | + </object> |
192 | + <packing> |
193 | + <property name="position">1</property> |
194 | + </packing> |
195 | + </child> |
196 | + </object> |
197 | + <object class="GtkAdjustment" id="adjustment1"> |
198 | + <property name="upper">10000</property> |
199 | + <property name="step_increment">1</property> |
200 | + </object> |
201 | + <object class="GtkAdjustment" id="adjustment2"> |
202 | + <property name="upper">10000</property> |
203 | + <property name="step_increment">1</property> |
204 | + </object> |
205 | +</interface> |
206 | |
207 | === modified file 'data/devices.ui' |
208 | --- data/devices.ui 2010-10-21 21:14:24 +0000 |
209 | +++ data/devices.ui 2010-12-17 17:27:34 +0000 |
210 | @@ -7,7 +7,37 @@ |
211 | <property name="border_width">10</property> |
212 | <property name="spacing">10</property> |
213 | <child> |
214 | - <placeholder/> |
215 | + <object class="GtkScrolledWindow" id="scrolledwindow1"> |
216 | + <property name="visible">True</property> |
217 | + <property name="can_focus">True</property> |
218 | + <property name="hscrollbar_policy">automatic</property> |
219 | + <property name="vscrollbar_policy">automatic</property> |
220 | + <child> |
221 | + <object class="GtkViewport" id="viewport1"> |
222 | + <property name="visible">True</property> |
223 | + <property name="resize_mode">queue</property> |
224 | + <property name="shadow_type">none</property> |
225 | + <child> |
226 | + <object class="GtkAlignment" id="alignment1"> |
227 | + <property name="visible">True</property> |
228 | + <property name="xscale">0</property> |
229 | + <property name="yscale">0</property> |
230 | + <child> |
231 | + <object class="GtkVBox" id="devices"> |
232 | + <property name="visible">True</property> |
233 | + <child> |
234 | + <placeholder/> |
235 | + </child> |
236 | + </object> |
237 | + </child> |
238 | + </object> |
239 | + </child> |
240 | + </object> |
241 | + </child> |
242 | + </object> |
243 | + <packing> |
244 | + <property name="position">0</property> |
245 | + </packing> |
246 | </child> |
247 | </object> |
248 | </interface> |
249 | |
250 | === modified file 'data/folders.ui' |
251 | --- data/folders.ui 2010-12-14 15:54:29 +0000 |
252 | +++ data/folders.ui 2010-12-17 17:27:34 +0000 |
253 | @@ -31,7 +31,7 @@ |
254 | <property name="resize_mode">queue</property> |
255 | <property name="shadow_type">none</property> |
256 | <child> |
257 | - <object class="GtkAlignment" id="folders_alignment"> |
258 | + <object class="GtkAlignment" id="folders"> |
259 | <property name="visible">True</property> |
260 | <property name="xscale">0</property> |
261 | <property name="yscale">0</property> |
262 | |
263 | === modified file 'ubuntuone/controlpanel/gtk/gui.py' |
264 | --- ubuntuone/controlpanel/gtk/gui.py 2010-12-14 19:16:25 +0000 |
265 | +++ ubuntuone/controlpanel/gtk/gui.py 2010-12-17 17:27:34 +0000 |
266 | @@ -47,6 +47,8 @@ |
267 | |
268 | from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH, |
269 | DBUS_PREFERENCES_IFACE) |
270 | +from ubuntuone.controlpanel.backend import (DEVICE_TYPE_PHONE, |
271 | + DEVICE_TYPE_COMPUTER) |
272 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
273 | from ubuntuone.controlpanel.utils import get_data_file |
274 | |
275 | @@ -69,6 +71,12 @@ |
276 | _('Sync data between computers.\n' |
277 | '<small>«this is custom text #<i>N</i>»</small>')] |
278 | WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE |
279 | +KILOBYTES = 1024 |
280 | + |
281 | + |
282 | +def bool_str(value): |
283 | + """Return the string representation of a bool (dbus-compatible).""" |
284 | + return 'True' if value else '' |
285 | |
286 | |
287 | def filter_by_app_name(f): |
288 | @@ -151,6 +159,8 @@ |
289 | self.set_title(self.TITLE % {'app_name': U1_APP_NAME}) |
290 | self.set_position(gtk.WIN_POS_CENTER_ALWAYS) |
291 | self.set_icon_name('ubuntuone') |
292 | + self.set_geometry_hints(max_width=736, max_height=525) # bug #683164 |
293 | + |
294 | self.connect('delete-event', lambda w, e: gtk.main_quit()) |
295 | self.show() |
296 | |
297 | @@ -377,7 +387,7 @@ |
298 | def __init__(self): |
299 | UbuntuOneBin.__init__(self) |
300 | ControlPanelMixin.__init__(self, filename='account.ui') |
301 | - self.pack_start(self.itself) |
302 | + self.add(self.itself) |
303 | self.show() |
304 | |
305 | self.backend.connect_to_signal('AccountInfoReady', |
306 | @@ -421,7 +431,7 @@ |
307 | def __init__(self): |
308 | UbuntuOneBin.__init__(self) |
309 | ControlPanelMixin.__init__(self, filename='folders.ui') |
310 | - self.pack_start(self.itself) |
311 | + self.add(self.itself) |
312 | self.show_all() |
313 | |
314 | self.backend.connect_to_signal('VolumesInfoReady', |
315 | @@ -433,13 +443,12 @@ |
316 | self.label_alignment.add(self.volumes_label) |
317 | self._subscribed = [] |
318 | |
319 | - @log_call(logger.debug) |
320 | def on_volumes_info_ready(self, info): |
321 | """Backend notifies of volumes info.""" |
322 | self.volumes_label.stop() |
323 | |
324 | if self.volumes is not None: |
325 | - self.folders_alignment.remove(self.volumes) |
326 | + self.folders.remove(self.volumes) |
327 | self.volumes = None |
328 | |
329 | if not info: |
330 | @@ -473,7 +482,7 @@ |
331 | self._subscribed.append(subscribed) |
332 | self.volumes.attach(subscribed, 1, 2, i + 1, i + 2, xoptions=0) |
333 | |
334 | - self.folders_alignment.add(self.volumes) |
335 | + self.folders.add(self.volumes) |
336 | |
337 | @log_call(logger.error) |
338 | def on_volumes_info_error(self, error_dict=None): |
339 | @@ -485,7 +494,7 @@ |
340 | def on_subscribed_clicked(self, checkbutton): |
341 | """The user toggled 'checkbutton'.""" |
342 | volume_id = checkbutton.get_label() |
343 | - subscribed = 'True' if checkbutton.get_active() else '' |
344 | + subscribed = bool_str(checkbutton.get_active()) |
345 | self.backend.change_volume_settings(volume_id, |
346 | {'subscribed': subscribed}) |
347 | |
348 | @@ -495,6 +504,140 @@ |
349 | self.volumes_label.start() |
350 | |
351 | |
352 | +class Device(gtk.VBox, ControlPanelMixin): |
353 | + """The devices panel.""" |
354 | + |
355 | + DEVICE_CHANGE_ERROR = _('The settings could not be changed,\n' |
356 | + 'previous values were restored.') |
357 | + |
358 | + def __init__(self): |
359 | + gtk.VBox.__init__(self) |
360 | + ControlPanelMixin.__init__(self, filename='device.ui') |
361 | + |
362 | + self._updating = False |
363 | + self._last_settings = {} |
364 | + self.configurable = False |
365 | + |
366 | + self.update(device_id='', device_name='', limit_bandwidth=False, |
367 | + max_upload_speed=0, max_download_speed=0) |
368 | + |
369 | + self.add(self.itself) |
370 | + self.device_id.hide() |
371 | + self.show() |
372 | + |
373 | + self.backend.connect_to_signal('DeviceSettingsChanged', |
374 | + self.on_device_settings_changed) |
375 | + self.backend.connect_to_signal('DeviceSettingsChangeError', |
376 | + self.on_device_settings_change_error) |
377 | + |
378 | + def _change_device_settings(self, *args): |
379 | + """Update backend settings for this device.""" |
380 | + if self._updating: |
381 | + return |
382 | + |
383 | + # Not disabling the GUI to avoid annyong twitchings |
384 | + #self.set_sensitive(False) |
385 | + self.warning_label.set_text('') |
386 | + self.backend.change_device_settings(self.device_id.get_text(), |
387 | + self.__dict__) |
388 | + |
389 | + def _block_signals(f): |
390 | + """Execute 'f' while having the _updating flag set.""" |
391 | + |
392 | + # pylint: disable=E0213,W0212,E1102 |
393 | + |
394 | + @wraps(f) |
395 | + def inner(self, *args, **kwargs): |
396 | + """Execute 'f' while having the _updating flag set.""" |
397 | + old = self._updating |
398 | + self._updating = True |
399 | + |
400 | + result = f(self, *args, **kwargs) |
401 | + |
402 | + self._updating = old |
403 | + return result |
404 | + |
405 | + return inner |
406 | + |
407 | + on_limit_bandwidth_toggled = _change_device_settings |
408 | + on_max_upload_speed_value_changed = _change_device_settings |
409 | + on_max_download_speed_value_changed = _change_device_settings |
410 | + |
411 | + @_block_signals |
412 | + def update(self, **kwargs): |
413 | + """Update according to named parameters. |
414 | + |
415 | + Possible settings are: |
416 | + * device_id (string, not shown to the user) |
417 | + * device_name (string) |
418 | + * type (either DEVICE_TYPE_PHONE or DEVICE_TYPE_COMPUTER) |
419 | + * configurable (True/False) |
420 | + * if configurable, the following can be set: |
421 | + * limit_bandwidth (True/False) |
422 | + * max_upload_speed (bytes) |
423 | + * max_download_speed (bytes) |
424 | + |
425 | + """ |
426 | + if 'device_id' in kwargs: |
427 | + self.device_id.set_text(kwargs['device_id']) |
428 | + |
429 | + if 'device_name' in kwargs: |
430 | + self.device_name.set_markup('<b>%s</b>' % kwargs['device_name']) |
431 | + |
432 | + if 'device_type' in kwargs: |
433 | + dtype = kwargs['device_type'] |
434 | + if dtype in (DEVICE_TYPE_COMPUTER, DEVICE_TYPE_PHONE): |
435 | + self.device_type.set_from_icon_name(dtype.lower(), |
436 | + gtk.ICON_SIZE_BUTTON) |
437 | + |
438 | + if 'configurable' in kwargs: |
439 | + self.configurable = bool(kwargs['configurable']) |
440 | + self.throttling.set_visible(self.configurable) |
441 | + |
442 | + if 'limit_bandwidth' in kwargs: |
443 | + self.limit_bandwidth.set_active(bool(kwargs['limit_bandwidth'])) |
444 | + |
445 | + for speed in ('max_upload_speed', 'max_download_speed'): |
446 | + if speed in kwargs: |
447 | + value = int(kwargs[speed]) // KILOBYTES |
448 | + getattr(self, speed).set_value(value) |
449 | + |
450 | + self._last_settings = self.__dict__ |
451 | + |
452 | + @property |
453 | + def __dict__(self): |
454 | + result = { |
455 | + 'device_id': self.device_id.get_text(), |
456 | + 'device_name': self.device_name.get_text(), |
457 | + 'device_type': self.device_type.get_icon_name()[0].capitalize(), |
458 | + 'configurable': bool_str(self.configurable), |
459 | + 'limit_bandwidth': bool_str(self.limit_bandwidth.get_active()), |
460 | + 'max_upload_speed': \ |
461 | + str(self.max_upload_speed.get_value_as_int() * KILOBYTES), |
462 | + 'max_download_speed': \ |
463 | + str(self.max_download_speed.get_value_as_int() * KILOBYTES), |
464 | + } |
465 | + return result |
466 | + |
467 | + @log_call(logger.info) |
468 | + def on_device_settings_changed(self, device_id): |
469 | + """The change of this device settings succeded.""" |
470 | + if device_id != self.device_id.get_text(): |
471 | + return |
472 | + self.set_sensitive(True) |
473 | + self.warning_label.set_text('') |
474 | + self._last_settings = self.__dict__ |
475 | + |
476 | + @log_call(logger.error) |
477 | + def on_device_settings_change_error(self, device_id, error_dict=None): |
478 | + """The change of this device settings failed.""" |
479 | + if device_id != self.device_id.get_text(): |
480 | + return |
481 | + self.update(**self._last_settings) |
482 | + self._set_warning(self.DEVICE_CHANGE_ERROR, self.warning_label) |
483 | + self.set_sensitive(True) |
484 | + |
485 | + |
486 | class DevicesPanel(UbuntuOneBin, ControlPanelMixin): |
487 | """The devices panel.""" |
488 | |
489 | @@ -504,9 +647,29 @@ |
490 | def __init__(self): |
491 | UbuntuOneBin.__init__(self) |
492 | ControlPanelMixin.__init__(self, filename='devices.ui') |
493 | - self.pack_start(self.itself) |
494 | + self.add(self.itself) |
495 | self.show() |
496 | |
497 | + self.backend.connect_to_signal('DevicesInfoReady', |
498 | + self.on_devices_info_ready) |
499 | + self.backend.connect_to_signal('DevicesInfoError', |
500 | + self.on_devices_info_error) |
501 | + self.backend.devices_info() |
502 | + |
503 | + def on_devices_info_ready(self, info): |
504 | + """Backend notifies of devices info.""" |
505 | + for device_info in info: |
506 | + device = Device() |
507 | + device_info['device_name'] = device_info.pop('name', '') |
508 | + device_info['device_type'] = device_info.pop('type', |
509 | + DEVICE_TYPE_COMPUTER) |
510 | + device.update(**device_info) |
511 | + self.devices.pack_start(device) |
512 | + |
513 | + @log_call(logger.error) |
514 | + def on_devices_info_error(self, error_dict=None): |
515 | + """Backend notifies of an error when fetching volumes info.""" |
516 | + |
517 | |
518 | class ApplicationsPanel(UbuntuOneBin, ControlPanelMixin): |
519 | """The applications panel.""" |
520 | @@ -517,7 +680,7 @@ |
521 | def __init__(self): |
522 | UbuntuOneBin.__init__(self) |
523 | ControlPanelMixin.__init__(self, filename='applications.ui') |
524 | - self.pack_start(self.itself) |
525 | + self.add(self.itself) |
526 | self.show() |
527 | |
528 | |
529 | @@ -543,7 +706,7 @@ |
530 | def __init__(self): |
531 | gtk.VBox.__init__(self) |
532 | ControlPanelMixin.__init__(self, filename='management.ui') |
533 | - self.pack_start(self.itself) |
534 | + self.add(self.itself) |
535 | self.show() |
536 | |
537 | self.backend.connect_to_signal('AccountInfoReady', |
538 | |
539 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py' |
540 | --- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-14 19:16:25 +0000 |
541 | +++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-17 17:27:34 +0000 |
542 | @@ -45,6 +45,23 @@ |
543 | {'volume_id': '2', 'suggested_path': '~/baz', 'subscribed': 'True'}, |
544 | ] |
545 | |
546 | +FAKE_DEVICE_INFO = { |
547 | + 'device_id': '1258-6854', 'device_name': 'Baz', 'device_type': 'Computer', |
548 | + 'configurable': 'True', 'limit_bandwidth': 'True', |
549 | + 'max_upload_speed': '1000', 'max_download_speed': '72548', |
550 | +} |
551 | + |
552 | +FAKE_DEVICES_INFO = [ |
553 | + {'device_id': '0', 'name': 'Foo', 'type': 'Computer', 'configurable': ''}, |
554 | + {'device_id': '1', 'name': 'Bar', 'type': 'Phone', 'configurable': ''}, |
555 | + {'device_id': '2', 'name': 'Z', 'type': 'Computer', |
556 | + 'configurable': 'True', 'limit_bandwidth': '', |
557 | + 'max_upload_speed': '0', 'max_download_speed': '0'}, |
558 | + {'device_id': '1258-6854', 'name': 'Baz', 'type': 'Computer', |
559 | + 'configurable': 'True', 'limit_bandwidth': 'True', |
560 | + 'max_upload_speed': '1000', 'max_download_speed': '72548'}, |
561 | +] |
562 | + |
563 | |
564 | class FakedObject(object): |
565 | """Fake an object, record every call.""" |
566 | @@ -108,8 +125,10 @@ |
567 | bus_name = gui.DBUS_BUS_NAME |
568 | object_path = gui.DBUS_PREFERENCES_PATH |
569 | iface = gui.DBUS_PREFERENCES_IFACE |
570 | - exposed_methods = ['account_info', 'devices_info', 'volumes_info', |
571 | - 'file_sync_status', 'change_volume_settings'] |
572 | + exposed_methods = [ |
573 | + 'account_info', 'devices_info', 'change_device_settings', |
574 | + 'volumes_info', 'change_volume_settings', 'file_sync_status', |
575 | + ] |
576 | |
577 | |
578 | class FakedSessionBus(object): |
579 | @@ -268,8 +287,8 @@ |
580 | self.assertEqual(self.ui.get_icon_name(), 'ubuntuone') |
581 | |
582 | def test_max_size(self): |
583 | - """Max size is not bigger than 966x576 (LP: #645526).""" |
584 | - self.assertTrue(self.ui.get_size_request() <= (966, 576)) |
585 | + """Max size is not bigger than 736x525 (LP: #645526, LP: #683164).""" |
586 | + self.assertTrue(self.ui.get_size_request() <= (736, 525)) |
587 | |
588 | |
589 | class ControlPanelTestCase(ControlPanelMixinTestCase): |
590 | @@ -801,8 +820,7 @@ |
591 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
592 | |
593 | self.assertFalse(self.ui.label_alignment.get_visible()) |
594 | - self.assertEqual(self.ui.folders_alignment.get_children(), |
595 | - [self.ui.volumes]) |
596 | + self.assertEqual(self.ui.folders.get_children(), [self.ui.volumes]) |
597 | |
598 | volumes = self.ui.volumes.get_children() |
599 | volumes.reverse() |
600 | @@ -828,8 +846,8 @@ |
601 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
602 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
603 | |
604 | - self.assertEqual(len(self.ui.folders_alignment.get_children()), 1) |
605 | - child = self.ui.folders_alignment.get_children()[0] |
606 | + self.assertEqual(len(self.ui.folders.get_children()), 1) |
607 | + child = self.ui.folders.get_children()[0] |
608 | self.assertEqual(child, self.ui.volumes) |
609 | |
610 | volumes = filter(lambda w: isinstance(w, gui.gtk.CheckButton), |
611 | @@ -840,7 +858,7 @@ |
612 | """When there are no volumes, a notification is shown.""" |
613 | self.ui.on_volumes_info_ready([]) |
614 | # no volumes table |
615 | - self.assertEqual(len(self.ui.folders_alignment.get_children()), 0) |
616 | + self.assertEqual(len(self.ui.folders.get_children()), 0) |
617 | self.assertTrue(self.ui.volumes is None) |
618 | |
619 | def test_on_subscribed_clicked(self): |
620 | @@ -852,14 +870,14 @@ |
621 | checkbutton.clicked() |
622 | fid = checkbutton.get_label() |
623 | |
624 | - subscribed = 'True' if checkbutton.get_active() else '' |
625 | + subscribed = gui.bool_str(checkbutton.get_active()) |
626 | self.assert_backend_called(method, |
627 | (fid, {'subscribed': subscribed})) |
628 | # clean backend calls |
629 | self.ui.backend._called.pop(method) |
630 | |
631 | checkbutton.clicked() |
632 | - subscribed = 'True' if checkbutton.get_active() else '' |
633 | + subscribed = gui.bool_str(checkbutton.get_active()) |
634 | self.assert_backend_called('change_volume_settings', |
635 | (fid, {'subscribed': subscribed})) |
636 | |
637 | @@ -881,6 +899,239 @@ |
638 | self.test_on_volumes_info_ready_with_no_volumes() |
639 | |
640 | |
641 | +class DeviceTestCase(ControlPanelMixinTestCase): |
642 | + """The test suite for the device widget.""" |
643 | + |
644 | + klass = gui.Device |
645 | + ui_filename = 'device.ui' |
646 | + |
647 | + def assert_device_equal(self, device, expected): |
648 | + """Assert that the device has the values from expected.""" |
649 | + self.assertEqual(device.device_id.get_text(), |
650 | + expected['device_id']) |
651 | + self.assertEqual(device.device_name.get_text(), |
652 | + expected['device_name']) |
653 | + self.assertEqual(device.device_type.get_icon_name()[0], |
654 | + expected['device_type'].lower()) |
655 | + self.assertEqual(device.configurable, |
656 | + bool(expected['configurable'])) |
657 | + self.assertEqual(device.limit_bandwidth.get_active(), |
658 | + bool(expected['limit_bandwidth'])) |
659 | + |
660 | + value = int(expected['max_upload_speed']) // gui.KILOBYTES |
661 | + self.assertEqual(device.max_upload_speed.get_value_as_int(), value) |
662 | + value = int(expected['max_download_speed']) // gui.KILOBYTES |
663 | + self.assertEqual(device.max_download_speed.get_value_as_int(), value) |
664 | + |
665 | + def assert_device_settings_changed(self): |
666 | + """Changing throttling settings updates the backend properly.""" |
667 | + expected = self.ui.__dict__ |
668 | + self.assert_backend_called('change_device_settings', |
669 | + (self.ui.device_id.get_text(), expected)) |
670 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
671 | + |
672 | + def modify_settings(self): |
673 | + """Modify settings so values actually change.""" |
674 | + new_val = not self.ui.limit_bandwidth.get_active() |
675 | + self.ui.limit_bandwidth.set_active(new_val) |
676 | + |
677 | + new_val = self.ui.max_upload_speed.get_value_as_int() + 1 |
678 | + self.ui.max_upload_speed.set_value(new_val) |
679 | + |
680 | + new_val = self.ui.max_download_speed.get_value_as_int() + 1 |
681 | + self.ui.max_download_speed.set_value(new_val) |
682 | + |
683 | + def test_is_a_vbox(self): |
684 | + """Inherits from VBox.""" |
685 | + self.assertIsInstance(self.ui, gui.gtk.VBox) |
686 | + |
687 | + def test_inner_widget_is_packed(self): |
688 | + """The 'itself' vbox is packed into the widget.""" |
689 | + self.assertIn(self.ui.itself, self.ui.get_children()) |
690 | + |
691 | + def test_is_visible(self): |
692 | + """Is visible.""" |
693 | + self.assertTrue(self.ui.get_visible()) |
694 | + |
695 | + def test_is_sensitive(self): |
696 | + """Is sensitive.""" |
697 | + self.assertTrue(self.ui.get_sensitive()) |
698 | + |
699 | + def test_warning_label_is_cleared(self): |
700 | + """The warning label is cleared.""" |
701 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
702 | + |
703 | + def test_device_id_is_hidden(self): |
704 | + """The device id label is hidden.""" |
705 | + self.assertFalse(self.ui.device_id.get_visible()) |
706 | + |
707 | + def test_default_values(self): |
708 | + """Default values are correct.""" |
709 | + self.assertEqual(self.ui.device_id.get_text(), '') |
710 | + self.assertEqual(self.ui.device_name.get_text(), '') |
711 | + self.assertEqual(self.ui.device_type.get_icon_name()[0], |
712 | + gui.DEVICE_TYPE_COMPUTER.lower()) |
713 | + self.assertEqual(self.ui.configurable, False) |
714 | + self.assertEqual(self.ui.limit_bandwidth.get_active(), False) |
715 | + self.assertEqual(self.ui.max_upload_speed.get_value_as_int(), 0) |
716 | + self.assertEqual(self.ui.max_download_speed.get_value_as_int(), 0) |
717 | + |
718 | + def test_init_does_not_call_backend(self): |
719 | + """When updating, the backend is not called.""" |
720 | + self.assertEqual(self.ui.backend._called, {}) |
721 | + |
722 | + def test_update_device_id(self): |
723 | + """A device can be updated from a dict.""" |
724 | + value = '741-822-963' |
725 | + self.ui.update(device_id=value) |
726 | + self.assertEqual(value, self.ui.device_id.get_text()) |
727 | + |
728 | + def test_update_device_name(self): |
729 | + """A device can be updated from a dict.""" |
730 | + value = 'The death star' |
731 | + self.ui.update(device_name=value) |
732 | + self.assertEqual(value, self.ui.device_name.get_text()) |
733 | + |
734 | + def test_update_unicode_device_name(self): |
735 | + """A device can be updated from a dict.""" |
736 | + value = u'Ñoño Ñandú' |
737 | + self.ui.update(device_name=value) |
738 | + self.assertEqual(value, self.ui.device_name.get_text()) |
739 | + |
740 | + def test_update_device_type_computer(self): |
741 | + """A device can be updated from a dict.""" |
742 | + dtype = gui.DEVICE_TYPE_COMPUTER |
743 | + self.ui.update(device_type=dtype) |
744 | + self.assertEqual((dtype.lower(), gui.gtk.ICON_SIZE_BUTTON), |
745 | + self.ui.device_type.get_icon_name()) |
746 | + |
747 | + def test_update_device_type_phone(self): |
748 | + """A device can be updated from a dict.""" |
749 | + dtype = gui.DEVICE_TYPE_PHONE |
750 | + self.ui.update(device_type=dtype) |
751 | + self.assertEqual((dtype.lower(), gui.gtk.ICON_SIZE_BUTTON), |
752 | + self.ui.device_type.get_icon_name()) |
753 | + |
754 | + def test_update_configurable(self): |
755 | + """A device can be updated from a dict.""" |
756 | + self.ui.update(configurable='') |
757 | + self.assertFalse(self.ui.configurable) |
758 | + self.assertFalse(self.ui.throttling.get_visible()) |
759 | + |
760 | + def test_update_non_configurable(self): |
761 | + """A device can be updated from a dict.""" |
762 | + self.ui.update(configurable='True') |
763 | + self.assertTrue(self.ui.configurable) |
764 | + self.assertTrue(self.ui.throttling.get_visible()) |
765 | + |
766 | + def test_update_limit_bandwidth(self): |
767 | + """A device can be updated from a dict.""" |
768 | + self.ui.update(limit_bandwidth='') |
769 | + self.assertFalse(self.ui.limit_bandwidth.get_active()) |
770 | + |
771 | + self.ui.update(limit_bandwidth='True') |
772 | + self.assertTrue(self.ui.limit_bandwidth.get_active()) |
773 | + |
774 | + def test_update_upload_speed(self): |
775 | + """A device can be updated from a dict.""" |
776 | + value = '12345' |
777 | + self.ui.update(max_upload_speed=value) |
778 | + self.assertEqual(int(value) // gui.KILOBYTES, |
779 | + self.ui.max_upload_speed.get_value_as_int()) |
780 | + |
781 | + def test_update_download_speed(self): |
782 | + """A device can be updated from a dict.""" |
783 | + value = '987654' |
784 | + self.ui.update(max_download_speed=value) |
785 | + self.assertEqual(int(value) // gui.KILOBYTES, |
786 | + self.ui.max_download_speed.get_value_as_int()) |
787 | + |
788 | + def test_update_does_not_call_backend(self): |
789 | + """When updating, the backend is not called.""" |
790 | + self.ui.update(**FAKE_DEVICE_INFO) |
791 | + self.assertEqual(self.ui.backend._called, {}) |
792 | + self.assert_device_equal(self.ui, FAKE_DEVICE_INFO) |
793 | + |
794 | + def test_on_limit_bandwidth_toggled(self): |
795 | + """When toggling limit_bandwidth, backend is updated.""" |
796 | + self.ui.limit_bandwidth.toggled() |
797 | + self.assert_device_settings_changed() |
798 | + |
799 | + def test_on_max_upload_speed_value_changed(self): |
800 | + """When setting max_upload_speed, backend is updated.""" |
801 | + self.ui.max_upload_speed.set_value(25) |
802 | + self.assert_device_settings_changed() |
803 | + |
804 | + def test_on_max_download_speed_value_changed(self): |
805 | + """When setting max_download_speed, backend is updated.""" |
806 | + self.ui.max_download_speed.set_value(52) |
807 | + self.assert_device_settings_changed() |
808 | + |
809 | + def test_backend_signals(self): |
810 | + """The proper signals are connected to the backend.""" |
811 | + self.assertEqual(self.ui.backend._signals['DeviceSettingsChanged'], |
812 | + [self.ui.on_device_settings_changed]) |
813 | + self.assertEqual(self.ui.backend._signals['DeviceSettingsChangeError'], |
814 | + [self.ui.on_device_settings_change_error]) |
815 | + |
816 | + def test_on_device_settings_changed(self): |
817 | + """When settings were changed for this device, enable it.""" |
818 | + self.modify_settings() |
819 | + did = self.ui.device_id.get_text() |
820 | + self.ui.on_device_settings_changed(device_id=did) |
821 | + |
822 | + self.assertTrue(self.ui.get_sensitive()) |
823 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
824 | + self.assertEqual(self.ui.__dict__, self.ui._last_settings) |
825 | + |
826 | + def test_on_device_settings_change_after_error(self): |
827 | + """Change success after error.""" |
828 | + self.modify_settings() |
829 | + did = self.ui.device_id.get_text() |
830 | + self.ui.on_device_settings_change_error(device_id=did) # change failed |
831 | + |
832 | + self.test_on_device_settings_changed() |
833 | + |
834 | + def test_on_device_settings_changed_different_id(self): |
835 | + """When settings were changed for other device, nothing changes.""" |
836 | + self.modify_settings() |
837 | + self.ui.on_device_settings_changed(device_id='yadda') |
838 | + |
839 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
840 | + |
841 | + def test_on_device_settings_change_error(self): |
842 | + """When settings were not changed for this device, notify the user. |
843 | + |
844 | + Also, confirm that old values were restored. |
845 | + |
846 | + """ |
847 | + self.ui.update(**FAKE_DEVICE_INFO) # use known values |
848 | + |
849 | + self.modify_settings() |
850 | + |
851 | + did = self.ui.device_id.get_text() |
852 | + self.ui.on_device_settings_change_error(device_id=did) # change failed |
853 | + |
854 | + self.assertTrue(self.ui.get_sensitive()) |
855 | + self.assert_warning_correct(self.ui.warning_label, |
856 | + self.ui.DEVICE_CHANGE_ERROR) |
857 | + self.assert_device_equal(self.ui, FAKE_DEVICE_INFO) # restored info |
858 | + |
859 | + def test_on_device_settings_change_error_after_success(self): |
860 | + """Change error after success.""" |
861 | + self.modify_settings() |
862 | + did = self.ui.device_id.get_text() |
863 | + self.ui.on_device_settings_changed(device_id=did) |
864 | + |
865 | + self.test_on_device_settings_change_error() |
866 | + |
867 | + def test_on_device_settings_change_error_different_id(self): |
868 | + """When settings were not changed for other device, do nothing.""" |
869 | + self.modify_settings() |
870 | + self.ui.on_device_settings_change_error(device_id='yudo') |
871 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
872 | + |
873 | + |
874 | class DevicesTestCase(ControlPanelMixinTestCase): |
875 | """The test suite for the devices panel.""" |
876 | |
877 | @@ -899,6 +1150,50 @@ |
878 | """Is visible.""" |
879 | self.assertTrue(self.ui.get_visible()) |
880 | |
881 | + def test_backend_signals(self): |
882 | + """The proper signals are connected to the backend.""" |
883 | + self.assertEqual(self.ui.backend._signals['DevicesInfoReady'], |
884 | + [self.ui.on_devices_info_ready]) |
885 | + self.assertEqual(self.ui.backend._signals['DevicesInfoError'], |
886 | + [self.ui.on_devices_info_error]) |
887 | + |
888 | + def test_devices_info_is_requested(self): |
889 | + """The devices info is requested to the backend.""" |
890 | + self.assert_backend_called('devices_info', ()) |
891 | + |
892 | + def test_on_devices_info_ready(self): |
893 | + """The devices info is processed when ready.""" |
894 | + self.ui.on_devices_info_ready(FAKE_DEVICES_INFO) |
895 | + |
896 | + children = self.ui.devices.get_children() |
897 | + self.assertEqual(len(children), len(FAKE_DEVICES_INFO)) |
898 | + |
899 | + for child, device in zip(children, FAKE_DEVICES_INFO): |
900 | + self.assertIsInstance(child, gui.Device) |
901 | + |
902 | + self.assertEqual(device['device_id'], |
903 | + child.device_id.get_text()) |
904 | + self.assertEqual(device['device_name'], |
905 | + child.device_name.get_text()) |
906 | + self.assertEqual(device['device_type'].lower(), |
907 | + child.device_type.get_icon_name()[0]) |
908 | + self.assertEqual(bool(device['configurable']), |
909 | + child.configurable) |
910 | + |
911 | + if bool(device['configurable']): |
912 | + self.assertEqual(bool(device['limit_bandwidth']), |
913 | + child.limit_bandwidth.get_active()) |
914 | + value = int(device['max_upload_speed']) // gui.KILOBYTES |
915 | + self.assertEqual(value, |
916 | + child.max_upload_speed.get_value_as_int()) |
917 | + value = int(device['max_download_speed']) // gui.KILOBYTES |
918 | + self.assertEqual(value, |
919 | + child.max_download_speed.get_value_as_int()) |
920 | + |
921 | + def test_on_devices_info_error(self): |
922 | + """The devices info couldn't be retrieved.""" |
923 | + self.ui.on_devices_info_error() |
924 | + |
925 | |
926 | class ApplicationsTestCase(ControlPanelMixinTestCase): |
927 | """The test suite for the applications panel.""" |
I am probably missing a lot of context, but when blindly trying to test this, I get:
beuno@beuno- laptop: ~$ bzr branch lp:~nataliabidart/ubuntuone-control-panel/devices laptop: ~$ cd devices/ laptop: ~/devices$ EBUG=True PYTHONPATH=. ./bin/ubuntuone -control- panel-backend ubuntuone- control- panel-backend" , line 24, in <module> controlpanel import dbus_service beuno/devices/ ubuntuone/ controlpanel/ dbus_service. py", line 32, in <module> controlpanel. backend import ( beuno/devices/ ubuntuone/ controlpanel/ backend. py", line 24, in <module> controlpanel import dbus_client beuno/devices/ ubuntuone/ controlpanel/ dbus_client. py", line 28, in <module> platform. linux import dbus_interface as sd_dbus_iface
Branched 44 revision(s).
beuno@beuno-
beuno@beuno-
Traceback (most recent call last):
File "./bin/
from ubuntuone.
File "/home/
from ubuntuone.
File "/home/
from ubuntuone.
File "/home/
from ubuntuone.
ImportError: No module named platform.linux