Merge lp:~nataliabidart/ubuntuone-control-panel/devices into lp:ubuntuone-control-panel

Proposed by Natalia Bidart
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
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Roman Yepishev (community) fieldtest Approve
Review via email: mp+43975@code.launchpad.net

Commit message

* Implemented devices tab (LP: #690649).

* Maximun size is set using geometry hints (LP: #683164).

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-control-panel-backend
terminal 2: DEBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-gtk

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/ubuntuone/syncdaemon.conf file.

To post a comment you must log in.
Revision history for this message
Martin Albisetti (beuno) wrote :

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
Branched 44 revision(s).
beuno@beuno-laptop:~$ cd devices/
beuno@beuno-laptop:~/devices$ EBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-backend
Traceback (most recent call last):
  File "./bin/ubuntuone-control-panel-backend", line 24, in <module>
    from ubuntuone.controlpanel import dbus_service
  File "/home/beuno/devices/ubuntuone/controlpanel/dbus_service.py", line 32, in <module>
    from ubuntuone.controlpanel.backend import (
  File "/home/beuno/devices/ubuntuone/controlpanel/backend.py", line 24, in <module>
    from ubuntuone.controlpanel import dbus_client
  File "/home/beuno/devices/ubuntuone/controlpanel/dbus_client.py", line 28, in <module>
    from ubuntuone.platform.linux import dbus_interface as sd_dbus_iface
ImportError: No module named platform.linux

Revision history for this message
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-preferences. It is pretty minor but it looks like it redraws the whole device entry after every change of widget values.

This can be seen here - http://ubuntuone.com/p/U8u/

review: Needs Fixing
45. By Natalia Bidart

Merged trunk in.

46. By Natalia Bidart

UI is not disabled while changing setting for a device.

Revision history for this message
Roman Yepishev (rye) wrote :

Yep, no flickering occurs.

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

Approved, fieldtested on narwhal.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'data/device.ui'
--- data/device.ui 1970-01-01 00:00:00 +0000
+++ data/device.ui 2010-12-17 17:27:34 +0000
@@ -0,0 +1,201 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<interface>
3 <requires lib="gtk+" version="2.16"/>
4 <!-- interface-naming-policy project-wide -->
5 <object class="GtkVBox" id="itself">
6 <property name="visible">True</property>
7 <property name="spacing">5</property>
8 <child>
9 <object class="GtkHBox" id="hbox1">
10 <property name="visible">True</property>
11 <property name="spacing">15</property>
12 <child>
13 <object class="GtkVBox" id="vbox1">
14 <property name="visible">True</property>
15 <property name="spacing">5</property>
16 <child>
17 <object class="GtkHBox" id="hbox2">
18 <property name="visible">True</property>
19 <child>
20 <object class="GtkImage" id="device_type">
21 <property name="visible">True</property>
22 <property name="icon_name">computer</property>
23 </object>
24 <packing>
25 <property name="expand">False</property>
26 <property name="position">0</property>
27 </packing>
28 </child>
29 <child>
30 <object class="GtkLabel" id="device_name">
31 <property name="visible">True</property>
32 <property name="xalign">0</property>
33 <property name="xpad">5</property>
34 <property name="label">My Laptop</property>
35 </object>
36 <packing>
37 <property name="position">1</property>
38 </packing>
39 </child>
40 <child>
41 <object class="GtkLabel" id="device_id">
42 <property name="label" translatable="yes">device id (hidden)</property>
43 </object>
44 <packing>
45 <property name="expand">False</property>
46 <property name="position">2</property>
47 </packing>
48 </child>
49 <child>
50 <object class="GtkLabel" id="date_added">
51 <property name="label">30.02.09</property>
52 </object>
53 <packing>
54 <property name="expand">False</property>
55 <property name="position">3</property>
56 </packing>
57 </child>
58 </object>
59 <packing>
60 <property name="position">0</property>
61 </packing>
62 </child>
63 <child>
64 <object class="GtkTable" id="throttling">
65 <property name="visible">True</property>
66 <property name="n_rows">3</property>
67 <property name="n_columns">2</property>
68 <property name="column_spacing">3</property>
69 <property name="row_spacing">3</property>
70 <child>
71 <object class="GtkCheckButton" id="limit_bandwidth">
72 <property name="label" translatable="yes">Limit bandwidth usage</property>
73 <property name="visible">True</property>
74 <property name="can_focus">True</property>
75 <property name="receives_default">False</property>
76 <property name="draw_indicator">True</property>
77 <signal name="toggled" handler="on_limit_bandwidth_toggled"/>
78 </object>
79 </child>
80 <child>
81 <object class="GtkLabel" id="max_upload_speed_label">
82 <property name="visible">True</property>
83 <property name="xalign">1</property>
84 <property name="xpad">5</property>
85 <property name="label" translatable="yes">Max upload speed (KiB/s)</property>
86 </object>
87 <packing>
88 <property name="top_attach">1</property>
89 <property name="bottom_attach">2</property>
90 </packing>
91 </child>
92 <child>
93 <object class="GtkLabel" id="max_download_speed_label">
94 <property name="visible">True</property>
95 <property name="xalign">1</property>
96 <property name="xpad">5</property>
97 <property name="label" translatable="yes">Max download speed (KiB/s)</property>
98 </object>
99 <packing>
100 <property name="top_attach">2</property>
101 <property name="bottom_attach">3</property>
102 </packing>
103 </child>
104 <child>
105 <object class="GtkSpinButton" id="max_upload_speed">
106 <property name="visible">True</property>
107 <property name="can_focus">True</property>
108 <property name="invisible_char">•</property>
109 <property name="activates_default">True</property>
110 <property name="adjustment">adjustment1</property>
111 <signal name="value_changed" handler="on_max_upload_speed_value_changed"/>
112 </object>
113 <packing>
114 <property name="left_attach">1</property>
115 <property name="right_attach">2</property>
116 <property name="top_attach">1</property>
117 <property name="bottom_attach">2</property>
118 <property name="x_options">GTK_FILL</property>
119 <property name="y_options">GTK_FILL</property>
120 </packing>
121 </child>
122 <child>
123 <object class="GtkSpinButton" id="max_download_speed">
124 <property name="visible">True</property>
125 <property name="can_focus">True</property>
126 <property name="invisible_char">•</property>
127 <property name="activates_default">True</property>
128 <property name="adjustment">adjustment2</property>
129 <signal name="value_changed" handler="on_max_download_speed_value_changed"/>
130 </object>
131 <packing>
132 <property name="left_attach">1</property>
133 <property name="right_attach">2</property>
134 <property name="top_attach">2</property>
135 <property name="bottom_attach">3</property>
136 </packing>
137 </child>
138 <child>
139 <placeholder/>
140 </child>
141 </object>
142 <packing>
143 <property name="expand">False</property>
144 <property name="position">1</property>
145 </packing>
146 </child>
147 </object>
148 <packing>
149 <property name="expand">False</property>
150 <property name="position">0</property>
151 </packing>
152 </child>
153 <child>
154 <object class="GtkVButtonBox" id="vbuttonbox1">
155 <property name="visible">True</property>
156 <property name="layout_style">start</property>
157 <child>
158 <object class="GtkButton" id="remove">
159 <property name="label">gtk-remove</property>
160 <property name="visible">True</property>
161 <property name="can_focus">True</property>
162 <property name="receives_default">True</property>
163 <property name="use_stock">True</property>
164 </object>
165 <packing>
166 <property name="expand">False</property>
167 <property name="fill">False</property>
168 <property name="position">0</property>
169 </packing>
170 </child>
171 </object>
172 <packing>
173 <property name="expand">False</property>
174 <property name="pack_type">end</property>
175 <property name="position">1</property>
176 </packing>
177 </child>
178 </object>
179 <packing>
180 <property name="expand">False</property>
181 <property name="position">0</property>
182 </packing>
183 </child>
184 <child>
185 <object class="GtkLabel" id="warning_label">
186 <property name="visible">True</property>
187 </object>
188 <packing>
189 <property name="position">1</property>
190 </packing>
191 </child>
192 </object>
193 <object class="GtkAdjustment" id="adjustment1">
194 <property name="upper">10000</property>
195 <property name="step_increment">1</property>
196 </object>
197 <object class="GtkAdjustment" id="adjustment2">
198 <property name="upper">10000</property>
199 <property name="step_increment">1</property>
200 </object>
201</interface>
0202
=== modified file 'data/devices.ui'
--- data/devices.ui 2010-10-21 21:14:24 +0000
+++ data/devices.ui 2010-12-17 17:27:34 +0000
@@ -7,7 +7,37 @@
7 <property name="border_width">10</property>7 <property name="border_width">10</property>
8 <property name="spacing">10</property>8 <property name="spacing">10</property>
9 <child>9 <child>
10 <placeholder/>10 <object class="GtkScrolledWindow" id="scrolledwindow1">
11 <property name="visible">True</property>
12 <property name="can_focus">True</property>
13 <property name="hscrollbar_policy">automatic</property>
14 <property name="vscrollbar_policy">automatic</property>
15 <child>
16 <object class="GtkViewport" id="viewport1">
17 <property name="visible">True</property>
18 <property name="resize_mode">queue</property>
19 <property name="shadow_type">none</property>
20 <child>
21 <object class="GtkAlignment" id="alignment1">
22 <property name="visible">True</property>
23 <property name="xscale">0</property>
24 <property name="yscale">0</property>
25 <child>
26 <object class="GtkVBox" id="devices">
27 <property name="visible">True</property>
28 <child>
29 <placeholder/>
30 </child>
31 </object>
32 </child>
33 </object>
34 </child>
35 </object>
36 </child>
37 </object>
38 <packing>
39 <property name="position">0</property>
40 </packing>
11 </child>41 </child>
12 </object>42 </object>
13</interface>43</interface>
1444
=== modified file 'data/folders.ui'
--- data/folders.ui 2010-12-14 15:54:29 +0000
+++ data/folders.ui 2010-12-17 17:27:34 +0000
@@ -31,7 +31,7 @@
31 <property name="resize_mode">queue</property>31 <property name="resize_mode">queue</property>
32 <property name="shadow_type">none</property>32 <property name="shadow_type">none</property>
33 <child>33 <child>
34 <object class="GtkAlignment" id="folders_alignment">34 <object class="GtkAlignment" id="folders">
35 <property name="visible">True</property>35 <property name="visible">True</property>
36 <property name="xscale">0</property>36 <property name="xscale">0</property>
37 <property name="yscale">0</property>37 <property name="yscale">0</property>
3838
=== modified file 'ubuntuone/controlpanel/gtk/gui.py'
--- ubuntuone/controlpanel/gtk/gui.py 2010-12-14 19:16:25 +0000
+++ ubuntuone/controlpanel/gtk/gui.py 2010-12-17 17:27:34 +0000
@@ -47,6 +47,8 @@
4747
48from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH,48from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH,
49 DBUS_PREFERENCES_IFACE)49 DBUS_PREFERENCES_IFACE)
50from ubuntuone.controlpanel.backend import (DEVICE_TYPE_PHONE,
51 DEVICE_TYPE_COMPUTER)
50from ubuntuone.controlpanel.logger import setup_logging, log_call52from ubuntuone.controlpanel.logger import setup_logging, log_call
51from ubuntuone.controlpanel.utils import get_data_file53from ubuntuone.controlpanel.utils import get_data_file
5254
@@ -69,6 +71,12 @@
69 _('Sync data between computers.\n'71 _('Sync data between computers.\n'
70 '<small>«this is custom text #<i>N</i>»</small>')]72 '<small>«this is custom text #<i>N</i>»</small>')]
71WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE73WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE
74KILOBYTES = 1024
75
76
77def bool_str(value):
78 """Return the string representation of a bool (dbus-compatible)."""
79 return 'True' if value else ''
7280
7381
74def filter_by_app_name(f):82def filter_by_app_name(f):
@@ -151,6 +159,8 @@
151 self.set_title(self.TITLE % {'app_name': U1_APP_NAME})159 self.set_title(self.TITLE % {'app_name': U1_APP_NAME})
152 self.set_position(gtk.WIN_POS_CENTER_ALWAYS)160 self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
153 self.set_icon_name('ubuntuone')161 self.set_icon_name('ubuntuone')
162 self.set_geometry_hints(max_width=736, max_height=525) # bug #683164
163
154 self.connect('delete-event', lambda w, e: gtk.main_quit())164 self.connect('delete-event', lambda w, e: gtk.main_quit())
155 self.show()165 self.show()
156166
@@ -377,7 +387,7 @@
377 def __init__(self):387 def __init__(self):
378 UbuntuOneBin.__init__(self)388 UbuntuOneBin.__init__(self)
379 ControlPanelMixin.__init__(self, filename='account.ui')389 ControlPanelMixin.__init__(self, filename='account.ui')
380 self.pack_start(self.itself)390 self.add(self.itself)
381 self.show()391 self.show()
382392
383 self.backend.connect_to_signal('AccountInfoReady',393 self.backend.connect_to_signal('AccountInfoReady',
@@ -421,7 +431,7 @@
421 def __init__(self):431 def __init__(self):
422 UbuntuOneBin.__init__(self)432 UbuntuOneBin.__init__(self)
423 ControlPanelMixin.__init__(self, filename='folders.ui')433 ControlPanelMixin.__init__(self, filename='folders.ui')
424 self.pack_start(self.itself)434 self.add(self.itself)
425 self.show_all()435 self.show_all()
426436
427 self.backend.connect_to_signal('VolumesInfoReady',437 self.backend.connect_to_signal('VolumesInfoReady',
@@ -433,13 +443,12 @@
433 self.label_alignment.add(self.volumes_label)443 self.label_alignment.add(self.volumes_label)
434 self._subscribed = []444 self._subscribed = []
435445
436 @log_call(logger.debug)
437 def on_volumes_info_ready(self, info):446 def on_volumes_info_ready(self, info):
438 """Backend notifies of volumes info."""447 """Backend notifies of volumes info."""
439 self.volumes_label.stop()448 self.volumes_label.stop()
440449
441 if self.volumes is not None:450 if self.volumes is not None:
442 self.folders_alignment.remove(self.volumes)451 self.folders.remove(self.volumes)
443 self.volumes = None452 self.volumes = None
444453
445 if not info:454 if not info:
@@ -473,7 +482,7 @@
473 self._subscribed.append(subscribed)482 self._subscribed.append(subscribed)
474 self.volumes.attach(subscribed, 1, 2, i + 1, i + 2, xoptions=0)483 self.volumes.attach(subscribed, 1, 2, i + 1, i + 2, xoptions=0)
475484
476 self.folders_alignment.add(self.volumes)485 self.folders.add(self.volumes)
477486
478 @log_call(logger.error)487 @log_call(logger.error)
479 def on_volumes_info_error(self, error_dict=None):488 def on_volumes_info_error(self, error_dict=None):
@@ -485,7 +494,7 @@
485 def on_subscribed_clicked(self, checkbutton):494 def on_subscribed_clicked(self, checkbutton):
486 """The user toggled 'checkbutton'."""495 """The user toggled 'checkbutton'."""
487 volume_id = checkbutton.get_label()496 volume_id = checkbutton.get_label()
488 subscribed = 'True' if checkbutton.get_active() else ''497 subscribed = bool_str(checkbutton.get_active())
489 self.backend.change_volume_settings(volume_id,498 self.backend.change_volume_settings(volume_id,
490 {'subscribed': subscribed})499 {'subscribed': subscribed})
491500
@@ -495,6 +504,140 @@
495 self.volumes_label.start()504 self.volumes_label.start()
496505
497506
507class Device(gtk.VBox, ControlPanelMixin):
508 """The devices panel."""
509
510 DEVICE_CHANGE_ERROR = _('The settings could not be changed,\n'
511 'previous values were restored.')
512
513 def __init__(self):
514 gtk.VBox.__init__(self)
515 ControlPanelMixin.__init__(self, filename='device.ui')
516
517 self._updating = False
518 self._last_settings = {}
519 self.configurable = False
520
521 self.update(device_id='', device_name='', limit_bandwidth=False,
522 max_upload_speed=0, max_download_speed=0)
523
524 self.add(self.itself)
525 self.device_id.hide()
526 self.show()
527
528 self.backend.connect_to_signal('DeviceSettingsChanged',
529 self.on_device_settings_changed)
530 self.backend.connect_to_signal('DeviceSettingsChangeError',
531 self.on_device_settings_change_error)
532
533 def _change_device_settings(self, *args):
534 """Update backend settings for this device."""
535 if self._updating:
536 return
537
538 # Not disabling the GUI to avoid annyong twitchings
539 #self.set_sensitive(False)
540 self.warning_label.set_text('')
541 self.backend.change_device_settings(self.device_id.get_text(),
542 self.__dict__)
543
544 def _block_signals(f):
545 """Execute 'f' while having the _updating flag set."""
546
547 # pylint: disable=E0213,W0212,E1102
548
549 @wraps(f)
550 def inner(self, *args, **kwargs):
551 """Execute 'f' while having the _updating flag set."""
552 old = self._updating
553 self._updating = True
554
555 result = f(self, *args, **kwargs)
556
557 self._updating = old
558 return result
559
560 return inner
561
562 on_limit_bandwidth_toggled = _change_device_settings
563 on_max_upload_speed_value_changed = _change_device_settings
564 on_max_download_speed_value_changed = _change_device_settings
565
566 @_block_signals
567 def update(self, **kwargs):
568 """Update according to named parameters.
569
570 Possible settings are:
571 * device_id (string, not shown to the user)
572 * device_name (string)
573 * type (either DEVICE_TYPE_PHONE or DEVICE_TYPE_COMPUTER)
574 * configurable (True/False)
575 * if configurable, the following can be set:
576 * limit_bandwidth (True/False)
577 * max_upload_speed (bytes)
578 * max_download_speed (bytes)
579
580 """
581 if 'device_id' in kwargs:
582 self.device_id.set_text(kwargs['device_id'])
583
584 if 'device_name' in kwargs:
585 self.device_name.set_markup('<b>%s</b>' % kwargs['device_name'])
586
587 if 'device_type' in kwargs:
588 dtype = kwargs['device_type']
589 if dtype in (DEVICE_TYPE_COMPUTER, DEVICE_TYPE_PHONE):
590 self.device_type.set_from_icon_name(dtype.lower(),
591 gtk.ICON_SIZE_BUTTON)
592
593 if 'configurable' in kwargs:
594 self.configurable = bool(kwargs['configurable'])
595 self.throttling.set_visible(self.configurable)
596
597 if 'limit_bandwidth' in kwargs:
598 self.limit_bandwidth.set_active(bool(kwargs['limit_bandwidth']))
599
600 for speed in ('max_upload_speed', 'max_download_speed'):
601 if speed in kwargs:
602 value = int(kwargs[speed]) // KILOBYTES
603 getattr(self, speed).set_value(value)
604
605 self._last_settings = self.__dict__
606
607 @property
608 def __dict__(self):
609 result = {
610 'device_id': self.device_id.get_text(),
611 'device_name': self.device_name.get_text(),
612 'device_type': self.device_type.get_icon_name()[0].capitalize(),
613 'configurable': bool_str(self.configurable),
614 'limit_bandwidth': bool_str(self.limit_bandwidth.get_active()),
615 'max_upload_speed': \
616 str(self.max_upload_speed.get_value_as_int() * KILOBYTES),
617 'max_download_speed': \
618 str(self.max_download_speed.get_value_as_int() * KILOBYTES),
619 }
620 return result
621
622 @log_call(logger.info)
623 def on_device_settings_changed(self, device_id):
624 """The change of this device settings succeded."""
625 if device_id != self.device_id.get_text():
626 return
627 self.set_sensitive(True)
628 self.warning_label.set_text('')
629 self._last_settings = self.__dict__
630
631 @log_call(logger.error)
632 def on_device_settings_change_error(self, device_id, error_dict=None):
633 """The change of this device settings failed."""
634 if device_id != self.device_id.get_text():
635 return
636 self.update(**self._last_settings)
637 self._set_warning(self.DEVICE_CHANGE_ERROR, self.warning_label)
638 self.set_sensitive(True)
639
640
498class DevicesPanel(UbuntuOneBin, ControlPanelMixin):641class DevicesPanel(UbuntuOneBin, ControlPanelMixin):
499 """The devices panel."""642 """The devices panel."""
500643
@@ -504,9 +647,29 @@
504 def __init__(self):647 def __init__(self):
505 UbuntuOneBin.__init__(self)648 UbuntuOneBin.__init__(self)
506 ControlPanelMixin.__init__(self, filename='devices.ui')649 ControlPanelMixin.__init__(self, filename='devices.ui')
507 self.pack_start(self.itself)650 self.add(self.itself)
508 self.show()651 self.show()
509652
653 self.backend.connect_to_signal('DevicesInfoReady',
654 self.on_devices_info_ready)
655 self.backend.connect_to_signal('DevicesInfoError',
656 self.on_devices_info_error)
657 self.backend.devices_info()
658
659 def on_devices_info_ready(self, info):
660 """Backend notifies of devices info."""
661 for device_info in info:
662 device = Device()
663 device_info['device_name'] = device_info.pop('name', '')
664 device_info['device_type'] = device_info.pop('type',
665 DEVICE_TYPE_COMPUTER)
666 device.update(**device_info)
667 self.devices.pack_start(device)
668
669 @log_call(logger.error)
670 def on_devices_info_error(self, error_dict=None):
671 """Backend notifies of an error when fetching volumes info."""
672
510673
511class ApplicationsPanel(UbuntuOneBin, ControlPanelMixin):674class ApplicationsPanel(UbuntuOneBin, ControlPanelMixin):
512 """The applications panel."""675 """The applications panel."""
@@ -517,7 +680,7 @@
517 def __init__(self):680 def __init__(self):
518 UbuntuOneBin.__init__(self)681 UbuntuOneBin.__init__(self)
519 ControlPanelMixin.__init__(self, filename='applications.ui')682 ControlPanelMixin.__init__(self, filename='applications.ui')
520 self.pack_start(self.itself)683 self.add(self.itself)
521 self.show()684 self.show()
522685
523686
@@ -543,7 +706,7 @@
543 def __init__(self):706 def __init__(self):
544 gtk.VBox.__init__(self)707 gtk.VBox.__init__(self)
545 ControlPanelMixin.__init__(self, filename='management.ui')708 ControlPanelMixin.__init__(self, filename='management.ui')
546 self.pack_start(self.itself)709 self.add(self.itself)
547 self.show()710 self.show()
548711
549 self.backend.connect_to_signal('AccountInfoReady',712 self.backend.connect_to_signal('AccountInfoReady',
550713
=== modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py'
--- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-14 19:16:25 +0000
+++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-17 17:27:34 +0000
@@ -45,6 +45,23 @@
45 {'volume_id': '2', 'suggested_path': '~/baz', 'subscribed': 'True'},45 {'volume_id': '2', 'suggested_path': '~/baz', 'subscribed': 'True'},
46]46]
4747
48FAKE_DEVICE_INFO = {
49 'device_id': '1258-6854', 'device_name': 'Baz', 'device_type': 'Computer',
50 'configurable': 'True', 'limit_bandwidth': 'True',
51 'max_upload_speed': '1000', 'max_download_speed': '72548',
52}
53
54FAKE_DEVICES_INFO = [
55 {'device_id': '0', 'name': 'Foo', 'type': 'Computer', 'configurable': ''},
56 {'device_id': '1', 'name': 'Bar', 'type': 'Phone', 'configurable': ''},
57 {'device_id': '2', 'name': 'Z', 'type': 'Computer',
58 'configurable': 'True', 'limit_bandwidth': '',
59 'max_upload_speed': '0', 'max_download_speed': '0'},
60 {'device_id': '1258-6854', 'name': 'Baz', 'type': 'Computer',
61 'configurable': 'True', 'limit_bandwidth': 'True',
62 'max_upload_speed': '1000', 'max_download_speed': '72548'},
63]
64
4865
49class FakedObject(object):66class FakedObject(object):
50 """Fake an object, record every call."""67 """Fake an object, record every call."""
@@ -108,8 +125,10 @@
108 bus_name = gui.DBUS_BUS_NAME125 bus_name = gui.DBUS_BUS_NAME
109 object_path = gui.DBUS_PREFERENCES_PATH126 object_path = gui.DBUS_PREFERENCES_PATH
110 iface = gui.DBUS_PREFERENCES_IFACE127 iface = gui.DBUS_PREFERENCES_IFACE
111 exposed_methods = ['account_info', 'devices_info', 'volumes_info',128 exposed_methods = [
112 'file_sync_status', 'change_volume_settings']129 'account_info', 'devices_info', 'change_device_settings',
130 'volumes_info', 'change_volume_settings', 'file_sync_status',
131 ]
113132
114133
115class FakedSessionBus(object):134class FakedSessionBus(object):
@@ -268,8 +287,8 @@
268 self.assertEqual(self.ui.get_icon_name(), 'ubuntuone')287 self.assertEqual(self.ui.get_icon_name(), 'ubuntuone')
269288
270 def test_max_size(self):289 def test_max_size(self):
271 """Max size is not bigger than 966x576 (LP: #645526)."""290 """Max size is not bigger than 736x525 (LP: #645526, LP: #683164)."""
272 self.assertTrue(self.ui.get_size_request() <= (966, 576))291 self.assertTrue(self.ui.get_size_request() <= (736, 525))
273292
274293
275class ControlPanelTestCase(ControlPanelMixinTestCase):294class ControlPanelTestCase(ControlPanelMixinTestCase):
@@ -801,8 +820,7 @@
801 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)820 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
802821
803 self.assertFalse(self.ui.label_alignment.get_visible())822 self.assertFalse(self.ui.label_alignment.get_visible())
804 self.assertEqual(self.ui.folders_alignment.get_children(),823 self.assertEqual(self.ui.folders.get_children(), [self.ui.volumes])
805 [self.ui.volumes])
806824
807 volumes = self.ui.volumes.get_children()825 volumes = self.ui.volumes.get_children()
808 volumes.reverse()826 volumes.reverse()
@@ -828,8 +846,8 @@
828 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)846 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
829 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)847 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
830848
831 self.assertEqual(len(self.ui.folders_alignment.get_children()), 1)849 self.assertEqual(len(self.ui.folders.get_children()), 1)
832 child = self.ui.folders_alignment.get_children()[0]850 child = self.ui.folders.get_children()[0]
833 self.assertEqual(child, self.ui.volumes)851 self.assertEqual(child, self.ui.volumes)
834852
835 volumes = filter(lambda w: isinstance(w, gui.gtk.CheckButton),853 volumes = filter(lambda w: isinstance(w, gui.gtk.CheckButton),
@@ -840,7 +858,7 @@
840 """When there are no volumes, a notification is shown."""858 """When there are no volumes, a notification is shown."""
841 self.ui.on_volumes_info_ready([])859 self.ui.on_volumes_info_ready([])
842 # no volumes table860 # no volumes table
843 self.assertEqual(len(self.ui.folders_alignment.get_children()), 0)861 self.assertEqual(len(self.ui.folders.get_children()), 0)
844 self.assertTrue(self.ui.volumes is None)862 self.assertTrue(self.ui.volumes is None)
845863
846 def test_on_subscribed_clicked(self):864 def test_on_subscribed_clicked(self):
@@ -852,14 +870,14 @@
852 checkbutton.clicked()870 checkbutton.clicked()
853 fid = checkbutton.get_label()871 fid = checkbutton.get_label()
854872
855 subscribed = 'True' if checkbutton.get_active() else ''873 subscribed = gui.bool_str(checkbutton.get_active())
856 self.assert_backend_called(method,874 self.assert_backend_called(method,
857 (fid, {'subscribed': subscribed}))875 (fid, {'subscribed': subscribed}))
858 # clean backend calls876 # clean backend calls
859 self.ui.backend._called.pop(method)877 self.ui.backend._called.pop(method)
860878
861 checkbutton.clicked()879 checkbutton.clicked()
862 subscribed = 'True' if checkbutton.get_active() else ''880 subscribed = gui.bool_str(checkbutton.get_active())
863 self.assert_backend_called('change_volume_settings',881 self.assert_backend_called('change_volume_settings',
864 (fid, {'subscribed': subscribed}))882 (fid, {'subscribed': subscribed}))
865883
@@ -881,6 +899,239 @@
881 self.test_on_volumes_info_ready_with_no_volumes()899 self.test_on_volumes_info_ready_with_no_volumes()
882900
883901
902class DeviceTestCase(ControlPanelMixinTestCase):
903 """The test suite for the device widget."""
904
905 klass = gui.Device
906 ui_filename = 'device.ui'
907
908 def assert_device_equal(self, device, expected):
909 """Assert that the device has the values from expected."""
910 self.assertEqual(device.device_id.get_text(),
911 expected['device_id'])
912 self.assertEqual(device.device_name.get_text(),
913 expected['device_name'])
914 self.assertEqual(device.device_type.get_icon_name()[0],
915 expected['device_type'].lower())
916 self.assertEqual(device.configurable,
917 bool(expected['configurable']))
918 self.assertEqual(device.limit_bandwidth.get_active(),
919 bool(expected['limit_bandwidth']))
920
921 value = int(expected['max_upload_speed']) // gui.KILOBYTES
922 self.assertEqual(device.max_upload_speed.get_value_as_int(), value)
923 value = int(expected['max_download_speed']) // gui.KILOBYTES
924 self.assertEqual(device.max_download_speed.get_value_as_int(), value)
925
926 def assert_device_settings_changed(self):
927 """Changing throttling settings updates the backend properly."""
928 expected = self.ui.__dict__
929 self.assert_backend_called('change_device_settings',
930 (self.ui.device_id.get_text(), expected))
931 self.assertEqual(self.ui.warning_label.get_text(), '')
932
933 def modify_settings(self):
934 """Modify settings so values actually change."""
935 new_val = not self.ui.limit_bandwidth.get_active()
936 self.ui.limit_bandwidth.set_active(new_val)
937
938 new_val = self.ui.max_upload_speed.get_value_as_int() + 1
939 self.ui.max_upload_speed.set_value(new_val)
940
941 new_val = self.ui.max_download_speed.get_value_as_int() + 1
942 self.ui.max_download_speed.set_value(new_val)
943
944 def test_is_a_vbox(self):
945 """Inherits from VBox."""
946 self.assertIsInstance(self.ui, gui.gtk.VBox)
947
948 def test_inner_widget_is_packed(self):
949 """The 'itself' vbox is packed into the widget."""
950 self.assertIn(self.ui.itself, self.ui.get_children())
951
952 def test_is_visible(self):
953 """Is visible."""
954 self.assertTrue(self.ui.get_visible())
955
956 def test_is_sensitive(self):
957 """Is sensitive."""
958 self.assertTrue(self.ui.get_sensitive())
959
960 def test_warning_label_is_cleared(self):
961 """The warning label is cleared."""
962 self.assertEqual(self.ui.warning_label.get_text(), '')
963
964 def test_device_id_is_hidden(self):
965 """The device id label is hidden."""
966 self.assertFalse(self.ui.device_id.get_visible())
967
968 def test_default_values(self):
969 """Default values are correct."""
970 self.assertEqual(self.ui.device_id.get_text(), '')
971 self.assertEqual(self.ui.device_name.get_text(), '')
972 self.assertEqual(self.ui.device_type.get_icon_name()[0],
973 gui.DEVICE_TYPE_COMPUTER.lower())
974 self.assertEqual(self.ui.configurable, False)
975 self.assertEqual(self.ui.limit_bandwidth.get_active(), False)
976 self.assertEqual(self.ui.max_upload_speed.get_value_as_int(), 0)
977 self.assertEqual(self.ui.max_download_speed.get_value_as_int(), 0)
978
979 def test_init_does_not_call_backend(self):
980 """When updating, the backend is not called."""
981 self.assertEqual(self.ui.backend._called, {})
982
983 def test_update_device_id(self):
984 """A device can be updated from a dict."""
985 value = '741-822-963'
986 self.ui.update(device_id=value)
987 self.assertEqual(value, self.ui.device_id.get_text())
988
989 def test_update_device_name(self):
990 """A device can be updated from a dict."""
991 value = 'The death star'
992 self.ui.update(device_name=value)
993 self.assertEqual(value, self.ui.device_name.get_text())
994
995 def test_update_unicode_device_name(self):
996 """A device can be updated from a dict."""
997 value = u'Ñoño Ñandú'
998 self.ui.update(device_name=value)
999 self.assertEqual(value, self.ui.device_name.get_text())
1000
1001 def test_update_device_type_computer(self):
1002 """A device can be updated from a dict."""
1003 dtype = gui.DEVICE_TYPE_COMPUTER
1004 self.ui.update(device_type=dtype)
1005 self.assertEqual((dtype.lower(), gui.gtk.ICON_SIZE_BUTTON),
1006 self.ui.device_type.get_icon_name())
1007
1008 def test_update_device_type_phone(self):
1009 """A device can be updated from a dict."""
1010 dtype = gui.DEVICE_TYPE_PHONE
1011 self.ui.update(device_type=dtype)
1012 self.assertEqual((dtype.lower(), gui.gtk.ICON_SIZE_BUTTON),
1013 self.ui.device_type.get_icon_name())
1014
1015 def test_update_configurable(self):
1016 """A device can be updated from a dict."""
1017 self.ui.update(configurable='')
1018 self.assertFalse(self.ui.configurable)
1019 self.assertFalse(self.ui.throttling.get_visible())
1020
1021 def test_update_non_configurable(self):
1022 """A device can be updated from a dict."""
1023 self.ui.update(configurable='True')
1024 self.assertTrue(self.ui.configurable)
1025 self.assertTrue(self.ui.throttling.get_visible())
1026
1027 def test_update_limit_bandwidth(self):
1028 """A device can be updated from a dict."""
1029 self.ui.update(limit_bandwidth='')
1030 self.assertFalse(self.ui.limit_bandwidth.get_active())
1031
1032 self.ui.update(limit_bandwidth='True')
1033 self.assertTrue(self.ui.limit_bandwidth.get_active())
1034
1035 def test_update_upload_speed(self):
1036 """A device can be updated from a dict."""
1037 value = '12345'
1038 self.ui.update(max_upload_speed=value)
1039 self.assertEqual(int(value) // gui.KILOBYTES,
1040 self.ui.max_upload_speed.get_value_as_int())
1041
1042 def test_update_download_speed(self):
1043 """A device can be updated from a dict."""
1044 value = '987654'
1045 self.ui.update(max_download_speed=value)
1046 self.assertEqual(int(value) // gui.KILOBYTES,
1047 self.ui.max_download_speed.get_value_as_int())
1048
1049 def test_update_does_not_call_backend(self):
1050 """When updating, the backend is not called."""
1051 self.ui.update(**FAKE_DEVICE_INFO)
1052 self.assertEqual(self.ui.backend._called, {})
1053 self.assert_device_equal(self.ui, FAKE_DEVICE_INFO)
1054
1055 def test_on_limit_bandwidth_toggled(self):
1056 """When toggling limit_bandwidth, backend is updated."""
1057 self.ui.limit_bandwidth.toggled()
1058 self.assert_device_settings_changed()
1059
1060 def test_on_max_upload_speed_value_changed(self):
1061 """When setting max_upload_speed, backend is updated."""
1062 self.ui.max_upload_speed.set_value(25)
1063 self.assert_device_settings_changed()
1064
1065 def test_on_max_download_speed_value_changed(self):
1066 """When setting max_download_speed, backend is updated."""
1067 self.ui.max_download_speed.set_value(52)
1068 self.assert_device_settings_changed()
1069
1070 def test_backend_signals(self):
1071 """The proper signals are connected to the backend."""
1072 self.assertEqual(self.ui.backend._signals['DeviceSettingsChanged'],
1073 [self.ui.on_device_settings_changed])
1074 self.assertEqual(self.ui.backend._signals['DeviceSettingsChangeError'],
1075 [self.ui.on_device_settings_change_error])
1076
1077 def test_on_device_settings_changed(self):
1078 """When settings were changed for this device, enable it."""
1079 self.modify_settings()
1080 did = self.ui.device_id.get_text()
1081 self.ui.on_device_settings_changed(device_id=did)
1082
1083 self.assertTrue(self.ui.get_sensitive())
1084 self.assertEqual(self.ui.warning_label.get_text(), '')
1085 self.assertEqual(self.ui.__dict__, self.ui._last_settings)
1086
1087 def test_on_device_settings_change_after_error(self):
1088 """Change success after error."""
1089 self.modify_settings()
1090 did = self.ui.device_id.get_text()
1091 self.ui.on_device_settings_change_error(device_id=did) # change failed
1092
1093 self.test_on_device_settings_changed()
1094
1095 def test_on_device_settings_changed_different_id(self):
1096 """When settings were changed for other device, nothing changes."""
1097 self.modify_settings()
1098 self.ui.on_device_settings_changed(device_id='yadda')
1099
1100 self.assertEqual(self.ui.warning_label.get_text(), '')
1101
1102 def test_on_device_settings_change_error(self):
1103 """When settings were not changed for this device, notify the user.
1104
1105 Also, confirm that old values were restored.
1106
1107 """
1108 self.ui.update(**FAKE_DEVICE_INFO) # use known values
1109
1110 self.modify_settings()
1111
1112 did = self.ui.device_id.get_text()
1113 self.ui.on_device_settings_change_error(device_id=did) # change failed
1114
1115 self.assertTrue(self.ui.get_sensitive())
1116 self.assert_warning_correct(self.ui.warning_label,
1117 self.ui.DEVICE_CHANGE_ERROR)
1118 self.assert_device_equal(self.ui, FAKE_DEVICE_INFO) # restored info
1119
1120 def test_on_device_settings_change_error_after_success(self):
1121 """Change error after success."""
1122 self.modify_settings()
1123 did = self.ui.device_id.get_text()
1124 self.ui.on_device_settings_changed(device_id=did)
1125
1126 self.test_on_device_settings_change_error()
1127
1128 def test_on_device_settings_change_error_different_id(self):
1129 """When settings were not changed for other device, do nothing."""
1130 self.modify_settings()
1131 self.ui.on_device_settings_change_error(device_id='yudo')
1132 self.assertEqual(self.ui.warning_label.get_text(), '')
1133
1134
884class DevicesTestCase(ControlPanelMixinTestCase):1135class DevicesTestCase(ControlPanelMixinTestCase):
885 """The test suite for the devices panel."""1136 """The test suite for the devices panel."""
8861137
@@ -899,6 +1150,50 @@
899 """Is visible."""1150 """Is visible."""
900 self.assertTrue(self.ui.get_visible())1151 self.assertTrue(self.ui.get_visible())
9011152
1153 def test_backend_signals(self):
1154 """The proper signals are connected to the backend."""
1155 self.assertEqual(self.ui.backend._signals['DevicesInfoReady'],
1156 [self.ui.on_devices_info_ready])
1157 self.assertEqual(self.ui.backend._signals['DevicesInfoError'],
1158 [self.ui.on_devices_info_error])
1159
1160 def test_devices_info_is_requested(self):
1161 """The devices info is requested to the backend."""
1162 self.assert_backend_called('devices_info', ())
1163
1164 def test_on_devices_info_ready(self):
1165 """The devices info is processed when ready."""
1166 self.ui.on_devices_info_ready(FAKE_DEVICES_INFO)
1167
1168 children = self.ui.devices.get_children()
1169 self.assertEqual(len(children), len(FAKE_DEVICES_INFO))
1170
1171 for child, device in zip(children, FAKE_DEVICES_INFO):
1172 self.assertIsInstance(child, gui.Device)
1173
1174 self.assertEqual(device['device_id'],
1175 child.device_id.get_text())
1176 self.assertEqual(device['device_name'],
1177 child.device_name.get_text())
1178 self.assertEqual(device['device_type'].lower(),
1179 child.device_type.get_icon_name()[0])
1180 self.assertEqual(bool(device['configurable']),
1181 child.configurable)
1182
1183 if bool(device['configurable']):
1184 self.assertEqual(bool(device['limit_bandwidth']),
1185 child.limit_bandwidth.get_active())
1186 value = int(device['max_upload_speed']) // gui.KILOBYTES
1187 self.assertEqual(value,
1188 child.max_upload_speed.get_value_as_int())
1189 value = int(device['max_download_speed']) // gui.KILOBYTES
1190 self.assertEqual(value,
1191 child.max_download_speed.get_value_as_int())
1192
1193 def test_on_devices_info_error(self):
1194 """The devices info couldn't be retrieved."""
1195 self.ui.on_devices_info_error()
1196
9021197
903class ApplicationsTestCase(ControlPanelMixinTestCase):1198class ApplicationsTestCase(ControlPanelMixinTestCase):
904 """The test suite for the applications panel."""1199 """The test suite for the applications panel."""

Subscribers

People subscribed via source and target branches