Merge lp:~nataliabidart/ubuntu-sso-client/run-that-ui into lp:ubuntu-sso-client
- run-that-ui
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Roberto Alsina |
Approved revision: | 860 |
Merged at revision: | 856 |
Proposed branch: | lp:~nataliabidart/ubuntu-sso-client/run-that-ui |
Merge into: | lp:ubuntu-sso-client |
Prerequisite: | lp:~nataliabidart/ubuntu-sso-client/dont-let-it-go |
Diff against target: |
1277 lines (+293/-450) 12 files modified
ubuntu_sso/__init__.py (+4/-0) ubuntu_sso/credentials.py (+83/-157) ubuntu_sso/gtk/gui.py (+3/-13) ubuntu_sso/gtk/tests/test_gui.py (+4/-4) ubuntu_sso/main/__init__.py (+19/-12) ubuntu_sso/main/tests/__init__.py (+8/-3) ubuntu_sso/main/tests/test_clients.py (+8/-24) ubuntu_sso/main/tests/test_common.py (+14/-12) ubuntu_sso/qt/tests/show_gui.py (+2/-2) ubuntu_sso/tests/__init__.py (+1/-2) ubuntu_sso/tests/test_credentials.py (+147/-213) ubuntu_sso/utils/runner/glib.py (+0/-8) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntu-sso-client/run-that-ui |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Diego Sarmentero (community) | Approve | ||
Review via email: mp+92496@code.launchpad.net |
Commit message
- Execute the UI as a separated process from the sso main thread
(LP: #919330).
Description of the change
This branch provides the ability to run UIs in a separate process.
To test it IRL in Linux, you should do the following twice (one for the glib spawner, one for the Qt one):
* remove your U1 token
* run the sso service:
GLib -> U1_DEBUG=True PATH=bin/:$PATH PYTHONPATH=. bin/ubuntu-
Qt -> USE_QT_
* open the Ubuntu One Control Panel and login/register/
* profit
Please note that we still have to code a fix for bug #930137.
- 858. By Natalia Bidart
-
Merged updates from dependency branch.
- 859. By Natalia Bidart
-
Merged trunk in.
- 860. By Natalia Bidart
-
Avoig having double escapes on arguments.
Roberto Alsina (ralsina) wrote : | # |
+1 looks good to me
Preview Diff
1 | === modified file 'ubuntu_sso/__init__.py' |
2 | --- ubuntu_sso/__init__.py 2010-10-07 21:09:01 +0000 |
3 | +++ ubuntu_sso/__init__.py 2012-02-10 17:15:32 +0000 |
4 | @@ -29,3 +29,7 @@ |
5 | DBUS_CREDENTIALS_IFACE = "com.ubuntu.sso.CredentialsManagement" |
6 | |
7 | NO_OP = lambda *args, **kwargs: None |
8 | + |
9 | +# return code for UIs |
10 | +USER_SUCCESS = 0 |
11 | +USER_CANCELLATION = 10 |
12 | |
13 | === modified file 'ubuntu_sso/credentials.py' |
14 | --- ubuntu_sso/credentials.py 2012-02-09 21:57:15 +0000 |
15 | +++ ubuntu_sso/credentials.py 2012-02-10 17:15:32 +0000 |
16 | @@ -23,27 +23,22 @@ |
17 | * register |
18 | * login |
19 | |
20 | -The first three return a Deferred that will be fired when the operation was |
21 | +All the methods return a Deferred that will be fired when the operation was |
22 | completed. |
23 | |
24 | -The second two use the 'success_cb', 'error_cb' and 'denial_cb' to signal the |
25 | -caller when the credentials were retrieved successfully, when there was an |
26 | -error or when the user denied the access to the application, respectively. |
27 | - |
28 | For details, please read the Credentials class documentation. |
29 | |
30 | """ |
31 | |
32 | -import sys |
33 | -import traceback |
34 | - |
35 | from functools import wraps |
36 | |
37 | from twisted.internet import defer |
38 | |
39 | -from ubuntu_sso import NO_OP |
40 | +from ubuntu_sso import USER_CANCELLATION, USER_SUCCESS |
41 | from ubuntu_sso.keyring import Keyring |
42 | from ubuntu_sso.logger import setup_logging |
43 | +from ubuntu_sso.utils import runner |
44 | + |
45 | |
46 | logger = setup_logging('ubuntu_sso.credentials') |
47 | |
48 | @@ -53,44 +48,19 @@ |
49 | HELP_TEXT_KEY = 'help_text' |
50 | WINDOW_ID_KEY = 'window_id' |
51 | PING_URL_KEY = 'ping_url' |
52 | -UI_MODULE_KEY = 'ui_module' |
53 | -UI_CLASS_KEY = 'ui_class' |
54 | -SUCCESS_CB_KEY = 'success_cb' |
55 | -ERROR_CB_KEY = 'error_cb' |
56 | -DENIAL_CB_KEY = 'denial_cb' |
57 | -ERROR_KEY = 'error_message' |
58 | -ERROR_DETAIL_KEY = 'detailed_error' |
59 | - |
60 | - |
61 | -def handle_exceptions(msg): |
62 | - """Handle exceptions using 'msg' as error message.""" |
63 | - |
64 | - def middle(f): |
65 | - """Decorate 'f' to catch all errors.""" |
66 | - |
67 | - @wraps(f) |
68 | - def inner(self, *a, **kw): |
69 | - """Call 'f' within a try-except block. |
70 | - |
71 | - If any exception occurs, self.error_cb is called and the exception |
72 | - is logged. |
73 | - """ |
74 | - result = None |
75 | - try: |
76 | - result = f(self, *a, **kw) |
77 | - except: # pylint: disable=W0702 |
78 | - logger.exception('%s (app_name: %s): %s.', |
79 | - f.__name__, self.app_name, msg) |
80 | - logger.error('%s (app_name: %s): Calling error_cb at %r.', |
81 | - f.__name__, self.app_name, self.error_cb) |
82 | - error_dict = {ERROR_KEY: msg, |
83 | - ERROR_DETAIL_KEY: traceback.format_exc()} |
84 | - self.error_cb(error_dict) |
85 | - return result |
86 | - |
87 | - return inner |
88 | - |
89 | - return middle |
90 | +UI_EXECUTABLE_KEY = 'ui_executable' |
91 | + |
92 | + |
93 | +class CredentialsError(Exception): |
94 | + """Generic credentials error.""" |
95 | + |
96 | + |
97 | +class UserCancellationError(CredentialsError): |
98 | + """The user cancelled the process of authentication.""" |
99 | + |
100 | + |
101 | +class UserNotValidatedError(CredentialsError): |
102 | + """The user is not validated.""" |
103 | |
104 | |
105 | def handle_failures(msg): |
106 | @@ -104,20 +74,17 @@ |
107 | def inner(self, *a, **kw): |
108 | """Call 'f' within a try-except block. |
109 | |
110 | - If any exception occurs, self.error_cb is called and the exception |
111 | - is logged. |
112 | + If any exception occurs, the exception is logged and re-raised. |
113 | + |
114 | """ |
115 | result = None |
116 | try: |
117 | result = yield f(self, *a, **kw) |
118 | - except Exception: # pylint: disable=W0703 |
119 | + except: |
120 | logger.exception('%s (app_name: %s): %s.', |
121 | f.__name__, self.app_name, msg) |
122 | - logger.error('%s (app_name: %s): Calling error_cb at %r.', |
123 | - f.__name__, self.app_name, self.error_cb) |
124 | - error_dict = {ERROR_KEY: msg, |
125 | - ERROR_DETAIL_KEY: traceback.format_exc()} |
126 | - self.error_cb(error_dict) |
127 | + raise |
128 | + |
129 | defer.returnValue(result) |
130 | |
131 | return inner |
132 | @@ -130,13 +97,12 @@ |
133 | |
134 | def __init__(self, app_name, tc_url=None, help_text='', |
135 | window_id=0, ping_url=None, |
136 | - ui_module='ubuntu_sso.gtk.gui', ui_class='UbuntuSSOClientGUI', |
137 | - success_cb=NO_OP, error_cb=NO_OP, denial_cb=NO_OP): |
138 | + ui_executable='ubuntu-sso-login-gtk'): |
139 | """Return a Credentials management object. |
140 | |
141 | 'app_name' is the application name to be displayed in the GUI. |
142 | |
143 | - 'tc_url' is the URL pointing to Terms & Conditions. If None, no |
144 | + 'tc_url' is the url pointing to Terms & Conditions. If None, no |
145 | TOS agreement will be displayed. |
146 | |
147 | 'help_text' is an explanatory text for the end-users, will be shown |
148 | @@ -148,9 +114,9 @@ |
149 | 'ping_url' is the url that will be pinged when a user registers/logins |
150 | successfully. The user email will be attached to 'ping_url'. |
151 | |
152 | - 'success_cb' will be called when the credentials were retrieved |
153 | - successfully. Two params will be passed: the app_name and the |
154 | - credentials per se. The credentials is a dictionary of the form: |
155 | + |
156 | + When the credentials are retrieved successfully, a dictionary like the |
157 | + one below is returned: |
158 | |
159 | {'token': <value>, |
160 | 'token_secret': <value>, |
161 | @@ -158,65 +124,42 @@ |
162 | 'consumer_secret': <value>, |
163 | 'name': <the token name, matches "[app_name] @ [host name]">} |
164 | |
165 | - 'error_cb' will be called when the credentials retrieval failed. Two |
166 | - params will be passed: the app_name, and an error dict with 2 keys: |
167 | - the error message (user friendly, not translatable so far), and |
168 | - the detailed error (usually the traceback). |
169 | - |
170 | - 'denial_cb' will be called when the user denied the credentials to the |
171 | - caller. A single param is passed: the app_name. |
172 | - |
173 | """ |
174 | self.app_name = app_name |
175 | self.help_text = help_text |
176 | self.window_id = window_id |
177 | self.ping_url = ping_url |
178 | self.tc_url = tc_url |
179 | - self.ui_module = ui_module |
180 | - self.ui_class = ui_class |
181 | - self._success_cb = success_cb |
182 | - self._error_cb = error_cb |
183 | - self.denial_cb = denial_cb |
184 | - self.inner = None # will hold the GUI or SSOLoginRoot instance |
185 | + self.ui_executable = ui_executable |
186 | |
187 | - @handle_failures(msg='Problem while retrieving credentials') |
188 | @defer.inlineCallbacks |
189 | - def _login_success_cb(self, app_name, email): |
190 | - """Store credentials when the login/registration succeeded. |
191 | - |
192 | - Return 0 on success, and a non-zero value (or None) on error. |
193 | - |
194 | - """ |
195 | - logger.info('Login/registration was successful for app %r, email %r', |
196 | - app_name, email) |
197 | - creds = yield self.find_credentials() |
198 | - if creds is not None: |
199 | - assert len(creds) > 0, 'Creds are empty! This should not happen' |
200 | - self.success_cb(creds) |
201 | - defer.returnValue(0) |
202 | - |
203 | - def _auth_denial_cb(self, app_name): |
204 | - """The user decided not to allow the registration or login.""" |
205 | - logger.warning('Login/registration was denied to app %r', app_name) |
206 | - self.denial_cb(app_name) |
207 | - |
208 | - @handle_exceptions(msg='Problem opening the Ubuntu SSO user interface') |
209 | def _show_ui(self, login_only): |
210 | """Shows the UI, connect outcome signals.""" |
211 | - |
212 | - __import__(self.ui_module) |
213 | - gui = sys.modules[self.ui_module] |
214 | - |
215 | - self.inner = getattr(gui, self.ui_class)(app_name=self.app_name, |
216 | - tc_url=self.tc_url, help_text=self.help_text, |
217 | - ping_url=self.ping_url, window_id=self.window_id, |
218 | - login_only=login_only) |
219 | - |
220 | - self.inner.login_success_callback = self._login_success_cb |
221 | - self.inner.registration_success_callback = self._login_success_cb |
222 | - self.inner.user_cancellation_callback = self._auth_denial_cb |
223 | - |
224 | - @handle_exceptions(msg='Problem logging with email and password.') |
225 | + args = [str(self.ui_executable)] |
226 | + for arg in ('app_name', 'help_text', 'ping_url', |
227 | + 'tc_url', 'window_id'): |
228 | + value = getattr(self, arg) |
229 | + if value: |
230 | + args.append('--%s' % arg) |
231 | + args.append("%s" % str(value)) |
232 | + |
233 | + if login_only: |
234 | + args.append('--login_only') |
235 | + |
236 | + return_code = yield runner.spawn_program(args) |
237 | + logger.info('_show_ui: received from the ui return code %r.', |
238 | + return_code) |
239 | + |
240 | + credentials = None |
241 | + if return_code == USER_SUCCESS: |
242 | + credentials = yield self.find_credentials() |
243 | + elif return_code == USER_CANCELLATION: |
244 | + raise UserCancellationError() |
245 | + else: |
246 | + raise CredentialsError(return_code) |
247 | + |
248 | + defer.returnValue(credentials) |
249 | + |
250 | def _do_login(self, email, password): |
251 | """Login using email/password, connect outcome signals.""" |
252 | from ubuntu_sso.main import SSOLogin |
253 | @@ -234,62 +177,43 @@ |
254 | |
255 | def LoginError(self, app_name, error): |
256 | """There was an error on login.""" |
257 | + error = CredentialsError(error['errtype']) |
258 | d.errback(error) |
259 | |
260 | def UserNotValidated(self, app_name, email): |
261 | """User is not validated.""" |
262 | - d.callback(None) |
263 | + d.errback(UserNotValidatedError(email)) |
264 | |
265 | # pylint: enable=C0103 |
266 | |
267 | - self.inner = SSOLogin(proxy=DummyProxy()) |
268 | - self.inner.login(app_name=self.app_name, |
269 | - email=email, password=password) |
270 | - |
271 | - def _success(result): |
272 | - """Check if 'result' is a valid token, and callback properly.""" |
273 | - if result is not None: |
274 | - return self._login_success_cb(self.app_name, email) |
275 | - else: |
276 | - error_dict = { |
277 | - 'errtype': 'UserNotValidated', |
278 | - 'message': email, |
279 | - } |
280 | - self._error_cb(self.app_name, error_dict) |
281 | - |
282 | - d.addCallback(_success) |
283 | - d.addErrback(lambda f: self._error_cb(self.app_name, f.value)) |
284 | - |
285 | - @handle_failures(msg='Problem while retrieving credentials') |
286 | + inner = SSOLogin(proxy=DummyProxy()) |
287 | + inner.login(app_name=self.app_name, |
288 | + email=email, password=password, |
289 | + ping_url=self.ping_url) |
290 | + |
291 | + d.addCallback(lambda _: self.find_credentials()) |
292 | + return d |
293 | + |
294 | @defer.inlineCallbacks |
295 | def _login_or_register(self, login_only, email=None, password=None): |
296 | - """Get credentials if found else prompt the GUI.""" |
297 | + """Get credentials if found else prompt the GUI. |
298 | + |
299 | + Will return either the credentials, or will raise UserCancellationError |
300 | + if the user aborted the operation when the UI was opened. |
301 | + |
302 | + """ |
303 | logger.info("_login_or_register: login_only=%r email=%r.", |
304 | - login_only, email) |
305 | + login_only, email) |
306 | token = yield self.find_credentials() |
307 | - if token is not None and len(token) > 0: |
308 | - self.success_cb(token) |
309 | - elif token == {}: |
310 | + if not token: |
311 | if email and password: |
312 | - self._do_login(email, password) |
313 | + token = yield self._do_login(email, password) |
314 | else: |
315 | - self._show_ui(login_only) |
316 | - else: |
317 | - # something went wrong with find_credentials, already handled. |
318 | - logger.info('_login_or_register: call to "find_credentials" went ' |
319 | - 'wrong, and was already handled.') |
320 | - |
321 | - def error_cb(self, error_dict): |
322 | - """Handle error and notify the caller.""" |
323 | - logger.error('Calling error callback at %r (error is %r).', |
324 | - self._error_cb, error_dict) |
325 | - self._error_cb(self.app_name, error_dict) |
326 | - |
327 | - def success_cb(self, creds): |
328 | - """Handle success and notify the caller.""" |
329 | - logger.debug('Calling success callback at %r.', self._success_cb) |
330 | - self._success_cb(self.app_name, creds) |
331 | - |
332 | + token = yield self._show_ui(login_only) |
333 | + |
334 | + defer.returnValue(token) |
335 | + |
336 | + @handle_failures(msg='Problem while getting credentials from the keyring') |
337 | @defer.inlineCallbacks |
338 | def find_credentials(self): |
339 | """Get the credentials for 'self.app_name'. Return {} if not there.""" |
340 | @@ -298,20 +222,22 @@ |
341 | 'result is {}? %s', self.app_name, creds is None) |
342 | defer.returnValue(creds if creds is not None else {}) |
343 | |
344 | - @defer.inlineCallbacks |
345 | + @handle_failures(msg='Problem while clearing credentials in the keyring') |
346 | def clear_credentials(self): |
347 | """Clear the credentials for 'self.app_name'.""" |
348 | - yield Keyring().delete_credentials(self.app_name) |
349 | + return Keyring().delete_credentials(self.app_name) |
350 | |
351 | - @defer.inlineCallbacks |
352 | + @handle_failures(msg='Problem while storing credentials in the keyring') |
353 | def store_credentials(self, token): |
354 | """Store the credentials for 'self.app_name'.""" |
355 | - yield Keyring().set_credentials(self.app_name, token) |
356 | + return Keyring().set_credentials(self.app_name, token) |
357 | |
358 | + @handle_failures(msg='Problem while performing register') |
359 | def register(self): |
360 | """Get credentials if found else prompt the GUI to register.""" |
361 | return self._login_or_register(login_only=False) |
362 | |
363 | + @handle_failures(msg='Problem while performing login') |
364 | def login(self, email=None, password=None): |
365 | """Get credentials if found else prompt the GUI to login. |
366 | |
367 | |
368 | === modified file 'ubuntu_sso/gtk/gui.py' |
369 | --- ubuntu_sso/gtk/gui.py 2012-02-09 21:57:15 +0000 |
370 | +++ ubuntu_sso/gtk/gui.py 2012-02-10 17:15:32 +0000 |
371 | @@ -33,6 +33,8 @@ |
372 | from ubuntu_sso import ( |
373 | main, |
374 | NO_OP, |
375 | + USER_CANCELLATION, |
376 | + USER_SUCCESS, |
377 | utils, |
378 | ) |
379 | from ubuntu_sso.logger import setup_gui_logging |
380 | @@ -110,9 +112,6 @@ |
381 | HELP_TEXT_COLOR = parse_color("#bfbfbf") |
382 | WARNING_TEXT_COLOR = parse_color("red") |
383 | |
384 | -USER_CANCELLATION = 10 |
385 | -LOGIN_SUCCESS = REGISTRATION_SUCCESS = 0 |
386 | - |
387 | |
388 | def log_call(f): |
389 | """Decorator to log call funtions.""" |
390 | @@ -218,12 +217,6 @@ |
391 | window_id = kwargs.get('window_id', 0) |
392 | self.close_callback = kwargs.get('close_callback', NO_OP) |
393 | self.backend = None |
394 | - # the following 3 callbacks will be removed as soon as |
395 | - # the backend stop using them |
396 | - self.login_success_callback = NO_OP |
397 | - self.registration_success_callback = NO_OP |
398 | - self.user_cancellation_callback = NO_OP |
399 | - |
400 | self.user_email = None |
401 | self.user_password = None |
402 | |
403 | @@ -726,9 +719,8 @@ |
404 | while Gtk.events_pending(): |
405 | Gtk.main_iteration() |
406 | |
407 | - return_code = LOGIN_SUCCESS |
408 | + return_code = USER_SUCCESS |
409 | if not self._done: |
410 | - self.user_cancellation_callback(self.app_name) |
411 | return_code = USER_CANCELLATION |
412 | logger.info('Return code will be %r.', return_code) |
413 | |
414 | @@ -1081,7 +1073,6 @@ |
415 | @log_call |
416 | def on_email_validated(self, app_name, email, *args, **kwargs): |
417 | """User email was successfully verified.""" |
418 | - self.registration_success_callback(self.app_name, email) |
419 | self.finish_success() |
420 | |
421 | @log_call |
422 | @@ -1097,7 +1088,6 @@ |
423 | @log_call |
424 | def on_logged_in(self, app_name, email, *args, **kwargs): |
425 | """User was successfully logged in.""" |
426 | - self.login_success_callback(self.app_name, email) |
427 | self.finish_success() |
428 | |
429 | @log_call |
430 | |
431 | === modified file 'ubuntu_sso/gtk/tests/test_gui.py' |
432 | --- ubuntu_sso/gtk/tests/test_gui.py 2012-02-09 21:57:15 +0000 |
433 | +++ ubuntu_sso/gtk/tests/test_gui.py 2012-02-10 17:15:32 +0000 |
434 | @@ -2108,7 +2108,7 @@ |
435 | self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL) |
436 | self.ui.on_close_clicked() |
437 | |
438 | - self.assertEqual(self._called, ((gui.REGISTRATION_SUCCESS,), {})) |
439 | + self.assertEqual(self._called, ((gui.USER_SUCCESS,), {})) |
440 | |
441 | def test_on_email_validation_error_proper_callback_is_called(self): |
442 | """On EmailValidationError, USER_CANCELLATION is called.""" |
443 | @@ -2122,7 +2122,7 @@ |
444 | self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL) |
445 | self.ui.on_close_clicked() |
446 | |
447 | - self.assertEqual(self._called, ((gui.LOGIN_SUCCESS,), {})) |
448 | + self.assertEqual(self._called, ((gui.USER_SUCCESS,), {})) |
449 | |
450 | def test_on_login_error_proper_callback_is_called(self): |
451 | """On LoginError, USER_CANCELLATION is called.""" |
452 | @@ -2145,7 +2145,7 @@ |
453 | self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL) |
454 | self.ui.on_close_clicked() |
455 | |
456 | - self.assertEqual(self._called, ((gui.REGISTRATION_SUCCESS,), {})) |
457 | + self.assertEqual(self._called, ((gui.USER_SUCCESS,), {})) |
458 | |
459 | def test_login_success_even_if_prior_login_error(self): |
460 | """Only one callback is called with the final outcome. |
461 | @@ -2160,7 +2160,7 @@ |
462 | self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL) |
463 | self.ui.on_close_clicked() |
464 | |
465 | - self.assertEqual(self._called, ((gui.LOGIN_SUCCESS,), {})) |
466 | + self.assertEqual(self._called, ((gui.USER_SUCCESS,), {})) |
467 | |
468 | def test_user_cancelation_even_if_prior_registration_error(self): |
469 | """Only one callback is called with the final outcome. |
470 | |
471 | === modified file 'ubuntu_sso/main/__init__.py' |
472 | --- ubuntu_sso/main/__init__.py 2012-02-09 21:17:54 +0000 |
473 | +++ ubuntu_sso/main/__init__.py 2012-02-10 17:15:32 +0000 |
474 | @@ -29,14 +29,11 @@ |
475 | from ubuntu_sso.account import Account |
476 | from ubuntu_sso.credentials import ( |
477 | Credentials, |
478 | - DENIAL_CB_KEY, |
479 | - ERROR_CB_KEY, |
480 | HELP_TEXT_KEY, |
481 | PING_URL_KEY, |
482 | - SUCCESS_CB_KEY, |
483 | TC_URL_KEY, |
484 | - UI_CLASS_KEY, |
485 | - UI_MODULE_KEY, |
486 | + UI_EXECUTABLE_KEY, |
487 | + UserCancellationError, |
488 | WINDOW_ID_KEY, |
489 | ) |
490 | from ubuntu_sso.keyring import get_token_name, Keyring |
491 | @@ -302,15 +299,12 @@ |
492 | self.shutdown_func() |
493 | |
494 | valid_keys = (HELP_TEXT_KEY, PING_URL_KEY, TC_URL_KEY, |
495 | - UI_CLASS_KEY, UI_MODULE_KEY, WINDOW_ID_KEY) |
496 | + UI_EXECUTABLE_KEY, WINDOW_ID_KEY) |
497 | |
498 | def _parse_args(self, args): |
499 | """Retrieve values from the generic param 'args'.""" |
500 | result = dict(i for i in args.iteritems() if i[0] in self.valid_keys) |
501 | result[WINDOW_ID_KEY] = int(args.get(WINDOW_ID_KEY, 0)) |
502 | - result[SUCCESS_CB_KEY] = self.CredentialsFound |
503 | - result[ERROR_CB_KEY] = self.CredentialsError |
504 | - result[DENIAL_CB_KEY] = self.AuthorizationDenied |
505 | return result |
506 | |
507 | @log_call(logger.info) |
508 | @@ -403,6 +397,13 @@ |
509 | d.addCallback(_success_cb) |
510 | d.addErrback(_error_cb, app_name) |
511 | |
512 | + def _process_failures(self, failure, app_name): |
513 | + """Process failure returned by the Credentials module.""" |
514 | + if failure.check(UserCancellationError): |
515 | + self.AuthorizationDenied(app_name) |
516 | + else: |
517 | + self.CredentialsError(app_name, failure.value) |
518 | + |
519 | def clear_credentials(self, app_name, args): |
520 | """Clear the credentials for an application. |
521 | |
522 | @@ -439,13 +440,17 @@ |
523 | """Get credentials if found else prompt GUI to register.""" |
524 | self.ref_count += 1 |
525 | obj = Credentials(app_name, **self._parse_args(args)) |
526 | - obj.register() |
527 | + d = obj.register() |
528 | + d.addCallback(lambda creds: self.CredentialsFound(app_name, creds)) |
529 | + d.addErrback(self._process_failures, app_name) |
530 | |
531 | def login(self, app_name, args): |
532 | """Get credentials if found else prompt GUI to login.""" |
533 | self.ref_count += 1 |
534 | obj = Credentials(app_name, **self._parse_args(args)) |
535 | - obj.login() |
536 | + d = obj.login() |
537 | + d.addCallback(lambda creds: self.CredentialsFound(app_name, creds)) |
538 | + d.addErrback(self._process_failures, app_name) |
539 | |
540 | def login_email_password(self, app_name, args): |
541 | """Get credentials if found else try to login. |
542 | @@ -458,7 +463,9 @@ |
543 | email = args.pop('email') |
544 | password = args.pop('password') |
545 | obj = Credentials(app_name, **self._parse_args(args)) |
546 | - obj.login(email=email, password=password) |
547 | + d = obj.login(email=email, password=password) |
548 | + d.addCallback(lambda creds: self.CredentialsFound(app_name, creds)) |
549 | + d.addErrback(self._process_failures, app_name) |
550 | |
551 | |
552 | # pylint: enable=C0103 |
553 | |
554 | === modified file 'ubuntu_sso/main/tests/__init__.py' |
555 | --- ubuntu_sso/main/tests/__init__.py 2012-01-04 20:30:13 +0000 |
556 | +++ ubuntu_sso/main/tests/__init__.py 2012-02-10 17:15:32 +0000 |
557 | @@ -38,9 +38,6 @@ |
558 | class FakedCredentials(object): |
559 | """A very dummy Credentials object.""" |
560 | |
561 | - def __init__(self, *a, **kw): |
562 | - self.login = self.register = lambda *a, **kw: None |
563 | - |
564 | def find_credentials(self, *a, **kw): |
565 | """Retrieve credentials.""" |
566 | return defer.succeed(TOKEN) |
567 | @@ -52,3 +49,11 @@ |
568 | def store_credentials(self, *a, **kw): |
569 | """Store credentials.""" |
570 | return defer.succeed(None) |
571 | + |
572 | + def register(self, *a, **kw): |
573 | + """Register.""" |
574 | + return defer.succeed(TOKEN) |
575 | + |
576 | + def login(self, *a, **kw): |
577 | + """Login.""" |
578 | + return defer.succeed(TOKEN) |
579 | |
580 | === modified file 'ubuntu_sso/main/tests/test_clients.py' |
581 | --- ubuntu_sso/main/tests/test_clients.py 2012-01-11 13:17:10 +0000 |
582 | +++ ubuntu_sso/main/tests/test_clients.py 2012-02-10 17:15:32 +0000 |
583 | @@ -259,9 +259,12 @@ |
584 | class CredentialsManagementProxyTestCase(AbstractTestCase): |
585 | """Tests for the CredentialsManagementProxy DBus interface.""" |
586 | |
587 | + args = dict(foo='bar', fuh='baz') |
588 | + params = (APP_NAME, args) |
589 | + success_signal = 'CredentialsFound' |
590 | error_signal = 'CredentialsError' |
591 | - args = dict(foo='bar', fuh='baz') |
592 | - params = (APP_NAME, args) |
593 | + backend_result = TOKEN |
594 | + success_result = (APP_NAME, backend_result) |
595 | |
596 | @defer.inlineCallbacks |
597 | def setUp(self): |
598 | @@ -285,9 +288,6 @@ |
599 | """Test the find_credentials method.""" |
600 | |
601 | method = 'find_credentials' |
602 | - success_signal = 'CredentialsFound' |
603 | - backend_result = TOKEN |
604 | - success_result = (APP_NAME, backend_result) |
605 | |
606 | @skipIfOS('win32', 'find_credentials_sync is only provided in Linux ' |
607 | 'due to compatibility issues with old clients.') |
608 | @@ -336,35 +336,19 @@ |
609 | success_result = (APP_NAME,) |
610 | |
611 | |
612 | -class CredentialsManagementOpsTestCase(CredentialsManagementProxyTestCase): |
613 | - """Tests for the CredentialsManagementProxy login/register methods.""" |
614 | - |
615 | - success_signal = 'CredentialsFound' |
616 | - success_result = (APP_NAME, TOKEN) |
617 | - |
618 | - def _backend_succeed(self, *args, **kwargs): |
619 | - """Make self.patchable_backend return self.backend_result.""" |
620 | - self.sso_service.cred_manager.CredentialsFound(APP_NAME, TOKEN) |
621 | - |
622 | - def _backend_fail(self, *args, **kwargs): |
623 | - """Make self.patchable_backend fail.""" |
624 | - self.sso_service.cred_manager.CredentialsError(APP_NAME, |
625 | - dict(errtype='ValueError')) |
626 | - |
627 | - |
628 | -class RegisterTestCase(CredentialsManagementOpsTestCase): |
629 | +class RegisterTestCase(CredentialsManagementProxyTestCase): |
630 | """Test the register method.""" |
631 | |
632 | method = 'register' |
633 | |
634 | |
635 | -class LoginOnlyTestCase(CredentialsManagementOpsTestCase): |
636 | +class LoginOnlyTestCase(CredentialsManagementProxyTestCase): |
637 | """Test the login method.""" |
638 | |
639 | method = 'login' |
640 | |
641 | |
642 | -class LoginEmailPasswordTestCase(CredentialsManagementOpsTestCase): |
643 | +class LoginEmailPasswordTestCase(CredentialsManagementProxyTestCase): |
644 | """Test the login method.""" |
645 | |
646 | method = 'login_email_password' |
647 | |
648 | === modified file 'ubuntu_sso/main/tests/test_common.py' |
649 | --- ubuntu_sso/main/tests/test_common.py 2012-02-09 21:17:54 +0000 |
650 | +++ ubuntu_sso/main/tests/test_common.py 2012-02-10 17:15:32 +0000 |
651 | @@ -31,8 +31,7 @@ |
652 | UbuntuSSOService, |
653 | ) |
654 | from ubuntu_sso.main import (HELP_TEXT_KEY, PING_URL_KEY, |
655 | - TC_URL_KEY, UI_CLASS_KEY, UI_MODULE_KEY, WINDOW_ID_KEY, |
656 | - SUCCESS_CB_KEY, ERROR_CB_KEY, DENIAL_CB_KEY) |
657 | + TC_URL_KEY, UI_EXECUTABLE_KEY, WINDOW_ID_KEY) |
658 | from ubuntu_sso.main.tests import FakedCredentials |
659 | from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID, |
660 | CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, PING_URL, TOKEN, |
661 | @@ -540,7 +539,7 @@ |
662 | base_args = { |
663 | HELP_TEXT_KEY: HELP_TEXT, PING_URL_KEY: PING_URL, |
664 | TC_URL_KEY: TC_URL, WINDOW_ID_KEY: WINDOW_ID, |
665 | - UI_CLASS_KEY: 'SuperUI', UI_MODULE_KEY: 'foo.bar.baz', |
666 | + UI_EXECUTABLE_KEY: 'super-ui', |
667 | } |
668 | |
669 | @defer.inlineCallbacks |
670 | @@ -661,14 +660,19 @@ |
671 | |
672 | def test_register(self): |
673 | """Keep proper track of ongoing requests.""" |
674 | - yield self.assert_refcounter_increased('register') |
675 | + yield self.assert_refcounter_increased('register', |
676 | + 'CredentialsFound') |
677 | |
678 | def test_login(self): |
679 | """Keep proper track of ongoing requests.""" |
680 | - yield self.assert_refcounter_increased('login') |
681 | + yield self.assert_refcounter_increased('login', |
682 | + 'CredentialsFound') |
683 | |
684 | def test_several_requests(self): |
685 | """Requests can be nested.""" |
686 | + # by patching the CredentialsFound signal, the counter is not decreased |
687 | + self.patch(self.obj, 'CredentialsFound', lambda *a: None) |
688 | + |
689 | self.obj.login(APP_NAME, self.args) |
690 | self.obj.register(APP_NAME, self.args) |
691 | self.obj.login(APP_NAME, self.args) |
692 | @@ -731,7 +735,6 @@ |
693 | """When ref count reaches 0, queue shutdown op.""" |
694 | self.patch(self.obj, 'timeout_func', self._set_called) |
695 | self.obj.login(APP_NAME, self.args) |
696 | - self.obj.CredentialsFound(APP_NAME, TOKEN) |
697 | |
698 | self.assertEqual(self._called, |
699 | ((TIMEOUT_INTERVAL, self.obj.shutdown), {})) |
700 | @@ -739,6 +742,8 @@ |
701 | def test_on_non_zero_ref_count_do_not_shutdown(self): |
702 | """If ref count is not 0, do not queue shutdown op.""" |
703 | self.patch(self.obj, 'timeout_func', self._set_called) |
704 | + # by patching the CredentialsFound signal, the counter is not decreased |
705 | + self.patch(self.obj, 'CredentialsFound', lambda *a: None) |
706 | self.obj.login(APP_NAME, self.args) |
707 | |
708 | self.assertEqual(self._called, False) |
709 | @@ -748,6 +753,8 @@ |
710 | |
711 | def fake_timeout_func(interval, func): |
712 | """Start a new request when the timer is started.""" |
713 | + # the counter is not decreased |
714 | + self.patch(self.obj, 'CredentialsFound', lambda *a: None) |
715 | self.obj.register(APP_NAME, self.args) |
716 | assert self.obj.ref_count > 0 |
717 | func() |
718 | @@ -756,8 +763,7 @@ |
719 | self.patch(self.obj, 'shutdown_func', self._set_called) |
720 | |
721 | self.obj.login(APP_NAME, self.args) |
722 | - self.obj.CredentialsFound(APP_NAME, TOKEN) |
723 | - # counter reached 0, timeout_func was called |
724 | + # counter reached 0, timeout_func was called and register was called |
725 | |
726 | self.assertEqual(self._called, False, 'shutdown_func was not called') |
727 | |
728 | @@ -773,7 +779,6 @@ |
729 | self.patch(self.obj, 'shutdown_func', self._set_called) |
730 | |
731 | self.obj.login(APP_NAME, self.args) |
732 | - self.obj.CredentialsFound(APP_NAME, TOKEN) |
733 | # counter reached 0, timeout_func was called |
734 | |
735 | self.assertEqual(self._called, ((), {}), 'shutdown_func was called') |
736 | @@ -895,9 +900,6 @@ |
737 | yield super(CredentialsManagementOpsTestCase, self).setUp() |
738 | self.args = dict((k, str(v)) for k, v in self.base_args.iteritems()) |
739 | self.cred_args = self.base_args.copy() |
740 | - self.cred_args[SUCCESS_CB_KEY] = self.obj.CredentialsFound |
741 | - self.cred_args[ERROR_CB_KEY] = self.obj.CredentialsError |
742 | - self.cred_args[DENIAL_CB_KEY] = self.obj.AuthorizationDenied |
743 | |
744 | def test_register(self): |
745 | """The registration is correct.""" |
746 | |
747 | === modified file 'ubuntu_sso/qt/tests/show_gui.py' |
748 | --- ubuntu_sso/qt/tests/show_gui.py 2012-01-26 15:34:16 +0000 |
749 | +++ ubuntu_sso/qt/tests/show_gui.py 2012-02-10 17:15:32 +0000 |
750 | @@ -33,7 +33,7 @@ |
751 | TC_URL_KEY, |
752 | HELP_TEXT_KEY, |
753 | WINDOW_ID_KEY, |
754 | - UI_MODULE_KEY, |
755 | + UI_EXECUTABLE_KEY, |
756 | ) |
757 | |
758 | |
759 | @@ -58,7 +58,7 @@ |
760 | TC_URL_KEY: 'http://www.google.com', |
761 | HELP_TEXT_KEY: 'This is a test.', |
762 | WINDOW_ID_KEY: '0', |
763 | - UI_MODULE_KEY: 'ubuntu_sso.qt.gui', |
764 | + UI_EXECUTABLE_KEY: 'ubuntu-sso-login-gtk', |
765 | }) |
766 | print "called ok" |
767 | yield d |
768 | |
769 | === modified file 'ubuntu_sso/tests/__init__.py' |
770 | --- ubuntu_sso/tests/__init__.py 2012-01-24 22:26:45 +0000 |
771 | +++ ubuntu_sso/tests/__init__.py 2012-02-10 17:15:32 +0000 |
772 | @@ -33,8 +33,7 @@ |
773 | CAPTCHA_SOLUTION = u'william Byrd ñandú' |
774 | EMAIL = u'test@example.com' |
775 | EMAIL_TOKEN = u'B2Pgtf' |
776 | -GTK_GUI_CLASS = 'UbuntuSSOClientGUI' |
777 | -GTK_GUI_MODULE = 'ubuntu_sso.gtk.gui' |
778 | +GTK_GUI_EXE = 'ubuntu-sso-login-gtk' |
779 | HELP_TEXT = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed |
780 | lorem nibh. Suspendisse gravida nulla non nunc suscipit pulvinar tempus ut |
781 | augue. Morbi consequat, ligula a elementum pretium, dolor nulla tempus metus, |
782 | |
783 | === modified file 'ubuntu_sso/tests/test_credentials.py' |
784 | --- ubuntu_sso/tests/test_credentials.py 2012-02-09 21:57:15 +0000 |
785 | +++ ubuntu_sso/tests/test_credentials.py 2012-02-10 17:15:32 +0000 |
786 | @@ -21,12 +21,12 @@ |
787 | from twisted.trial.unittest import TestCase |
788 | from ubuntuone.devtools.handlers import MementoHandler |
789 | |
790 | +import ubuntu_sso.main |
791 | + |
792 | from ubuntu_sso import credentials |
793 | -import ubuntu_sso.main |
794 | -from ubuntu_sso.credentials import (APP_NAME_KEY, HELP_TEXT_KEY, NO_OP, |
795 | - PING_URL_KEY, TC_URL_KEY, UI_CLASS_KEY, UI_MODULE_KEY, WINDOW_ID_KEY, |
796 | - ERROR_KEY, ERROR_DETAIL_KEY) |
797 | -from ubuntu_sso.tests import (APP_NAME, EMAIL, GTK_GUI_CLASS, GTK_GUI_MODULE, |
798 | +from ubuntu_sso.credentials import (APP_NAME_KEY, HELP_TEXT_KEY, |
799 | + PING_URL_KEY, TC_URL_KEY, UI_EXECUTABLE_KEY, WINDOW_ID_KEY) |
800 | +from ubuntu_sso.tests import (APP_NAME, EMAIL, GTK_GUI_EXE, |
801 | HELP_TEXT, PASSWORD, PING_URL, TC_URL, TOKEN, WINDOW_ID) |
802 | |
803 | |
804 | @@ -42,19 +42,18 @@ |
805 | |
806 | KWARGS = { |
807 | APP_NAME_KEY: APP_NAME, |
808 | - TC_URL_KEY: TC_URL, |
809 | HELP_TEXT_KEY: HELP_TEXT, |
810 | WINDOW_ID_KEY: WINDOW_ID, |
811 | PING_URL_KEY: PING_URL, |
812 | - UI_MODULE_KEY: 'ubuntu_sso.tests.test_credentials', |
813 | - UI_CLASS_KEY: 'FakedClientGUI', |
814 | + TC_URL_KEY: TC_URL, |
815 | + UI_EXECUTABLE_KEY: 'foo-bar-baz', |
816 | } |
817 | |
818 | UI_KWARGS = { |
819 | APP_NAME_KEY: APP_NAME, |
820 | + HELP_TEXT_KEY: HELP_TEXT, |
821 | PING_URL_KEY: PING_URL, |
822 | TC_URL_KEY: TC_URL, |
823 | - HELP_TEXT_KEY: HELP_TEXT, |
824 | WINDOW_ID_KEY: WINDOW_ID, |
825 | } |
826 | |
827 | @@ -63,39 +62,17 @@ |
828 | """An error to be used while testing.""" |
829 | |
830 | |
831 | -class FailingClient(object): |
832 | - """Fake a failing client.""" |
833 | - |
834 | - err_msg = 'A failing class.' |
835 | - |
836 | - def __init__(self, *args, **kwargs): |
837 | - raise SampleMiscException(self.err_msg) |
838 | - |
839 | - |
840 | -class FakedClientGUI(object): |
841 | - """Fake a SSO GUI.""" |
842 | - |
843 | - def __init__(self, *args, **kwargs): |
844 | - self.args = args |
845 | - self.kwargs = kwargs |
846 | - self.login_success_callback = None |
847 | - self.registration_success_callback = None |
848 | - self.user_cancellation_callback = None |
849 | - |
850 | - |
851 | class FakedSSOLogin(object): |
852 | """Fake a SSOLogin.""" |
853 | |
854 | - args = [] |
855 | - kwargs = {} |
856 | + proxy = None |
857 | |
858 | def __init__(self, proxy): |
859 | """Nothing.""" |
860 | + FakedSSOLogin.proxy = proxy |
861 | |
862 | def login(self, *args, **kwargs): |
863 | """Fake login.""" |
864 | - self.args = args |
865 | - self.kwargs = kwargs |
866 | |
867 | |
868 | class BasicTestCase(TestCase): |
869 | @@ -125,53 +102,12 @@ |
870 | def setUp(self): |
871 | """Init.""" |
872 | yield super(CredentialsTestCase, self).setUp() |
873 | - self.obj = credentials.Credentials(success_cb=self.success, |
874 | - error_cb=self.error, |
875 | - denial_cb=self.denial, |
876 | - **KWARGS) |
877 | - |
878 | - def success(self, *args, **kwargs): |
879 | - """To be called on success.""" |
880 | - self._set_called('success', *args, **kwargs) |
881 | - |
882 | - def error(self, *args, **kwargs): |
883 | - """To be called on error.""" |
884 | - self._set_called('error', *args, **kwargs) |
885 | - |
886 | - def denial(self, *args, **kwargs): |
887 | - """To be called on credentials denial.""" |
888 | - self._set_called('denial', *args, **kwargs) |
889 | - |
890 | - def assert_error_cb_called(self, msg, detailed_error=None): |
891 | - """Check that self.error_cb was called with proper arguments.""" |
892 | - self.assertEqual(len(self._called), 2) |
893 | - self.assertEqual(self._called[0][0], 'error') |
894 | - self.assertEqual(self._called[0][1], APP_NAME) |
895 | - error_dict = self._called[0][2] |
896 | - self.assertEqual(error_dict[ERROR_KEY], msg) |
897 | - if detailed_error is not None: |
898 | - self.assertIn(str(detailed_error), error_dict[ERROR_DETAIL_KEY]) |
899 | - else: |
900 | - self.assertNotIn(ERROR_DETAIL_KEY, error_dict) |
901 | - self.assertEqual(self._called[1], {}) |
902 | + self.obj = credentials.Credentials(**KWARGS) |
903 | |
904 | |
905 | class CredentialsCallbacksTestCase(CredentialsTestCase): |
906 | """Test suite for the Credentials callbacks.""" |
907 | |
908 | - def test_callbacks_are_stored(self): |
909 | - """Creation callbacks are stored.""" |
910 | - self.assertEqual(self.obj._success_cb, self.success) |
911 | - self.assertEqual(self.obj._error_cb, self.error) |
912 | - self.assertEqual(self.obj.denial_cb, self.denial) |
913 | - |
914 | - def test_callbacks_default_to_no_op(self): |
915 | - """The callbacks are a no-operation if not given.""" |
916 | - self.obj = credentials.Credentials(**KWARGS) |
917 | - self.assertEqual(self.obj._success_cb, NO_OP) |
918 | - self.assertEqual(self.obj._error_cb, NO_OP) |
919 | - self.assertEqual(self.obj.denial_cb, NO_OP) |
920 | - |
921 | def test_creation_parameters_are_stored(self): |
922 | """Creation parameters are stored.""" |
923 | for key, value in KWARGS.iteritems(): |
924 | @@ -209,78 +145,13 @@ |
925 | |
926 | self.assertEqual(getattr(self.obj, PING_URL_KEY), None) |
927 | |
928 | - def test_ui_class_defaults_to_gtk(self): |
929 | + def test_ui_executable_defaults_to_gtk(self): |
930 | """The ui class defaults to gtk.""" |
931 | newkw = KWARGS.copy() |
932 | - newkw.pop(UI_CLASS_KEY) |
933 | - self.obj = credentials.Credentials(**newkw) |
934 | - |
935 | - self.assertEqual(getattr(self.obj, UI_CLASS_KEY), GTK_GUI_CLASS) |
936 | - |
937 | - def test_ui_module_defaults_to_gtk(self): |
938 | - """The ui module defaults to gtk.""" |
939 | - newkw = KWARGS.copy() |
940 | - newkw.pop(UI_MODULE_KEY) |
941 | - self.obj = credentials.Credentials(**newkw) |
942 | - |
943 | - self.assertEqual(getattr(self.obj, UI_MODULE_KEY), GTK_GUI_MODULE) |
944 | - |
945 | - def test_success_cb(self): |
946 | - """Success callback calls the caller.""" |
947 | - self.obj.gui = None |
948 | - self.obj.success_cb(creds=TOKEN) |
949 | - |
950 | - self.assertEqual(self._called, (('success', APP_NAME, TOKEN), {}), |
951 | - 'caller _success_cb was called.') |
952 | - |
953 | - def test_error_cb(self): |
954 | - """Error callback calls the caller.""" |
955 | - error_dict = {'foo': 'bar'} |
956 | - self.obj.error_cb(error_dict=error_dict) |
957 | - |
958 | - self.assertEqual(self._called, (('error', APP_NAME, error_dict), {}), |
959 | - 'caller _error_cb was called.') |
960 | - |
961 | - |
962 | -class CredentialsLoginSuccessTestCase(CredentialsTestCase): |
963 | - """Test suite for the Credentials login success callback.""" |
964 | - |
965 | - @defer.inlineCallbacks |
966 | - def test_cred_not_found(self): |
967 | - """On auth success, if cred not found, self.error_cb is called.""" |
968 | - self.patch(credentials.Keyring, 'get_credentials', |
969 | - lambda kr, app: defer.succeed(None)) |
970 | - |
971 | - result = yield self.obj._login_success_cb(APP_NAME, EMAIL) |
972 | - |
973 | - msg = 'Creds are empty! This should not happen' |
974 | - self.assert_error_cb_called(msg='Problem while retrieving credentials', |
975 | - detailed_error=AssertionError(msg)) |
976 | - self.assertEqual(result, None) |
977 | - |
978 | - @defer.inlineCallbacks |
979 | - def test_cred_error(self): |
980 | - """On auth success, if cred failed, self.error_cb is called.""" |
981 | - expected_error = SampleMiscException() |
982 | - self.patch(credentials.Keyring, 'get_credentials', |
983 | - lambda kr, app: defer.fail(expected_error)) |
984 | - |
985 | - result = yield self.obj._login_success_cb(APP_NAME, EMAIL) |
986 | - |
987 | - msg = 'Problem while retrieving credentials' |
988 | - self.assert_error_cb_called(msg=msg, detailed_error=expected_error) |
989 | - self.assertEqual(result, None) |
990 | - self.assertTrue(self.memento.check_exception(SampleMiscException)) |
991 | - |
992 | - |
993 | -class CredentialsAuthDeniedTestCase(CredentialsTestCase): |
994 | - """Test suite for the Credentials auth denied callback.""" |
995 | - |
996 | - def test_auth_denial_cb(self): |
997 | - """On auth denied, self.denial_cb is called.""" |
998 | - self.obj._auth_denial_cb(app_name=APP_NAME) |
999 | - |
1000 | - self.assertEqual(self._called, (('denial', APP_NAME), {})) |
1001 | + newkw.pop(UI_EXECUTABLE_KEY) |
1002 | + self.obj = credentials.Credentials(**newkw) |
1003 | + |
1004 | + self.assertEqual(getattr(self.obj, UI_EXECUTABLE_KEY), GTK_GUI_EXE) |
1005 | |
1006 | |
1007 | class FindCredentialsTestCase(CredentialsTestCase): |
1008 | @@ -367,89 +238,128 @@ |
1009 | operation = 'register' |
1010 | login_only = False |
1011 | kwargs = {} |
1012 | - inner_class = FakedClientGUI |
1013 | |
1014 | @defer.inlineCallbacks |
1015 | def setUp(self): |
1016 | yield super(RegisterTestCase, self).setUp() |
1017 | - self.inner_kwargs = UI_KWARGS.copy() |
1018 | - self.inner_kwargs['login_only'] = self.login_only |
1019 | + self.deferred = defer.Deferred() |
1020 | + self.patch_inner(self.fake_inner) |
1021 | + self.inner_args = [ |
1022 | + KWARGS[UI_EXECUTABLE_KEY], |
1023 | + '--app_name', APP_NAME, |
1024 | + '--help_text', HELP_TEXT, |
1025 | + '--ping_url', PING_URL, |
1026 | + '--tc_url', TC_URL, |
1027 | + '--window_id', str(WINDOW_ID), |
1028 | + ] |
1029 | + if self.login_only: |
1030 | + self.inner_args.append('--login_only') |
1031 | + |
1032 | + self._next_inner_result = 0 |
1033 | + |
1034 | self.method_call = getattr(self.obj, self.operation) |
1035 | |
1036 | + def patch_inner(self, f): |
1037 | + """Patch the inner call.""" |
1038 | + self.patch(credentials.runner, 'spawn_program', f) |
1039 | + |
1040 | + def fake_inner(self, args): |
1041 | + """Fake the runner.spawn_program.""" |
1042 | + self.deferred.callback(args) |
1043 | + |
1044 | + if self._next_inner_result == credentials.USER_SUCCESS: |
1045 | + # fake that tokens were retrieved from the UI |
1046 | + self.patch(credentials.Keyring, 'get_credentials', |
1047 | + lambda kr, app: defer.succeed(TOKEN)) |
1048 | + |
1049 | + return defer.succeed(self._next_inner_result) |
1050 | + |
1051 | + def fail_inner(self, args): |
1052 | + """Make the inner call fail.""" |
1053 | + return defer.fail(SampleMiscException(args)) |
1054 | + |
1055 | + def assert_exc_msg_logged(self, exception_class, msg): |
1056 | + """Check that 'msg' was logged as part as the logger.exception call.""" |
1057 | + for rec in self.memento.records: |
1058 | + if rec.exc_info and rec.exc_info[0] == exception_class: |
1059 | + self.assertIn(msg, rec.getMessage()) |
1060 | + break |
1061 | + |
1062 | @defer.inlineCallbacks |
1063 | def test_with_existent_token(self): |
1064 | """The operation returns the credentials if already in keyring.""" |
1065 | self.patch(credentials.Keyring, 'get_credentials', |
1066 | lambda kr, app: defer.succeed(TOKEN)) |
1067 | |
1068 | - yield self.method_call(**self.kwargs) |
1069 | + result = yield self.method_call(**self.kwargs) |
1070 | |
1071 | - self.assertEqual(self._called, (('success', APP_NAME, TOKEN), {})) |
1072 | + self.assertEqual(result, TOKEN) |
1073 | + self.assertFalse(self.deferred.called, 'No program was spawnned.') |
1074 | |
1075 | @defer.inlineCallbacks |
1076 | - def test_without_existent_token(self): |
1077 | + def test_without_existent_token_and_return_code_success(self): |
1078 | """The operation returns the credentials gathered by the inner call.""" |
1079 | self.patch(credentials.Keyring, 'get_credentials', |
1080 | lambda kr, app: defer.succeed(None)) |
1081 | - |
1082 | - yield self.method_call(**self.kwargs) |
1083 | - |
1084 | - self.assertEqual(self.obj.inner.kwargs, self.inner_kwargs) |
1085 | + self._next_inner_result = credentials.USER_SUCCESS |
1086 | + |
1087 | + result = yield self.method_call(**self.kwargs) |
1088 | + self.assertEqual(result, TOKEN) |
1089 | + |
1090 | + # the ui was opened and proper params were passed |
1091 | + args = yield self.deferred |
1092 | + self.assertEqual(self.inner_args, args) |
1093 | + |
1094 | + @defer.inlineCallbacks |
1095 | + def test_without_existent_token_and_return_code_cancel(self, exc=None): |
1096 | + """The operation returns exc if defined, else UserCancellationError.""" |
1097 | + self.patch(credentials.Keyring, 'get_credentials', |
1098 | + lambda kr, app: defer.succeed(None)) |
1099 | + self._next_inner_result = credentials.USER_CANCELLATION |
1100 | + |
1101 | + if exc is None: |
1102 | + exc = credentials.UserCancellationError |
1103 | + yield self.assertFailure(self.method_call(**self.kwargs), exc) |
1104 | + |
1105 | + @defer.inlineCallbacks |
1106 | + def test_without_existent_token_and_return_other(self, result=None): |
1107 | + """The operation returns CredentialsError with 'result'.""" |
1108 | + self.patch(credentials.Keyring, 'get_credentials', |
1109 | + lambda kr, app: defer.succeed(None)) |
1110 | + self._next_inner_result = credentials.USER_CANCELLATION * 2 # other |
1111 | + |
1112 | + exc = yield self.assertFailure(self.method_call(**self.kwargs), |
1113 | + credentials.CredentialsError) |
1114 | + if result is None: |
1115 | + result = self._next_inner_result |
1116 | + self.assertEqual(exc.args, (result,)) |
1117 | |
1118 | @defer.inlineCallbacks |
1119 | def test_with_exception_on_credentials(self): |
1120 | """The operation calls the error callback if a exception occurs.""" |
1121 | - expected_error = SampleMiscException() |
1122 | self.patch(credentials.Keyring, 'get_credentials', |
1123 | - lambda kr, app: defer.fail(expected_error)) |
1124 | - |
1125 | - yield self.method_call(**self.kwargs) |
1126 | - |
1127 | - msg = 'Problem while retrieving credentials' |
1128 | - self.assert_error_cb_called(msg=msg, detailed_error=expected_error) |
1129 | + lambda kr, app: defer.fail(SampleMiscException())) |
1130 | + |
1131 | + yield self.assertFailure(self.method_call(**self.kwargs), |
1132 | + SampleMiscException) |
1133 | + |
1134 | self.assertTrue(self.memento.check_exception(SampleMiscException)) |
1135 | + msg = 'Problem while getting credentials from the keyring' |
1136 | + self.assert_exc_msg_logged(SampleMiscException, msg) |
1137 | |
1138 | @defer.inlineCallbacks |
1139 | - def test_with_exception_on_inner_call(self, msg=None): |
1140 | + def test_with_exception_on_inner_call(self): |
1141 | """The operation calls the error callback if a exception occurs.""" |
1142 | self.patch(credentials.Keyring, 'get_credentials', |
1143 | lambda kr, app: defer.succeed(None)) |
1144 | - self.obj.ui_class = 'FailingClient' |
1145 | - |
1146 | - yield self.method_call(**self.kwargs) |
1147 | - |
1148 | - if msg is None: |
1149 | - msg = 'Problem opening the Ubuntu SSO user interface' |
1150 | - self.assert_error_cb_called(msg=msg, |
1151 | - detailed_error=SampleMiscException(FailingClient.err_msg)) |
1152 | - self.assertTrue(self.memento.check_exception(SampleMiscException, |
1153 | - FailingClient.err_msg)) |
1154 | - |
1155 | - @defer.inlineCallbacks |
1156 | - def test_connects_inner_signals(self): |
1157 | - """Inner callbacks are properly connected.""" |
1158 | - self.patch(credentials.Keyring, 'get_credentials', |
1159 | - lambda kr, app: defer.succeed(None)) |
1160 | - yield self.method_call(**self.kwargs) |
1161 | - |
1162 | - self.assertEqual(self.obj.inner.login_success_callback, |
1163 | - self.obj._login_success_cb) |
1164 | - self.assertEqual(self.obj.inner.registration_success_callback, |
1165 | - self.obj._login_success_cb) |
1166 | - self.assertEqual(self.obj.inner.user_cancellation_callback, |
1167 | - self.obj._auth_denial_cb) |
1168 | - |
1169 | - @defer.inlineCallbacks |
1170 | - def test_inner_object_is_created(self): |
1171 | - """The inner object is created and stored.""" |
1172 | - self.patch(credentials.Keyring, 'get_credentials', |
1173 | - lambda kr, app: defer.succeed(None)) |
1174 | - |
1175 | - yield self.method_call(**self.kwargs) |
1176 | - |
1177 | - self.assertIsInstance(self.obj.inner, self.inner_class) |
1178 | - self.assertEqual(self.obj.inner.args, ()) |
1179 | - self.assertEqual(self.obj.inner.kwargs, self.inner_kwargs) |
1180 | + self.patch_inner(self.fail_inner) |
1181 | + |
1182 | + yield self.assertFailure(self.method_call(**self.kwargs), |
1183 | + SampleMiscException) |
1184 | + |
1185 | + self.assertTrue(self.memento.check_exception(SampleMiscException)) |
1186 | + msg = 'Problem while performing %s' % self.operation |
1187 | + self.assert_exc_msg_logged(SampleMiscException, msg) |
1188 | |
1189 | |
1190 | class LoginTestCase(RegisterTestCase): |
1191 | @@ -465,22 +375,46 @@ |
1192 | operation = 'login' |
1193 | login_only = True |
1194 | kwargs = {'email': EMAIL, 'password': PASSWORD} |
1195 | - inner_class = FakedSSOLogin |
1196 | |
1197 | @defer.inlineCallbacks |
1198 | def setUp(self): |
1199 | yield super(LoginEmailPasswordTestCase, self).setUp() |
1200 | - self.inner_kwargs = self.kwargs.copy() |
1201 | - self.inner_kwargs[APP_NAME_KEY] = APP_NAME |
1202 | self.patch(ubuntu_sso.main, 'SSOLogin', FakedSSOLogin) |
1203 | - |
1204 | - def test_with_exception_on_inner_call(self, msg=None): |
1205 | - """The operation calls the error callback if a exception occurs.""" |
1206 | - self.patch(ubuntu_sso.main, 'SSOLogin', FailingClient) |
1207 | - msg = 'Problem logging with email and password.' |
1208 | - return super(LoginEmailPasswordTestCase, |
1209 | - self).test_with_exception_on_inner_call(msg=msg) |
1210 | - |
1211 | - def test_connects_inner_signals(self): |
1212 | - """Inner callbacks are properly connected.""" |
1213 | - # there is no inner callbacks for the SSOLoginRoot object |
1214 | + self.patch_inner(self.fake_inner) |
1215 | + self.inner_args = dict(app_name=APP_NAME, |
1216 | + email=EMAIL, password=PASSWORD, |
1217 | + ping_url=PING_URL) |
1218 | + |
1219 | + def patch_inner(self, f): |
1220 | + """Patch the inner call.""" |
1221 | + self.patch(FakedSSOLogin, 'login', f) |
1222 | + |
1223 | + def fake_inner(self, **kwargs): |
1224 | + """Fake the runner.spawn_program.""" |
1225 | + self.deferred.callback(kwargs) |
1226 | + |
1227 | + if self._next_inner_result == credentials.USER_SUCCESS: |
1228 | + # fake that tokens were retrieved from the UI |
1229 | + self.patch(credentials.Keyring, 'get_credentials', |
1230 | + lambda kr, app: defer.succeed(TOKEN)) |
1231 | + FakedSSOLogin.proxy.LoggedIn(APP_NAME, EMAIL) |
1232 | + elif self._next_inner_result == credentials.USER_CANCELLATION: |
1233 | + FakedSSOLogin.proxy.UserNotValidated(APP_NAME, EMAIL) |
1234 | + else: |
1235 | + FakedSSOLogin.proxy.LoginError(APP_NAME, {'errtype': 'foo'}) |
1236 | + |
1237 | + def fail_inner(self, **kwargs): |
1238 | + """Make the inner call fail.""" |
1239 | + raise SampleMiscException(kwargs) |
1240 | + |
1241 | + def test_without_existent_token_and_return_code_cancel(self, exc=None): |
1242 | + """The operation returns UserNotValidatedError.""" |
1243 | + exc = credentials.UserNotValidatedError |
1244 | + return super(LoginEmailPasswordTestCase, self).\ |
1245 | + test_without_existent_token_and_return_code_cancel(exc=exc) |
1246 | + |
1247 | + def test_without_existent_token_and_return_other(self, result=None): |
1248 | + """The operation returns CredentialsError with 'result'.""" |
1249 | + result = 'foo' |
1250 | + return super(LoginEmailPasswordTestCase, self).\ |
1251 | + test_without_existent_token_and_return_other(result=result) |
1252 | |
1253 | === modified file 'ubuntu_sso/utils/runner/glib.py' |
1254 | --- ubuntu_sso/utils/runner/glib.py 2012-02-10 14:05:09 +0000 |
1255 | +++ ubuntu_sso/utils/runner/glib.py 2012-02-10 17:15:32 +0000 |
1256 | @@ -19,11 +19,6 @@ |
1257 | import os |
1258 | |
1259 | # pylint: disable=E0611,F0401 |
1260 | -try: |
1261 | - from shlex import quote |
1262 | -except ImportError: |
1263 | - from pipes import quote |
1264 | - |
1265 | from gi.repository import GLib |
1266 | # pylint: enable=E0611,F0401 |
1267 | |
1268 | @@ -66,9 +61,6 @@ |
1269 | msg = 'GError is: code %r, message %r' % (gerror.code, gerror.message) |
1270 | error_handler(msg=msg, failed_to_start=failed_to_start) |
1271 | |
1272 | - # escape arguments |
1273 | - args = [quote(a) for a in args] |
1274 | - |
1275 | flags = GLib.SpawnFlags.DO_NOT_REAP_CHILD | \ |
1276 | GLib.SpawnFlags.SEARCH_PATH | \ |
1277 | GLib.SpawnFlags.STDOUT_TO_DEV_NULL | \ |
+1