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
1=== modified file 'ubuntu_sso/credentials.py'
2--- ubuntu_sso/credentials.py 2012-04-09 17:38:24 +0000
3+++ ubuntu_sso/credentials.py 2012-05-04 19:12:19 +0000
4@@ -49,7 +49,12 @@
5
6 from twisted.internet import defer
7
8-from ubuntu_sso import UI_EXECUTABLE_GTK, USER_CANCELLATION, USER_SUCCESS
9+from ubuntu_sso import (
10+ UI_EXECUTABLE_GTK,
11+ UI_EXECUTABLE_QT,
12+ USER_CANCELLATION,
13+ USER_SUCCESS,
14+)
15 from ubuntu_sso.keyring import Keyring
16 from ubuntu_sso.logger import setup_logging
17 from ubuntu_sso.utils import get_bin_dir, runner
18@@ -79,6 +84,10 @@
19 """The user is not validated."""
20
21
22+class GUINotAvailableError(CredentialsError):
23+ """No user graphical interface is available."""
24+
25+
26 def handle_failures(msg):
27 """Handle failures using 'msg' as error message."""
28
29@@ -153,14 +162,32 @@
30
31 @defer.inlineCallbacks
32 def _show_ui(self, login_only):
33- """Shows the UI, connect outcome signals."""
34- ui_exe_path = os.path.join(get_bin_dir(), self.ui_executable)
35- if not os.path.exists(ui_exe_path):
36- logger.debug('Falling back to the GTK+ UI since the given %r '
37- 'does not exist', ui_exe_path)
38- ui_exe_path = os.path.join(get_bin_dir(), UI_EXECUTABLE_GTK)
39-
40- assert os.path.exists(ui_exe_path)
41+ """Show the UI and wait for it to finish.
42+
43+ Upon analyzing returning code from the UI process, emit proper signals
44+ to the caller.
45+
46+ The caller can specify a preference for the UI (so far either GTK+ or
47+ Qt UIs are available), but if the preferred one is not available, the
48+ service will try to open any available UI.
49+
50+ If no GUI is available, GUINotAvailableError will be raised.
51+
52+ """
53+ guis = (self.ui_executable, UI_EXECUTABLE_GTK, UI_EXECUTABLE_QT)
54+ for gui_exe_path in guis:
55+ ui_exe_path = os.path.join(get_bin_dir(), gui_exe_path)
56+ ui_exe_exists = os.path.exists(ui_exe_path)
57+ logger.debug('Attempting to use the following executable as GUI: '
58+ '%r (exists? %r).', ui_exe_path, ui_exe_exists)
59+ if not ui_exe_exists:
60+ logger.error('The given UI path %r does not exist.',
61+ ui_exe_path)
62+ else:
63+ break
64+ else:
65+ raise GUINotAvailableError('Can not find a GUI to present to the '
66+ 'user (tried with %r). Aborting.' % repr(guis))
67
68 args = [ui_exe_path]
69 for arg in ('app_name', 'help_text', 'ping_url', 'policy_url',
70
71=== modified file 'ubuntu_sso/tests/test_credentials.py'
72--- ubuntu_sso/tests/test_credentials.py 2012-04-09 17:38:24 +0000
73+++ ubuntu_sso/tests/test_credentials.py 2012-05-04 19:12:19 +0000
74@@ -37,7 +37,7 @@
75
76 import ubuntu_sso.main
77
78-from ubuntu_sso import credentials, utils
79+from ubuntu_sso import credentials
80 from ubuntu_sso.credentials import (
81 APP_NAME_KEY,
82 HELP_TEXT_KEY,
83@@ -111,6 +111,8 @@
84 class BasicTestCase(TestCase):
85 """Test case with a helper tracker."""
86
87+ bin_dir = 'some/bin/dir'
88+
89 @defer.inlineCallbacks
90 def setUp(self):
91 """Init."""
92@@ -121,6 +123,8 @@
93 self.memento.setLevel(logging.DEBUG)
94 credentials.logger.addHandler(self.memento)
95
96+ self.patch(credentials, 'get_bin_dir', lambda: self.bin_dir)
97+
98 def _set_called(self, *args, **kwargs):
99 """Set _called to True."""
100 self._called = (args, kwargs)
101@@ -285,8 +289,7 @@
102 yield super(RegisterTestCase, self).setUp()
103 self.deferred = defer.Deferred()
104 self.patch_inner(self.fake_inner)
105- self.exe_path = os.path.join(utils.get_bin_dir(),
106- KWARGS[UI_EXECUTABLE_KEY])
107+ self.exe_path = os.path.join(self.bin_dir, KWARGS[UI_EXECUTABLE_KEY])
108 self.inner_args = [
109 self.exe_path,
110 '--app_name', APP_NAME,
111@@ -347,11 +350,9 @@
112 lambda kr, app: defer.succeed(None))
113 self._next_inner_result = credentials.USER_SUCCESS
114
115- # patch os.path.exists so it also returns True when queried for the
116- # faked exe_path
117- really_exists = os.path.exists
118+ # patch os.path.exists so it returns True for the faked exe_path
119 self.patch(credentials.os.path, 'exists',
120- lambda path: path == self.exe_path or really_exists(path))
121+ lambda path: path == self.exe_path)
122
123 result = yield self.method_call(**self.kwargs)
124 self.assertEqual(result, TOKEN)
125@@ -368,34 +369,71 @@
126 self._next_inner_result = credentials.USER_SUCCESS
127
128 assert not os.path.exists(self.exe_path)
129+ self.patch(credentials.os.path, 'exists',
130+ lambda path: path.endswith(credentials.UI_EXECUTABLE_GTK))
131
132 result = yield self.method_call(**self.kwargs)
133 self.assertEqual(result, TOKEN)
134
135 # the ui was opened and proper params were passed
136 args = yield self.deferred
137- self.inner_args[0] = os.path.join(utils.get_bin_dir(),
138+ self.inner_args[0] = os.path.join(self.bin_dir,
139 credentials.UI_EXECUTABLE_GTK)
140 self.assertEqual(self.inner_args, args)
141
142 @defer.inlineCallbacks
143+ def test_ui_executable_uses_qt_ui_if_gtk_not_available(self):
144+ """The executable loads the QT UI if the GTK+ does not exist."""
145+ self.patch(credentials.Keyring, 'get_credentials',
146+ lambda kr, app: defer.succeed(None))
147+ self._next_inner_result = credentials.USER_SUCCESS
148+
149+ self.patch(credentials.os.path, 'exists',
150+ lambda path: path.endswith(credentials.UI_EXECUTABLE_QT))
151+
152+ result = yield self.method_call(**self.kwargs)
153+ self.assertEqual(result, TOKEN)
154+
155+ # the ui was opened and proper params were passed
156+ args = yield self.deferred
157+ self.inner_args[0] = os.path.join(self.bin_dir,
158+ credentials.UI_EXECUTABLE_QT)
159+ self.assertEqual(self.inner_args, args)
160+
161+ @defer.inlineCallbacks
162+ def test_raises_exception_if_no_ui_available(self):
163+ """If no GUI is available, raise GUINotAvailableError."""
164+ self.patch(credentials.Keyring, 'get_credentials',
165+ lambda kr, app: defer.succeed(None))
166+ self.patch(credentials.os.path, 'exists', lambda path: False)
167+
168+ f = self.method_call(**self.kwargs)
169+ yield self.assertFailure(f, credentials.GUINotAvailableError)
170+
171+ @defer.inlineCallbacks
172 def test_without_existent_token_and_return_code_cancel(self, exc=None):
173 """The operation returns exc if defined, else UserCancellationError."""
174 self.patch(credentials.Keyring, 'get_credentials',
175 lambda kr, app: defer.succeed(None))
176 self._next_inner_result = credentials.USER_CANCELLATION
177
178+ self.patch(credentials.os.path, 'exists',
179+ lambda path: path == self.exe_path)
180+
181 if exc is None:
182 exc = credentials.UserCancellationError
183 yield self.assertFailure(self.method_call(**self.kwargs), exc)
184
185 @defer.inlineCallbacks
186- def test_without_existent_token_and_return_other(self, result=None):
187+ def test_without_existent_token_and_return_other_code(self, result=None):
188 """The operation returns CredentialsError with 'result'."""
189 self.patch(credentials.Keyring, 'get_credentials',
190 lambda kr, app: defer.succeed(None))
191 self._next_inner_result = credentials.USER_CANCELLATION * 2 # other
192
193+ self.patch(credentials.os.path, 'exists',
194+ lambda path: path == self.exe_path)
195+
196 exc = yield self.assertFailure(self.method_call(**self.kwargs),
197 credentials.CredentialsError)
198 if result is None:
199@@ -422,6 +460,9 @@
200 lambda kr, app: defer.succeed(None))
201 self.patch_inner(self.fail_inner)
202
203+ self.patch(credentials.os.path, 'exists',
204+ lambda path: path == self.exe_path)
205+
206 yield self.assertFailure(self.method_call(**self.kwargs),
207 SampleMiscException)
208
209@@ -478,14 +519,20 @@
210 def test_ui_executable_falls_back_to_gtk(self):
211 """This check does not apply for this test case."""
212
213+ def test_ui_executable_uses_qt_ui_if_gtk_not_available(self):
214+ """This check does not apply for this test case."""
215+
216+ def test_raises_exception_if_no_ui_available(self):
217+ """This check does not apply for this test case."""
218+
219 def test_without_existent_token_and_return_code_cancel(self, exc=None):
220 """The operation returns UserNotValidatedError."""
221 exc = credentials.UserNotValidatedError
222 return super(LoginEmailPasswordTestCase, self).\
223 test_without_existent_token_and_return_code_cancel(exc=exc)
224
225- def test_without_existent_token_and_return_other(self, result=None):
226+ def test_without_existent_token_and_return_other_code(self, result=None):
227 """The operation returns CredentialsError with 'result'."""
228 result = 'foo'
229 return super(LoginEmailPasswordTestCase, self).\
230- test_without_existent_token_and_return_other(result=result)
231+ test_without_existent_token_and_return_other_code(result=result)

Subscribers

People subscribed via source and target branches