Merge lp:~chipaca/ubuntuone-client/devices-tab into lp:ubuntuone-client

Proposed by dobey
Status: Superseded
Proposed branch: lp:~chipaca/ubuntuone-client/devices-tab
Merge into: lp:ubuntuone-client
Diff against target: 917 lines (+514/-216)
3 files modified
bin/ubuntuone-preferences (+373/-163)
tests/test_preferences.py (+139/-53)
ubuntuone/syncdaemon/dbus_interface.py (+2/-0)
To merge this branch: bzr merge lp:~chipaca/ubuntuone-client/devices-tab
Reviewer Review Type Date Requested Status
Eric Casteleijn (community) Needs Fixing
Review via email: mp+20608@code.launchpad.net

This proposal has been superseded by a proposal from 2010-03-05.

Commit message

Add other devices and management ui to Devices tab

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) wrote :

Getting keyring errors

Revision history for this message
Eric Casteleijn (thisfred) wrote :
Download full text (8.5 KiB)

===============================================================================
[ERROR]: tests.test_preferences.PreferencesTests.test_bw_throttling

Traceback (most recent call last):
  File "/home/eric/canonical/ubuntuone-client/devices-tab/contrib/mocker.py", line 102, in test_method_wrapper
    result = test_method()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/tests/test_preferences.py", line 96, in test_bw_throttling
    dialog = self.u1prefs.UbuntuOneDialog()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 473, in __init__
    self.__construct()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 686, in __construct
    self.devices.get_devices()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 258, in get_devices
    token = get_access_token()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 84, in get_access_token
    'oauth-consumer-key': 'ubuntuone'})
gnomekeyring.IOError:
===============================================================================
[ERROR]: tests.test_preferences.PreferencesTests.test_quota_display

Traceback (most recent call last):
  File "/home/eric/canonical/ubuntuone-client/devices-tab/contrib/mocker.py", line 102, in test_method_wrapper
    result = test_method()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/tests/test_preferences.py", line 107, in test_quota_display
    dialog = self.u1prefs.UbuntuOneDialog()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 473, in __init__
    self.__construct()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 686, in __construct
    self.devices.get_devices()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 258, in get_devices
    token = get_access_token()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 84, in get_access_token
    'oauth-consumer-key': 'ubuntuone'})
gnomekeyring.IOError:
===============================================================================
[ERROR]: tests.test_preferences.PreferencesTests.test_request_account_info

Traceback (most recent call last):
  File "/home/eric/canonical/ubuntuone-client/devices-tab/contrib/mocker.py", line 102, in test_method_wrapper
    result = test_method()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/tests/test_preferences.py", line 135, in test_request_account_info
    dialog = self.u1prefs.UbuntuOneDialog()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 473, in __init__
    self.__construct()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 686, in __construct
    self.devices.get_devices()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 258, in get_devices
    token = get_access_token()
  File "/home/eric/canonical/ubuntuone-client/devices-tab/bin/ubuntuone-preferences", line 84, in get_access_token
    'oauth-consumer...

Read more...

review: Needs Fixing
403. By John Lenton

merged with trunk

404. By John Lenton

merged with trunk (again)

405. By John Lenton

minor refactoring (to get tests working again) and tests. Still TODO: test the REST calls are done

406. By John Lenton

worked around a bug in the rest api

407. By John Lenton

fixed issues raised by dobey

408. By John Lenton

fixed issues raised by nessa

409. By John Lenton

