Merge lp:~nataliabidart/ubuntuone-control-panel/remove-devices into lp:ubuntuone-control-panel
- remove-devices
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | dobey | ||||
Approved revision: | 44 | ||||
Merged at revision: | 38 | ||||
Proposed branch: | lp:~nataliabidart/ubuntuone-control-panel/remove-devices | ||||
Merge into: | lp:ubuntuone-control-panel | ||||
Prerequisite: | lp:~nataliabidart/ubuntuone-control-panel/new-sso-iface | ||||
Diff against target: |
857 lines (+301/-79) 7 files modified
data/device.ui (+13/-11) ubuntuone/controlpanel/__init__.py (+1/-1) ubuntuone/controlpanel/backend.py (+37/-12) ubuntuone/controlpanel/gtk/gui.py (+75/-18) ubuntuone/controlpanel/gtk/tests/test_gui.py (+142/-31) ubuntuone/controlpanel/tests/test_backend.py (+26/-1) ubuntuone/controlpanel/webclient.py (+7/-5) |
||||
To merge this branch: | bzr merge lp:~nataliabidart/ubuntuone-control-panel/remove-devices | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Manuel de la Peña (community) | Approve | ||
Roberto Alsina (community) | fieldtest | Approve | |
Review via email: mp+44139@code.launchpad.net |
Commit message
* Devices can now be removed (LP: #691295).
Description of the change
To run the tests, do:
./run-tests
To test IRL, open 2 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. The 'Remove' button is fully functional so be careful.
Among the suggested tests if removing the local device (ie the computer you're testing on). You should be taken to the overview panel after that to re-add your account.
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~nataliabidart/ubuntuone-control-panel/remove-devices into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.
Running test suite for ubuntuone/
ubuntuone.
BackendAccoun
test_
test_
test_
test_
test_get_token ... [OK]
BackendBasicT
test_
test_
test_get_token ... [OK]
BackendDevice
test_
test_
test_
test_
test_
test_
test_
test_
test_get_token ... [OK]
test_
test_
test_
BackendSyncSt
test_
test_
test_disabled ... [OK]
test_
test_error ... [OK]
test_get_token ... [OK]
test_idle ... [OK]
test_
test_
test_
test_
test_
test_
test_
test_
test_unknown ... ...
Preview Diff
1 | === modified file 'data/device.ui' |
2 | --- data/device.ui 2010-12-16 20:56:57 +0000 |
3 | +++ data/device.ui 2010-12-18 17:42:12 +0000 |
4 | @@ -2,6 +2,14 @@ |
5 | <interface> |
6 | <requires lib="gtk+" version="2.16"/> |
7 | <!-- interface-naming-policy project-wide --> |
8 | + <object class="GtkAdjustment" id="adjustment2"> |
9 | + <property name="upper">10000</property> |
10 | + <property name="step_increment">1</property> |
11 | + </object> |
12 | + <object class="GtkAdjustment" id="adjustment1"> |
13 | + <property name="upper">10000</property> |
14 | + <property name="step_increment">1</property> |
15 | + </object> |
16 | <object class="GtkVBox" id="itself"> |
17 | <property name="visible">True</property> |
18 | <property name="spacing">5</property> |
19 | @@ -74,7 +82,7 @@ |
20 | <property name="can_focus">True</property> |
21 | <property name="receives_default">False</property> |
22 | <property name="draw_indicator">True</property> |
23 | - <signal name="toggled" handler="on_limit_bandwidth_toggled"/> |
24 | + <signal name="toggled" handler="on_limit_bandwidth_toggled" swapped="no"/> |
25 | </object> |
26 | </child> |
27 | <child> |
28 | @@ -108,7 +116,7 @@ |
29 | <property name="invisible_char">•</property> |
30 | <property name="activates_default">True</property> |
31 | <property name="adjustment">adjustment1</property> |
32 | - <signal name="value_changed" handler="on_max_upload_speed_value_changed"/> |
33 | + <signal name="value-changed" handler="on_max_upload_speed_value_changed" swapped="no"/> |
34 | </object> |
35 | <packing> |
36 | <property name="left_attach">1</property> |
37 | @@ -126,7 +134,7 @@ |
38 | <property name="invisible_char">•</property> |
39 | <property name="activates_default">True</property> |
40 | <property name="adjustment">adjustment2</property> |
41 | - <signal name="value_changed" handler="on_max_download_speed_value_changed"/> |
42 | + <signal name="value-changed" handler="on_max_download_speed_value_changed" swapped="no"/> |
43 | </object> |
44 | <packing> |
45 | <property name="left_attach">1</property> |
46 | @@ -161,6 +169,8 @@ |
47 | <property name="can_focus">True</property> |
48 | <property name="receives_default">True</property> |
49 | <property name="use_stock">True</property> |
50 | + <signal name="activate" handler="on_remove_clicked" swapped="no"/> |
51 | + <signal name="clicked" handler="on_remove_clicked" swapped="no"/> |
52 | </object> |
53 | <packing> |
54 | <property name="expand">False</property> |
55 | @@ -190,12 +200,4 @@ |
56 | </packing> |
57 | </child> |
58 | </object> |
59 | - <object class="GtkAdjustment" id="adjustment1"> |
60 | - <property name="upper">10000</property> |
61 | - <property name="step_increment">1</property> |
62 | - </object> |
63 | - <object class="GtkAdjustment" id="adjustment2"> |
64 | - <property name="upper">10000</property> |
65 | - <property name="step_increment">1</property> |
66 | - </object> |
67 | </interface> |
68 | |
69 | === modified file 'ubuntuone/controlpanel/__init__.py' |
70 | --- ubuntuone/controlpanel/__init__.py 2010-10-08 01:31:34 +0000 |
71 | +++ ubuntuone/controlpanel/__init__.py 2010-12-18 17:42:12 +0000 |
72 | @@ -29,4 +29,4 @@ |
73 | DBUS_PREFERENCES_PATH = "/preferences" |
74 | DBUS_PREFERENCES_IFACE = "com.ubuntuone.controlpanel.Preferences" |
75 | |
76 | -WEBSERVICE_BASE_URL = "https://one.ubuntu.com/api/" |
77 | +WEBSERVICE_BASE_URL = u"https://one.ubuntu.com/api/" |
78 | |
79 | === modified file 'ubuntuone/controlpanel/backend.py' |
80 | --- ubuntuone/controlpanel/backend.py 2010-12-18 17:42:11 +0000 |
81 | +++ ubuntuone/controlpanel/backend.py 2010-12-18 17:42:12 +0000 |
82 | @@ -49,6 +49,11 @@ |
83 | STATUS_KEY = 'status' |
84 | |
85 | |
86 | +def bool_str(value): |
87 | + """Return the string representation of a bool (dbus-compatible).""" |
88 | + return 'True' if value else '' |
89 | + |
90 | + |
91 | class ControlBackend(object): |
92 | """The control panel backend.""" |
93 | |
94 | @@ -124,6 +129,15 @@ |
95 | credentials = yield dbus_client.get_credentials() |
96 | returnValue(credentials["token"]) |
97 | |
98 | + @inlineCallbacks |
99 | + def device_is_local(self, device_id): |
100 | + """Return whether 'device_id' is the local devicew or not.""" |
101 | + dtype, did = self.type_n_id(device_id) |
102 | + local_token = yield self.get_token() |
103 | + is_local = (dtype == DEVICE_TYPE_COMPUTER and did == local_token) |
104 | + logger.info('device_is_local: result %r, ', is_local) |
105 | + returnValue(is_local) |
106 | + |
107 | @log_call(logger.debug) |
108 | @inlineCallbacks |
109 | def account_info(self): |
110 | @@ -146,7 +160,6 @@ |
111 | def devices_info(self): |
112 | """Get the user devices info.""" |
113 | result = [] |
114 | - this_token = yield self.get_token() |
115 | limit_bw = yield dbus_client.bandwidth_throttling_enabled() |
116 | limits = yield dbus_client.get_throttling_limits() |
117 | |
118 | @@ -159,16 +172,22 @@ |
119 | di["configurable"] = '' |
120 | if di["type"] == DEVICE_TYPE_COMPUTER: |
121 | di["device_id"] = di["type"] + d["token"] |
122 | - if d["token"] == this_token: |
123 | - di["configurable"] = 'True' |
124 | - di["limit_bandwidth"] = 'True' if limit_bw else '' |
125 | - upload = limits["upload"] |
126 | - download = limits["download"] |
127 | - di[UPLOAD_KEY] = str(upload) |
128 | - di[DOWNLOAD_KEY] = str(download) |
129 | if di["type"] == DEVICE_TYPE_PHONE: |
130 | di["device_id"] = di["type"] + str(d["id"]) |
131 | |
132 | + is_local = yield self.device_is_local(di["device_id"]) |
133 | + di["is_local"] = bool_str(is_local) |
134 | + # currently, only local devices are configurable. |
135 | + # eventually, more the devices will be configurable. |
136 | + di["configurable"] = bool_str(is_local) |
137 | + |
138 | + if bool(di["configurable"]): |
139 | + di["limit_bandwidth"] = bool_str(limit_bw) |
140 | + upload = limits["upload"] |
141 | + download = limits["download"] |
142 | + di[UPLOAD_KEY] = str(upload) |
143 | + di[DOWNLOAD_KEY] = str(download) |
144 | + |
145 | # date_added is not in the webservice yet (LP: #673668) |
146 | # di["date_added"] = "" |
147 | |
148 | @@ -190,9 +209,7 @@ |
149 | @inlineCallbacks |
150 | def change_device_settings(self, device_id, settings): |
151 | """Change the settings for the given device.""" |
152 | - dtype, did = self.type_n_id(device_id) |
153 | - local_token = yield self.get_token() |
154 | - is_local = (dtype == DEVICE_TYPE_COMPUTER and did == local_token) |
155 | + is_local = yield self.device_is_local(device_id) |
156 | |
157 | if is_local and "limit_bandwidth" in settings: |
158 | limit_bw = bool(settings["limit_bandwidth"]) |
159 | @@ -222,8 +239,16 @@ |
160 | def remove_device(self, device_id): |
161 | """Remove a device's tokens from the sso server.""" |
162 | dtype, did = self.type_n_id(device_id) |
163 | - api = DEVICE_REMOVE_API % (dtype, did) |
164 | + is_local = yield self.device_is_local(device_id) |
165 | + |
166 | + api = DEVICE_REMOVE_API % (dtype.lower(), did) |
167 | yield self.wc.call_api(api) |
168 | + |
169 | + if is_local: |
170 | + logger.warning('remove_device: device is local, id %r, ' |
171 | + 'clearing credentials.', device_id) |
172 | + yield dbus_client.clear_credentials() |
173 | + |
174 | returnValue(device_id) |
175 | |
176 | @log_call(logger.debug) |
177 | |
178 | === modified file 'ubuntuone/controlpanel/gtk/gui.py' |
179 | --- ubuntuone/controlpanel/gtk/gui.py 2010-12-17 17:25:10 +0000 |
180 | +++ ubuntuone/controlpanel/gtk/gui.py 2010-12-18 17:42:12 +0000 |
181 | @@ -48,7 +48,7 @@ |
182 | from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH, |
183 | DBUS_PREFERENCES_IFACE) |
184 | from ubuntuone.controlpanel.backend import (DEVICE_TYPE_PHONE, |
185 | - DEVICE_TYPE_COMPUTER) |
186 | + DEVICE_TYPE_COMPUTER, bool_str) |
187 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
188 | from ubuntuone.controlpanel.utils import get_data_file |
189 | |
190 | @@ -74,11 +74,6 @@ |
191 | KILOBYTES = 1024 |
192 | |
193 | |
194 | -def bool_str(value): |
195 | - """Return the string representation of a bool (dbus-compatible).""" |
196 | - return 'True' if value else '' |
197 | - |
198 | - |
199 | def filter_by_app_name(f): |
200 | """Excecute 'f' filtering by app_name.""" |
201 | |
202 | @@ -187,15 +182,23 @@ |
203 | self._window_id = window_id |
204 | |
205 | self.management = None |
206 | - self.overview = OverviewPanel(window_id=window_id) |
207 | + self.overview = None |
208 | + self.on_show_overview_panel() |
209 | + self.show() |
210 | + |
211 | + def on_show_overview_panel(self, widget=None): |
212 | + """Show the overview panel.""" |
213 | + for child in self.get_children(): |
214 | + self.remove(child) |
215 | + |
216 | + self.overview = OverviewPanel(window_id=self._window_id) |
217 | self.overview.connect('credentials-found', |
218 | self.on_show_management_panel) |
219 | self.add(self.overview) |
220 | - self.show() |
221 | |
222 | def on_show_management_panel(self, widget=None, |
223 | credentials_are_new=False, token=None): |
224 | - """Show the netbook (main panel).""" |
225 | + """Show the notebook (main panel).""" |
226 | if self.overview in self.get_children(): |
227 | self.remove(self.overview) |
228 | |
229 | @@ -206,6 +209,9 @@ |
230 | |
231 | self.add(self.management) |
232 | |
233 | + self.management.connect('local-device-removed', |
234 | + self.on_show_overview_panel) |
235 | + |
236 | |
237 | class UbuntuOneBin(gtk.VBox): |
238 | """A Ubuntu One bin.""" |
239 | @@ -505,10 +511,11 @@ |
240 | |
241 | |
242 | class Device(gtk.VBox, ControlPanelMixin): |
243 | - """The devices panel.""" |
244 | + """The device widget.""" |
245 | |
246 | DEVICE_CHANGE_ERROR = _('The settings could not be changed,\n' |
247 | 'previous values were restored.') |
248 | + DEVICE_REMOVAL_ERROR = _('The device could not be removed.') |
249 | |
250 | def __init__(self): |
251 | gtk.VBox.__init__(self) |
252 | @@ -516,19 +523,25 @@ |
253 | |
254 | self._updating = False |
255 | self._last_settings = {} |
256 | + self.id = None |
257 | + self.is_local = False |
258 | self.configurable = False |
259 | |
260 | - self.update(device_id='', device_name='', limit_bandwidth=False, |
261 | + self.update(device_id=None, device_name='', |
262 | + is_local=False, configurable=False, limit_bandwidth=False, |
263 | max_upload_speed=0, max_download_speed=0) |
264 | |
265 | self.add(self.itself) |
266 | - self.device_id.hide() |
267 | self.show() |
268 | |
269 | self.backend.connect_to_signal('DeviceSettingsChanged', |
270 | self.on_device_settings_changed) |
271 | self.backend.connect_to_signal('DeviceSettingsChangeError', |
272 | self.on_device_settings_change_error) |
273 | + self.backend.connect_to_signal('DeviceRemoved', |
274 | + self.on_device_removed) |
275 | + self.backend.connect_to_signal('DeviceRemovalError', |
276 | + self.on_device_removal_error) |
277 | |
278 | def _change_device_settings(self, *args): |
279 | """Update backend settings for this device.""" |
280 | @@ -538,8 +551,7 @@ |
281 | # Not disabling the GUI to avoid annyong twitchings |
282 | #self.set_sensitive(False) |
283 | self.warning_label.set_text('') |
284 | - self.backend.change_device_settings(self.device_id.get_text(), |
285 | - self.__dict__) |
286 | + self.backend.change_device_settings(self.id, self.__dict__) |
287 | |
288 | def _block_signals(f): |
289 | """Execute 'f' while having the _updating flag set.""" |
290 | @@ -563,6 +575,11 @@ |
291 | on_max_upload_speed_value_changed = _change_device_settings |
292 | on_max_download_speed_value_changed = _change_device_settings |
293 | |
294 | + def on_remove_clicked(self, widget): |
295 | + """Remove button was clicked or activated.""" |
296 | + self.backend.remove_device(self.id) |
297 | + self.set_sensitive(False) |
298 | + |
299 | @_block_signals |
300 | def update(self, **kwargs): |
301 | """Update according to named parameters. |
302 | @@ -571,6 +588,7 @@ |
303 | * device_id (string, not shown to the user) |
304 | * device_name (string) |
305 | * type (either DEVICE_TYPE_PHONE or DEVICE_TYPE_COMPUTER) |
306 | + * is_local (True/False) |
307 | * configurable (True/False) |
308 | * if configurable, the following can be set: |
309 | * limit_bandwidth (True/False) |
310 | @@ -579,7 +597,7 @@ |
311 | |
312 | """ |
313 | if 'device_id' in kwargs: |
314 | - self.device_id.set_text(kwargs['device_id']) |
315 | + self.id = kwargs['device_id'] |
316 | |
317 | if 'device_name' in kwargs: |
318 | self.device_name.set_markup('<b>%s</b>' % kwargs['device_name']) |
319 | @@ -590,6 +608,9 @@ |
320 | self.device_type.set_from_icon_name(dtype.lower(), |
321 | gtk.ICON_SIZE_BUTTON) |
322 | |
323 | + if 'is_local' in kwargs: |
324 | + self.is_local = bool(kwargs['is_local']) |
325 | + |
326 | if 'configurable' in kwargs: |
327 | self.configurable = bool(kwargs['configurable']) |
328 | self.throttling.set_visible(self.configurable) |
329 | @@ -607,9 +628,10 @@ |
330 | @property |
331 | def __dict__(self): |
332 | result = { |
333 | - 'device_id': self.device_id.get_text(), |
334 | + 'device_id': self.id, |
335 | 'device_name': self.device_name.get_text(), |
336 | 'device_type': self.device_type.get_icon_name()[0].capitalize(), |
337 | + 'is_local': bool_str(self.is_local), |
338 | 'configurable': bool_str(self.configurable), |
339 | 'limit_bandwidth': bool_str(self.limit_bandwidth.get_active()), |
340 | 'max_upload_speed': \ |
341 | @@ -622,7 +644,7 @@ |
342 | @log_call(logger.info) |
343 | def on_device_settings_changed(self, device_id): |
344 | """The change of this device settings succeded.""" |
345 | - if device_id != self.device_id.get_text(): |
346 | + if device_id != self.id: |
347 | return |
348 | self.set_sensitive(True) |
349 | self.warning_label.set_text('') |
350 | @@ -631,12 +653,27 @@ |
351 | @log_call(logger.error) |
352 | def on_device_settings_change_error(self, device_id, error_dict=None): |
353 | """The change of this device settings failed.""" |
354 | - if device_id != self.device_id.get_text(): |
355 | + if device_id != self.id: |
356 | return |
357 | self.update(**self._last_settings) |
358 | self._set_warning(self.DEVICE_CHANGE_ERROR, self.warning_label) |
359 | self.set_sensitive(True) |
360 | |
361 | + @log_call(logger.warning) |
362 | + def on_device_removed(self, device_id): |
363 | + """The removal of this device succeded.""" |
364 | + if device_id != self.id: |
365 | + return |
366 | + self.hide() |
367 | + |
368 | + @log_call(logger.error) |
369 | + def on_device_removal_error(self, device_id, error_dict=None): |
370 | + """The removal of this device failed.""" |
371 | + if device_id != self.id: |
372 | + return |
373 | + self._set_warning(self.DEVICE_REMOVAL_ERROR, self.warning_label) |
374 | + self.set_sensitive(True) |
375 | + |
376 | |
377 | class DevicesPanel(UbuntuOneBin, ControlPanelMixin): |
378 | """The devices panel.""" |
379 | @@ -650,10 +687,14 @@ |
380 | self.add(self.itself) |
381 | self.show() |
382 | |
383 | + self._devices = {} |
384 | + |
385 | self.backend.connect_to_signal('DevicesInfoReady', |
386 | self.on_devices_info_ready) |
387 | self.backend.connect_to_signal('DevicesInfoError', |
388 | self.on_devices_info_error) |
389 | + self.backend.connect_to_signal('DeviceRemoved', |
390 | + self.on_device_removed) |
391 | self.backend.devices_info() |
392 | |
393 | def on_devices_info_ready(self, info): |
394 | @@ -665,11 +706,22 @@ |
395 | DEVICE_TYPE_COMPUTER) |
396 | device.update(**device_info) |
397 | self.devices.pack_start(device) |
398 | + self._devices[device.id] = device |
399 | |
400 | @log_call(logger.error) |
401 | def on_devices_info_error(self, error_dict=None): |
402 | """Backend notifies of an error when fetching volumes info.""" |
403 | |
404 | + @log_call(logger.warning) |
405 | + def on_device_removed(self, device_id): |
406 | + """The removal of a device succeded.""" |
407 | + if device_id in self._devices: |
408 | + child = self._devices.pop(device_id) |
409 | + self.devices.remove(child) |
410 | + |
411 | + if child.is_local: |
412 | + self.emit('local-device-removed') |
413 | + |
414 | |
415 | class ApplicationsPanel(UbuntuOneBin, ControlPanelMixin): |
416 | """The applications panel.""" |
417 | @@ -750,6 +802,8 @@ |
418 | self.notebook.insert_page(getattr(self, tab), position=page_num) |
419 | |
420 | self.folders_button.connect('clicked', lambda b: self.folders.load()) |
421 | + sig = 'local-device-removed' |
422 | + self.devices.connect(sig, lambda widget: self.emit(sig)) |
423 | |
424 | self.backend.account_info() |
425 | self.backend.file_sync_status() |
426 | @@ -818,3 +872,6 @@ |
427 | gobject.signal_new('credentials-found', OverviewPanel, |
428 | gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, |
429 | (gobject.TYPE_BOOLEAN, gobject.TYPE_PYOBJECT)) |
430 | + |
431 | +gobject.signal_new('local-device-removed', DevicesPanel, |
432 | + gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()) |
433 | |
434 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py' |
435 | --- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-17 17:25:10 +0000 |
436 | +++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-18 17:42:12 +0000 |
437 | @@ -47,19 +47,21 @@ |
438 | |
439 | FAKE_DEVICE_INFO = { |
440 | 'device_id': '1258-6854', 'device_name': 'Baz', 'device_type': 'Computer', |
441 | - 'configurable': 'True', 'limit_bandwidth': 'True', |
442 | + 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True', |
443 | 'max_upload_speed': '1000', 'max_download_speed': '72548', |
444 | } |
445 | |
446 | FAKE_DEVICES_INFO = [ |
447 | - {'device_id': '0', 'name': 'Foo', 'type': 'Computer', 'configurable': ''}, |
448 | - {'device_id': '1', 'name': 'Bar', 'type': 'Phone', 'configurable': ''}, |
449 | + {'device_id': '0', 'name': 'Foo', 'type': 'Computer', |
450 | + 'is_local': '', 'configurable': ''}, |
451 | + {'device_id': '1', 'name': 'Bar', 'type': 'Phone', |
452 | + 'is_local': '', 'configurable': ''}, |
453 | {'device_id': '2', 'name': 'Z', 'type': 'Computer', |
454 | - 'configurable': 'True', 'limit_bandwidth': '', |
455 | + 'is_local': '', 'configurable': 'True', 'limit_bandwidth': '', |
456 | 'max_upload_speed': '0', 'max_download_speed': '0'}, |
457 | {'device_id': '1258-6854', 'name': 'Baz', 'type': 'Computer', |
458 | - 'configurable': 'True', 'limit_bandwidth': 'True', |
459 | - 'max_upload_speed': '1000', 'max_download_speed': '72548'}, |
460 | + 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True', |
461 | + 'max_upload_speed': '1000', 'max_download_speed': '72548'}, # local |
462 | ] |
463 | |
464 | |
465 | @@ -128,6 +130,7 @@ |
466 | exposed_methods = [ |
467 | 'account_info', 'devices_info', 'change_device_settings', |
468 | 'volumes_info', 'change_volume_settings', 'file_sync_status', |
469 | + 'remove_device', |
470 | ] |
471 | |
472 | |
473 | @@ -356,6 +359,15 @@ |
474 | self.assertEqual(self.ui.management.notebook.get_current_page(), |
475 | self.ui.management.FOLDERS_PAGE) |
476 | |
477 | + def test_local_device_removed_shows_overview_panel(self): |
478 | + """On 'local-device-removed' signal, the overview panel is shown.""" |
479 | + self.ui.overview.emit('credentials-found', True, object()) |
480 | + self.ui.management.emit('local-device-removed') |
481 | + |
482 | + children = self.ui.get_children() |
483 | + self.assertEqual(1, len(children)) |
484 | + self.assertTrue(children[0] is self.ui.overview) |
485 | + |
486 | |
487 | class UbuntuOneBinTestCase(BaseTestCase): |
488 | """The test suite for a Ubuntu One panel.""" |
489 | @@ -907,12 +919,14 @@ |
490 | |
491 | def assert_device_equal(self, device, expected): |
492 | """Assert that the device has the values from expected.""" |
493 | - self.assertEqual(device.device_id.get_text(), |
494 | + self.assertEqual(device.id, |
495 | expected['device_id']) |
496 | self.assertEqual(device.device_name.get_text(), |
497 | expected['device_name']) |
498 | self.assertEqual(device.device_type.get_icon_name()[0], |
499 | expected['device_type'].lower()) |
500 | + self.assertEqual(device.is_local, |
501 | + bool(expected['is_local'])) |
502 | self.assertEqual(device.configurable, |
503 | bool(expected['configurable'])) |
504 | self.assertEqual(device.limit_bandwidth.get_active(), |
505 | @@ -927,7 +941,7 @@ |
506 | """Changing throttling settings updates the backend properly.""" |
507 | expected = self.ui.__dict__ |
508 | self.assert_backend_called('change_device_settings', |
509 | - (self.ui.device_id.get_text(), expected)) |
510 | + (self.ui.id, expected)) |
511 | self.assertEqual(self.ui.warning_label.get_text(), '') |
512 | |
513 | def modify_settings(self): |
514 | @@ -961,16 +975,13 @@ |
515 | """The warning label is cleared.""" |
516 | self.assertEqual(self.ui.warning_label.get_text(), '') |
517 | |
518 | - def test_device_id_is_hidden(self): |
519 | - """The device id label is hidden.""" |
520 | - self.assertFalse(self.ui.device_id.get_visible()) |
521 | - |
522 | def test_default_values(self): |
523 | """Default values are correct.""" |
524 | - self.assertEqual(self.ui.device_id.get_text(), '') |
525 | + self.assertEqual(self.ui.id, None) |
526 | self.assertEqual(self.ui.device_name.get_text(), '') |
527 | self.assertEqual(self.ui.device_type.get_icon_name()[0], |
528 | gui.DEVICE_TYPE_COMPUTER.lower()) |
529 | + self.assertEqual(self.ui.is_local, False) |
530 | self.assertEqual(self.ui.configurable, False) |
531 | self.assertEqual(self.ui.limit_bandwidth.get_active(), False) |
532 | self.assertEqual(self.ui.max_upload_speed.get_value_as_int(), 0) |
533 | @@ -980,12 +991,6 @@ |
534 | """When updating, the backend is not called.""" |
535 | self.assertEqual(self.ui.backend._called, {}) |
536 | |
537 | - def test_update_device_id(self): |
538 | - """A device can be updated from a dict.""" |
539 | - value = '741-822-963' |
540 | - self.ui.update(device_id=value) |
541 | - self.assertEqual(value, self.ui.device_id.get_text()) |
542 | - |
543 | def test_update_device_name(self): |
544 | """A device can be updated from a dict.""" |
545 | value = 'The death star' |
546 | @@ -1012,13 +1017,23 @@ |
547 | self.assertEqual((dtype.lower(), gui.gtk.ICON_SIZE_BUTTON), |
548 | self.ui.device_type.get_icon_name()) |
549 | |
550 | - def test_update_configurable(self): |
551 | + def test_update_is_not_local(self): |
552 | + """A device can be updated from a dict.""" |
553 | + self.ui.update(is_local='') |
554 | + self.assertFalse(self.ui.is_local) |
555 | + |
556 | + def test_update_is_local(self): |
557 | + """A device can be updated from a dict.""" |
558 | + self.ui.update(is_local='True') |
559 | + self.assertTrue(self.ui.is_local) |
560 | + |
561 | + def test_update_non_configurable(self): |
562 | """A device can be updated from a dict.""" |
563 | self.ui.update(configurable='') |
564 | self.assertFalse(self.ui.configurable) |
565 | self.assertFalse(self.ui.throttling.get_visible()) |
566 | |
567 | - def test_update_non_configurable(self): |
568 | + def test_update_configurable(self): |
569 | """A device can be updated from a dict.""" |
570 | self.ui.update(configurable='True') |
571 | self.assertTrue(self.ui.configurable) |
572 | @@ -1073,12 +1088,15 @@ |
573 | [self.ui.on_device_settings_changed]) |
574 | self.assertEqual(self.ui.backend._signals['DeviceSettingsChangeError'], |
575 | [self.ui.on_device_settings_change_error]) |
576 | + self.assertEqual(self.ui.backend._signals['DeviceRemoved'], |
577 | + [self.ui.on_device_removed]) |
578 | + self.assertEqual(self.ui.backend._signals['DeviceRemovalError'], |
579 | + [self.ui.on_device_removal_error]) |
580 | |
581 | def test_on_device_settings_changed(self): |
582 | """When settings were changed for this device, enable it.""" |
583 | self.modify_settings() |
584 | - did = self.ui.device_id.get_text() |
585 | - self.ui.on_device_settings_changed(device_id=did) |
586 | + self.ui.on_device_settings_changed(device_id=self.ui.id) |
587 | |
588 | self.assertTrue(self.ui.get_sensitive()) |
589 | self.assertEqual(self.ui.warning_label.get_text(), '') |
590 | @@ -1087,8 +1105,7 @@ |
591 | def test_on_device_settings_change_after_error(self): |
592 | """Change success after error.""" |
593 | self.modify_settings() |
594 | - did = self.ui.device_id.get_text() |
595 | - self.ui.on_device_settings_change_error(device_id=did) # change failed |
596 | + self.ui.on_device_settings_change_error(device_id=self.ui.id) # error |
597 | |
598 | self.test_on_device_settings_changed() |
599 | |
600 | @@ -1109,8 +1126,7 @@ |
601 | |
602 | self.modify_settings() |
603 | |
604 | - did = self.ui.device_id.get_text() |
605 | - self.ui.on_device_settings_change_error(device_id=did) # change failed |
606 | + self.ui.on_device_settings_change_error(device_id=self.ui.id) # error |
607 | |
608 | self.assertTrue(self.ui.get_sensitive()) |
609 | self.assert_warning_correct(self.ui.warning_label, |
610 | @@ -1120,8 +1136,7 @@ |
611 | def test_on_device_settings_change_error_after_success(self): |
612 | """Change error after success.""" |
613 | self.modify_settings() |
614 | - did = self.ui.device_id.get_text() |
615 | - self.ui.on_device_settings_changed(device_id=did) |
616 | + self.ui.on_device_settings_changed(device_id=self.ui.id) |
617 | |
618 | self.test_on_device_settings_change_error() |
619 | |
620 | @@ -1131,6 +1146,49 @@ |
621 | self.ui.on_device_settings_change_error(device_id='yudo') |
622 | self.assertEqual(self.ui.warning_label.get_text(), '') |
623 | |
624 | + def test_remove(self): |
625 | + """Clicking on remove calls the backend properly.""" |
626 | + self.ui.is_local = False |
627 | + self.ui.remove.clicked() |
628 | + |
629 | + self.assert_backend_called('remove_device', (self.ui.id,)) |
630 | + self.assertFalse(self.ui.get_sensitive(), |
631 | + 'Must be disabled while removing.') |
632 | + |
633 | + def test_on_device_removed(self): |
634 | + """On this device removed, hide and destroy.""" |
635 | + self.ui.remove.clicked() |
636 | + self.ui.on_device_removed(device_id=self.ui.id) |
637 | + |
638 | + self.assertFalse(self.ui.get_visible(), |
639 | + 'Must not be visible after removed.') |
640 | + |
641 | + def test_on_device_removed_other_id(self): |
642 | + """On other device removed, do nothing.""" |
643 | + self.ui.remove.clicked() |
644 | + self.ui.on_device_removed(device_id='foo') |
645 | + |
646 | + self.assertTrue(self.ui.get_visible(), |
647 | + 'Must be visible after other device was removed.') |
648 | + |
649 | + def test_on_device_removal_error(self): |
650 | + """On this device removal error, re-enable and show error.""" |
651 | + self.ui.remove.clicked() |
652 | + self.ui.on_device_removal_error(device_id=self.ui.id) |
653 | + |
654 | + self.assertTrue(self.ui.get_sensitive(), |
655 | + 'Must be enabled after removal error.') |
656 | + self.assert_warning_correct(self.ui.warning_label, |
657 | + self.ui.DEVICE_REMOVAL_ERROR) |
658 | + |
659 | + def test_on_device_removal_error_other_id(self): |
660 | + """On other device removal error, do nothing.""" |
661 | + self.ui.remove.clicked() |
662 | + self.ui.on_device_removal_error(device_id='foo') |
663 | + |
664 | + self.assertFalse(self.ui.get_sensitive(), |
665 | + 'Must be disabled after other device removal error.') |
666 | + |
667 | |
668 | class DevicesTestCase(ControlPanelMixinTestCase): |
669 | """The test suite for the devices panel.""" |
670 | @@ -1156,6 +1214,8 @@ |
671 | [self.ui.on_devices_info_ready]) |
672 | self.assertEqual(self.ui.backend._signals['DevicesInfoError'], |
673 | [self.ui.on_devices_info_error]) |
674 | + self.assertEqual(self.ui.backend._signals['DeviceRemoved'], |
675 | + [self.ui.on_device_removed]) |
676 | |
677 | def test_devices_info_is_requested(self): |
678 | """The devices info is requested to the backend.""" |
679 | @@ -1171,12 +1231,13 @@ |
680 | for child, device in zip(children, FAKE_DEVICES_INFO): |
681 | self.assertIsInstance(child, gui.Device) |
682 | |
683 | - self.assertEqual(device['device_id'], |
684 | - child.device_id.get_text()) |
685 | + self.assertEqual(device['device_id'], child.id) |
686 | self.assertEqual(device['device_name'], |
687 | child.device_name.get_text()) |
688 | self.assertEqual(device['device_type'].lower(), |
689 | child.device_type.get_icon_name()[0]) |
690 | + self.assertEqual(bool(device['is_local']), |
691 | + child.is_local) |
692 | self.assertEqual(bool(device['configurable']), |
693 | child.configurable) |
694 | |
695 | @@ -1190,9 +1251,51 @@ |
696 | self.assertEqual(value, |
697 | child.max_download_speed.get_value_as_int()) |
698 | |
699 | + def test_on_devices_info_ready_have_devices_cached(self): |
700 | + """The devices are cached for further removal.""" |
701 | + self.ui.on_devices_info_ready(FAKE_DEVICES_INFO) |
702 | + |
703 | + for child in self.ui.devices.get_children(): |
704 | + self.assertTrue(self.ui._devices[child.id] is child) |
705 | + |
706 | def test_on_devices_info_error(self): |
707 | """The devices info couldn't be retrieved.""" |
708 | self.ui.on_devices_info_error() |
709 | + # add test! |
710 | + |
711 | + def test_on_device_removed(self): |
712 | + """When a child device was removed, remove and destroy.""" |
713 | + self.ui.on_devices_info_ready(FAKE_DEVICES_INFO) |
714 | + did = FAKE_DEVICES_INFO[0]['device_id'] |
715 | + device = self.ui._devices[did] |
716 | + self.ui.on_device_removed(device_id=did) |
717 | + |
718 | + self.assertTrue(device not in self.ui.devices.get_children()) |
719 | + self.assertTrue(did not in self.ui._devices) |
720 | + |
721 | + def test_on_local_device_removed(self): |
722 | + """Removing the local device emits local-device-removed.""" |
723 | + self.ui.connect('local-device-removed', self._set_called) |
724 | + |
725 | + self.ui.on_devices_info_ready(FAKE_DEVICES_INFO) |
726 | + local_device = FAKE_DEVICES_INFO[-1] |
727 | + assert bool(local_device['is_local']) |
728 | + local_device_id = local_device['device_id'] |
729 | + assert self.ui._devices[local_device_id].is_local |
730 | + |
731 | + self.ui.on_device_removed(device_id=local_device_id) |
732 | + |
733 | + self.assertEqual(self._called, ((self.ui,), {})) |
734 | + |
735 | + def test_on_device_removed_for_no_child_device(self): |
736 | + """On other device removed, do nothing.""" |
737 | + self.ui.on_devices_info_ready(FAKE_DEVICES_INFO) |
738 | + old_devices = self.ui.devices.get_children() |
739 | + |
740 | + self.ui.on_device_removed(device_id='foo') |
741 | + |
742 | + new_devices = self.ui.devices.get_children() |
743 | + self.assertEqual(new_devices, old_devices) |
744 | |
745 | |
746 | class ApplicationsTestCase(ControlPanelMixinTestCase): |
747 | @@ -1413,3 +1516,11 @@ |
748 | self.assert_warning_correct(self.ui.status_label, |
749 | self.ui.FILE_SYNC_ERROR) |
750 | self.assertFalse(self.ui.status_label.active) |
751 | + |
752 | + def test_local_device_removed_is_emitted(self): |
753 | + """Signal local-device-removed is sent when DevicesPanel emits it.""" |
754 | + self.ui.connect('local-device-removed', self._set_called) |
755 | + |
756 | + self.ui.devices.emit('local-device-removed') |
757 | + |
758 | + self.assertEqual(self._called, ((self.ui,), {})) |
759 | |
760 | === modified file 'ubuntuone/controlpanel/tests/test_backend.py' |
761 | --- ubuntuone/controlpanel/tests/test_backend.py 2010-12-18 17:42:11 +0000 |
762 | +++ ubuntuone/controlpanel/tests/test_backend.py 2010-12-18 17:42:12 +0000 |
763 | @@ -114,9 +114,11 @@ |
764 | "device_id": "ComputerABCDEF01234token", |
765 | "name": "Ubuntu One @ darkstar", |
766 | "type": "Computer", |
767 | + "is_local": '', |
768 | "configurable": '', |
769 | }, |
770 | { |
771 | + 'is_local': 'True', |
772 | 'configurable': 'True', |
773 | 'device_id': 'ComputerABC1234DEF', |
774 | 'limit_bandwidth': '', |
775 | @@ -130,6 +132,7 @@ |
776 | "name": "Nokia E65", |
777 | "type": "Phone", |
778 | "configurable": '', |
779 | + "is_local": '', |
780 | }, |
781 | ] |
782 | |
783 | @@ -301,6 +304,16 @@ |
784 | token = yield self.be.get_token() |
785 | self.assertEqual(token, SAMPLE_CREDENTIALS["token"]) |
786 | |
787 | + @inlineCallbacks |
788 | + def test_device_is_local(self): |
789 | + """The device_is_local returns the right result.""" |
790 | + result = yield self.be.device_is_local(self.local_token) |
791 | + self.assertTrue(result) |
792 | + |
793 | + did = EXPECTED_DEVICES_INFO[0]['device_id'] |
794 | + result = yield self.be.device_is_local(did) |
795 | + self.assertFalse(result) |
796 | + |
797 | |
798 | class BackendAccountTestCase(BackendBasicTestCase): |
799 | """Account tests for the backend.""" |
800 | @@ -345,11 +358,23 @@ |
801 | """The remove_device method calls the right api.""" |
802 | dtype, did = "Computer", "SAMPLE-TOKEN" |
803 | device_id = dtype + did |
804 | - apiurl = DEVICE_REMOVE_API % (dtype, did) |
805 | + apiurl = DEVICE_REMOVE_API % (dtype.lower(), did) |
806 | # pylint: disable=E1101 |
807 | self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON |
808 | result = yield self.be.remove_device(device_id) |
809 | self.assertEqual(result, device_id) |
810 | + # credentials were not cleared |
811 | + self.assertEqual(MockDBusClient.creds, SAMPLE_CREDENTIALS) |
812 | + |
813 | + @inlineCallbacks |
814 | + def test_remove_device_clear_credentials_if_local_device(self): |
815 | + """The remove_device method clears the credentials if is local.""" |
816 | + apiurl = DEVICE_REMOVE_API % ('computer', SAMPLE_CREDENTIALS['token']) |
817 | + # pylint: disable=E1101 |
818 | + self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON |
819 | + yield self.be.remove_device(self.local_token) |
820 | + # credentials were cleared |
821 | + self.assertEqual(MockDBusClient.creds, None) |
822 | |
823 | @inlineCallbacks |
824 | def test_remove_device_fails(self): |
825 | |
826 | === modified file 'ubuntuone/controlpanel/webclient.py' |
827 | --- ubuntuone/controlpanel/webclient.py 2010-12-02 16:23:03 +0000 |
828 | +++ ubuntuone/controlpanel/webclient.py 2010-12-18 17:42:12 +0000 |
829 | @@ -48,22 +48,24 @@ |
830 | |
831 | def _handler(self, session, msg, d): |
832 | """Handle the result of an http message.""" |
833 | - logger.debug("WebClient: got http response: %d", msg.status_code) |
834 | + logger.debug("WebClient: got http response %d for uri %r", |
835 | + msg.status_code, msg.get_uri().to_string(False)) |
836 | + data = msg.response_body.data |
837 | if msg.status_code == 200: |
838 | - result = simplejson.loads(msg.response_body.data) |
839 | + result = simplejson.loads(data) |
840 | d.callback(result) |
841 | else: |
842 | - e = WebClientError(msg.status_code, msg) |
843 | + e = WebClientError(msg.status_code, data) |
844 | d.errback(e) |
845 | |
846 | def _call_api_with_creds(self, credentials, api_name): |
847 | """Get a given url from the webservice with credentials.""" |
848 | - url = self.base_url + api_name |
849 | + url = (self.base_url + api_name).encode('utf-8') |
850 | method = "GET" |
851 | + logger.debug("WebClient: getting url: %s, %s", method, url) |
852 | msg = Soup.Message.new(method, url) |
853 | add_oauth_headers(msg.request_headers.append, method, url, credentials) |
854 | d = defer.Deferred() |
855 | - logger.debug("WebClient: getting url: %s", url) |
856 | self.session.queue_message(msg, self._handler, d) |
857 | return d |
858 |
It works.