Merge lp:~nataliabidart/ubuntu-sso-client/use-ui-no-matter-what-stable-3-0 into lp:ubuntu-sso-client/stable-3-0

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 838
Merged at revision: 838
Proposed branch: lp:~nataliabidart/ubuntu-sso-client/use-ui-no-matter-what-stable-3-0
Merge into: lp:ubuntu-sso-client/stable-3-0
Diff against target: 231 lines (+94/-20)
2 files modified
ubuntu_sso/credentials.py (+36/-9)
ubuntu_sso/tests/test_credentials.py (+58/-11)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu-sso-client/use-ui-no-matter-what-stable-3-0
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
dobey (community) Approve
Review via email: mp+104796@code.launchpad.net

Commit message

- Made the service use the Qt UI if available, and if no UI is installed, propagate a decent error instead of AssertionError (LP: #994632).

To post a comment you must log in.
Revision history for this message
dobey (dobey) :
review: Approve
Revision history for this message
Roberto Alsina (ralsina) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'ubuntu_sso/credentials.py'
--- ubuntu_sso/credentials.py 2012-04-09 17:38:24 +0000
+++ ubuntu_sso/credentials.py 2012-05-04 19:12:19 +0000
@@ -49,7 +49,12 @@
4949
50from twisted.internet import defer50from twisted.internet import defer
5151
52from ubuntu_sso import UI_EXECUTABLE_GTK, USER_CANCELLATION, USER_SUCCESS52from ubuntu_sso import (
53 UI_EXECUTABLE_GTK,
54 UI_EXECUTABLE_QT,
55 USER_CANCELLATION,
56 USER_SUCCESS,
57)
53from ubuntu_sso.keyring import Keyring58from ubuntu_sso.keyring import Keyring
54from ubuntu_sso.logger import setup_logging59from ubuntu_sso.logger import setup_logging
55from ubuntu_sso.utils import get_bin_dir, runner60from ubuntu_sso.utils import get_bin_dir, runner
@@ -79,6 +84,10 @@
79 """The user is not validated."""84 """The user is not validated."""
8085
8186
87class GUINotAvailableError(CredentialsError):
88 """No user graphical interface is available."""
89
90
82def handle_failures(msg):91def handle_failures(msg):
83 """Handle failures using 'msg' as error message."""92 """Handle failures using 'msg' as error message."""
8493
@@ -153,14 +162,32 @@
153162
154 @defer.inlineCallbacks163 @defer.inlineCallbacks
155 def _show_ui(self, login_only):164 def _show_ui(self, login_only):
156 """Shows the UI, connect outcome signals."""165 """Show the UI and wait for it to finish.
157 ui_exe_path = os.path.join(get_bin_dir(), self.ui_executable)166
158 if not os.path.exists(ui_exe_path):167 Upon analyzing returning code from the UI process, emit proper signals
159 logger.debug('Falling back to the GTK+ UI since the given %r '168 to the caller.
160 'does not exist', ui_exe_path)169
161 ui_exe_path = os.path.join(get_bin_dir(), UI_EXECUTABLE_GTK)170 The caller can specify a preference for the UI (so far either GTK+ or
162171 Qt UIs are available), but if the preferred one is not available, the
163 assert os.path.exists(ui_exe_path)172 service will try to open any available UI.
173
174 If no GUI is available, GUINotAvailableError will be raised.
175
176 """
177 guis = (self.ui_executable, UI_EXECUTABLE_GTK, UI_EXECUTABLE_QT)
178 for gui_exe_path in guis:
179 ui_exe_path = os.path.join(get_bin_dir(), gui_exe_path)
180 ui_exe_exists = os.path.exists(ui_exe_path)
181 logger.debug('Attempting to use the following executable as GUI: '
182 '%r (exists? %r).', ui_exe_path, ui_exe_exists)
183 if not ui_exe_exists:
184 logger.error('The given UI path %r does not exist.',
185 ui_exe_path)
186 else:
187 break
188 else:
189 raise GUINotAvailableError('Can not find a GUI to present to the '
190 'user (tried with %r). Aborting.' % repr(guis))
164191
165 args = [ui_exe_path]192 args = [ui_exe_path]
166 for arg in ('app_name', 'help_text', 'ping_url', 'policy_url',193 for arg in ('app_name', 'help_text', 'ping_url', 'policy_url',
167194
=== modified file 'ubuntu_sso/tests/test_credentials.py'
--- ubuntu_sso/tests/test_credentials.py 2012-04-09 17:38:24 +0000
+++ ubuntu_sso/tests/test_credentials.py 2012-05-04 19:12:19 +0000
@@ -37,7 +37,7 @@
3737
38import ubuntu_sso.main38import ubuntu_sso.main
3939
40from ubuntu_sso import credentials, utils40from ubuntu_sso import credentials
41from ubuntu_sso.credentials import (41from ubuntu_sso.credentials import (
42 APP_NAME_KEY,42 APP_NAME_KEY,
43 HELP_TEXT_KEY,43 HELP_TEXT_KEY,
@@ -111,6 +111,8 @@
111class BasicTestCase(TestCase):111class BasicTestCase(TestCase):
112 """Test case with a helper tracker."""112 """Test case with a helper tracker."""
113113
114 bin_dir = 'some/bin/dir'
115
114 @defer.inlineCallbacks116 @defer.inlineCallbacks
115 def setUp(self):117 def setUp(self):
116 """Init."""118 """Init."""
@@ -121,6 +123,8 @@
121 self.memento.setLevel(logging.DEBUG)123 self.memento.setLevel(logging.DEBUG)
122 credentials.logger.addHandler(self.memento)124 credentials.logger.addHandler(self.memento)
123125
126 self.patch(credentials, 'get_bin_dir', lambda: self.bin_dir)
127
124 def _set_called(self, *args, **kwargs):128 def _set_called(self, *args, **kwargs):
125 """Set _called to True."""129 """Set _called to True."""
126 self._called = (args, kwargs)130 self._called = (args, kwargs)
@@ -285,8 +289,7 @@
285 yield super(RegisterTestCase, self).setUp()289 yield super(RegisterTestCase, self).setUp()
286 self.deferred = defer.Deferred()290 self.deferred = defer.Deferred()
287 self.patch_inner(self.fake_inner)291 self.patch_inner(self.fake_inner)
288 self.exe_path = os.path.join(utils.get_bin_dir(),292 self.exe_path = os.path.join(self.bin_dir, KWARGS[UI_EXECUTABLE_KEY])
289 KWARGS[UI_EXECUTABLE_KEY])
290 self.inner_args = [293 self.inner_args = [
291 self.exe_path,294 self.exe_path,
292 '--app_name', APP_NAME,295 '--app_name', APP_NAME,
@@ -347,11 +350,9 @@
347 lambda kr, app: defer.succeed(None))350 lambda kr, app: defer.succeed(None))
348 self._next_inner_result = credentials.USER_SUCCESS351 self._next_inner_result = credentials.USER_SUCCESS
349352
350 # patch os.path.exists so it also returns True when queried for the353 # patch os.path.exists so it returns True for the faked exe_path
351 # faked exe_path
352 really_exists = os.path.exists
353 self.patch(credentials.os.path, 'exists',354 self.patch(credentials.os.path, 'exists',
354 lambda path: path == self.exe_path or really_exists(path))355 lambda path: path == self.exe_path)
355356
356 result = yield self.method_call(**self.kwargs)357 result = yield self.method_call(**self.kwargs)
357 self.assertEqual(result, TOKEN)358 self.assertEqual(result, TOKEN)
@@ -368,34 +369,71 @@
368 self._next_inner_result = credentials.USER_SUCCESS369 self._next_inner_result = credentials.USER_SUCCESS
369370
370 assert not os.path.exists(self.exe_path)371 assert not os.path.exists(self.exe_path)
372 self.patch(credentials.os.path, 'exists',
373 lambda path: path.endswith(credentials.UI_EXECUTABLE_GTK))
371374
372 result = yield self.method_call(**self.kwargs)375 result = yield self.method_call(**self.kwargs)
373 self.assertEqual(result, TOKEN)376 self.assertEqual(result, TOKEN)
374377
375 # the ui was opened and proper params were passed378 # the ui was opened and proper params were passed
376 args = yield self.deferred379 args = yield self.deferred
377 self.inner_args[0] = os.path.join(utils.get_bin_dir(),380 self.inner_args[0] = os.path.join(self.bin_dir,
378 credentials.UI_EXECUTABLE_GTK)381 credentials.UI_EXECUTABLE_GTK)
379 self.assertEqual(self.inner_args, args)382 self.assertEqual(self.inner_args, args)
380383
381 @defer.inlineCallbacks384 @defer.inlineCallbacks
385 def test_ui_executable_uses_qt_ui_if_gtk_not_available(self):
386 """The executable loads the QT UI if the GTK+ does not exist."""
387 self.patch(credentials.Keyring, 'get_credentials',
388 lambda kr, app: defer.succeed(None))
389 self._next_inner_result = credentials.USER_SUCCESS
390
391 self.patch(credentials.os.path, 'exists',
392 lambda path: path.endswith(credentials.UI_EXECUTABLE_QT))
393
394 result = yield self.method_call(**self.kwargs)
395 self.assertEqual(result, TOKEN)
396
397 # the ui was opened and proper params were passed
398 args = yield self.deferred
399 self.inner_args[0] = os.path.join(self.bin_dir,
400 credentials.UI_EXECUTABLE_QT)
401 self.assertEqual(self.inner_args, args)
402
403 @defer.inlineCallbacks
404 def test_raises_exception_if_no_ui_available(self):
405 """If no GUI is available, raise GUINotAvailableError."""
406 self.patch(credentials.Keyring, 'get_credentials',
407 lambda kr, app: defer.succeed(None))
408 self.patch(credentials.os.path, 'exists', lambda path: False)
409
410 f = self.method_call(**self.kwargs)
411 yield self.assertFailure(f, credentials.GUINotAvailableError)
412
413 @defer.inlineCallbacks
382 def test_without_existent_token_and_return_code_cancel(self, exc=None):414 def test_without_existent_token_and_return_code_cancel(self, exc=None):
383 """The operation returns exc if defined, else UserCancellationError."""415 """The operation returns exc if defined, else UserCancellationError."""
384 self.patch(credentials.Keyring, 'get_credentials',416 self.patch(credentials.Keyring, 'get_credentials',
385 lambda kr, app: defer.succeed(None))417 lambda kr, app: defer.succeed(None))
386 self._next_inner_result = credentials.USER_CANCELLATION418 self._next_inner_result = credentials.USER_CANCELLATION
387419
420 self.patch(credentials.os.path, 'exists',
421 lambda path: path == self.exe_path)
422
388 if exc is None:423 if exc is None:
389 exc = credentials.UserCancellationError424 exc = credentials.UserCancellationError
390 yield self.assertFailure(self.method_call(**self.kwargs), exc)425 yield self.assertFailure(self.method_call(**self.kwargs), exc)
391426
392 @defer.inlineCallbacks427 @defer.inlineCallbacks
393 def test_without_existent_token_and_return_other(self, result=None):428 def test_without_existent_token_and_return_other_code(self, result=None):
394 """The operation returns CredentialsError with 'result'."""429 """The operation returns CredentialsError with 'result'."""
395 self.patch(credentials.Keyring, 'get_credentials',430 self.patch(credentials.Keyring, 'get_credentials',
396 lambda kr, app: defer.succeed(None))431 lambda kr, app: defer.succeed(None))
397 self._next_inner_result = credentials.USER_CANCELLATION * 2 # other432 self._next_inner_result = credentials.USER_CANCELLATION * 2 # other
398433
434 self.patch(credentials.os.path, 'exists',
435 lambda path: path == self.exe_path)
436
399 exc = yield self.assertFailure(self.method_call(**self.kwargs),437 exc = yield self.assertFailure(self.method_call(**self.kwargs),
400 credentials.CredentialsError)438 credentials.CredentialsError)
401 if result is None:439 if result is None:
@@ -422,6 +460,9 @@
422 lambda kr, app: defer.succeed(None))460 lambda kr, app: defer.succeed(None))
423 self.patch_inner(self.fail_inner)461 self.patch_inner(self.fail_inner)
424462
463 self.patch(credentials.os.path, 'exists',
464 lambda path: path == self.exe_path)
465
425 yield self.assertFailure(self.method_call(**self.kwargs),466 yield self.assertFailure(self.method_call(**self.kwargs),
426 SampleMiscException)467 SampleMiscException)
427468
@@ -478,14 +519,20 @@
478 def test_ui_executable_falls_back_to_gtk(self):519 def test_ui_executable_falls_back_to_gtk(self):
479 """This check does not apply for this test case."""520 """This check does not apply for this test case."""
480521
522 def test_ui_executable_uses_qt_ui_if_gtk_not_available(self):
523 """This check does not apply for this test case."""
524
525 def test_raises_exception_if_no_ui_available(self):
526 """This check does not apply for this test case."""
527
481 def test_without_existent_token_and_return_code_cancel(self, exc=None):528 def test_without_existent_token_and_return_code_cancel(self, exc=None):
482 """The operation returns UserNotValidatedError."""529 """The operation returns UserNotValidatedError."""
483 exc = credentials.UserNotValidatedError530 exc = credentials.UserNotValidatedError
484 return super(LoginEmailPasswordTestCase, self).\531 return super(LoginEmailPasswordTestCase, self).\
485 test_without_existent_token_and_return_code_cancel(exc=exc)532 test_without_existent_token_and_return_code_cancel(exc=exc)
486533
487 def test_without_existent_token_and_return_other(self, result=None):534 def test_without_existent_token_and_return_other_code(self, result=None):
488 """The operation returns CredentialsError with 'result'."""535 """The operation returns CredentialsError with 'result'."""
489 result = 'foo'536 result = 'foo'
490 return super(LoginEmailPasswordTestCase, self).\537 return super(LoginEmailPasswordTestCase, self).\
491 test_without_existent_token_and_return_other(result=result)538 test_without_existent_token_and_return_other_code(result=result)

Subscribers

People subscribed via source and target branches