more changed by dobey (and some older ones)

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/ubuntuone-preferences'
--- bin/ubuntuone-preferences 2010-03-01 22:10:22 +0000
+++ bin/ubuntuone-preferences 2010-03-05 17:04:28 +0000
@@ -23,6 +23,7 @@
23import pygtk23import pygtk
24pygtk.require('2.0')24pygtk.require('2.0')
25import gobject25import gobject
26import glib
26import gtk27import gtk
27import os28import os
28import gettext29import gettext
@@ -32,6 +33,11 @@
32from oauth import oauth33from oauth import oauth
33from ubuntuone import clientdefs34from ubuntuone import clientdefs
34from ubuntuone.syncdaemon.tools import SyncDaemonTool35from ubuntuone.syncdaemon.tools import SyncDaemonTool
36from ubuntuone.syncdaemon.logger import LOGFOLDER
37
38import logging
39import sys
40import httplib, urlparse, socket
3541
36import dbus.service42import dbus.service
37from ConfigParser import ConfigParser43from ConfigParser import ConfigParser
@@ -39,6 +45,11 @@
39from dbus.mainloop.glib import DBusGMainLoop45from dbus.mainloop.glib import DBusGMainLoop
40from xdg.BaseDirectory import xdg_config_home46from xdg.BaseDirectory import xdg_config_home
4147
48logging.basicConfig(
49 filename=os.path.join(LOGFOLDER, 'u1-prefs.log'),
50 level=logging.DEBUG,
51 format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
52logger = logging.getLogger("ubuntuone-preferences")
42DBusGMainLoop(set_as_default=True)53DBusGMainLoop(set_as_default=True)
4354
44_ = gettext.gettext55_ = gettext.gettext
@@ -65,10 +76,358 @@
65 pass76 pass
6677
6778
79def get_access_token(keyring):
80 items = []
81 items = keyring.find_items_sync(
82 keyring.ITEM_GENERIC_SECRET,
83 {'ubuntuone-realm': "https://ubuntuone.com",
84 'oauth-consumer-key': 'ubuntuone'})
85 secret = items[0].secret
86 return oauth.OAuthToken.from_string(secret)
87
88
89class DevicesWidget(gtk.Table):
90 """
91 the Devices tab
92 """
93 def __init__(self,
94 bus,
95 keyring=gnomekeyring,
96 realm='https://ubuntuone.com',
97 consumer_key='ubuntuone',
98 url='https://one.ubuntu.com/api/1.0/devices/'):
99 super(DevicesWidget, self).__init__(rows=2, columns=3)
100 self.bus = bus
101 self.keyring = keyring
102 self.sdtool = SyncDaemonTool(bus)
103 self.set_border_width(6)
104 self.set_row_spacings(6)
105 self.set_col_spacings(6)
106 self.devices = None
107 self.realm = realm
108 self.consumer_key = consumer_key
109 self.base_url = url
110 self.conn = None
111 self.consumer = None
112 self.table_widgets = []
113
114 self.connected = None # i.e. unknown
115 self.conn_btn = None
116 self.up_spin = None
117 self.dn_spin = None
118 self.bw_chk = None
119 self.bw_limited = False
120 self.up_limit = 2097152
121 self.dn_limit = 2097152
122
123 self._update_id = 0
124
125 self.status_label = gtk.Label("Please wait...")
126 self.attach(self.status_label, 0, 3, 2, 3)
127
128 self.description = gtk.Label("The devices connected to with your personal cloud network are listed below")
129 self.description.set_alignment(0., .5)
130 self.description.set_line_wrap(True)
131 self.attach(self.description, 0, 3, 0, 1, xpadding=12, ypadding=12)
132
133 def update_bw_settings(self):
134 """
135 Push the bandwidth settings at syncdaemon
136 """
137 try:
138 client = self.bus.get_object(DBUS_IFACE_NAME, "/config",
139 follow_name_owner_changes=True)
140 iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME)
141 iface.set_throttling_limits(self.dn_limit, self.up_limit,
142 reply_handler=dbus_async,
143 error_handler=self.error)
144 if self.bw_limited:
145 iface.enable_bandwidth_throttling(reply_handler=dbus_async,
146 error_handler=self.error)
147 else:
148 iface.disable_bandwidth_throttling(reply_handler=dbus_async,
149 error_handler=self.error)
150 except DBusException, e:
151 self.error(str(e))
152
153 def handle_bw_controls_changed(self, *a):
154 """
155 Sync the bandwidth throttling model with the view.
156
157 Start a timer to sync with syncdaemon too.
158 """
159 # Remove the timeout ...
160 if self._update_id != 0:
161 gobject.source_remove(self._update_id)
162
163 # sync the model ...
164 self.bw_limited = self.bw_chk.get_active()
165 self.up_limit = self.up_spin.get_value_as_int() * 1024
166 self.dn_limit = self.dn_spin.get_value_as_int() * 1024
167
168 # ... and add the timeout back
169 self._update_id = gobject.timeout_add_seconds(
170 1, self.update_bw_settings)
171
172 def handle_bw_checkbox_toggled(self, checkbox, *widgets):
173 """
174 Callback for the bandwidth togglebutton
175 """
176 active = checkbox.get_active()
177 for widget in widgets:
178 widget.set_sensitive(active)
179 self.handle_bw_controls_changed()
180
181 def handle_limits(self, limits):
182 """
183 Callback for when syncdaemon tells us its throttling limits
184 """
185 self.up_limit = int(limits['upload'])
186 self.dn_limit = int(limits['download'])
187 if self.up_spin is not None and self.dn_spin is not None:
188 self.up_spin.set_value(self.up_limit / 1024)
189 self.dn_spin.set_value(self.dn_limit / 1024)
190
191 def handle_throttling_enabled(self, enabled):
192 """
193 Callback for when syncdaemon tells us whether throttling is enabled
194 """
195 self.bw_limited = enabled
196 if self.bw_chk is not None:
197 self.bw_chk.set_active(enabled)
198
199 def handle_state_change(self, new_state):
200 """
201 Callback for when syncdaemon's state changes
202 """
203 if new_state['is_error']:
204 # this syncdaemon isn't going to connect no more
205 self.connected = None
206 else:
207 self.connected = new_state['is_connected']
208 if self.conn_btn is not None:
209 if self.connected:
210 self.conn_btn.set_label(_("_Disconnect"))
211 else:
212 self.conn_btn.set_label(_("_Connect"))
213 if self.connected is None:
214 self.conn_btn.set_sensitive(False)
215 else:
216 self.conn_btn.set_sensitive(True)
217
218 def error(self, msg):
219 """
220 Clear the table and show the error message in its place.
221
222 This might be better as an error dialo.
223 """
224 self.clear_devices_view()
225 self.status_label.set_markup("<b>Error:</b> %s" % msg)
226
227 def request(self, path='', method='GET'):
228 """
229 Helper that makes an oauth-wrapped rest request.
230
231 XXX duplication with request_REST_info (but this one should be async)
232 """
233 url = self.base_url + path
234
235 token = get_access_token(self.keyring)
236
237 oauth_request = oauth.OAuthRequest.from_consumer_and_token(
238 http_url=url,
239 http_method=method,
240 oauth_consumer=self.consumer,
241 token=token,
242 parameters='')
243 oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
244 self.consumer, token)
245
246 scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
247
248 conn = httplib.HTTPSConnection(netloc)
249 try:
250 conn.request(method, path, headers=oauth_request.to_header())
251 except socket.error:
252 return None
253 return conn
254
255 def get_devices(self):
256 """
257 Ask the server for a list of devices, and hook up
258 parse_devices to run on the result (when it gets here).
259 """
260 try:
261 token = get_access_token(self.keyring)
262 except gnomekeyring.NoMatchError:
263 self.error("No token in the keyring")
264 self.devices = []
265 else:
266 self.consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")
267
268 self.conn = self.request()
269 if self.conn is None:
270 self.clear_devices_view()
271 self.error('Unable to connect')
272 else:
273 glib.io_add_watch(
274 self.conn.sock,
275 glib.IO_IN | glib.IO_PRI | glib.IO_ERR | glib.IO_HUP,
276 self.parse_devices)
277
278 def parse_devices(self, *a):
279 """
280 Parse the list of devices, and hook up list_devices if it worked
281 """
282 response = self.conn.getresponse() # shouldn't block
283 if response.status == 200:
284 response = response.read() # neither shouold this
285 self.devices = simplejson.loads(response)
286 gobject.idle_add(self.list_devices)
287 else:
288 self.clear_devices_view()
289 self.error(response.reason)
290 return False
291
292 def clear_devices_view(self):
293 """
294 Clear out all the widgets except from the table, the
295 description and the status_label
296 """
297 for i in self.get_children():
298 if i not in (self.description, self.status_label):
299 i.destroy()
300 self.conn_btn = None
301 self.up_spin = None
302 self.dn_spin = None
303 self.bw_chk = None
304
305 def list_devices(self):
306 """
307 Populate the table with the list of devices.
308
309 If the list of devices is empty, make a fake one that refers
310 to the local machine (to get the connect/restart buttons).
311 """
312 self.resize(len(self.devices)+1, 3)
313
314 self.clear_devices_view()
315
316 token = get_access_token(self.keyring)
317
318 if not self.devices:
319 # a stopgap device so you can at least try to connect
320 self.devices = [{'kind': 'Computer',
321 'description': _("<LOCAL MACHINE>"),
322 'token': token.key,
323 'FAKE': 'YES'}]
324
325 name_label = gtk.Label()
326 name_label.set_markup("<b>Name</b>")
327 name_label.set_alignment(0., .5)
328 self.attach(name_label, 1, 2, 1, 2)
329 self.status_label.set_label("")
330
331 i = 1
332 for row in self.devices:
333 i += 1
334 img = gtk.Image()
335 img.set_from_icon_name(row['kind'].lower(), gtk.ICON_SIZE_DND)
336 desc = gtk.Label(row['description'])
337 desc.set_alignment(0., .5)
338 self.attach(img, 0, 1, i, i+1)
339 self.attach(desc, 1, 2, i, i+1)
340 if 'FAKE' not in row:
341 # we don't include the "Remove" button for the fake entry :)
342 butn = gtk.Button('Remove')
343 butn.connect('clicked', self.remove,
344 row['kind'], row['token'])
345 self.attach(butn, 2, 3, i, i+1, xoptions=0, yoptions=0)
346 if row['token'] == token.key:
347 self.bw_chk = ck_btn = gtk.CheckButton(
348 _("_Limit Bandwidth Usage"))
349 ck_btn.set_active(self.bw_limited)
350 up_lbl = gtk.Label(_("Upload (kB/s):"))
351 up_lbl.set_alignment(0., .5)
352 adj = gtk.Adjustment(value=self.up_limit/1024.,
353 lower=0.0, upper=4096.0,
354 step_incr=1.0, page_incr=16.0)
355 self.up_spin = up_btn = gtk.SpinButton(adj)
356 up_btn.connect("value-changed", self.handle_bw_controls_changed)
357 dn_lbl = gtk.Label(_("Download (kB/s):"))
358 dn_lbl.set_alignment(0., .5)
359 adj = gtk.Adjustment(value=self.dn_limit/1024.,
360 lower=0.0, upper=4096.0,
361 step_incr=1.0, page_incr=16.0)
362 self.dn_spin = dn_btn = gtk.SpinButton(adj)
363 dn_btn.connect("value-changed", self.handle_bw_controls_changed)
364 ck_btn.connect('toggled', self.handle_bw_checkbox_toggled,
365 up_lbl, up_btn, dn_lbl, dn_btn)
366 self.handle_bw_checkbox_toggled(ck_btn,
367 up_lbl, up_btn, dn_lbl, dn_btn)
368
369 self.conn_btn = gtk.Button(_('_Connect'))
370 if self.connected is None:
371 self.conn_btn.set_sensitive(False)
372 elif self.connected:
373 self.conn_btn.set_label(_('_Disconnect'))
374 self.conn_btn.connect('clicked', self.handle_connect_button)
375 restart_btn = gtk.Button(_('_Restart'))
376 restart_btn.connect('clicked', self.handle_restart_button)
377 btn_box = gtk.HButtonBox()
378 btn_box.add(self.conn_btn)
379 btn_box.add(restart_btn)
380
381 i += 1
382 self.attach(ck_btn, 1, 3, i, i+1)
383 i += 1
384 self.attach(up_lbl, 1, 2, i, i+1)
385 self.attach(up_btn, 2, 3, i, i+1)
386 i += 1
387 self.attach(dn_lbl, 1, 2, i, i+1)
388 self.attach(dn_btn, 2, 3, i, i+1)
389 i += 1
390 self.attach(btn_box, 1, 3, i, i+1)
391 i += 2
392 self.show_all()
393
394 def handle_connect_button(self, *a):
395 """
396 Callback for the Connect/Disconnect button
397 """
398 self.conn_btn.set_sensitive(False)
399 if self.connected:
400 d = self.sdtool.disconnect()
401 else:
402 d = self.sdtool.connect()
403
404 def handle_restart_button(self, *a):
405 """
406 Callback for the Restart button
407 """
408 self.sdtool.quit().addCallbacks(lambda _: self.sdtool.start())
409
410 def remove(self, button, kind, token):
411 """
412 Callback for the Remove button. Starts an async request to
413 remove a device.
414 """
415 self.conn = self.request('remove/%s/%s' % (kind.lower(), token))
416 if self.conn is None:
417 self.clear_devices_view()
418 self.error('Unable to connect')
419 else:
420 glib.io_add_watch(
421 self.conn.sock,
422 glib.IO_IN | glib.IO_PRI | glib.IO_ERR | glib.IO_HUP,
423 self.parse_devices)
424
425
426
68class UbuntuOneDialog(gtk.Dialog):427class UbuntuOneDialog(gtk.Dialog):
69 """Preferences dialog."""428 """Preferences dialog."""
70429
71 def __init__(self, config=None, *args, **kw):430 def __init__(self, config=None, keyring=gnomekeyring, *args, **kw):
72 """Initializes our config dialog."""431 """Initializes our config dialog."""
73 super(UbuntuOneDialog, self).__init__(*args, **kw)432 super(UbuntuOneDialog, self).__init__(*args, **kw)
74 self.set_title(_("Ubuntu One Preferences"))433 self.set_title(_("Ubuntu One Preferences"))
@@ -80,12 +439,8 @@
80 self.connect("close", self.__handle_response, gtk.RESPONSE_CLOSE)439 self.connect("close", self.__handle_response, gtk.RESPONSE_CLOSE)
81 self.connect("response", self.__handle_response)440 self.connect("response", self.__handle_response)
82441
83 self.bw_enabled = False
84 self.up_limit = 2097152
85 self.dn_limit = 2097152
86
87 self.__bus = dbus.SessionBus()442 self.__bus = dbus.SessionBus()
88 self.keyring = gnomekeyring443 self.keyring = keyring
89444
90 self.__bus.add_signal_receiver(445 self.__bus.add_signal_receiver(
91 handler_function=self.__got_state,446 handler_function=self.__got_state,
@@ -108,15 +463,13 @@
108 # Timeout ID to avoid spamming DBus from spinbutton changes463 # Timeout ID to avoid spamming DBus from spinbutton changes
109 self.__update_id = 0464 self.__update_id = 0
110465
111 # Connectivity status
112 self.connected = False
113
114 # SD Tool object466 # SD Tool object
115 self.sdtool = SyncDaemonTool(self.__bus)467 self.sdtool = SyncDaemonTool(self.__bus)
116 self.sdtool.get_status().addCallbacks(lambda _: self.__got_state,468 self.sdtool.get_status().addCallbacks(lambda _: self.__got_state,
117 self.__sd_error)469 self.__sd_error)
118 # Build the dialog470 # Build the dialog
119 self.__construct()471 self.__construct()
472 logger.debug("starting")
120473
121 def quit(self):474 def quit(self):
122 """Exit the main loop."""475 """Exit the main loop."""
@@ -132,97 +485,23 @@
132485
133 def __got_state(self, state):486 def __got_state(self, state):
134 """Got the state of syncdaemon."""487 """Got the state of syncdaemon."""
135 self.connected = bool(state['is_connected'])488 self.devices.handle_state_change(state)
136 if self.connected:
137 self.conn_btn.set_label(_("Disconnect"))
138 else:
139 self.conn_btn.set_label(_("Connect"))
140 self.conn_btn.set_sensitive(True)
141489
142 def __got_limits(self, limits):490 def __got_limits(self, limits):
143 """Got the throttling limits."""491 """Got the throttling limits."""
144 self.up_limit = int(limits['upload'])492 logger.debug("got limits: %s" % (limits,))
145 self.dn_limit = int(limits['download'])493 self.devices.handle_limits(limits)
146 self.up_spin.set_value(self.up_limit / 1024)
147 self.dn_spin.set_value(self.dn_limit / 1024)
148494
149 def __got_enabled(self, enabled):495 def __got_enabled(self, enabled):
150 """Got the throttling enabled config."""496 """Got the throttling enabled config."""
151 self.bw_enabled = bool(enabled)497 self.devices.handle_throttling_enabled(enabled)
152 self.limit_check.set_active(self.bw_enabled)
153
154 def __update_bw_settings(self):
155 """Update the bandwidth throttling config in syncdaemon."""
156 self.bw_enabled = self.limit_check.get_active()
157 self.up_limit = self.up_spin.get_value_as_int() * 1024
158 self.dn_limit = self.dn_spin.get_value_as_int() * 1024
159
160 try:
161 client = self.__bus.get_object(DBUS_IFACE_NAME, "/config",
162 follow_name_owner_changes=True)
163 iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME)
164 iface.set_throttling_limits(self.dn_limit, self.up_limit,
165 reply_handler=dbus_async,
166 error_handler=self.__dbus_error)
167 if self.bw_enabled:
168 iface.enable_bandwidth_throttling(
169 reply_handler=dbus_async,
170 error_handler=self.__dbus_error)
171 else:
172 iface.disable_bandwidth_throttling(
173 reply_handler=dbus_async,
174 error_handler=self.__dbus_error)
175 except DBusException, e:
176 self.__dbus_error(e)
177498
178 def __handle_response(self, dialog, response):499 def __handle_response(self, dialog, response):
179 """Handle the dialog's response."""500 """Handle the dialog's response."""
180 self.hide()501 self.hide()
181 self.__update_bw_settings()502 self.devices.update_bw_settings()
182 gtk.main_quit()503 gtk.main_quit()
183504
184 def __bw_limit_toggled(self, button, data=None):
185 """Toggle the bw limit panel."""
186 self.bw_enabled = self.limit_check.get_active()
187 self.bw_table.set_sensitive(self.bw_enabled)
188 try:
189 client = self.__bus.get_object(DBUS_IFACE_NAME, "/config",
190 follow_name_owner_changes=True)
191 iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME)
192 iface.set_throttling_limits(self.dn_limit, self.up_limit,
193 reply_handler=dbus_async,
194 error_handler=self.__dbus_error)
195 if self.bw_enabled:
196 iface.enable_bandwidth_throttling(
197 reply_handler=dbus_async,
198 error_handler=self.__dbus_error)
199 else:
200 iface.disable_bandwidth_throttling(
201 reply_handler=dbus_async,
202 error_handler=self.__dbus_error)
203 except DBusException, e:
204 self.__dbus_error(e)
205
206 def __spinner_changed(self, button, data=None):
207 """Remove timeout and add anew."""
208 if self.__update_id != 0:
209 gobject.source_remove(self.__update_id)
210
211 self.__update_id = gobject.timeout_add_seconds(
212 1, self.__update_bw_settings)
213
214 def __connect_toggled(self, button, data=None):
215 """Toggle the connection state..."""
216 self.conn_btn.set_sensitive(False)
217 if self.connected:
218 self.sdtool.start().addCallbacks(
219 lambda _: self.sdtool.disconnect(),
220 self.__sd_error)
221 else:
222 self.sdtool.start().addCallbacks(
223 lambda _: self.sdtool.connect(),
224 self.__sd_error)
225
226 def _format_for_gb_display(self, bytes):505 def _format_for_gb_display(self, bytes):
227 """Format bytes into reasonable gb display."""506 """Format bytes into reasonable gb display."""
228 gb = bytes / 1024 / 1024 / 1024507 gb = bytes / 1024 / 1024 / 1024
@@ -247,12 +526,7 @@
247 def request_REST_info(self, url, method):526 def request_REST_info(self, url, method):
248 """Make a REST request and return the resulting dict, or None."""527 """Make a REST request and return the resulting dict, or None."""
249 consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")528 consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")
250 items = []529 token = get_access_token(self.keyring)
251 items = self.keyring.find_items_sync(
252 gnomekeyring.ITEM_GENERIC_SECRET,
253 {'ubuntuone-realm': "https://ubuntuone.com",
254 'oauth-consumer-key': consumer.key})
255 token = oauth.OAuthToken.from_string(items[0].secret)
256 request = oauth.OAuthRequest.from_consumer_and_token(530 request = oauth.OAuthRequest.from_consumer_and_token(
257 http_url=url, http_method=method, oauth_consumer=consumer,531 http_url=url, http_method=method, oauth_consumer=consumer,
258 token=token)532 token=token)
@@ -403,75 +677,11 @@
403 self.mail_label.show()677 self.mail_label.show()
404678
405 # Devices tab679 # Devices tab
406 devices = gtk.VBox(spacing=12)680 self.devices = DevicesWidget(self.__bus, self.keyring)
407 devices.set_border_width(6)681 self.notebook.append_page(self.devices)
408 self.notebook.append_page(devices)682 self.notebook.set_tab_label_text(self.devices, _("Devices"))
409 self.notebook.set_tab_label_text(devices, _("Devices"))683 self.devices.show_all()
410 devices.show()684 self.devices.get_devices()
411
412 # Bandwidth limiting
413 self.limit_check = gtk.CheckButton(_("_Limit Bandwidth Usage"))
414 self.limit_check.connect("toggled", self.__bw_limit_toggled)
415 devices.pack_start(self.limit_check, False, False)
416 self.limit_check.show()
417
418 hbox = gtk.HBox(spacing=12)
419 devices.pack_start(hbox, False, False)
420 hbox.show()
421
422 label = gtk.Label()
423 hbox.pack_start(label, False, False)
424 label.show()
425
426 rbox = gtk.VBox(spacing=12)
427 hbox.pack_start(rbox, False, False)
428 rbox.show()
429
430 # Now put the bw limit bits in a table too
431 self.bw_table = gtk.Table(rows=2, columns=2)
432 self.bw_table.set_row_spacings(6)
433 self.bw_table.set_col_spacings(6)
434 self.bw_table.set_sensitive(False)
435 rbox.pack_start(self.bw_table, False, False)
436 self.bw_table.show()
437
438 # Upload speed
439 label = gtk.Label(_("Maximum _upload speed (KB/s):"))
440 label.set_use_underline(True)
441 label.set_alignment(0, 0.5)
442 self.bw_table.attach(label, 0, 1, 0, 1)
443 label.show()
444
445 adjustment = gtk.Adjustment(value=2048.0, lower=0.0, upper=4096.0,
446 step_incr=64.0, page_incr=128.0)
447 self.up_spin = gtk.SpinButton(adjustment)
448 self.up_spin.connect("value-changed", self.__spinner_changed)
449 label.set_mnemonic_widget(self.up_spin)
450 self.bw_table.attach(self.up_spin, 1, 2, 0, 1)
451 self.up_spin.show()
452
453 # Download speed
454 label = gtk.Label(_("Maximum _download speed (KB/s):"))
455 label.set_use_underline(True)
456 label.set_alignment(0, 0.5)
457 self.bw_table.attach(label, 0, 1, 1, 2)
458 label.show()
459 adjustment = gtk.Adjustment(value=2048.0, lower=64.0, upper=8192.0,
460 step_incr=64.0, page_incr=128.0)
461 self.dn_spin = gtk.SpinButton(adjustment)
462 self.dn_spin.connect("value-changed", self.__spinner_changed)
463 label.set_mnemonic_widget(self.dn_spin)
464 self.bw_table.attach(self.dn_spin, 1, 2, 1, 2)
465 self.dn_spin.show()
466
467 alignment = gtk.Alignment(1.0, 0.5)
468 rbox.pack_end(alignment, False, False)
469 alignment.show()
470
471 self.conn_btn = gtk.Button(_("Connect"))
472 self.conn_btn.connect('clicked', self.__connect_toggled)
473 alignment.add(self.conn_btn)
474 self.conn_btn.show()
475685
476 # Services tab686 # Services tab
477 services = gtk.VBox(spacing=12)687 services = gtk.VBox(spacing=12)
478688
=== modified file 'tests/test_preferences.py'
--- tests/test_preferences.py 2010-02-18 16:02:23 +0000
+++ tests/test_preferences.py 2010-03-05 17:04:28 +0000
@@ -53,26 +53,24 @@
5353
54 self.item_id = 99954 self.item_id = 999
5555
56 ex = self.expect(self.item.item_id)56 self.item.item_id
57 ex.result(self.item_id)57 self.mocker.result(self.item_id)
58 ex.count(0, None)58 self.mocker.count(0, None)
5959
60 ex = self.expect(self.item.secret)60 self.item.secret
61 ex.result('oauth_token=access_key&oauth_token_secret=access_secret')61 self.mocker.result('oauth_token=access_key'
62 ex.count(0, None)62 '&oauth_token_secret=access_secret')
6363 self.mocker.count(0, None)
64 def expect_token_query(self):64
65 """Expects the keyring to be queried for a token."""65 self.keyring.find_items_sync(
66 return self.expect(66 None,
67 self.keyring.find_items_sync(67 {'ubuntuone-realm': 'https://ubuntuone.com',
68 gnomekeyring.ITEM_GENERIC_SECRET,68 'oauth-consumer-key': 'ubuntuone'})
69 {'ubuntuone-realm': 'https://ubuntuone.com',69 self.mocker.count(0, None)
70 'oauth-consumer-key': 'ubuntuone'})70 self.mocker.result([self.item])
71 )71 self.keyring.ITEM_GENERIC_SECRET
7272 self.mocker.count(0, None)
73 def mock_has_token(self):73 self.mocker.result(None)
74 """Mocks a cached token in the keyring."""
75 self.expect_token_query().result([self.item])
7674
77 def tearDown(self):75 def tearDown(self):
78 # collect all signal receivers registered during the test76 # collect all signal receivers registered during the test
@@ -93,18 +91,108 @@
93 def test_bw_throttling(self):91 def test_bw_throttling(self):
94 """Test that toggling bw throttling works correctly."""92 """Test that toggling bw throttling works correctly."""
95 self.mocker.replay()93 self.mocker.replay()
96 dialog = self.u1prefs.UbuntuOneDialog()94 widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring)
97 self.assertTrue(dialog is not None)95 try:
98 dialog.notebook.set_current_page(1)96 widget.devices = []
99 self.assertFalse(dialog.bw_table.get_property('sensitive'))97 widget.list_devices()
100 dialog.limit_check.set_active(True)98 self.assertFalse(widget.bw_limited,
101 self.assertTrue(dialog.bw_table.get_property('sensitive'))99 "the bandwidth should start out not limited")
102 dialog.destroy()100 self.assertTrue(widget.bw_chk,
101 "the checkbox should be present")
102 self.assertFalse(widget.bw_chk.get_active(),
103 "the checkbox should start out unchecked")
104 self.assertFalse(widget.up_spin.get_property('sensitive') or
105 widget.dn_spin.get_property('sensitive'),
106 "the spinbuttons should start out unsensitive")
107 widget.bw_chk.set_active(True)
108 self.assertTrue(widget.bw_chk.get_active(),
109 "the checkbox should now be checked")
110 self.assertTrue(widget.up_spin.get_property('sensitive') and
111 widget.dn_spin.get_property('sensitive'),
112 "the spinbuttons should now be sensitive")
113 finally:
114 widget.destroy()
115
116 def test_list_devices_fills_devices_list_with_fake_result_when_empty(self):
117 self.mocker.replay()
118 widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring)
119 try:
120 widget.devices = []
121 widget.list_devices()
122 # the devices list is no longer empty
123 self.assertTrue(widget.devices)
124 # it has 'fake' data (referring to the local machine)
125 self.assertTrue('FAKE' in widget.devices[0])
126 finally:
127 widget.destroy()
128
129 def test_list_devices_shows_devices_list(self):
130 self.mocker.replay()
131 widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring)
132 try:
133 widget.devices = []
134 widget.list_devices()
135 # fake data now in devices
136 interesting = []
137 for i in widget.get_children():
138 clsname = i.__class__.__name__
139 if clsname == 'Image':
140 interesting.append((clsname, i.get_icon_name()[0]))
141 if clsname in ('Label', 'Button', 'CheckButton'):
142 interesting.append((clsname, i.get_label()))
143 # check there is an image of a computer in there
144 self.assertTrue(('Image', 'computer') in interesting)
145 # check a placeholder for the local machine description is there
146 self.assertTrue(('Label', '<LOCAL MACHINE>') in interesting)
147 # check the bw limitation stuff is there
148 self.assertTrue(('CheckButton', '_Limit Bandwidth Usage')
149 in interesting)
150 self.assertTrue(('Label', 'Download (kB/s):') in interesting)
151 self.assertTrue(('Label', 'Upload (kB/s):') in interesting)
152 # check the 'Remove' button is *not* there
153 self.assertTrue(('Button', 'Remove') not in interesting)
154 finally:
155 widget.destroy()
156
157 def test_list_devices_shows_real_devices_list(self):
158 self.mocker.replay()
159 widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring)
160 try:
161 widget.devices = [{'kind': 'Computer',
162 'description': 'xyzzy',
163 'token': 'blah'},
164 {'kind': 'Phone',
165 'description': 'quux',
166 'token': '1234'}]
167 widget.list_devices()
168 # fake data now in devices
169 interesting = []
170 for i in widget.get_children():
171 clsname = i.__class__.__name__
172 if clsname == 'Image':
173 interesting.append((clsname, i.get_icon_name()[0]))
174 if clsname in ('Label', 'Button', 'CheckButton'):
175 interesting.append((clsname, i.get_label()))
176 # check there is an image of a computer in there
177 self.assertTrue(('Image', 'computer') in interesting)
178 # and of a phone
179 self.assertTrue(('Image', 'phone') in interesting)
180 # check a label of the local machine description is there
181 self.assertTrue(('Label', 'xyzzy') in interesting)
182 # check the bw limitation stuff is not there (no local machine)
183 self.assertTrue(('CheckButton', '_Limit Bandwidth Usage')
184 not in interesting)
185 self.assertTrue(('Label', 'Download (kB/s):') not in interesting)
186 self.assertTrue(('Label', 'Upload (kB/s):') not in interesting)
187 # check the 'Remove' button is there
188 self.assertTrue(('Button', 'Remove') in interesting)
189 finally:
190 widget.destroy()
103191
104 def test_quota_display(self):192 def test_quota_display(self):
105 """Test that quota display works correctly."""193 """Test that quota display works correctly."""
106 self.mocker.replay()194 self.mocker.replay()
107 dialog = self.u1prefs.UbuntuOneDialog()195 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
108 self.assertTrue(dialog is not None)196 self.assertTrue(dialog is not None)
109 self.assertEqual(dialog.usage_graph.get_fraction(), 0.0)197 self.assertEqual(dialog.usage_graph.get_fraction(), 0.0)
110 dialog.update_quota_display(1024, 2048)198 dialog.update_quota_display(1024, 2048)
@@ -113,11 +201,6 @@
113201
114 def test_request_quota_info(self):202 def test_request_quota_info(self):
115 """Test that we can request the quota info properly."""203 """Test that we can request the quota info properly."""
116 self.mock_has_token()
117 dialog = self.u1prefs.UbuntuOneDialog()
118 self.assertTrue(dialog is not None)
119 dialog.keyring = self.keyring
120 self.assertEqual(dialog.usage_graph.get_fraction(), 0.0)
121 response = { 'status' : '200' }204 response = { 'status' : '200' }
122 content = '{"total":2048, "used":1024}'205 content = '{"total":2048, "used":1024}'
123 client = self.mocker.mock()206 client = self.mocker.mock()
@@ -125,16 +208,15 @@
125 self.expect(client.request('https://one.ubuntu.com/api/quota/',208 self.expect(client.request('https://one.ubuntu.com/api/quota/',
126 'GET', KWARGS)).result((response, content))209 'GET', KWARGS)).result((response, content))
127 self.mocker.replay()210 self.mocker.replay()
211 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
212 self.assertTrue(dialog is not None)
213 self.assertEqual(dialog.usage_graph.get_fraction(), 0.0)
128 dialog.request_quota_info()214 dialog.request_quota_info()
129 self.assertEqual(dialog.usage_graph.get_fraction(), 0.5)215 self.assertEqual(dialog.usage_graph.get_fraction(), 0.5)
130 dialog.destroy()216 dialog.destroy()
131217
132 def test_request_account_info(self):218 def test_request_account_info(self):
133 """Test that we can request the account info properly."""219 """Test that we can request the account info properly."""
134 self.mock_has_token()
135 dialog = self.u1prefs.UbuntuOneDialog()
136 self.assertTrue(dialog is not None)
137 dialog.keyring = self.keyring
138 response = { 'status' : '200' }220 response = { 'status' : '200' }
139 content = '''{"username": "ubuntuone", "nickname": "Ubuntu One",221 content = '''{"username": "ubuntuone", "nickname": "Ubuntu One",
140 "email": "uone@example.com"}222 "email": "uone@example.com"}
@@ -144,6 +226,8 @@
144 self.expect(client.request('https://one.ubuntu.com/api/account/',226 self.expect(client.request('https://one.ubuntu.com/api/account/',
145 'GET', KWARGS)).result((response, content))227 'GET', KWARGS)).result((response, content))
146 self.mocker.replay()228 self.mocker.replay()
229 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
230 self.assertTrue(dialog is not None)
147 dialog.request_account_info()231 dialog.request_account_info()
148 self.assertEqual(dialog.name_label.get_text(), 'Ubuntu One')232 self.assertEqual(dialog.name_label.get_text(), 'Ubuntu One')
149 self.assertEqual(dialog.user_label.get_text(), 'ubuntuone')233 self.assertEqual(dialog.user_label.get_text(), 'ubuntuone')
@@ -152,13 +236,14 @@
152236
153 def test_toggle_bookmarks(self):237 def test_toggle_bookmarks(self):
154 """Test toggling the bookmarks service on/off."""238 """Test toggling the bookmarks service on/off."""
155 dialog = self.u1prefs.UbuntuOneDialog()239 toggle_db_sync = self.mocker.mock()
240 self.expect(toggle_db_sync('bookmarks', False))
241 self.expect(toggle_db_sync('bookmarks', True))
242 self.expect(toggle_db_sync('bookmarks', False))
243 self.mocker.replay()
244 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
156 self.assertTrue(dialog is not None)245 self.assertTrue(dialog is not None)
157 dialog.toggle_db_sync = self.mocker.mock()246 dialog.toggle_db_sync = toggle_db_sync
158 self.expect(dialog.toggle_db_sync('bookmarks', False))
159 self.expect(dialog.toggle_db_sync('bookmarks', True))
160 self.expect(dialog.toggle_db_sync('bookmarks', False))
161 self.mocker.replay()
162 dialog.bookmarks_check.set_active(True)247 dialog.bookmarks_check.set_active(True)
163 self.assertTrue(dialog.bookmarks_check.get_active())248 self.assertTrue(dialog.bookmarks_check.get_active())
164 dialog.bookmarks_check.set_active(False)249 dialog.bookmarks_check.set_active(False)
@@ -168,13 +253,14 @@
168253
169 def test_toggle_contacts(self):254 def test_toggle_contacts(self):
170 """Test toggling the contacts service on/off."""255 """Test toggling the contacts service on/off."""
171 dialog = self.u1prefs.UbuntuOneDialog()256 toggle_db_sync = self.mocker.mock()
257 self.expect(toggle_db_sync('contacts', False))
258 self.expect(toggle_db_sync('contacts', True))
259 self.expect(toggle_db_sync('contacts', False))
260 self.mocker.replay()
261 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
172 self.assertTrue(dialog is not None)262 self.assertTrue(dialog is not None)
173 dialog.toggle_db_sync = self.mocker.mock()263 dialog.toggle_db_sync = toggle_db_sync
174 self.expect(dialog.toggle_db_sync('contacts', False))
175 self.expect(dialog.toggle_db_sync('contacts', True))
176 self.expect(dialog.toggle_db_sync('contacts', False))
177 self.mocker.replay()
178 dialog.abook_check.set_active(True)264 dialog.abook_check.set_active(True)
179 self.assertTrue(dialog.abook_check.get_active())265 self.assertTrue(dialog.abook_check.get_active())
180 dialog.abook_check.set_active(False)266 dialog.abook_check.set_active(False)
@@ -184,9 +270,9 @@
184270
185 def test_toggle_files(self):271 def test_toggle_files(self):
186 """Test toggling the files service on/off."""272 """Test toggling the files service on/off."""
187 dialog = self.u1prefs.UbuntuOneDialog()273 self.mocker.replay()
274 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
188 self.assertTrue(dialog is not None)275 self.assertTrue(dialog is not None)
189 self.mocker.replay()
190 dialog.files_check.set_active(True)276 dialog.files_check.set_active(True)
191 self.assertTrue(dialog.files_check.get_active())277 self.assertTrue(dialog.files_check.get_active())
192 dialog.files_check.set_active(False)278 dialog.files_check.set_active(False)
@@ -196,9 +282,9 @@
196282
197 def test_toggle_files_and_music(self):283 def test_toggle_files_and_music(self):
198 """Test toggling the files and music services on/off."""284 """Test toggling the files and music services on/off."""
199 dialog = self.u1prefs.UbuntuOneDialog()285 self.mocker.replay()
286 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
200 self.assertTrue(dialog is not None)287 self.assertTrue(dialog is not None)
201 self.mocker.replay()
202 dialog.files_check.set_active(False)288 dialog.files_check.set_active(False)
203 self.assertFalse(dialog.files_check.get_active())289 self.assertFalse(dialog.files_check.get_active())
204 self.assertFalse(dialog.music_check.props.sensitive)290 self.assertFalse(dialog.music_check.props.sensitive)
205291
=== modified file 'ubuntuone/syncdaemon/dbus_interface.py'
--- ubuntuone/syncdaemon/dbus_interface.py 2010-03-03 15:04:02 +0000
+++ ubuntuone/syncdaemon/dbus_interface.py 2010-03-05 17:04:28 +0000
@@ -1010,6 +1010,7 @@
1010 configured.1010 configured.
1011 The values are bytes/second1011 The values are bytes/second
1012 """1012 """
1013 logger.debug("called get_throttling_limits")
1013 try:1014 try:
1014 aq = self.dbus_iface.action_queue1015 aq = self.dbus_iface.action_queue
1015 download = -11016 download = -1
@@ -1037,6 +1038,7 @@
1037 def set_throttling_limits(self, download, upload,1038 def set_throttling_limits(self, download, upload,
1038 reply_handler=None, error_handler=None):1039 reply_handler=None, error_handler=None):
1039 """Set the read and write limits. The expected values are bytes/sec."""1040 """Set the read and write limits. The expected values are bytes/sec."""
1041 logger.debug("called set_throttling_limits")
1040 try:1042 try:
1041 # modify and save the config file1043 # modify and save the config file
1042 user_config = config.get_user_config()1044 user_config = config.get_user_config()

Subscribers

People subscribed via source and target branches