Merge lp:~nataliabidart/ubuntuone-control-panel/handle-sd-timeout into lp:ubuntuone-control-panel
- handle-sd-timeout
- Merge into trunk
| Status: | Merged | ||||
|---|---|---|---|---|---|
| Approved by: | Natalia Bidart on 2010-12-03 | ||||
| Approved revision: | 28 | ||||
| Merged at revision: | 27 | ||||
| Proposed branch: | lp:~nataliabidart/ubuntuone-control-panel/handle-sd-timeout | ||||
| Merge into: | lp:ubuntuone-control-panel | ||||
| Diff against target: |
941 lines (+309/-136) 17 files modified
ubuntuone/controlpanel/backend.py (+1/-0) ubuntuone/controlpanel/dbus_client.py (+8/-0) ubuntuone/controlpanel/dbus_service.py (+66/-10) ubuntuone/controlpanel/gtk/tests/__init__.py (+0/-16) ubuntuone/controlpanel/gtk/tests/test_gui.py (+2/-3) ubuntuone/controlpanel/gtk/tests/test_widgets.py (+7/-6) ubuntuone/controlpanel/gtk/widgets.py (+1/-0) ubuntuone/controlpanel/integrationtests/__init__.py (+1/-0) ubuntuone/controlpanel/integrationtests/test_dbus_client_sso.py (+1/-0) ubuntuone/controlpanel/integrationtests/test_dbus_service.py (+82/-4) ubuntuone/controlpanel/integrationtests/test_webclient.py (+2/-1) ubuntuone/controlpanel/tests/__init__.py (+17/-0) ubuntuone/controlpanel/tests/test_backend.py (+24/-35) ubuntuone/controlpanel/tests/test_utils.py (+62/-2) ubuntuone/controlpanel/tests/testcase.py (+0/-54) ubuntuone/controlpanel/utils.py (+29/-0) ubuntuone/controlpanel/webclient.py (+6/-5) |
||||
| To merge this branch: | bzr merge lp:~nataliabidart/ubuntuone-control-panel/handle-sd-timeout | ||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| John Lenton | 2010-12-02 | Approve on 2010-12-03 | |
|
Review via email:
|
|||
Commit Message
Handling properly any failure between dbus calls and errbacking deferreds (LP: #683760).
Description of the Change
When passing DBus error signals to deferred's errback, we need to transform the twisted Failures into string-string dicts. Otherwise, we get traces like:
Unhandled error in Deferred:
Traceback (most recent call last):
File "/usr/lib/
_inlineCall
File "/usr/lib/
deferred.
File "/usr/lib/
self.
File "/usr/lib/
self.
--- <exception caught here> ---
File "/usr/lib/
self.result = callback(
File "/usr/lib/
message.
exceptions.
To reproduce:
* Temporally make syncdaemon dbus service unavailable (edit /usr/share/
* from trunk, run:
in terminal 1) PYTHONPATH=. ./bin/ubuntuone
in terminal 2) PYTHONPATH=. ./bin/ubuntuone
* you'll see the trace I pasted above on terminal 1.
From this branch, reproduce the test case and this time you'll get, on terminal 1:
ERROR:ubuntuone
Preview Diff
| 1 | === modified file 'ubuntuone/controlpanel/backend.py' |
| 2 | --- ubuntuone/controlpanel/backend.py 2010-11-17 21:28:55 +0000 |
| 3 | +++ ubuntuone/controlpanel/backend.py 2010-12-02 20:41:22 +0000 |
| 4 | @@ -1,6 +1,7 @@ |
| 5 | # -*- coding: utf-8 -*- |
| 6 | |
| 7 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 8 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 9 | # |
| 10 | # Copyright 2010 Canonical Ltd. |
| 11 | # |
| 12 | |
| 13 | === modified file 'ubuntuone/controlpanel/dbus_client.py' |
| 14 | --- ubuntuone/controlpanel/dbus_client.py 2010-11-17 21:28:55 +0000 |
| 15 | +++ ubuntuone/controlpanel/dbus_client.py 2010-12-02 20:41:22 +0000 |
| 16 | @@ -1,6 +1,7 @@ |
| 17 | # -*- coding: utf-8 -*- |
| 18 | |
| 19 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 20 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 21 | # |
| 22 | # Copyright 2010 Canonical Ltd. |
| 23 | # |
| 24 | @@ -26,6 +27,11 @@ |
| 25 | from ubuntuone.clientdefs import APP_NAME |
| 26 | from ubuntuone.syncdaemon import dbus_interface as sd_dbus_iface |
| 27 | |
| 28 | +from ubuntuone.controlpanel.logger import setup_logging |
| 29 | + |
| 30 | + |
| 31 | +logger = setup_logging('dbus_client') |
| 32 | + |
| 33 | |
| 34 | class CredentialsError(Exception): |
| 35 | """No credentials could be retrieved.""" |
| 36 | @@ -78,6 +84,8 @@ |
| 37 | |
| 38 | def get_syncdaemon_proxy(object_path, dbus_interface): |
| 39 | """Get a DBus proxy for syncdaemon at 'object_path':'dbus_interface'.""" |
| 40 | + logger.debug('get_syncdaemon_proxy: object_path %r, dbus_interface %r', |
| 41 | + object_path, dbus_interface) |
| 42 | bus = dbus.SessionBus() |
| 43 | obj = bus.get_object(bus_name=sd_dbus_iface.DBUS_IFACE_NAME, |
| 44 | object_path=object_path, |
| 45 | |
| 46 | === modified file 'ubuntuone/controlpanel/dbus_service.py' |
| 47 | --- ubuntuone/controlpanel/dbus_service.py 2010-11-15 21:05:30 +0000 |
| 48 | +++ ubuntuone/controlpanel/dbus_service.py 2010-12-02 20:41:22 +0000 |
| 49 | @@ -1,6 +1,7 @@ |
| 50 | # -*- coding: utf-8 -*- |
| 51 | |
| 52 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 53 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 54 | # |
| 55 | # Copyright 2010 Canonical Ltd. |
| 56 | # |
| 57 | @@ -24,9 +25,61 @@ |
| 58 | from dbus.mainloop.glib import DBusGMainLoop |
| 59 | from dbus.service import method, signal |
| 60 | |
| 61 | +from twisted.python.failure import Failure |
| 62 | + |
| 63 | from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH, |
| 64 | - DBUS_PREFERENCES_IFACE) |
| 65 | + DBUS_PREFERENCES_IFACE, utils) |
| 66 | from ubuntuone.controlpanel.backend import ControlBackend |
| 67 | +from ubuntuone.controlpanel.logger import setup_logging |
| 68 | + |
| 69 | + |
| 70 | +logger = setup_logging('dbus_service') |
| 71 | + |
| 72 | + |
| 73 | +def make_unicode(anything): |
| 74 | + """Transform 'anything' on an unicode.""" |
| 75 | + if not isinstance(anything, unicode): |
| 76 | + anything = str(anything).decode('utf8', 'replace') |
| 77 | + |
| 78 | + return anything |
| 79 | + |
| 80 | + |
| 81 | +def error_handler(error): |
| 82 | + """Handle 'error' properly to be able to call a dbus error signal. |
| 83 | + If 'error' is a Failure, then transform the exception in it to a error |
| 84 | + dict. If 'error' is a regular Exception, transform it. |
| 85 | + |
| 86 | + If 'error' is already a string-string dict, just pass it along. Build a |
| 87 | + generic error dict in any other case. |
| 88 | + |
| 89 | + """ |
| 90 | + result = {} |
| 91 | + if isinstance(error, Failure): |
| 92 | + result = utils.failure_to_error_dict(error) |
| 93 | + elif isinstance(error, Exception): |
| 94 | + result = utils.exception_to_error_dict(error) |
| 95 | + elif isinstance(error, dict): |
| 96 | + # ensure that both keys and values are unicodes |
| 97 | + result = dict(map(make_unicode, i) for i in error.iteritems()) |
| 98 | + else: |
| 99 | + msg = 'Got unexpected error argument %r' % error |
| 100 | + result = {utils.ERROR_TYPE: 'UnknownError', utils.ERROR_MESSAGE: msg} |
| 101 | + |
| 102 | + return result |
| 103 | + |
| 104 | + |
| 105 | +def transform_failure(f): |
| 106 | + """Decorator to apply to DBus error signals. |
| 107 | + |
| 108 | + With this call, a Failure is transformed into a string-string dict. |
| 109 | + |
| 110 | + """ |
| 111 | + def inner(error, *a): |
| 112 | + """Do the Failure transformation.""" |
| 113 | + error_dict = error_handler(error) |
| 114 | + return f(error_dict) |
| 115 | + |
| 116 | + return inner |
| 117 | |
| 118 | |
| 119 | class ControlPanelBackend(dbus.service.Object): |
| 120 | @@ -36,6 +89,7 @@ |
| 121 | """Create this instance of the backend.""" |
| 122 | super(ControlPanelBackend, self).__init__(*args, **kwargs) |
| 123 | self.backend = backend |
| 124 | + logger.debug('ControlPanelBackend created with %r, %r', args, kwargs) |
| 125 | |
| 126 | # pylint: disable=C0103 |
| 127 | |
| 128 | @@ -44,7 +98,7 @@ |
| 129 | """Find out the account info for the current logged in user.""" |
| 130 | d = self.backend.account_info() |
| 131 | d.addCallback(self.AccountInfoReady) |
| 132 | - d.addErrback(self.AccountInfoError) |
| 133 | + d.addErrback(transform_failure(self.AccountInfoError)) |
| 134 | |
| 135 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}") |
| 136 | def AccountInfoReady(self, info): |
| 137 | @@ -61,7 +115,7 @@ |
| 138 | """Find out the devices info for the logged in user.""" |
| 139 | d = self.backend.devices_info() |
| 140 | d.addCallback(self.DevicesInfoReady) |
| 141 | - d.addErrback(self.DevicesInfoError) |
| 142 | + d.addErrback(transform_failure(self.DevicesInfoError)) |
| 143 | |
| 144 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}") |
| 145 | def DevicesInfoReady(self, info): |
| 146 | @@ -78,7 +132,7 @@ |
| 147 | """Configure a given device.""" |
| 148 | d = self.backend.change_device_settings(token, settings) |
| 149 | d.addCallback(self.DeviceSettingsChanged) |
| 150 | - d.addErrback(self.DeviceSettingsChangeError) |
| 151 | + d.addErrback(transform_failure(self.DeviceSettingsChangeError)) |
| 152 | |
| 153 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") |
| 154 | def DeviceSettingsChanged(self, token): |
| 155 | @@ -95,7 +149,7 @@ |
| 156 | """Remove a given device.""" |
| 157 | d = self.backend.remove_device(token) |
| 158 | d.addCallback(self.DeviceRemoved) |
| 159 | - d.addErrback(self.DeviceRemovalError) |
| 160 | + d.addErrback(transform_failure(self.DeviceRemovalError)) |
| 161 | |
| 162 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") |
| 163 | def DeviceRemoved(self, token): |
| 164 | @@ -112,7 +166,7 @@ |
| 165 | """Get the status of the file sync service.""" |
| 166 | d = self.backend.file_sync_status() |
| 167 | d.addCallback(lambda args: self.FileSyncStatusReady(*args)) |
| 168 | - d.addErrback(self.FileSyncStatusError) |
| 169 | + d.addErrback(transform_failure(self.FileSyncStatusError)) |
| 170 | |
| 171 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="bs") |
| 172 | def FileSyncStatusReady(self, enabled, status): |
| 173 | @@ -129,12 +183,14 @@ |
| 174 | """Find out the volumes info for the logged in user.""" |
| 175 | d = self.backend.volumes_info() |
| 176 | d.addCallback(self.VolumesInfoReady) |
| 177 | - d.addErrback(self.VolumesInfoError) |
| 178 | + d.addErrback(transform_failure(self.VolumesInfoError)) |
| 179 | |
| 180 | + @utils.log_call(logger.info) |
| 181 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}") |
| 182 | def VolumesInfoReady(self, info): |
| 183 | """The info for the volumes is available right now.""" |
| 184 | |
| 185 | + @utils.log_call(logger.error) |
| 186 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}") |
| 187 | def VolumesInfoError(self, error): |
| 188 | """The info for the volumes is currently unavailable.""" |
| 189 | @@ -146,7 +202,7 @@ |
| 190 | """Configure a given volume.""" |
| 191 | d = self.backend.change_volume_settings(volume_id, settings) |
| 192 | d.addCallback(self.VolumeSettingsChanged) |
| 193 | - d.addErrback(self.VolumeSettingsChangeError) |
| 194 | + d.addErrback(transform_failure(self.VolumeSettingsChangeError)) |
| 195 | |
| 196 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") |
| 197 | def VolumeSettingsChanged(self, token): |
| 198 | @@ -163,7 +219,7 @@ |
| 199 | """Check if the extension to sync bookmarks is installed.""" |
| 200 | d = self.backend.query_bookmark_extension() |
| 201 | d.addCallback(self.QueryBookmarksResult) |
| 202 | - d.addErrback(self.QueryBookmarksError) |
| 203 | + d.addErrback(transform_failure(self.QueryBookmarksError)) |
| 204 | |
| 205 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="b") |
| 206 | def QueryBookmarksResult(self, enabled): |
| 207 | @@ -180,7 +236,7 @@ |
| 208 | """Install the extension to sync bookmarks.""" |
| 209 | d = self.backend.install_bookmarks_extension() |
| 210 | d.addCallback(lambda _: self.InstallBookmarksSuccess()) |
| 211 | - d.addErrback(self.InstallBookmarksError) |
| 212 | + d.addErrback(transform_failure(self.InstallBookmarksError)) |
| 213 | |
| 214 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="") |
| 215 | def InstallBookmarksSuccess(self): |
| 216 | |
| 217 | === modified file 'ubuntuone/controlpanel/gtk/tests/__init__.py' |
| 218 | --- ubuntuone/controlpanel/gtk/tests/__init__.py 2010-11-30 19:51:01 +0000 |
| 219 | +++ ubuntuone/controlpanel/gtk/tests/__init__.py 2010-12-02 20:41:22 +0000 |
| 220 | @@ -17,19 +17,3 @@ |
| 221 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
| 222 | |
| 223 | """The test suite for the GTK UI for the control panel for Ubuntu One.""" |
| 224 | - |
| 225 | -from twisted.trial.unittest import TestCase |
| 226 | - |
| 227 | - |
| 228 | -class BaseTestCase(TestCase): |
| 229 | - """Basics for testing.""" |
| 230 | - |
| 231 | - def setUp(self): |
| 232 | - self._called = False |
| 233 | - |
| 234 | - def tearDown(self): |
| 235 | - self._called = False |
| 236 | - |
| 237 | - def _set_called(self, *args, **kwargs): |
| 238 | - """Store 'args' and 'kwargs' for test assertions.""" |
| 239 | - self._called = (args, kwargs) |
| 240 | |
| 241 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py' |
| 242 | --- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-11-30 20:13:10 +0000 |
| 243 | +++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-02 20:41:22 +0000 |
| 244 | @@ -27,8 +27,7 @@ |
| 245 | from ubuntuone.devtools.handlers import MementoHandler |
| 246 | |
| 247 | from ubuntuone.controlpanel.gtk import gui |
| 248 | -from ubuntuone.controlpanel.tests import TOKEN |
| 249 | -from ubuntuone.controlpanel.gtk import tests |
| 250 | +from ubuntuone.controlpanel.tests import TOKEN, TestCase |
| 251 | |
| 252 | # Attribute 'yyy' defined outside __init__ |
| 253 | # pylint: disable=W0201 |
| 254 | @@ -133,7 +132,7 @@ |
| 255 | return FakedSSOBackend(obj, dbus_interface, *args, **kwargs) |
| 256 | |
| 257 | |
| 258 | -class BaseTestCase(tests.BaseTestCase): |
| 259 | +class BaseTestCase(TestCase): |
| 260 | """Basics for testing.""" |
| 261 | |
| 262 | # self.klass is not callable |
| 263 | |
| 264 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_widgets.py' |
| 265 | --- ubuntuone/controlpanel/gtk/tests/test_widgets.py 2010-11-30 20:36:38 +0000 |
| 266 | +++ ubuntuone/controlpanel/gtk/tests/test_widgets.py 2010-12-02 20:41:22 +0000 |
| 267 | @@ -18,10 +18,11 @@ |
| 268 | |
| 269 | """The test suite for the extra widgets.""" |
| 270 | |
| 271 | -from ubuntuone.controlpanel.gtk import widgets, tests |
| 272 | - |
| 273 | - |
| 274 | -class LoadingTestCase(tests.BaseTestCase): |
| 275 | +from ubuntuone.controlpanel.tests import TestCase |
| 276 | +from ubuntuone.controlpanel.gtk import widgets |
| 277 | + |
| 278 | + |
| 279 | +class LoadingTestCase(TestCase): |
| 280 | """Test suite for the Loading widget (a spinner plus a label).""" |
| 281 | |
| 282 | def setUp(self): |
| 283 | @@ -59,7 +60,7 @@ |
| 284 | widgets.gtk.gdk.Color(expected)) |
| 285 | |
| 286 | |
| 287 | -class LabelLoadingTestCase(tests.BaseTestCase): |
| 288 | +class LabelLoadingTestCase(TestCase): |
| 289 | """Test suite for the LabelLoading widget. |
| 290 | |
| 291 | (a label that shows a Loading until the text is set). |
| 292 | @@ -155,7 +156,7 @@ |
| 293 | self.assertEqual(actual, widgets.gtk.gdk.Color(expected)) |
| 294 | |
| 295 | |
| 296 | -class PanelTitleTestCase(tests.BaseTestCase): |
| 297 | +class PanelTitleTestCase(TestCase): |
| 298 | """Tets case for a special title for each management panel.""" |
| 299 | |
| 300 | TITLE = '<b>Foo Bar</b>' |
| 301 | |
| 302 | === modified file 'ubuntuone/controlpanel/gtk/widgets.py' |
| 303 | --- ubuntuone/controlpanel/gtk/widgets.py 2010-11-30 20:36:38 +0000 |
| 304 | +++ ubuntuone/controlpanel/gtk/widgets.py 2010-12-02 20:41:22 +0000 |
| 305 | @@ -1,5 +1,6 @@ |
| 306 | # -*- coding: utf-8 -*- |
| 307 | |
| 308 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 309 | # Authors: Evan Dandrea <evan.dandrea@canonical.com> |
| 310 | # |
| 311 | # Copyright 2009-2010 Canonical Ltd. |
| 312 | |
| 313 | === modified file 'ubuntuone/controlpanel/integrationtests/__init__.py' |
| 314 | --- ubuntuone/controlpanel/integrationtests/__init__.py 2010-11-17 21:28:55 +0000 |
| 315 | +++ ubuntuone/controlpanel/integrationtests/__init__.py 2010-12-02 20:41:22 +0000 |
| 316 | @@ -1,5 +1,6 @@ |
| 317 | # -*- coding: utf-8 -*- |
| 318 | |
| 319 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 320 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 321 | # |
| 322 | # Copyright 2010 Canonical Ltd. |
| 323 | |
| 324 | === modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_client_sso.py' |
| 325 | --- ubuntuone/controlpanel/integrationtests/test_dbus_client_sso.py 2010-11-15 16:00:23 +0000 |
| 326 | +++ ubuntuone/controlpanel/integrationtests/test_dbus_client_sso.py 2010-12-02 20:41:22 +0000 |
| 327 | @@ -1,6 +1,7 @@ |
| 328 | # -*- coding: utf-8 -*- |
| 329 | |
| 330 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 331 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 332 | # |
| 333 | # Copyright 2010 Canonical Ltd. |
| 334 | # |
| 335 | |
| 336 | === modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_service.py' |
| 337 | --- ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2010-11-17 22:05:32 +0000 |
| 338 | +++ ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2010-12-02 20:41:22 +0000 |
| 339 | @@ -1,6 +1,7 @@ |
| 340 | # -*- coding: utf-8 -*- |
| 341 | |
| 342 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 343 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 344 | # |
| 345 | # Copyright 2010 Canonical Ltd. |
| 346 | # |
| 347 | @@ -23,11 +24,11 @@ |
| 348 | |
| 349 | from twisted.internet import defer |
| 350 | from twisted.python.failure import Failure |
| 351 | -from ubuntuone.devtools.testcase import DBusTestCase as TestCase |
| 352 | |
| 353 | from ubuntuone.controlpanel import dbus_service |
| 354 | from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH, |
| 355 | DBUS_PREFERENCES_IFACE) |
| 356 | +from ubuntuone.controlpanel.integrationtests import TestCase |
| 357 | |
| 358 | |
| 359 | SAMPLE_ACCOUNT_INFO = { |
| 360 | @@ -120,7 +121,7 @@ |
| 361 | """Process the request with the given result.""" |
| 362 | if self.exception: |
| 363 | # pylint: disable=E1102 |
| 364 | - return defer.fail(self.exception()) |
| 365 | + return defer.fail(self.exception(result)) |
| 366 | return defer.succeed(result) |
| 367 | |
| 368 | def account_info(self): |
| 369 | @@ -189,12 +190,63 @@ |
| 370 | busname = dbus_service.get_busname() |
| 371 | self.assertEqual(busname.get_name(), DBUS_BUS_NAME) |
| 372 | |
| 373 | - |
| 374 | -class OperationsTestCase(DBusServiceTestCase): |
| 375 | + def test_error_handler_with_failure(self): |
| 376 | + """Ensure to build a string-string dict to pass to error signals.""" |
| 377 | + error = dbus_service.Failure(TypeError('oh no!')) |
| 378 | + expected = dbus_service.utils.failure_to_error_dict(error) |
| 379 | + |
| 380 | + result = dbus_service.error_handler(error) |
| 381 | + |
| 382 | + self.assertEqual(expected, result) |
| 383 | + |
| 384 | + def test_error_handler_with_exception(self): |
| 385 | + """Ensure to build a string-string dict to pass to error signals.""" |
| 386 | + error = TypeError('oh no, no again!') |
| 387 | + expected = dbus_service.utils.exception_to_error_dict(error) |
| 388 | + |
| 389 | + result = dbus_service.error_handler(error) |
| 390 | + |
| 391 | + self.assertEqual(expected, result) |
| 392 | + |
| 393 | + def test_error_handler_with_string_dict(self): |
| 394 | + """Ensure to build a string-string dict to pass to error signals.""" |
| 395 | + expected = {'test': 'me'} |
| 396 | + |
| 397 | + result = dbus_service.error_handler(expected) |
| 398 | + |
| 399 | + self.assertEqual(expected, result) |
| 400 | + |
| 401 | + def test_error_handler_with_non_string_dict(self): |
| 402 | + """Ensure to build a string-string dict to pass to error signals.""" |
| 403 | + expected = {'test': 0, 'qué?': None, |
| 404 | + 10: 'foo\xffbar', True: u'ñoño'} |
| 405 | + |
| 406 | + result = dbus_service.error_handler(expected) |
| 407 | + expected = dict(map(lambda x: x if isinstance(x, unicode) else |
| 408 | + str(x).decode('utf8', 'replace'), i) |
| 409 | + for i in expected.iteritems()) |
| 410 | + |
| 411 | + self.assertEqual(expected, result) |
| 412 | + |
| 413 | + def test_error_handler_default(self): |
| 414 | + """Ensure to build a string-string dict to pass to error signals.""" |
| 415 | + msg = 'Got unexpected error argument %r' % None |
| 416 | + expected = {dbus_service.utils.ERROR_TYPE: 'UnknownError', |
| 417 | + dbus_service.utils.ERROR_MESSAGE: msg} |
| 418 | + |
| 419 | + result = dbus_service.error_handler(None) |
| 420 | + |
| 421 | + self.assertEqual(expected, result) |
| 422 | + |
| 423 | + |
| 424 | +class OperationsTestCase(TestCase): |
| 425 | """Test for the DBus service operations.""" |
| 426 | |
| 427 | + timeout = 3 |
| 428 | + |
| 429 | def setUp(self): |
| 430 | super(OperationsTestCase, self).setUp() |
| 431 | + dbus_service.init_mainloop() |
| 432 | be = dbus_service.publish_backend(MockBackend()) |
| 433 | self.addCleanup(be.remove_from_connection) |
| 434 | bus = dbus.SessionBus() |
| 435 | @@ -381,3 +433,29 @@ |
| 436 | args = ("InstallBookmarksSuccess", "InstallBookmarksError", got_signal, |
| 437 | self.backend.install_bookmarks_extension) |
| 438 | return self.assert_correct_method_call(*args) |
| 439 | + |
| 440 | + |
| 441 | +class OperationsErrorTestCase(OperationsTestCase): |
| 442 | + """Test for the DBus service operations when there is an error.""" |
| 443 | + |
| 444 | + def setUp(self): |
| 445 | + super(OperationsErrorTestCase, self).setUp() |
| 446 | + self.patch(MockBackend, 'exception', AssertionError) |
| 447 | + |
| 448 | + def assert_correct_method_call(self, success_sig, error_sig, success_cb, |
| 449 | + method, *args): |
| 450 | + """Call parent instance swapping success_sig with error_sig. |
| 451 | + |
| 452 | + This is because we want to succeed the test when the error signal was |
| 453 | + received. |
| 454 | + |
| 455 | + """ |
| 456 | + |
| 457 | + def got_error_signal(error_dict): |
| 458 | + """The error signal was received.""" |
| 459 | + self.assertEqual(error_dict[dbus_service.utils.ERROR_TYPE], |
| 460 | + 'AssertionError') |
| 461 | + self.deferred.callback("success") |
| 462 | + |
| 463 | + return super(OperationsErrorTestCase, self).assert_correct_method_call( |
| 464 | + error_sig, success_sig, got_error_signal, method, *args) |
| 465 | |
| 466 | === modified file 'ubuntuone/controlpanel/integrationtests/test_webclient.py' |
| 467 | --- ubuntuone/controlpanel/integrationtests/test_webclient.py 2010-11-10 15:54:29 +0000 |
| 468 | +++ ubuntuone/controlpanel/integrationtests/test_webclient.py 2010-12-02 20:41:22 +0000 |
| 469 | @@ -1,6 +1,7 @@ |
| 470 | # -*- coding: utf-8 -*- |
| 471 | |
| 472 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 473 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 474 | # |
| 475 | # Copyright 2010 Canonical Ltd. |
| 476 | # |
| 477 | @@ -22,9 +23,9 @@ |
| 478 | from twisted.internet import defer |
| 479 | from twisted.internet.defer import inlineCallbacks |
| 480 | from twisted.web import server, resource |
| 481 | -from ubuntuone.devtools.testcase import DBusTestCase as TestCase |
| 482 | |
| 483 | from ubuntuone.controlpanel import webclient |
| 484 | +from ubuntuone.controlpanel.integrationtests import TestCase |
| 485 | |
| 486 | |
| 487 | SAMPLE_KEY = "result" |
| 488 | |
| 489 | === modified file 'ubuntuone/controlpanel/tests/__init__.py' |
| 490 | --- ubuntuone/controlpanel/tests/__init__.py 2010-10-07 20:13:07 +0000 |
| 491 | +++ ubuntuone/controlpanel/tests/__init__.py 2010-12-02 20:41:22 +0000 |
| 492 | @@ -18,8 +18,25 @@ |
| 493 | |
| 494 | """The test suite for the control panel for Ubuntu One.""" |
| 495 | |
| 496 | +from twisted.trial import unittest |
| 497 | + |
| 498 | + |
| 499 | TOKEN = {u'consumer_key': u'xQ7xDAz', |
| 500 | u'consumer_secret': u'KzCJWCTNbbntwfyCKKjomJDzlgqxLy', |
| 501 | u'token_name': u'test', |
| 502 | u'token': u'GkInOfSMGwTXAUoVQwLUoPxElEEUdhsLVNTPhxHJDUIeHCPNEo', |
| 503 | u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'} |
| 504 | + |
| 505 | + |
| 506 | +class TestCase(unittest.TestCase): |
| 507 | + """Basics for testing.""" |
| 508 | + |
| 509 | + def setUp(self): |
| 510 | + self._called = False |
| 511 | + |
| 512 | + def tearDown(self): |
| 513 | + self._called = False |
| 514 | + |
| 515 | + def _set_called(self, *args, **kwargs): |
| 516 | + """Store 'args' and 'kwargs' for test assertions.""" |
| 517 | + self._called = (args, kwargs) |
| 518 | |
| 519 | === modified file 'ubuntuone/controlpanel/tests/test_backend.py' |
| 520 | --- ubuntuone/controlpanel/tests/test_backend.py 2010-11-17 21:28:55 +0000 |
| 521 | +++ ubuntuone/controlpanel/tests/test_backend.py 2010-12-02 20:41:22 +0000 |
| 522 | @@ -1,6 +1,7 @@ |
| 523 | # -*- coding: utf-8 -*- |
| 524 | |
| 525 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 526 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 527 | # |
| 528 | # Copyright 2010 Canonical Ltd. |
| 529 | # |
| 530 | @@ -22,11 +23,11 @@ |
| 531 | |
| 532 | from twisted.internet import defer |
| 533 | from twisted.internet.defer import inlineCallbacks |
| 534 | -from twisted.trial.unittest import TestCase |
| 535 | |
| 536 | from ubuntuone.controlpanel import backend |
| 537 | from ubuntuone.controlpanel.backend import (ACCOUNT_API, QUOTA_API, |
| 538 | DEVICES_API, DEVICE_REMOVE_API) |
| 539 | +from ubuntuone.controlpanel.tests import TestCase |
| 540 | from ubuntuone.controlpanel.webclient import WebClientError |
| 541 | |
| 542 | SAMPLE_CREDENTIALS = {"token": "ABC1234DEF"} |
| 543 | @@ -191,17 +192,16 @@ |
| 544 | self.patch(backend, "WebClient", MockWebClient) |
| 545 | self.patch(backend, "dbus_client", MockDBusClient()) |
| 546 | self.local_token = "Computer" + SAMPLE_CREDENTIALS["token"] |
| 547 | + self.be = backend.ControlBackend() |
| 548 | |
| 549 | def test_backend_creation(self): |
| 550 | """The backend instance is successfully created.""" |
| 551 | - be = backend.ControlBackend() |
| 552 | - self.assertEqual(be.wc.__class__, MockWebClient) |
| 553 | + self.assertEqual(self.be.wc.__class__, MockWebClient) |
| 554 | |
| 555 | @inlineCallbacks |
| 556 | def test_get_token(self): |
| 557 | """The get_token method returns the right token.""" |
| 558 | - be = backend.ControlBackend() |
| 559 | - token = yield be.get_token() |
| 560 | + token = yield self.be.get_token() |
| 561 | self.assertEqual(token, SAMPLE_CREDENTIALS["token"]) |
| 562 | |
| 563 | |
| 564 | @@ -211,20 +211,18 @@ |
| 565 | @inlineCallbacks |
| 566 | def test_account_info(self): |
| 567 | """The account_info method exercises its callback.""" |
| 568 | - be = backend.ControlBackend() |
| 569 | # pylint: disable=E1101 |
| 570 | - be.wc.results[ACCOUNT_API] = SAMPLE_ACCOUNT_JSON |
| 571 | - be.wc.results[QUOTA_API] = SAMPLE_QUOTA_JSON |
| 572 | - result = yield be.account_info() |
| 573 | + self.be.wc.results[ACCOUNT_API] = SAMPLE_ACCOUNT_JSON |
| 574 | + self.be.wc.results[QUOTA_API] = SAMPLE_QUOTA_JSON |
| 575 | + result = yield self.be.account_info() |
| 576 | self.assertEqual(result, EXPECTED_ACCOUNT_INFO) |
| 577 | |
| 578 | @inlineCallbacks |
| 579 | def test_account_info_fails(self): |
| 580 | """The account_info method exercises its errback.""" |
| 581 | - be = backend.ControlBackend() |
| 582 | # pylint: disable=E1101 |
| 583 | - be.wc.failure = 404 |
| 584 | - yield self.assertFailure(be.account_info(), WebClientError) |
| 585 | + self.be.wc.failure = 404 |
| 586 | + yield self.assertFailure(self.be.account_info(), WebClientError) |
| 587 | |
| 588 | |
| 589 | class BackendDevicesTestCase(BackendBasicTestCase): |
| 590 | @@ -233,58 +231,52 @@ |
| 591 | @inlineCallbacks |
| 592 | def test_devices_info(self): |
| 593 | """The devices_info method exercises its callback.""" |
| 594 | - be = backend.ControlBackend() |
| 595 | # pylint: disable=E1101 |
| 596 | - be.wc.results[DEVICES_API] = SAMPLE_DEVICES_JSON |
| 597 | - result = yield be.devices_info() |
| 598 | + self.be.wc.results[DEVICES_API] = SAMPLE_DEVICES_JSON |
| 599 | + result = yield self.be.devices_info() |
| 600 | self.assertEqual(result, EXPECTED_DEVICES_INFO) |
| 601 | |
| 602 | @inlineCallbacks |
| 603 | def test_devices_info_fails(self): |
| 604 | """The devices_info method exercises its errback.""" |
| 605 | - be = backend.ControlBackend() |
| 606 | # pylint: disable=E1101 |
| 607 | - be.wc.failure = 404 |
| 608 | - yield self.assertFailure(be.devices_info(), WebClientError) |
| 609 | + self.be.wc.failure = 404 |
| 610 | + yield self.assertFailure(self.be.devices_info(), WebClientError) |
| 611 | |
| 612 | @inlineCallbacks |
| 613 | def test_remove_device(self): |
| 614 | """The remove_device method calls the right api.""" |
| 615 | - be = backend.ControlBackend() |
| 616 | dtype, did = "Computer", "SAMPLE-TOKEN" |
| 617 | device_id = dtype + did |
| 618 | apiurl = DEVICE_REMOVE_API % (dtype, did) |
| 619 | # pylint: disable=E1101 |
| 620 | - be.wc.results[apiurl] = SAMPLE_DEVICES_JSON |
| 621 | - result = yield be.remove_device(device_id) |
| 622 | + self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON |
| 623 | + result = yield self.be.remove_device(device_id) |
| 624 | self.assertEqual(result, device_id) |
| 625 | |
| 626 | @inlineCallbacks |
| 627 | def test_remove_device_fails(self): |
| 628 | """The remove_device method fails as expected.""" |
| 629 | - be = backend.ControlBackend() |
| 630 | # pylint: disable=E1101 |
| 631 | - be.wc.failure = 404 |
| 632 | - yield self.assertFailure(be.devices_info(), WebClientError) |
| 633 | + self.be.wc.failure = 404 |
| 634 | + yield self.assertFailure(self.be.devices_info(), WebClientError) |
| 635 | |
| 636 | @inlineCallbacks |
| 637 | def test_change_limit_bandwidth(self): |
| 638 | """The device settings are updated.""" |
| 639 | - be = backend.ControlBackend() |
| 640 | backend.dbus_client.throttling = False |
| 641 | - yield be.change_device_settings(self.local_token, |
| 642 | + yield self.be.change_device_settings(self.local_token, |
| 643 | {"limit_bandwidth": "1"}) |
| 644 | self.assertEqual(backend.dbus_client.throttling, True) |
| 645 | - yield be.change_device_settings(self.local_token, |
| 646 | + yield self.be.change_device_settings(self.local_token, |
| 647 | {"limit_bandwidth": "0"}) |
| 648 | self.assertEqual(backend.dbus_client.throttling, False) |
| 649 | |
| 650 | @inlineCallbacks |
| 651 | def test_change_upload_speed_limit(self): |
| 652 | """The device settings are updated.""" |
| 653 | - be = backend.ControlBackend() |
| 654 | backend.dbus_client.limits = {"download": -1, "upload": -1} |
| 655 | - yield be.change_device_settings(self.local_token, |
| 656 | + yield self.be.change_device_settings(self.local_token, |
| 657 | {"max_upload_speed": "1111"}) |
| 658 | self.assertEqual(backend.dbus_client.limits["upload"], 1111) |
| 659 | self.assertEqual(backend.dbus_client.limits["download"], -1) |
| 660 | @@ -292,9 +284,8 @@ |
| 661 | @inlineCallbacks |
| 662 | def test_change_download_speed_limit(self): |
| 663 | """The device settings are updated.""" |
| 664 | - be = backend.ControlBackend() |
| 665 | backend.dbus_client.limits = {"download": -1, "upload": -1} |
| 666 | - yield be.change_device_settings(self.local_token, |
| 667 | + yield self.be.change_device_settings(self.local_token, |
| 668 | {"max_download_speed": "99"}) |
| 669 | self.assertEqual(backend.dbus_client.limits["upload"], -1) |
| 670 | self.assertEqual(backend.dbus_client.limits["download"], 99) |
| 671 | @@ -302,7 +293,6 @@ |
| 672 | @inlineCallbacks |
| 673 | def test_changing_settings_for_wrong_id_has_no_effect(self): |
| 674 | """If the id is wrong, no settings are changed.""" |
| 675 | - be = backend.ControlBackend() |
| 676 | backend.dbus_client.throttling = False |
| 677 | backend.dbus_client.limits = {"download": -1, "upload": -1} |
| 678 | new_settings = { |
| 679 | @@ -310,7 +300,7 @@ |
| 680 | "max_upload_speed": "99", |
| 681 | "limit_bandwidth": "1", |
| 682 | } |
| 683 | - yield be.change_device_settings("wrong token!", new_settings) |
| 684 | + yield self.be.change_device_settings("wrong token!", new_settings) |
| 685 | self.assertEqual(backend.dbus_client.throttling, False) |
| 686 | self.assertEqual(backend.dbus_client.limits["upload"], -1) |
| 687 | self.assertEqual(backend.dbus_client.limits["download"], -1) |
| 688 | @@ -322,6 +312,5 @@ |
| 689 | @inlineCallbacks |
| 690 | def test_volumes_info(self): |
| 691 | """The volumes_info method exercises its callback.""" |
| 692 | - be = backend.ControlBackend() |
| 693 | - result = yield be.volumes_info() |
| 694 | + result = yield self.be.volumes_info() |
| 695 | self.assertEqual(result, SAMPLE_VOLUMES) |
| 696 | |
| 697 | === modified file 'ubuntuone/controlpanel/tests/test_utils.py' |
| 698 | --- ubuntuone/controlpanel/tests/test_utils.py 2010-11-10 19:50:29 +0000 |
| 699 | +++ ubuntuone/controlpanel/tests/test_utils.py 2010-12-02 20:41:22 +0000 |
| 700 | @@ -21,10 +21,10 @@ |
| 701 | import logging |
| 702 | import sys |
| 703 | |
| 704 | -from twisted.trial.unittest import TestCase |
| 705 | from ubuntuone.devtools.handlers import MementoHandler |
| 706 | |
| 707 | from ubuntuone.controlpanel import utils |
| 708 | +from ubuntuone.controlpanel.tests import TestCase |
| 709 | |
| 710 | |
| 711 | CONSTANTS_MODULE = 'ubuntuone.controlpanel.constants' |
| 712 | @@ -32,11 +32,18 @@ |
| 713 | |
| 714 | |
| 715 | class FakedConstantsModule(object): |
| 716 | - """FAke the 'ubuntuone.controlpanel.constants' module.""" |
| 717 | + """Fake the 'ubuntuone.controlpanel.constants' module.""" |
| 718 | |
| 719 | PROJECT_DIR = '/tmp/foo/bar' |
| 720 | |
| 721 | |
| 722 | +class FakedFailure(object): |
| 723 | + """Fake a twisted Failure.""" |
| 724 | + |
| 725 | + def __init__(self, value): |
| 726 | + self.value = value |
| 727 | + |
| 728 | + |
| 729 | class GetProjectDirTestCase(TestCase): |
| 730 | """Test case for get_project_dir when constants module is not defined.""" |
| 731 | |
| 732 | @@ -106,3 +113,56 @@ |
| 733 | result = utils.get_data_file(dummy_file) |
| 734 | expected = utils.os.path.join(dummy_dir, dummy_file) |
| 735 | self.assertEqual(expected, result) |
| 736 | + |
| 737 | + |
| 738 | +class ExceptionHandligTestCase(TestCase): |
| 739 | + """Test cases for exception handling.""" |
| 740 | + |
| 741 | + def test_is_dbus_no_reply(self): |
| 742 | + """The failure is a dbus no_reply error.""" |
| 743 | + exc = utils.dbus.exceptions.DBusException() |
| 744 | + exc._dbus_error_name = utils.DBUS_NO_REPLY |
| 745 | + |
| 746 | + result = utils.is_dbus_no_reply(FakedFailure(value=exc)) |
| 747 | + |
| 748 | + self.assertTrue(result) |
| 749 | + |
| 750 | + def test_other_dbus_error_is_not_dbus_no_reply(self): |
| 751 | + """Another dbus exception is not a dbus no_reply error.""" |
| 752 | + exc = utils.dbus.exceptions.DBusException() |
| 753 | + exc._dbus_error_name = utils.DBUS_SERVICE_UNKNOWN |
| 754 | + |
| 755 | + result = utils.is_dbus_no_reply(FakedFailure(value=exc)) |
| 756 | + |
| 757 | + self.assertFalse(result) |
| 758 | + |
| 759 | + def test_no_dbus_exception_is_not_dbus_no_reply(self): |
| 760 | + """A non dbus exception is not a dbus no_reply error.""" |
| 761 | + exc = AssertionError(utils.DBUS_NO_REPLY) |
| 762 | + |
| 763 | + result = utils.is_dbus_no_reply(FakedFailure(value=exc)) |
| 764 | + |
| 765 | + self.assertFalse(result) |
| 766 | + |
| 767 | + def test_exception_to_error_dict(self): |
| 768 | + """Transform a regular Exception into a string-string dictionary.""" |
| 769 | + msg = 'Something went wrong.' |
| 770 | + exc = AssertionError(msg) |
| 771 | + |
| 772 | + result = utils.exception_to_error_dict(exc) |
| 773 | + expected = {utils.ERROR_TYPE: exc.__class__.__name__, |
| 774 | + utils.ERROR_MESSAGE: unicode(exc)} |
| 775 | + |
| 776 | + self.assertEqual(expected, result) |
| 777 | + |
| 778 | + def test_failure_to_error_dict(self): |
| 779 | + """Transform a Failure into a string-string dictionary.""" |
| 780 | + msg = 'Something went wrong.' |
| 781 | + exc = AssertionError(msg) |
| 782 | + failure = FakedFailure(value=exc) |
| 783 | + |
| 784 | + result = utils.failure_to_error_dict(failure) |
| 785 | + expected = {utils.ERROR_TYPE: exc.__class__.__name__, |
| 786 | + utils.ERROR_MESSAGE: unicode(exc)} |
| 787 | + |
| 788 | + self.assertEqual(expected, result) |
| 789 | |
| 790 | === removed file 'ubuntuone/controlpanel/tests/testcase.py' |
| 791 | --- ubuntuone/controlpanel/tests/testcase.py 2010-09-20 17:17:06 +0000 |
| 792 | +++ ubuntuone/controlpanel/tests/testcase.py 1970-01-01 00:00:00 +0000 |
| 793 | @@ -1,54 +0,0 @@ |
| 794 | -# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com> |
| 795 | -# |
| 796 | -# Copyright 2009-2010 Canonical Ltd. |
| 797 | -# |
| 798 | -# This program is free software: you can redistribute it and/or modify it |
| 799 | -# under the terms of the GNU General Public License version 3, as published |
| 800 | -# by the Free Software Foundation. |
| 801 | -# |
| 802 | -# This program is distributed in the hope that it will be useful, but |
| 803 | -# WITHOUT ANY WARRANTY; without even the implied warranties of |
| 804 | -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 805 | -# PURPOSE. See the GNU General Public License for more details. |
| 806 | -# |
| 807 | -# You should have received a copy of the GNU General Public License along |
| 808 | -# with this program. If not, see <http://www.gnu.org/licenses/>. |
| 809 | -"""Test utilities.""" |
| 810 | - |
| 811 | -import logging |
| 812 | - |
| 813 | - |
| 814 | -class MementoHandler(logging.Handler): |
| 815 | - """ A handler class which store logging records in a list """ |
| 816 | - |
| 817 | - def __init__(self, *args, **kwargs): |
| 818 | - """ Create the instance, and add a records attribute. """ |
| 819 | - logging.Handler.__init__(self, *args, **kwargs) |
| 820 | - self.records = [] |
| 821 | - |
| 822 | - def emit(self, record): |
| 823 | - """ Just add the record to self.records. """ |
| 824 | - self.records.append(record) |
| 825 | - |
| 826 | - def check(self, level, *msgs): |
| 827 | - """Verifies that the msgs are logged in the specified level.""" |
| 828 | - for rec in self.records: |
| 829 | - if rec.levelno == level and all(m in rec.message for m in msgs): |
| 830 | - return True |
| 831 | - return False |
| 832 | - |
| 833 | - def check_debug(self, *msgs): |
| 834 | - """Shortcut for checking in DEBUG.""" |
| 835 | - return self.check(logging.DEBUG, *msgs) |
| 836 | - |
| 837 | - def check_info(self, *msgs): |
| 838 | - """Shortcut for checking in INFO.""" |
| 839 | - return self.check(logging.INFO, *msgs) |
| 840 | - |
| 841 | - def check_warning(self, *msgs): |
| 842 | - """Shortcut for checking in WARNING.""" |
| 843 | - return self.check(logging.WARNING, *msgs) |
| 844 | - |
| 845 | - def check_error(self, *msgs): |
| 846 | - """Shortcut for checking in ERROR.""" |
| 847 | - return self.check(logging.ERROR, *msgs) |
| 848 | |
| 849 | === modified file 'ubuntuone/controlpanel/utils.py' |
| 850 | --- ubuntuone/controlpanel/utils.py 2010-11-10 19:50:29 +0000 |
| 851 | +++ ubuntuone/controlpanel/utils.py 2010-12-02 20:41:22 +0000 |
| 852 | @@ -22,12 +22,21 @@ |
| 853 | |
| 854 | from functools import wraps |
| 855 | |
| 856 | +import dbus |
| 857 | + |
| 858 | from ubuntuone.controlpanel.logger import setup_logging |
| 859 | |
| 860 | |
| 861 | logger = setup_logging('utils') |
| 862 | + |
| 863 | DATA_SUFFIX = 'data' |
| 864 | |
| 865 | +DBUS_NO_REPLY = 'org.freedesktop.DBus.Error.NoReply' |
| 866 | +DBUS_SERVICE_UNKNOWN = 'org.freedesktop.DBus.Error.ServiceUnknown' |
| 867 | + |
| 868 | +ERROR_TYPE = 'error_type' |
| 869 | +ERROR_MESSAGE = 'error_msg' |
| 870 | + |
| 871 | |
| 872 | def get_project_dir(): |
| 873 | """Return the absolute path to this project's data/ dir. |
| 874 | @@ -75,3 +84,23 @@ |
| 875 | return inner |
| 876 | |
| 877 | return middle |
| 878 | + |
| 879 | + |
| 880 | +def is_dbus_no_reply(failure): |
| 881 | + """Decide if 'failure' is a DBus NoReply Error.""" |
| 882 | + exc = failure.value |
| 883 | + res = (isinstance(exc, dbus.exceptions.DBusException) and |
| 884 | + exc.get_dbus_name() == DBUS_NO_REPLY) |
| 885 | + return res |
| 886 | + |
| 887 | + |
| 888 | +def exception_to_error_dict(exc): |
| 889 | + """Transform a regular Exception into a dictionary.""" |
| 890 | + result = {ERROR_TYPE: exc.__class__.__name__, ERROR_MESSAGE: unicode(exc)} |
| 891 | + |
| 892 | + return result |
| 893 | + |
| 894 | + |
| 895 | +def failure_to_error_dict(failure): |
| 896 | + """Transform a twisted Failure into a dictionary.""" |
| 897 | + return exception_to_error_dict(failure.value) |
| 898 | |
| 899 | === modified file 'ubuntuone/controlpanel/webclient.py' |
| 900 | --- ubuntuone/controlpanel/webclient.py 2010-10-08 17:38:57 +0000 |
| 901 | +++ ubuntuone/controlpanel/webclient.py 2010-12-02 20:41:22 +0000 |
| 902 | @@ -1,4 +1,7 @@ |
| 903 | +# -*- coding: utf-8 -*- |
| 904 | + |
| 905 | # Authors: Alejandro J. Cura <alecu@canonical.com> |
| 906 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
| 907 | # |
| 908 | # Copyright 2010 Canonical Ltd. |
| 909 | # |
| 910 | @@ -45,7 +48,7 @@ |
| 911 | |
| 912 | def _handler(self, session, msg, d): |
| 913 | """Handle the result of an http message.""" |
| 914 | - logger.debug("got http response: %d", msg.status_code) |
| 915 | + logger.debug("WebClient: got http response: %d", msg.status_code) |
| 916 | if msg.status_code == 200: |
| 917 | result = simplejson.loads(msg.response_body.data) |
| 918 | d.callback(result) |
| 919 | @@ -55,20 +58,18 @@ |
| 920 | |
| 921 | def _call_api_with_creds(self, credentials, api_name): |
| 922 | """Get a given url from the webservice with credentials.""" |
| 923 | - logger.debug("got credentials") |
| 924 | url = self.base_url + api_name |
| 925 | method = "GET" |
| 926 | msg = Soup.Message.new(method, url) |
| 927 | add_oauth_headers(msg.request_headers.append, method, url, credentials) |
| 928 | d = defer.Deferred() |
| 929 | - logger.debug("getting url: %s", url) |
| 930 | + logger.debug("WebClient: getting url: %s", url) |
| 931 | self.session.queue_message(msg, self._handler, d) |
| 932 | return d |
| 933 | |
| 934 | def call_api(self, api_name): |
| 935 | """Get a given url from the webservice.""" |
| 936 | - logger.debug("calling api: %s", api_name) |
| 937 | - logger.debug("getting credentials") |
| 938 | + logger.debug("WebClient: calling api: %s", api_name) |
| 939 | d = self.get_credentials() |
| 940 | d.addCallback(self._call_api_with_creds, api_name) |
| 941 | return d |


As advertised.