Merge lp:~nataliabidart/ubuntu-sso-client/stable-3-0-update-2.99.2 into lp:ubuntu-sso-client/stable-3-0

Proposed by Natalia Bidart on 2012-01-17
Status: Merged
Approved by: dobey on 2012-01-17
Approved revision: 821
Merged at revision: 820
Proposed branch: lp:~nataliabidart/ubuntu-sso-client/stable-3-0-update-2.99.2
Merge into: lp:ubuntu-sso-client/stable-3-0
Diff against target: 8787 lines (+3936/-3449)
37 files modified
ubuntu_sso/account.py (+11/-14)
ubuntu_sso/credentials.py (+61/-25)
ubuntu_sso/logger.py (+20/-0)
ubuntu_sso/main/__init__.py (+257/-111)
ubuntu_sso/main/linux.py (+181/-164)
ubuntu_sso/main/tests/__init__.py (+37/-0)
ubuntu_sso/main/tests/test_clients.py (+373/-0)
ubuntu_sso/main/tests/test_common.py (+911/-155)
ubuntu_sso/main/tests/test_linux.py (+0/-1275)
ubuntu_sso/main/tests/test_windows.py (+18/-926)
ubuntu_sso/main/windows.py (+185/-663)
ubuntu_sso/networkstate/__init__.py (+8/-0)
ubuntu_sso/networkstate/linux.py (+24/-0)
ubuntu_sso/networkstate/tests/test_linux.py (+137/-2)
ubuntu_sso/networkstate/tests/test_windows.py (+64/-0)
ubuntu_sso/networkstate/windows.py (+13/-5)
ubuntu_sso/qt/controllers.py (+12/-3)
ubuntu_sso/qt/tests/__init__.py (+7/-4)
ubuntu_sso/qt/tests/login_u_p.py (+37/-29)
ubuntu_sso/qt/tests/show_gui.py (+36/-20)
ubuntu_sso/qt/tests/test_controllers.py (+0/-2)
ubuntu_sso/tests/__init__.py (+41/-0)
ubuntu_sso/tests/test_account.py (+17/-3)
ubuntu_sso/tests/test_credentials.py (+11/-14)
ubuntu_sso/utils/ipc.py (+361/-0)
ubuntu_sso/utils/tests/test_common.py (+5/-1)
ubuntu_sso/utils/tests/test_ipc.py (+505/-0)
ubuntu_sso/utils/tests/test_txsecrets.py (+22/-0)
ubuntu_sso/utils/webclient/common.py (+16/-4)
ubuntu_sso/utils/webclient/gsettings.py (+63/-0)
ubuntu_sso/utils/webclient/libsoup.py (+32/-4)
ubuntu_sso/utils/webclient/qtnetwork.py (+36/-6)
ubuntu_sso/utils/webclient/restful.py (+52/-0)
ubuntu_sso/utils/webclient/tests/test_gsettings.py (+131/-0)
ubuntu_sso/utils/webclient/tests/test_restful.py (+148/-0)
ubuntu_sso/utils/webclient/tests/test_webclient.py (+96/-16)
ubuntu_sso/utils/webclient/txweb.py (+8/-3)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu-sso-client/stable-3-0-update-2.99.2
Reviewer Review Type Date Requested Status
dobey (community) Approve on 2012-01-17
Roberto Alsina (community) 2012-01-17 Approve on 2012-01-17
Review via email: mp+88896@code.launchpad.net

Commit message

[ Alejandro J. Cura <email address hidden> ]
  - An async, proxy-enabled replacement for lazr.restfulclient
  - Fix filter that broke maverick and lucid build; removed demo script that
    gives lint error.
  - Proxy aware webclient with QtNetwork and libsoup backends, and integration
    tests (LP: #884963 LP: #884968 LP: #884970 LP: #884971 LP: #911844).
  - Silence warnings printed by dbus on the test run (LP: #912920).
[ Natalia B. Bidart <email address hidden> ]
  - Remove duplications and unify code for IPC using Twisted's Perspective
    Broker between ubuntu-sso-client and ubuntuone-client (LP: #834730).
[ Diego Sarmentero <email address hidden> ]
  - Fixed: Missing page: No network detected (LP: #804610).

To post a comment you must log in.
Roberto Alsina (ralsina) wrote :

+1

review: Approve
dobey (dobey) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_sso/account.py'
2--- ubuntu_sso/account.py 2011-09-22 14:29:21 +0000
3+++ ubuntu_sso/account.py 2012-01-17 16:51:50 +0000
4@@ -96,15 +96,12 @@
5 class Account(object):
6 """Login and register users using the Ubuntu Single Sign On service."""
7
8- def __init__(self, sso_service_class=None):
9+ def __init__(self, service_url=None):
10 """Create a new SSO Account manager."""
11- if sso_service_class is None:
12- self.sso_service_class = ServiceRoot
13+ if service_url is not None:
14+ self.service_url = service_url
15 else:
16- self.sso_service_class = sso_service_class
17-
18- self.service_url = os.environ.get('USSOC_SERVICE_URL', SERVICE_URL)
19-
20+ self.service_url = os.environ.get('USSOC_SERVICE_URL', SERVICE_URL)
21 logger.info('Created a new SSO access layer for service url %r',
22 self.service_url)
23
24@@ -134,7 +131,7 @@
25 """Generate a captcha using the SSO service."""
26 logger.debug('generate_captcha: requesting captcha, filename: %r',
27 filename)
28- sso_service = self.sso_service_class(None, self.service_url)
29+ sso_service = ServiceRoot(None, self.service_url)
30 captcha = sso_service.captchas.new()
31
32 # download captcha and save to 'filename'
33@@ -156,7 +153,7 @@
34 logger.debug('register_user: email: %r password: <hidden>, '
35 'displayname: %r, captcha_id: %r, captcha_solution: %r',
36 email, displayname, captcha_id, captcha_solution)
37- sso_service = self.sso_service_class(None, self.service_url)
38+ sso_service = ServiceRoot(None, self.service_url)
39 if not self._valid_email(email):
40 logger.error('register_user: InvalidEmailError for email: %r',
41 email)
42@@ -185,7 +182,7 @@
43 logger.debug('login: email: %r password: <hidden>, token_name: %r',
44 email, token_name)
45 basic = BasicHttpAuthorizer(email, password)
46- sso_service = self.sso_service_class(basic, self.service_url)
47+ sso_service = ServiceRoot(basic, self.service_url)
48 service = sso_service.authentications.authenticate
49
50 try:
51@@ -209,7 +206,7 @@
52 token['consumer_key'],
53 token['consumer_secret'],
54 oauth_token)
55- sso_service = self.sso_service_class(authorizer, self.service_url)
56+ sso_service = ServiceRoot(authorizer, self.service_url)
57
58 me_info = sso_service.accounts.me()
59 key = 'preferred_email'
60@@ -232,7 +229,7 @@
61 token['consumer_key'],
62 token['consumer_secret'],
63 oauth_token)
64- sso_service = self.sso_service_class(authorizer, self.service_url)
65+ sso_service = ServiceRoot(authorizer, self.service_url)
66 result = sso_service.accounts.validate_email(email_token=email_token)
67 logger.info('validate_email: email: %r result: %r', email, result)
68 if 'errors' in result:
69@@ -245,7 +242,7 @@
70
71 def request_password_reset_token(self, email):
72 """Request a token to reset the password for the account 'email'."""
73- sso_service = self.sso_service_class(None, self.service_url)
74+ sso_service = ServiceRoot(None, self.service_url)
75 service = sso_service.registrations.request_password_reset_token
76 try:
77 result = service(email=email)
78@@ -266,7 +263,7 @@
79 'request_password_reset_token'.
80
81 """
82- sso_service = self.sso_service_class(None, self.service_url)
83+ sso_service = ServiceRoot(None, self.service_url)
84 service = sso_service.registrations.set_new_password
85 try:
86 result = service(email=email, token=token,
87
88=== modified file 'ubuntu_sso/credentials.py'
89--- ubuntu_sso/credentials.py 2012-01-03 16:04:29 +0000
90+++ ubuntu_sso/credentials.py 2012-01-17 16:51:50 +0000
91@@ -44,7 +44,6 @@
92 from functools import wraps
93
94 from twisted.internet import defer
95-from twisted.internet.defer import inlineCallbacks, returnValue
96
97 from ubuntu_sso import NO_OP, utils
98 from ubuntu_sso.keyring import Keyring
99@@ -105,7 +104,7 @@
100 """Decorate 'f' to catch all errors."""
101
102 @wraps(f)
103- @inlineCallbacks
104+ @defer.inlineCallbacks
105 def inner(self, *a, **kw):
106 """Call 'f' within a try-except block.
107
108@@ -123,7 +122,7 @@
109 error_dict = {ERROR_KEY: msg,
110 ERROR_DETAIL_KEY: traceback.format_exc()}
111 self.error_cb(error_dict)
112- returnValue(result)
113+ defer.returnValue(result)
114
115 return inner
116
117@@ -185,7 +184,7 @@
118 self.inner = None # will hold the GUI or SSOLoginRoot instance
119
120 @handle_failures(msg='Problem while retrieving credentials')
121- @inlineCallbacks
122+ @defer.inlineCallbacks
123 def _login_success_cb(self, app_name, email):
124 """Store credentials when the login/registration succeeded.
125
126@@ -209,7 +208,7 @@
127 return
128
129 self.success_cb(creds)
130- returnValue(0)
131+ defer.returnValue(0)
132
133 def _auth_denial_cb(self, app_name):
134 """The user decided not to allow the registration or login."""
135@@ -265,16 +264,49 @@
136 @handle_exceptions(msg='Problem logging with email and password.')
137 def _do_login(self, email, password):
138 """Login using email/password, connect outcome signals."""
139- from ubuntu_sso.main import SSOLoginRoot
140- self.inner = SSOLoginRoot()
141- self.inner.login(app_name=self.app_name, email=email,
142- password=password,
143- result_cb=self._login_success_cb,
144- error_cb=self._error_cb,
145- not_validated_cb=self._error_cb)
146+ from ubuntu_sso.main import SSOLogin
147+
148+ d = defer.Deferred()
149+
150+ class DummyProxy(object):
151+ """A temporary proxy to handle non-traditional login."""
152+
153+ # pylint: disable=C0103
154+
155+ def LoggedIn(self, app_name, result):
156+ """User was logged in."""
157+ d.callback(result)
158+
159+ def LoginError(self, app_name, error):
160+ """There was an error on login."""
161+ d.errback(error)
162+
163+ def UserNotValidated(self, app_name, email):
164+ """User is not validated."""
165+ d.callback(None)
166+
167+ # pylint: enable=C0103
168+
169+ self.inner = SSOLogin(proxy=DummyProxy())
170+ self.inner.login(app_name=self.app_name,
171+ email=email, password=password)
172+
173+ def _success(result):
174+ """Check if 'result' is a valid token, and callback properly."""
175+ if result is not None:
176+ return self._login_success_cb(self.app_name, email)
177+ else:
178+ error_dict = {
179+ 'errtype': 'UserNotValidated',
180+ 'message': email,
181+ }
182+ self._error_cb(self.app_name, error_dict)
183+
184+ d.addCallback(_success)
185+ d.addErrback(lambda f: self._error_cb(self.app_name, f.value))
186
187 @handle_failures(msg='Problem while retrieving credentials')
188- @inlineCallbacks
189+ @defer.inlineCallbacks
190 def _login_or_register(self, login_only, email=None, password=None):
191 """Get credentials if found else prompt the GUI."""
192 logger.info("_login_or_register: login_only=%r email=%r.",
193@@ -303,20 +335,20 @@
194 logger.debug('Calling success callback at %r.', self._success_cb)
195 self._success_cb(self.app_name, creds)
196
197- @inlineCallbacks
198+ @defer.inlineCallbacks
199 def find_credentials(self):
200 """Get the credentials for 'self.app_name'. Return {} if not there."""
201 creds = yield Keyring().get_credentials(self.app_name)
202 logger.info('find_credentials: self.app_name %r, '
203 'result is {}? %s', self.app_name, creds is None)
204- returnValue(creds if creds is not None else {})
205+ defer.returnValue(creds if creds is not None else {})
206
207- @inlineCallbacks
208+ @defer.inlineCallbacks
209 def clear_credentials(self):
210 """Clear the credentials for 'self.app_name'."""
211 yield Keyring().delete_credentials(self.app_name)
212
213- @inlineCallbacks
214+ @defer.inlineCallbacks
215 def store_credentials(self, token):
216 """Store the credentials for 'self.app_name'."""
217 yield Keyring().set_credentials(self.app_name, token)
218@@ -325,11 +357,15 @@
219 """Get credentials if found else prompt the GUI to register."""
220 return self._login_or_register(login_only=False)
221
222- def login(self):
223- """Get credentials if found else prompt the GUI to login."""
224- return self._login_or_register(login_only=True)
225-
226- def login_email_password(self, email, password):
227- """Get credentials if found else login using email and password."""
228- return self._login_or_register(login_only=True,
229- email=email, password=password)
230+ def login(self, email=None, password=None):
231+ """Get credentials if found else prompt the GUI to login.
232+
233+ if 'email' and 'password' are given, do not prompt the user and use
234+ that to retrieve a token.
235+
236+ """
237+ if email is None or password is None:
238+ return self._login_or_register(login_only=True)
239+ else:
240+ return self._login_or_register(login_only=True,
241+ email=email, password=password)
242
243=== modified file 'ubuntu_sso/logger.py'
244--- ubuntu_sso/logger.py 2011-11-09 21:21:47 +0000
245+++ ubuntu_sso/logger.py 2012-01-17 16:51:50 +0000
246@@ -25,6 +25,7 @@
247 import os
248 import sys
249
250+from functools import wraps
251 from logging.handlers import RotatingFileHandler
252
253 from ubuntu_sso.xdg_base_directory import unicode_path, xdg_cache_home
254@@ -61,3 +62,22 @@
255 logger.addHandler(debug_handler)
256
257 return logger
258+
259+
260+def log_call(log_func):
261+ """Decorator to log, using 'log_func', calls to functions."""
262+
263+ def middle(f):
264+ """Return a function that will act as 'f' but will log the call."""
265+
266+ @wraps(f)
267+ def inner(instance, *a, **kw):
268+ """Call 'f(*a, **kw)' and return its result. Log that call."""
269+ log_func('%r: emitting %r with args %r and kwargs %r',
270+ instance.__class__.__name__, f.__name__, a, kw)
271+ result = f(instance, *a, **kw)
272+ return result
273+
274+ return inner
275+
276+ return middle
277
278=== modified file 'ubuntu_sso/main/__init__.py'
279--- ubuntu_sso/main/__init__.py 2011-12-19 19:35:06 +0000
280+++ ubuntu_sso/main/__init__.py 2012-01-17 16:51:50 +0000
281@@ -1,9 +1,5 @@
282 # -*- coding: utf-8 -*-
283 #
284-# Author: Natalia Bidart <natalia.bidart@canonical.com>
285-# Author: Alejandro J. Cura <alecu@canonical.com>
286-# Author: Manuel de la Pena <manuel@canonical.com>
287-#
288 # Copyright 2011 Canonical Ltd.
289 #
290 # This program is free software: you can redistribute it and/or modify it
291@@ -17,10 +13,18 @@
292 #
293 # You should have received a copy of the GNU General Public License along
294 # with this program. If not, see <http://www.gnu.org/licenses/>.
295-"""Main object implementations."""
296+"""Single Sign On client main module.
297+
298+Provides a utility which accepts requests to the Ubuntu Single Sign On
299+service. The OAuth process is handled, including adding the OAuth access token
300+to the local keyring.
301+
302+"""
303
304 import sys
305
306+from twisted.internet import defer
307+
308 from ubuntu_sso.account import Account
309 from ubuntu_sso.credentials import (
310 Credentials,
311@@ -35,16 +39,30 @@
312 WINDOW_ID_KEY,
313 )
314 from ubuntu_sso.keyring import get_token_name, Keyring
315-from ubuntu_sso.logger import setup_logging
316+from ubuntu_sso.logger import setup_logging, log_call
317
318
319 logger = setup_logging("ubuntu_sso.main")
320 U1_PING_URL = "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/"
321 TIMEOUT_INTERVAL = 10000 # 10 seconds
322
323+# pylint: disable=C0103
324+
325+if sys.platform == 'win32':
326+ from ubuntu_sso.main import windows
327+ source = windows
328+ TIMEOUT_INTERVAL = 10000000000 # forever (hack)
329+else:
330+ from ubuntu_sso.main import linux
331+ source = linux
332+
333+UbuntuSSOProxy = source.UbuntuSSOProxy
334+get_sso_client = source.get_sso_client
335+thread_execute = source.blocking
336+
337
338 def except_to_errdict(e):
339- """Turn an exception into a dictionary to return thru DBus."""
340+ """Turn an exception into a dictionary to return thru IPC."""
341 result = {
342 "errtype": e.__class__.__name__,
343 }
344@@ -58,35 +76,71 @@
345 return result
346
347
348-class SSOLoginRoot(object):
349+class SSOLogin(object):
350 """Login thru the Single Sign On service."""
351
352- def __init__(self, sso_login_processor_class=Account,
353- sso_service_class=None):
354+ def __init__(self, proxy):
355 """Initiate the Login object."""
356- self.sso_login_processor_class = sso_login_processor_class
357- self.processor = self.sso_login_processor_class(
358- sso_service_class=sso_service_class)
359-
360- def generate_captcha(self, app_name, filename, result_cb,
361- error_cb):
362+ self.processor = Account()
363+ self.proxy = proxy
364+
365+ @log_call(logger.debug)
366+ def CaptchaGenerated(self, app_name, result):
367+ """Signal thrown after the captcha is generated."""
368+ self.proxy.CaptchaGenerated(app_name, result)
369+
370+ @log_call(logger.debug)
371+ def CaptchaGenerationError(self, app_name, error):
372+ """Signal thrown when there's a problem generating the captcha."""
373+ error_dict = except_to_errdict(error)
374+ self.proxy.CaptchaGenerationError(app_name, error_dict)
375+
376+ def generate_captcha(self, app_name, filename):
377 """Call the matching method in the processor."""
378 def f():
379 """Inner function that will be run in a thread."""
380 return self.processor.generate_captcha(filename)
381- thread_execute(f, app_name, result_cb, error_cb)
382+ thread_execute(f, app_name,
383+ self.CaptchaGenerated, self.CaptchaGenerationError)
384+
385+ @log_call(logger.debug)
386+ def UserRegistered(self, app_name, result):
387+ """Signal thrown when the user is registered."""
388+ self.proxy.UserRegistered(app_name, result)
389+
390+ @log_call(logger.debug)
391+ def UserRegistrationError(self, app_name, error):
392+ """Signal thrown when there's a problem registering the user."""
393+ error_dict = except_to_errdict(error)
394+ self.proxy.UserRegistrationError(app_name, error_dict)
395
396 def register_user(self, app_name, email, password, name, captcha_id,
397- captcha_solution, result_cb, error_cb):
398+ captcha_solution):
399 """Call the matching method in the processor."""
400 def f():
401 """Inner function that will be run in a thread."""
402 return self.processor.register_user(email, password, name,
403 captcha_id, captcha_solution)
404- thread_execute(f, app_name, result_cb, error_cb)
405-
406- def login(self, app_name, email, password, result_cb,
407- error_cb, not_validated_cb):
408+ thread_execute(f, app_name,
409+ self.UserRegistered, self.UserRegistrationError)
410+
411+ @log_call(logger.debug)
412+ def LoggedIn(self, app_name, result):
413+ """Signal thrown when the user is logged in."""
414+ self.proxy.LoggedIn(app_name, result)
415+
416+ @log_call(logger.debug)
417+ def LoginError(self, app_name, error):
418+ """Signal thrown when there is a problem in the login."""
419+ error_dict = except_to_errdict(error)
420+ self.proxy.LoginError(app_name, error_dict)
421+
422+ @log_call(logger.debug)
423+ def UserNotValidated(self, app_name, email):
424+ """Signal thrown when the user is not validated."""
425+ self.proxy.UserNotValidated(app_name, email)
426+
427+ def login(self, app_name, email, password):
428 """Call the matching method in the processor."""
429 def f():
430 """Inner function that will be run in a thread."""
431@@ -103,18 +157,25 @@
432 is_validated = self.processor.is_validated(credentials)
433 logger.debug('user is validated? %r.', is_validated)
434 if is_validated:
435- # pylint: disable=E1101
436 d = Keyring().set_credentials(app_name, credentials)
437- d.addCallback(lambda _: result_cb(app_name, email))
438- d.addErrback(lambda failure: \
439- error_cb(app_name,
440- except_to_errdict(failure.value)))
441+ d.addCallback(lambda _: self.LoggedIn(app_name, email))
442+ d.addErrback(lambda f: self.LoginError(app_name, f.value))
443 else:
444- not_validated_cb(app_name, email)
445- thread_execute(f, app_name, success_cb, error_cb)
446-
447- def validate_email(self, app_name, email, password, email_token,
448- result_cb, error_cb):
449+ self.UserNotValidated(app_name, email)
450+ thread_execute(f, app_name, success_cb, self.LoginError)
451+
452+ @log_call(logger.debug)
453+ def EmailValidated(self, app_name, result):
454+ """Signal thrown after the email is validated."""
455+ self.proxy.EmailValidated(app_name, result)
456+
457+ @log_call(logger.debug)
458+ def EmailValidationError(self, app_name, error):
459+ """Signal thrown when there's a problem validating the email."""
460+ error_dict = except_to_errdict(error)
461+ self.proxy.EmailValidationError(app_name, error_dict)
462+
463+ def validate_email(self, app_name, email, password, email_token):
464 """Call the matching method in the processor."""
465
466 def f():
467@@ -126,33 +187,54 @@
468
469 def success_cb(app_name, credentials):
470 """Validation finished successfully."""
471- # pylint: disable=E1101
472 d = Keyring().set_credentials(app_name, credentials)
473- d.addCallback(lambda _: result_cb(app_name, email))
474- failure_cb = lambda f: error_cb(app_name, f.value)
475+ d.addCallback(lambda _: self.EmailValidated(app_name, email))
476+ failure_cb = lambda f: self.EmailValidationError(app_name, f.value)
477 d.addErrback(failure_cb)
478
479- thread_execute(f, app_name, success_cb, error_cb)
480-
481- def request_password_reset_token(self, app_name, email,
482- result_cb, error_cb):
483+ thread_execute(f, app_name, success_cb, self.EmailValidationError)
484+
485+ @log_call(logger.debug)
486+ def PasswordResetTokenSent(self, app_name, result):
487+ """Signal thrown when the token is succesfully sent."""
488+ self.proxy.PasswordResetTokenSent(app_name, result)
489+
490+ @log_call(logger.debug)
491+ def PasswordResetError(self, app_name, error):
492+ """Signal thrown when there's a problem sending the token."""
493+ error_dict = except_to_errdict(error)
494+ self.proxy.PasswordResetError(app_name, error_dict)
495+
496+ def request_password_reset_token(self, app_name, email):
497 """Call the matching method in the processor."""
498 def f():
499 """Inner function that will be run in a thread."""
500 return self.processor.request_password_reset_token(email)
501- thread_execute(f, app_name, result_cb, error_cb)
502-
503- def set_new_password(self, app_name, email, token, new_password,
504- result_cb, error_cb):
505+ thread_execute(f, app_name,
506+ self.PasswordResetTokenSent, self.PasswordResetError)
507+
508+ @log_call(logger.debug)
509+ def PasswordChanged(self, app_name, result):
510+ """Signal thrown when the token is succesfully sent."""
511+ self.proxy.PasswordChanged(app_name, result)
512+
513+ @log_call(logger.debug)
514+ def PasswordChangeError(self, app_name, error):
515+ """Signal thrown when there's a problem sending the token."""
516+ error_dict = except_to_errdict(error)
517+ self.proxy.PasswordChangeError(app_name, error_dict)
518+
519+ def set_new_password(self, app_name, email, token, new_password):
520 """Call the matching method in the processor."""
521 def f():
522 """Inner function that will be run in a thread."""
523 return self.processor.set_new_password(email, token,
524 new_password)
525- thread_execute(f, app_name, result_cb, error_cb)
526-
527-
528-class CredentialsManagementRoot(object):
529+ thread_execute(f, app_name,
530+ self.PasswordChanged, self.PasswordChangeError)
531+
532+
533+class CredentialsManagement(object):
534 """Object that manages credentials.
535
536 Every exposed method in this class requires one mandatory argument:
537@@ -178,27 +260,12 @@
538
539 """
540
541- def __init__(self, timeout_func, shutdown_func, found_cb, error_cb,
542- denied_cb, *args, **kwargs):
543- """Create a new instance.
544-
545- - 'found_cb' is a callback that will be executed when the credentials
546- were found.
547-
548- - 'error_cb' is a callback that will be executed when there was an
549- error getting the credentials.
550-
551- - 'denied_cb' is a callback that will be executed when the user denied
552- the use of the crendetials.
553-
554- """
555- super(CredentialsManagementRoot, self).__init__(*args, **kwargs)
556+ def __init__(self, timeout_func, shutdown_func, proxy):
557+ super(CredentialsManagement, self).__init__()
558 self._ref_count = 0
559 self.timeout_func = timeout_func
560 self.shutdown_func = shutdown_func
561- self.found_cb = found_cb
562- self.error_cb = error_cb
563- self.denied_cb = denied_cb
564+ self.proxy = proxy
565
566 def _get_ref_count(self):
567 """Get value of ref_count."""
568@@ -236,12 +303,54 @@
569 """Retrieve values from the generic param 'args'."""
570 result = dict(i for i in args.iteritems() if i[0] in self.valid_keys)
571 result[WINDOW_ID_KEY] = int(args.get(WINDOW_ID_KEY, 0))
572- result[SUCCESS_CB_KEY] = self.found_cb
573- result[ERROR_CB_KEY] = self.error_cb
574- result[DENIAL_CB_KEY] = self.denied_cb
575+ result[SUCCESS_CB_KEY] = self.CredentialsFound
576+ result[ERROR_CB_KEY] = self.CredentialsError
577+ result[DENIAL_CB_KEY] = self.AuthorizationDenied
578 return result
579
580- def find_credentials(self, app_name, args, success_cb, error_cb):
581+ @log_call(logger.info)
582+ def AuthorizationDenied(self, app_name):
583+ """Signal thrown when the user denies the authorization."""
584+ self.ref_count -= 1
585+ self.proxy.AuthorizationDenied(app_name)
586+
587+ # do not use log_call decorator since we should not log credentials
588+ def CredentialsFound(self, app_name, credentials):
589+ """Signal thrown when the credentials are found."""
590+ self.ref_count -= 1
591+ logger.info('%s: emitting CredentialsFound with app_name "%s".',
592+ self.__class__.__name__, app_name)
593+ self.proxy.CredentialsFound(app_name, credentials)
594+
595+ @log_call(logger.info)
596+ def CredentialsNotFound(self, app_name):
597+ """Signal thrown when the credentials are not found."""
598+ self.ref_count -= 1
599+ self.proxy.CredentialsNotFound(app_name)
600+
601+ @log_call(logger.info)
602+ def CredentialsCleared(self, app_name):
603+ """Signal thrown when the credentials were cleared."""
604+ self.ref_count -= 1
605+ self.proxy.CredentialsCleared(app_name)
606+
607+ @log_call(logger.info)
608+ def CredentialsStored(self, app_name):
609+ """Signal thrown when the credentials were cleared."""
610+ self.ref_count -= 1
611+ self.proxy.CredentialsStored(app_name)
612+
613+ @log_call(logger.error)
614+ def CredentialsError(self, app_name, error):
615+ """Signal thrown when there is a problem getting the credentials."""
616+ self.ref_count -= 1
617+ if isinstance(error, dict):
618+ error_dict = error
619+ else:
620+ error_dict = except_to_errdict(error)
621+ self.proxy.CredentialsError(app_name, error_dict)
622+
623+ def find_credentials(self, app_name, args, success_cb=None, error_cb=None):
624 """Look for the credentials for an application.
625
626 - 'app_name': the name of the application which credentials are
627@@ -249,21 +358,47 @@
628
629 - 'args' is a dictionary, currently not used.
630
631- - 'success_cb' is a callback that will be execute if the operation was
632+ - 'success_cb', if not None, will be executed if the operation was
633 a success.
634
635- - 'error_cb' is a callback that will be executed if the operation had
636+ - 'error_cb', if not None, will be executed if the operation had
637 an error.
638
639 """
640+ def _analize_creds(credentials):
641+ """Find credentials and notify using signals."""
642+ if credentials is not None and len(credentials) > 0:
643+ self.CredentialsFound(app_name, credentials)
644+ else:
645+ self.CredentialsNotFound(app_name)
646+
647+ def _tweaked_success_cb(creds):
648+ """Decrease ref counter and call 'success_cb'."""
649+ self.ref_count -= 1
650+ success_cb(creds)
651+
652+ if success_cb is None:
653+ _success_cb = _analize_creds
654+ else:
655+ _success_cb = _tweaked_success_cb
656+
657+ def _tweaked_error_cb(error, app):
658+ """Decrease ref counter and call 'error_cb', modifying the dict."""
659+ self.ref_count -= 1
660+ error_cb(except_to_errdict(error.value))
661+
662+ if error_cb is None:
663+ _error_cb = lambda f, _: self.CredentialsError(app_name, f.value)
664+ else:
665+ _error_cb = _tweaked_error_cb
666+
667 self.ref_count += 1
668 obj = Credentials(app_name)
669 d = obj.find_credentials()
670- # pylint: disable=E1101
671- d.addCallback(success_cb)
672- d.addErrback(error_cb, app_name)
673+ d.addCallback(_success_cb)
674+ d.addErrback(_error_cb, app_name)
675
676- def clear_credentials(self, app_name, args, success_cb, error_cb):
677+ def clear_credentials(self, app_name, args):
678 """Clear the credentials for an application.
679
680 - 'app_name': the name of the application which credentials are
681@@ -271,21 +406,14 @@
682
683 - 'args' is a dictionary, currently not used.
684
685- - 'success_cb' is a callback that will be execute if the operation was
686- a success.
687-
688- - 'error_cb' is a callback that will be executed if the operation had
689- an error.
690-
691 """
692 self.ref_count += 1
693 obj = Credentials(app_name)
694 d = obj.clear_credentials()
695- # pylint: disable=E1101
696- d.addCallback(success_cb)
697- d.addErrback(error_cb, app_name)
698+ d.addCallback(lambda _: self.CredentialsCleared(app_name))
699+ d.addErrback(lambda f: self.CredentialsError(app_name, f.value))
700
701- def store_credentials(self, app_name, args, success_cb, error_cb):
702+ def store_credentials(self, app_name, args):
703 """Store the token for an application.
704
705 - 'app_name': the name of the application which credentials are
706@@ -295,18 +423,12 @@
707 the following mandatory keys: 'token', 'token_key', 'consumer_key',
708 'consumer_secret'.
709
710- - 'success_cb' is a callback that will be execute if the operation was
711- a success.
712-
713- - 'error_cb' is a callback that will be executed if the operation had
714- an error.
715 """
716 self.ref_count += 1
717 obj = Credentials(app_name)
718 d = obj.store_credentials(args)
719- # pylint: disable=E1101
720- d.addCallback(success_cb)
721- d.addErrback(error_cb, app_name)
722+ d.addCallback(lambda _: self.CredentialsStored(app_name))
723+ d.addErrback(lambda f: self.CredentialsError(app_name, f.value))
724
725 def register(self, app_name, args):
726 """Get credentials if found else prompt GUI to register."""
727@@ -331,23 +453,47 @@
728 email = args.pop('email')
729 password = args.pop('password')
730 obj = Credentials(app_name, **self._parse_args(args))
731- obj.login_email_password(email=email, password=password)
732-
733-
734-# pylint: disable=C0103
735-
736-if sys.platform == 'win32':
737- from ubuntu_sso.main import windows
738- source = windows
739- TIMEOUT_INTERVAL = 10000000000 # forever
740-else:
741- from ubuntu_sso.main import linux
742- source = linux
743-
744-CredentialsManagement = source.CredentialsManagement
745-get_sso_login_backend = source.get_sso_login_backend
746-main = source.main
747-SSOLogin = source.SSOLogin
748-thread_execute = source.blocking
749+ obj.login(email=email, password=password)
750+
751
752 # pylint: enable=C0103
753+
754+class UbuntuSSOService(object):
755+ """Manager that exposes the diff referenceable objects."""
756+
757+ def __init__(self):
758+ self.proxy = UbuntuSSOProxy(self)
759+ self.sso_login = None
760+ self.cred_manager = None
761+
762+ @defer.inlineCallbacks
763+ def start(self):
764+ """Start the service."""
765+ logger.debug('Starting up Ubuntu SSO service...')
766+ try:
767+ yield self.proxy.start()
768+ except:
769+ logger.exception('Can not start Ubuntu SSO service:')
770+ raise
771+ else:
772+ logger.info('Ubuntu SSO service started.')
773+
774+ self.sso_login = SSOLogin(proxy=self.proxy.sso_login)
775+ self.cred_manager = CredentialsManagement(
776+ timeout_func=source.timeout_func,
777+ shutdown_func=source.shutdown_func,
778+ proxy=self.proxy.cred_manager)
779+
780+ def shutdown(self):
781+ """Shutdown the service."""
782+ return self.proxy.shutdown()
783+
784+
785+def main():
786+ """Run the backend service."""
787+ logger.info('Setting up Ubuntu SSO service.')
788+ source.start_setup()
789+ service = UbuntuSSOService()
790+ d = service.start()
791+ d.addBoth(source.finish_setup)
792+ source.main()
793
794=== modified file 'ubuntu_sso/main/linux.py'
795--- ubuntu_sso/main/linux.py 2011-12-19 19:35:06 +0000
796+++ ubuntu_sso/main/linux.py 2012-01-17 16:51:50 +0000
797@@ -13,23 +13,26 @@
798 #
799 # You should have received a copy of the GNU General Public License along
800 # with this program. If not, see <http://www.gnu.org/licenses/>.
801-"""Single Sign On login handler.
802-
803-An utility which accepts requests for Ubuntu Single Sign On login over D-Bus.
804-
805-The OAuth process is handled, including adding the OAuth access token to the
806-local keyring.
807+"""Main module implementation specific for linux.
808+
809+This module should never import from the multiplatform one (main/__init__.py),
810+but the other way around. Likewise, this module should *not* have any logic
811+regarding error processing or decision making about when to send a given
812+signal.
813+
814+Also, most of the logging is being made in the main module to avoid
815+duplication between the different platform implementations.
816
817 """
818
819 import threading
820 import signal
821-import sys
822
823 import dbus.mainloop.glib
824 import dbus.service
825 import gtk
826
827+from twisted.internet import defer
828
829 from ubuntu_sso import (
830 DBUS_ACCOUNT_PATH,
831@@ -39,13 +42,7 @@
832 DBUS_IFACE_USER_NAME,
833 NO_OP,
834 )
835-from ubuntu_sso.account import Account
836 from ubuntu_sso.logger import setup_logging
837-from ubuntu_sso.main import (
838- CredentialsManagementRoot,
839- SSOLoginRoot,
840- except_to_errdict,
841-)
842
843
844 # Disable the invalid name warning, as we have a lot of DBus style names
845@@ -62,162 +59,124 @@
846 try:
847 result_cb(app_name, f())
848 except Exception, e: # pylint: disable=W0703
849- msg = "Exception while running DBus blocking code in a thread:"
850+ msg = "Exception while running blocking code in a thread:"
851 logger.exception(msg)
852- error_cb(app_name, except_to_errdict(e))
853+ error_cb(app_name, e)
854 threading.Thread(target=_in_thread).start()
855
856
857-class SSOLogin(dbus.service.Object):
858+class SSOLoginProxy(dbus.service.Object):
859 """Login thru the Single Sign On service."""
860
861 # Operator not preceded by a space (fails with dbus decorators)
862 # pylint: disable=C0322
863
864- def __init__(self, bus_name, object_path=DBUS_ACCOUNT_PATH,
865- sso_login_processor_class=Account,
866- sso_service_class=None):
867+ def __init__(self, root, *args, **kwargs):
868 """Initiate the Login object."""
869- dbus.service.Object.__init__(self, object_path=object_path,
870- bus_name=bus_name)
871- self.root = SSOLoginRoot(sso_login_processor_class, sso_service_class)
872+ super(SSOLoginProxy, self).__init__(*args, **kwargs)
873+ self.root = root
874
875 # generate_capcha signals
876 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
877 def CaptchaGenerated(self, app_name, result):
878 """Signal thrown after the captcha is generated."""
879- logger.debug('SSOLogin: emitting CaptchaGenerated with app_name "%s" '
880- 'and result %r', app_name, result)
881
882 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
883 def CaptchaGenerationError(self, app_name, error):
884 """Signal thrown when there's a problem generating the captcha."""
885- logger.debug('SSOLogin: emitting CaptchaGenerationError with '
886- 'app_name "%s" and error %r', app_name, error)
887
888 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
889 in_signature='ss')
890 def generate_captcha(self, app_name, filename):
891 """Call the matching method in the processor."""
892- self.root.generate_captcha(app_name, filename,
893- self.CaptchaGenerated,
894- self.CaptchaGenerationError)
895+ self.root.sso_login.generate_captcha(app_name, filename)
896
897 # register_user signals
898 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
899 def UserRegistered(self, app_name, result):
900 """Signal thrown when the user is registered."""
901- logger.debug('SSOLogin: emitting UserRegistered with app_name "%s" '
902- 'and result %r', app_name, result)
903
904 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
905 def UserRegistrationError(self, app_name, error):
906 """Signal thrown when there's a problem registering the user."""
907- logger.debug('SSOLogin: emitting UserRegistrationError with '
908- 'app_name "%s" and error %r', app_name, error)
909
910 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
911 in_signature='ssssss')
912 def register_user(self, app_name, email, password, name,
913 captcha_id, captcha_solution):
914 """Call the matching method in the processor."""
915- self.root.register_user(app_name, email, password, name, captcha_id,
916- captcha_solution,
917- self.UserRegistered,
918- self.UserRegistrationError)
919+ self.root.sso_login.register_user(app_name, email, password, name,
920+ captcha_id, captcha_solution)
921
922 # login signals
923 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
924 def LoggedIn(self, app_name, result):
925 """Signal thrown when the user is logged in."""
926- logger.debug('SSOLogin: emitting LoggedIn with app_name "%s" '
927- 'and result %r', app_name, result)
928
929 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
930 def LoginError(self, app_name, error):
931 """Signal thrown when there is a problem in the login."""
932- logger.debug('SSOLogin: emitting LoginError with '
933- 'app_name "%s" and error %r', app_name, error)
934
935 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
936 def UserNotValidated(self, app_name, result):
937 """Signal thrown when the user is not validated."""
938- logger.debug('SSOLogin: emitting UserNotValidated with app_name "%s" '
939- 'and result %r', app_name, result)
940
941 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
942 in_signature='sss')
943 def login(self, app_name, email, password):
944 """Call the matching method in the processor."""
945- self.root.login(app_name, email, password, self.LoggedIn,
946- self.LoginError, self.UserNotValidated)
947+ self.root.sso_login.login(app_name, email, password)
948
949 # validate_email signals
950 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
951 def EmailValidated(self, app_name, result):
952 """Signal thrown after the email is validated."""
953- logger.debug('SSOLogin: emitting EmailValidated with app_name "%s" '
954- 'and result %r', app_name, result)
955
956 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
957 def EmailValidationError(self, app_name, error):
958 """Signal thrown when there's a problem validating the email."""
959- logger.debug('SSOLogin: emitting EmailValidationError with '
960- 'app_name "%s" and error %r', app_name, error)
961
962 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
963 in_signature='ssss')
964 def validate_email(self, app_name, email, password, email_token):
965 """Call the matching method in the processor."""
966- self.root.validate_email(app_name, email, password, email_token,
967- self.EmailValidated,
968- self.EmailValidationError)
969+ self.root.sso_login.validate_email(app_name,
970+ email, password, email_token)
971
972 # request_password_reset_token signals
973 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
974 def PasswordResetTokenSent(self, app_name, result):
975 """Signal thrown when the token is succesfully sent."""
976- logger.debug('SSOLogin: emitting PasswordResetTokenSent with app_name '
977- '"%s" and result %r', app_name, result)
978
979 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
980 def PasswordResetError(self, app_name, error):
981 """Signal thrown when there's a problem sending the token."""
982- logger.debug('SSOLogin: emitting PasswordResetError with '
983- 'app_name "%s" and error %r', app_name, error)
984
985 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
986 in_signature='ss')
987 def request_password_reset_token(self, app_name, email):
988 """Call the matching method in the processor."""
989- self.root.request_password_reset_token(app_name, email,
990- self.PasswordResetTokenSent,
991- self.PasswordResetError)
992+ self.root.sso_login.request_password_reset_token(app_name, email)
993
994 # set_new_password signals
995 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
996 def PasswordChanged(self, app_name, result):
997 """Signal thrown when the token is succesfully sent."""
998- logger.debug('SSOLogin: emitting PasswordChanged with app_name "%s" '
999- 'and result %r', app_name, result)
1000
1001 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
1002 def PasswordChangeError(self, app_name, error):
1003 """Signal thrown when there's a problem sending the token."""
1004- logger.debug('SSOLogin: emitting PasswordChangeError with '
1005- 'app_name "%s" and error %r', app_name, error)
1006
1007 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
1008 in_signature='ssss')
1009 def set_new_password(self, app_name, email, token, new_password):
1010 """Call the matching method in the processor."""
1011- self.root.set_new_password(app_name, email, token, new_password,
1012- self.PasswordChanged,
1013- self.PasswordChangeError)
1014-
1015-
1016-class CredentialsManagement(dbus.service.Object):
1017- """DBus object that manages credentials.
1018+ self.root.sso_login.set_new_password(app_name,
1019+ email, token, new_password)
1020+
1021+
1022+class CredentialsManagementProxy(dbus.service.Object):
1023+ """Object that manages credentials.
1024
1025 Every exposed method in this class requires one mandatory argument:
1026
1027@@ -242,67 +201,36 @@
1028
1029 """
1030
1031- def __init__(self, timeout_func, shutdown_func, *args, **kwargs):
1032- super(CredentialsManagement, self).__init__(*args, **kwargs)
1033- self.root = CredentialsManagementRoot(timeout_func, shutdown_func,
1034- self.CredentialsFound,
1035- self.CredentialsError,
1036- self.AuthorizationDenied)
1037+ def __init__(self, root, *args, **kwargs):
1038+ super(CredentialsManagementProxy, self).__init__(*args, **kwargs)
1039+ self.root = root
1040
1041 # Operator not preceded by a space (fails with dbus decorators)
1042 # pylint: disable=C0322
1043
1044- def _process_failure(self, failure, app_name):
1045- """Process the 'failure' and emit CredentialsError."""
1046- self.CredentialsError(app_name, except_to_errdict(failure.value))
1047-
1048- def shutdown(self):
1049- """If no ongoing requests, call self.shutdown_func."""
1050- logger.debug('shutdown!, ref_count is %r.', self.root.ref_count)
1051- self.root.shutdown()
1052-
1053 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s')
1054 def AuthorizationDenied(self, app_name):
1055 """Signal thrown when the user denies the authorization."""
1056- self.root.ref_count -= 1
1057- logger.info('%s: emitting AuthorizationDenied with app_name "%s".',
1058- self.__class__.__name__, app_name)
1059
1060 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='sa{ss}')
1061 def CredentialsFound(self, app_name, credentials):
1062 """Signal thrown when the credentials are found."""
1063- self.root.ref_count -= 1
1064- logger.info('%s: emitting CredentialsFound with app_name "%s".',
1065- self.__class__.__name__, app_name)
1066
1067 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s')
1068 def CredentialsNotFound(self, app_name):
1069 """Signal thrown when the credentials are not found."""
1070- self.root.ref_count -= 1
1071- logger.info('%s: emitting CredentialsNotFound with app_name "%s".',
1072- self.__class__.__name__, app_name)
1073
1074 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s')
1075 def CredentialsCleared(self, app_name):
1076 """Signal thrown when the credentials were cleared."""
1077- self.root.ref_count -= 1
1078- logger.info('%s: emitting CredentialsCleared with app_name "%s".',
1079- self.__class__.__name__, app_name)
1080
1081 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s')
1082 def CredentialsStored(self, app_name):
1083 """Signal thrown when the credentials were cleared."""
1084- self.root.ref_count -= 1
1085- logger.info('%s: emitting CredentialsStored with app_name "%s".',
1086- self.__class__.__name__, app_name)
1087
1088 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='sa{ss}')
1089 def CredentialsError(self, app_name, error_dict):
1090 """Signal thrown when there is a problem getting the credentials."""
1091- self.root.ref_count -= 1
1092- logger.error('%s: emitting CredentialsError with app_name "%s" and '
1093- 'error_dict %r.', self.__class__.__name__, app_name,
1094- error_dict)
1095
1096 @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
1097 in_signature='sa{ss}', out_signature='')
1098@@ -315,16 +243,7 @@
1099 - 'args' is a dictionary, currently not used.
1100
1101 """
1102-
1103- def success_cb(credentials):
1104- """Find credentials and notify using signals."""
1105- if credentials is not None and len(credentials) > 0:
1106- self.CredentialsFound(app_name, credentials)
1107- else:
1108- self.CredentialsNotFound(app_name)
1109-
1110- self.root.find_credentials(app_name, args, success_cb,
1111- self._process_failure)
1112+ self.root.cred_manager.find_credentials(app_name, args)
1113
1114 @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
1115 in_signature="sa{ss}", out_signature="a{ss}",
1116@@ -337,20 +256,13 @@
1117
1118 """
1119
1120- def decrease_counter_success(credentials):
1121- """Call 'reply_handler' and decrease the root ref counter."""
1122- reply_handler(credentials)
1123- self.root.ref_count -= 1
1124-
1125- def decrease_counter_error(failure, app_name):
1126- """Call 'error_handler' and decrease the root ref counter."""
1127- error_dict = except_to_errdict(failure.value)
1128- error_handler(dbus.service.DBusException(error_dict))
1129- self.root.ref_count -= 1
1130-
1131- self.root.find_credentials(app_name, args,
1132- success_cb=decrease_counter_success,
1133- error_cb=decrease_counter_error)
1134+ def _drop_dict(error_dict):
1135+ """Call 'error_handler' properly."""
1136+ error_handler(dbus.service.DBusException(error_dict['errtype']))
1137+
1138+ self.root.cred_manager.find_credentials(app_name, args,
1139+ success_cb=reply_handler,
1140+ error_cb=_drop_dict)
1141
1142 @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
1143 in_signature='sa{ss}', out_signature='')
1144@@ -363,9 +275,7 @@
1145 - 'args' is a dictionary, currently not used.
1146
1147 """
1148- self.root.clear_credentials(app_name, args,
1149- lambda _: self.CredentialsCleared(app_name),
1150- self._process_failure)
1151+ self.root.cred_manager.clear_credentials(app_name, args)
1152
1153 @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
1154 in_signature='sa{ss}', out_signature='')
1155@@ -380,21 +290,19 @@
1156 'consumer_secret'.
1157
1158 """
1159- self.root.store_credentials(app_name, args,
1160- lambda _: self.CredentialsStored(app_name),
1161- self._process_failure)
1162+ self.root.cred_manager.store_credentials(app_name, args)
1163
1164 @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
1165 in_signature='sa{ss}', out_signature='')
1166 def register(self, app_name, args):
1167 """Get credentials if found else prompt GUI to register."""
1168- self.root.register(app_name, args)
1169+ self.root.cred_manager.register(app_name, args)
1170
1171 @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
1172 in_signature='sa{ss}', out_signature='')
1173 def login(self, app_name, args):
1174 """Get credentials if found else prompt GUI to login."""
1175- self.root.login(app_name, args)
1176+ self.root.cred_manager.login(app_name, args)
1177
1178 @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE,
1179 in_signature='sa{ss}', out_signature='')
1180@@ -406,12 +314,121 @@
1181 returned trough the CredentialsFound signal.
1182
1183 """
1184- self.root.login_email_password(app_name, args)
1185-
1186-
1187-def get_sso_login_backend():
1188- """Get the backend for the Login service."""
1189- raise NotImplementedError()
1190+ self.root.cred_manager.login_email_password(app_name, args)
1191+
1192+
1193+class UbuntuSSOProxy(object):
1194+ """Object that exposes the diff referenceable objects."""
1195+
1196+ def __init__(self, root):
1197+ self.root = root
1198+ self.bus = dbus.SessionBus()
1199+ self.sso_login = None
1200+ self.cred_manager = None
1201+
1202+ def start(self):
1203+ """Start listening, nothing async to be done in this platform."""
1204+ # Register DBus service for making sure we run only one instance
1205+ name = self.bus.request_name(DBUS_BUS_NAME,
1206+ dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
1207+ if name == dbus.bus.REQUEST_NAME_REPLY_EXISTS:
1208+ raise AlreadyStartedError()
1209+
1210+ bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus=self.bus)
1211+ self.sso_login = SSOLoginProxy(self.root,
1212+ bus_name=bus_name,
1213+ object_path=DBUS_ACCOUNT_PATH)
1214+ self.cred_manager = CredentialsManagementProxy(self.root,
1215+ bus_name=bus_name,
1216+ object_path=DBUS_CREDENTIALS_PATH)
1217+
1218+ return defer.succeed(None)
1219+
1220+ def shutdown(self):
1221+ """Shutdown the service."""
1222+ self.sso_login.remove_from_connection()
1223+ self.cred_manager.remove_from_connection()
1224+ self.bus.release_name(DBUS_BUS_NAME)
1225+ return defer.succeed(None)
1226+
1227+
1228+# ============================== client classes ==============================
1229+
1230+
1231+class RemoteClient(object):
1232+ """Client that can perform calls to remote DBus object."""
1233+
1234+ bus_name = None
1235+ path = None
1236+ interface = None
1237+
1238+ def __init__(self):
1239+ self.bus = dbus.SessionBus()
1240+ obj = self.bus.get_object(bus_name=self.bus_name,
1241+ object_path=self.path,
1242+ follow_name_owner_changes=True)
1243+ self.dbus_iface = dbus.Interface(obj, dbus_interface=self.interface)
1244+ self.dbus_iface.call_method = self.call_method
1245+ self.dbus_iface.disconnect_from_signal = lambda _, sig: sig.remove()
1246+
1247+ def call_method(self, method_name, *args, **kwargs):
1248+ """Call asynchronously 'method_name(*args)'.
1249+
1250+ Return a deferred that will be fired when the call finishes.
1251+
1252+ """
1253+ d = defer.Deferred()
1254+
1255+ reply_handler = kwargs.get('reply_handler', None)
1256+ if reply_handler is not None:
1257+ d.addCallback(lambda a: reply_handler(*a))
1258+
1259+ error_handler = kwargs.get('error_handler', None)
1260+ if error_handler is not None:
1261+ d.addErrback(lambda f: error_handler(f.value))
1262+
1263+ self.bus.call_async(
1264+ bus_name=self.bus_name, object_path=self.path,
1265+ dbus_interface=self.interface, method=method_name,
1266+ signature=None, args=args,
1267+ reply_handler=lambda *a: d.callback(a),
1268+ error_handler=d.errback)
1269+
1270+ return d
1271+
1272+
1273+class SSOLoginClient(RemoteClient):
1274+ """Access the UserManagement DBus interface."""
1275+
1276+ bus_name = DBUS_BUS_NAME
1277+ path = DBUS_ACCOUNT_PATH
1278+ interface = DBUS_IFACE_USER_NAME
1279+
1280+
1281+class CredentialsManagementClient(RemoteClient):
1282+ """Access the CredentialsManagement DBus interface."""
1283+
1284+ bus_name = DBUS_BUS_NAME
1285+ path = DBUS_CREDENTIALS_PATH
1286+ interface = DBUS_CREDENTIALS_IFACE
1287+
1288+
1289+class UbuntuSSOClient(object):
1290+ """Base client that provides remote access to the sso API."""
1291+
1292+ def __init__(self):
1293+ self.sso_login = SSOLoginClient().dbus_iface
1294+ self.cred_manager = CredentialsManagementClient().dbus_iface
1295+
1296+ def disconnect(self):
1297+ """No need to disconnect DBus proxy objects."""
1298+ return defer.succeed(None)
1299+
1300+
1301+def get_sso_client():
1302+ """Get a client to access the SSO service."""
1303+ result = UbuntuSSOClient()
1304+ return defer.succeed(result)
1305
1306
1307 def sighup_handler(*a, **kw):
1308@@ -422,32 +439,32 @@
1309 #
1310 # gtk.main_quit and the logger methods are safe to be called from any
1311 # thread. Just don't call other random stuff here.
1312- logger.info("Stoping Ubuntu SSO login manager since SIGHUP was received.")
1313+ logger.info("Stoping Ubuntu SSO service since SIGHUP was received.")
1314 gtk.main_quit()
1315
1316
1317-def main():
1318- """Run the backend service."""
1319+class AlreadyStartedError(Exception):
1320+ """The backend service has already been started."""
1321+
1322+
1323+timeout_func = gtk.timeout_add
1324+shutdown_func = gtk.main_quit
1325+
1326+
1327+def start_setup():
1328+ """Setup the env to run the service."""
1329 dbus.mainloop.glib.threads_init()
1330 gtk.gdk.threads_init()
1331 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
1332
1333- bus = dbus.SessionBus()
1334- # Register DBus service for making sure we run only one instance
1335- name = bus.request_name(DBUS_BUS_NAME,
1336- dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
1337- if name == dbus.bus.REQUEST_NAME_REPLY_EXISTS:
1338- logger.error("Ubuntu SSO login manager already running, quitting.")
1339- sys.exit(0)
1340-
1341- logger.debug("Hooking up SIGHUP with handler %r.", sighup_handler)
1342- signal.signal(signal.SIGHUP, sighup_handler)
1343-
1344- logger.info("Starting Ubuntu SSO login manager for bus %r.", DBUS_BUS_NAME)
1345- bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus=dbus.SessionBus())
1346- SSOLogin(bus_name, object_path=DBUS_ACCOUNT_PATH)
1347- CredentialsManagement(timeout_func=gtk.timeout_add,
1348- shutdown_func=gtk.main_quit,
1349- bus_name=bus_name, object_path=DBUS_CREDENTIALS_PATH)
1350-
1351- gtk.main()
1352+
1353+def finish_setup(result):
1354+ """Run the specific mainloop only if no failure ocurred."""
1355+ if result is None: # no failure ocurred, start the service
1356+ logger.debug("Hooking up SIGHUP with handler %r.", sighup_handler)
1357+ signal.signal(signal.SIGHUP, sighup_handler)
1358+ gtk.main()
1359+
1360+
1361+def main():
1362+ """Run the specific mainloop."""
1363
1364=== modified file 'ubuntu_sso/main/tests/__init__.py'
1365--- ubuntu_sso/main/tests/__init__.py 2011-02-24 13:06:07 +0000
1366+++ ubuntu_sso/main/tests/__init__.py 2012-01-17 16:51:50 +0000
1367@@ -15,3 +15,40 @@
1368 # You should have received a copy of the GNU General Public License along
1369 # with this program. If not, see <http://www.gnu.org/licenses/>.
1370 """Test the different main implementations."""
1371+
1372+import sys
1373+
1374+from twisted.internet import defer
1375+
1376+from ubuntu_sso.tests import TOKEN
1377+
1378+# Invalid name "BaseTestCase"
1379+# pylint: disable=C0103
1380+
1381+if sys.platform == 'win32':
1382+ from ubuntu_sso.utils.tests.test_ipc import BaseIPCTestCase
1383+ BaseTestCase = BaseIPCTestCase
1384+else:
1385+ from ubuntuone.devtools.testcases.dbus import DBusTestCase
1386+ BaseTestCase = DBusTestCase
1387+
1388+# pylint: enable=C0103
1389+
1390+
1391+class FakedCredentials(object):
1392+ """A very dummy Credentials object."""
1393+
1394+ def __init__(self, *a, **kw):
1395+ self.login = self.register = lambda *a, **kw: None
1396+
1397+ def find_credentials(self, *a, **kw):
1398+ """Retrieve credentials."""
1399+ return defer.succeed(TOKEN)
1400+
1401+ def clear_credentials(self, *a, **kw):
1402+ """Clear credentials."""
1403+ return defer.succeed(None)
1404+
1405+ def store_credentials(self, *a, **kw):
1406+ """Store credentials."""
1407+ return defer.succeed(None)
1408
1409=== added file 'ubuntu_sso/main/tests/test_clients.py'
1410--- ubuntu_sso/main/tests/test_clients.py 1970-01-01 00:00:00 +0000
1411+++ ubuntu_sso/main/tests/test_clients.py 2012-01-17 16:51:50 +0000
1412@@ -0,0 +1,373 @@
1413+# -*- coding: utf-8 -*-
1414+#
1415+# Copyright 2012 Canonical Ltd.
1416+#
1417+# This program is free software: you can redistribute it and/or modify it
1418+# under the terms of the GNU General Public License version 3, as published
1419+# by the Free Software Foundation.
1420+#
1421+# This program is distributed in the hope that it will be useful, but
1422+# WITHOUT ANY WARRANTY; without even the implied warranties of
1423+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1424+# PURPOSE. See the GNU General Public License for more details.
1425+#
1426+# You should have received a copy of the GNU General Public License along
1427+# with this program. If not, see <http://www.gnu.org/licenses/>.
1428+"""Tests for the main SSO client code."""
1429+
1430+from twisted.internet import defer
1431+from ubuntuone.devtools.testcases import skipIfOS
1432+
1433+from ubuntu_sso import main
1434+from ubuntu_sso.tests import (
1435+ APP_NAME,
1436+ CAPTCHA_ID,
1437+ CAPTCHA_SOLUTION,
1438+ EMAIL,
1439+ EMAIL_TOKEN,
1440+ NAME,
1441+ PASSWORD,
1442+ TOKEN,
1443+)
1444+from ubuntu_sso.main.tests import BaseTestCase, FakedCredentials
1445+
1446+FILENAME = 'sample filename'
1447+
1448+
1449+class FakedKeyring(object):
1450+ """A faked Keyring object."""
1451+
1452+ _keys = {}
1453+
1454+ def get_credentials(self, app_name):
1455+ """Return the credentials for app_name."""
1456+ return defer.succeed(self._keys.get(app_name, {}))
1457+
1458+ def delete_credentials(self, app_name):
1459+ """Delete the credentials for app_name."""
1460+ self._keys.pop(app_name, None)
1461+ return defer.succeed(None)
1462+
1463+ def set_credentials(self, app_name, token):
1464+ """Store the credentials for app_name."""
1465+ self._keys[app_name] = token
1466+ return defer.succeed(None)
1467+
1468+
1469+class AbstractTestCase(BaseTestCase):
1470+ """The base test case with platform specific support."""
1471+
1472+ timeout = 2
1473+ method = None
1474+ backend_method = method
1475+ params = ()
1476+ success_signal = None
1477+ backend_result = None
1478+ success_result = None
1479+ error_signal = None
1480+
1481+ @defer.inlineCallbacks
1482+ def setUp(self):
1483+ yield super(AbstractTestCase, self).setUp()
1484+ self.keyring = FakedKeyring()
1485+ self.patch(main, 'Keyring', lambda: self.keyring)
1486+
1487+ # avoid putting stuff in the mainloops
1488+ self.patch(main.source, 'timeout_func', lambda *a: None)
1489+ self.patch(main.source, 'shutdown_func', lambda *a: None)
1490+
1491+ self.sso_service = main.UbuntuSSOService()
1492+ yield self.sso_service.start()
1493+ self.addCleanup(self.sso_service.shutdown)
1494+
1495+ self.sso_client = yield main.get_sso_client()
1496+ self.addCleanup(self.sso_client.disconnect)
1497+
1498+ self.client = self.patchable_backend = None
1499+
1500+ if self.backend_method is None:
1501+ self.backend_method = self.method
1502+
1503+ def _backend_succeed(self, *args, **kwargs):
1504+ """Make self.patchable_backend return self.backend_result."""
1505+ return self.backend_result
1506+
1507+ def _backend_fail(self, *args, **kwargs):
1508+ """Make self.patchable_backend raise an exception."""
1509+ raise ValueError((args, kwargs))
1510+
1511+ @defer.inlineCallbacks
1512+ def assert_method_correct(self, success_signal, error_signal,
1513+ patched_backend_method, expected_result=None):
1514+ """Calling 'self.method' works ok.
1515+
1516+ Check that 'success_signal' is emitted, and make the test fail if
1517+ error_signal is received.
1518+
1519+ The self.patchable_backend will be patched with
1520+ 'patched_backend_method'.
1521+
1522+ """
1523+ if self.method is None:
1524+ defer.returnValue(None)
1525+
1526+ d = defer.Deferred()
1527+
1528+ cb = lambda *a: d.callback(a)
1529+ match = self.client.connect_to_signal(success_signal, cb)
1530+ self.addCleanup(self.client.disconnect_from_signal,
1531+ success_signal, match)
1532+
1533+ eb = lambda *a: d.errback(AssertionError(a))
1534+ match = self.client.connect_to_signal(error_signal, eb)
1535+ self.addCleanup(self.client.disconnect_from_signal,
1536+ error_signal, match)
1537+
1538+ self.patch(self.patchable_backend, self.backend_method,
1539+ patched_backend_method)
1540+
1541+ yield self.client.call_method(self.method, *self.params)
1542+
1543+ result = yield d
1544+ self.assertEqual(expected_result, result)
1545+
1546+ def test_success(self):
1547+ """Test that the 'method' works ok."""
1548+ success_signal = self.success_signal
1549+ error_signal = self.error_signal
1550+ patched_backend_method = self._backend_succeed
1551+ expected_result = self.success_result
1552+
1553+ return self.assert_method_correct(success_signal, error_signal,
1554+ patched_backend_method, expected_result)
1555+
1556+ def test_error(self):
1557+ """Test that the 'method' fails as expected."""
1558+ success_signal = self.error_signal
1559+ error_signal = self.success_signal
1560+ patched_backend_method = self._backend_fail
1561+ expected_result = (APP_NAME, dict(errtype='ValueError'))
1562+
1563+ return self.assert_method_correct(success_signal, error_signal,
1564+ patched_backend_method, expected_result)
1565+
1566+
1567+class SSOLoginProxyTestCase(AbstractTestCase):
1568+ """Test the SSOLoginProxy interface."""
1569+
1570+ @defer.inlineCallbacks
1571+ def setUp(self):
1572+ yield super(SSOLoginProxyTestCase, self).setUp()
1573+ self.client = self.sso_client.sso_login
1574+ self.patchable_backend = self.sso_service.sso_login.processor
1575+
1576+
1577+class GenerateCaptchaTestCase(SSOLoginProxyTestCase):
1578+ """Test the generate_captcha method."""
1579+
1580+ method = 'generate_captcha'
1581+ params = (APP_NAME, FILENAME)
1582+ success_signal = 'CaptchaGenerated'
1583+ error_signal = 'CaptchaGenerationError'
1584+ backend_result = 'a captcha id'
1585+ success_result = (APP_NAME, backend_result)
1586+
1587+
1588+class RegisterUserTestCase(SSOLoginProxyTestCase):
1589+ """Test the register_user method."""
1590+
1591+ method = 'register_user'
1592+ params = (APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, CAPTCHA_SOLUTION)
1593+ success_signal = 'UserRegistered'
1594+ error_signal = 'UserRegistrationError'
1595+ backend_result = EMAIL
1596+ success_result = (APP_NAME, backend_result)
1597+
1598+
1599+class LoginTestCase(SSOLoginProxyTestCase):
1600+ """Test the login method."""
1601+
1602+ method = 'login'
1603+ params = (APP_NAME, EMAIL, PASSWORD)
1604+ success_signal = 'LoggedIn'
1605+ error_signal = 'LoginError'
1606+ backend_result = TOKEN
1607+ success_result = (APP_NAME, EMAIL)
1608+
1609+ @defer.inlineCallbacks
1610+ def test_success(self):
1611+ """Test that the 'method' works ok when the user is validated."""
1612+ self.patch(self.patchable_backend, 'is_validated', lambda _: True)
1613+
1614+ yield super(LoginTestCase, self).test_success()
1615+
1616+ expected_credentials = yield self.keyring.get_credentials(APP_NAME)
1617+ self.assertEqual(expected_credentials, TOKEN)
1618+
1619+ def test_not_validated(self):
1620+ """Test that the 'method' works ok when the user is not validated."""
1621+ self.patch(self.patchable_backend, 'is_validated', lambda _: False)
1622+ self.patch(self, 'success_signal', 'UserNotValidated')
1623+
1624+ return super(LoginTestCase, self).test_success()
1625+
1626+ def test_error_when_setting_credentials(self):
1627+ """The 'error_signal' is emitted when credentials can't be set."""
1628+ self.patch(self.patchable_backend, 'is_validated', lambda _: True)
1629+ exc = TypeError('foo')
1630+ self.patch(self.keyring, 'set_credentials', lambda *a: defer.fail(exc))
1631+ patched_backend_method = lambda *a, **kw: self.backend_result
1632+ expected_result = (APP_NAME, dict(errtype='TypeError', message='foo'))
1633+
1634+ return self.assert_method_correct('LoginError', 'LoggedIn',
1635+ patched_backend_method, expected_result)
1636+
1637+
1638+class ValidateEmailTestCase(SSOLoginProxyTestCase):
1639+ """Test the validate_email method."""
1640+
1641+ method = 'validate_email'
1642+ params = (APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN)
1643+ success_signal = 'EmailValidated'
1644+ error_signal = 'EmailValidationError'
1645+ backend_result = TOKEN
1646+ success_result = (APP_NAME, EMAIL)
1647+
1648+
1649+class RequestPasswordResetTokenTestCase(SSOLoginProxyTestCase):
1650+ """Test the request_password_reset_token method."""
1651+
1652+ method = 'request_password_reset_token'
1653+ params = (APP_NAME, EMAIL)
1654+ success_signal = 'PasswordResetTokenSent'
1655+ error_signal = 'PasswordResetError'
1656+ backend_result = EMAIL
1657+ success_result = (APP_NAME, backend_result)
1658+
1659+
1660+class SetNewPasswordTestCase(SSOLoginProxyTestCase):
1661+ """Test the set_new_password method."""
1662+
1663+ method = 'set_new_password'
1664+ params = (APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD)
1665+ success_signal = 'PasswordChanged'
1666+ error_signal = 'PasswordChangeError'
1667+ backend_result = EMAIL
1668+ success_result = (APP_NAME, backend_result)
1669+
1670+
1671+class CredentialsManagementProxyTestCase(AbstractTestCase):
1672+ """Tests for the CredentialsManagementProxy DBus interface."""
1673+
1674+ error_signal = 'CredentialsError'
1675+ args = dict(foo='bar', fuh='baz')
1676+ params = (APP_NAME, args)
1677+
1678+ @defer.inlineCallbacks
1679+ def setUp(self):
1680+ yield super(CredentialsManagementProxyTestCase, self).setUp()
1681+ self.credentials = FakedCredentials()
1682+ self.patch(main, 'Credentials', lambda *a, **kw: self.credentials)
1683+
1684+ self.client = self.sso_client.cred_manager
1685+ self.patchable_backend = self.credentials
1686+
1687+ def _backend_succeed(self, *args, **kwargs):
1688+ """Make self.patchable_backend return self.backend_result."""
1689+ return defer.succeed(self.backend_result)
1690+
1691+ def _backend_fail(self, *args, **kwargs):
1692+ """Make self.patchable_backend return a failed deferred."""
1693+ return defer.fail(ValueError((args, kwargs)))
1694+
1695+
1696+class FindCredentialsTestCase(CredentialsManagementProxyTestCase):
1697+ """Test the find_credentials method."""
1698+
1699+ method = 'find_credentials'
1700+ success_signal = 'CredentialsFound'
1701+ backend_result = TOKEN
1702+ success_result = (APP_NAME, backend_result)
1703+
1704+ @skipIfOS('win32', 'find_credentials_sync is only provided in Linux '
1705+ 'due to compatibility issues with old clients.')
1706+ @defer.inlineCallbacks
1707+ def test_find_credentials_sync(self):
1708+ """The credentials are asked and returned in a sync call."""
1709+ d = defer.Deferred()
1710+
1711+ self.client.call_method('find_credentials_sync',
1712+ APP_NAME, self.args,
1713+ reply_handler=d.callback,
1714+ error_handler=d.errback)
1715+ creds = yield d
1716+ self.assertEqual(creds, TOKEN)
1717+
1718+ @skipIfOS('win32', 'find_credentials_sync is only provided in Linux '
1719+ 'due to compatibility issues with old clients.')
1720+ @defer.inlineCallbacks
1721+ def test_find_credentials_sync_error(self):
1722+ """If find_credentials_sync fails, error_handler is called."""
1723+ self.patch(self.credentials, 'find_credentials', self._backend_fail)
1724+ d = defer.Deferred()
1725+
1726+ self.client.call_method('find_credentials_sync',
1727+ APP_NAME, self.args,
1728+ reply_handler=d.errback,
1729+ error_handler=d.callback)
1730+ error = yield d
1731+ error = error.args[0]
1732+ self.assertEqual(error, 'ValueError')
1733+
1734+
1735+class ClearCredentialsTestCase(CredentialsManagementProxyTestCase):
1736+ """Test the clear_credentials method."""
1737+
1738+ method = 'clear_credentials'
1739+ success_signal = 'CredentialsCleared'
1740+ success_result = (APP_NAME,)
1741+
1742+
1743+class StoreCredentialsTestCase(CredentialsManagementProxyTestCase):
1744+ """Test the store_credentials method."""
1745+
1746+ method = 'store_credentials'
1747+ success_signal = 'CredentialsStored'
1748+ success_result = (APP_NAME,)
1749+
1750+
1751+class CredentialsManagementOpsTestCase(CredentialsManagementProxyTestCase):
1752+ """Tests for the CredentialsManagementProxy login/register methods."""
1753+
1754+ success_signal = 'CredentialsFound'
1755+ success_result = (APP_NAME, TOKEN)
1756+
1757+ def _backend_succeed(self, *args, **kwargs):
1758+ """Make self.patchable_backend return self.backend_result."""
1759+ self.sso_service.cred_manager.CredentialsFound(APP_NAME, TOKEN)
1760+
1761+ def _backend_fail(self, *args, **kwargs):
1762+ """Make self.patchable_backend fail."""
1763+ self.sso_service.cred_manager.CredentialsError(APP_NAME,
1764+ dict(errtype='ValueError'))
1765+
1766+
1767+class RegisterTestCase(CredentialsManagementOpsTestCase):
1768+ """Test the register method."""
1769+
1770+ method = 'register'
1771+
1772+
1773+class LoginOnlyTestCase(CredentialsManagementOpsTestCase):
1774+ """Test the login method."""
1775+
1776+ method = 'login'
1777+
1778+
1779+class LoginEmailPasswordTestCase(CredentialsManagementOpsTestCase):
1780+ """Test the login method."""
1781+
1782+ method = 'login_email_password'
1783+ backend_method = 'login'
1784+ args = dict(email=EMAIL, password=PASSWORD)
1785+ params = (APP_NAME, args)
1786
1787=== modified file 'ubuntu_sso/main/tests/test_common.py'
1788--- ubuntu_sso/main/tests/test_common.py 2011-09-27 14:06:12 +0000
1789+++ ubuntu_sso/main/tests/test_common.py 2012-01-17 16:51:50 +0000
1790@@ -1,12 +1,6 @@
1791 # -*- coding: utf-8 -*-
1792 #
1793-# test_main - tests for ubuntu_sso.main
1794-#
1795-# Author: Natalia Bidart <natalia.bidart@canonical.com>
1796-# Author: Alejandro J. Cura <alecu@canonical.com>
1797-# Author: Manuel de la Pena <manuel@canonical.com>
1798-#
1799-# Copyright 2009-2010 Canonical Ltd.
1800+# Copyright 2009-2011 Canonical Ltd.
1801 #
1802 # This program is free software: you can redistribute it and/or modify it
1803 # under the terms of the GNU General Public License version 3, as published
1804@@ -19,161 +13,923 @@
1805 #
1806 # You should have received a copy of the GNU General Public License along
1807 # with this program. If not, see <http://www.gnu.org/licenses/>.
1808-"""Tests share by diff platforms."""
1809-
1810-from unittest import TestCase
1811-
1812-from mocker import MockerTestCase, MATCH
1813-
1814+"""Tests for the main SSO client code."""
1815+
1816+import logging
1817+
1818+from twisted.internet import defer
1819+from ubuntuone.devtools.handlers import MementoHandler
1820+
1821+from ubuntu_sso import main
1822 from ubuntu_sso.main import (
1823 CredentialsManagement,
1824+ except_to_errdict,
1825 SSOLogin,
1826+ thread_execute,
1827+ TIMEOUT_INTERVAL,
1828+ UbuntuSSOService,
1829 )
1830-
1831-
1832-class SSOLoginMockedTestCase(MockerTestCase):
1833- """Test that the call are relied correctly."""
1834-
1835- def setUp(self):
1836- """Setup tests."""
1837- super(SSOLoginMockedTestCase, self).setUp()
1838- self.root = self.mocker.mock()
1839- mockbusname = self.mocker.mock()
1840- mockbus = self.mocker.mock()
1841- mockbusname.get_bus()
1842- self.mocker.result(mockbus)
1843- self.login = SSOLogin(mockbus)
1844- self.login.root = self.root
1845- self.mocker.reset()
1846-
1847- def test_generate_captcha(self):
1848- """Test that the call is relayed."""
1849- app_name = 'app'
1850- filename = 'file'
1851- self.root.generate_captcha(app_name, filename,
1852- MATCH(callable), MATCH(callable))
1853- self.mocker.replay()
1854- self.login.generate_captcha(app_name, filename)
1855-
1856- def test_register_user(self):
1857- """Test that the call is relayed."""
1858- app_name = 'app'
1859- email = 'email'
1860- password = 'pwd'
1861- name = 'display name'
1862- captcha_id = 'id'
1863- captcha_solution = 'hello'
1864- self.root.register_user(app_name, email, password, name, captcha_id,
1865- captcha_solution,
1866- MATCH(callable), MATCH(callable))
1867- self.mocker.replay()
1868- self.login.register_user(app_name, email, password, name, captcha_id,
1869- captcha_solution)
1870-
1871- def test_login(self):
1872- """Test that the call is relayed."""
1873- app_name = 'app'
1874- email = 'email'
1875- password = 'password'
1876- self.root.login(app_name, email, password,
1877- MATCH(callable), MATCH(callable),
1878- MATCH(callable))
1879- self.mocker.mock()
1880- self.mocker.replay()
1881- self.login.login(app_name, email, password)
1882-
1883- def test_validate_email(self):
1884- """Test that the call is relayed."""
1885- app_name = 'app'
1886- email = 'email'
1887- password = 'passwrd'
1888- email_token = 'token'
1889- self.root.validate_email(app_name, email, password, email_token,
1890- MATCH(callable),
1891- MATCH(callable))
1892- self.mocker.replay()
1893- self.login.validate_email(app_name, email, password, email_token)
1894-
1895- def test_request_password_reset_token(self):
1896- """Test that the call is relayed."""
1897- app_name = 'app'
1898- email = 'email'
1899- self.root.request_password_reset_token(app_name, email,
1900- MATCH(callable),
1901- MATCH(callable))
1902- self.mocker.replay()
1903- self.login.request_password_reset_token(app_name, email)
1904-
1905- def test_set_new_password(self):
1906- """Test that the call is relayed."""
1907- app_name = 'app'
1908- email = 'email'
1909- token = 'token'
1910- new_password = 'new'
1911- self.root.set_new_password(app_name, email, token, new_password,
1912- MATCH(callable),
1913- MATCH(callable))
1914- self.mocker.replay()
1915- self.login.set_new_password(app_name, email, token, new_password)
1916-
1917-
1918-class CredentialsManagementMockedTestCase(MockerTestCase, TestCase):
1919- """Test that the call are relied correctly."""
1920-
1921- def setUp(self):
1922- """Setup tests."""
1923- super(CredentialsManagementMockedTestCase, self).setUp()
1924- self.root = self.mocker.mock()
1925- self.cred = CredentialsManagement(None, None)
1926- self.cred.root = self.root
1927-
1928+from ubuntu_sso.main import (HELP_TEXT_KEY, PING_URL_KEY,
1929+ TC_URL_KEY, UI_CLASS_KEY, UI_MODULE_KEY, WINDOW_ID_KEY,
1930+ SUCCESS_CB_KEY, ERROR_CB_KEY, DENIAL_CB_KEY)
1931+from ubuntu_sso.main.tests import FakedCredentials
1932+from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID,
1933+ CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, PING_URL, TOKEN,
1934+ WINDOW_ID, Recorder, TestCase)
1935+
1936+
1937+# Access to a protected member 'yyy' of a client class
1938+# pylint: disable=W0212
1939+
1940+
1941+class SampleException(Exception):
1942+ """The exception that will be thrown by the fake thread_execute."""
1943+
1944+
1945+def fake_ok_thread_execute(f, app, cb, eb):
1946+ """A fake thread_execute function that succeeds."""
1947+ cb(app, f())
1948+
1949+
1950+def fake_err_thread_execute(f, app, cb, eb):
1951+ """A fake thread_execute function that fails."""
1952+ try:
1953+ f()
1954+ except Exception, e: # pylint: disable=W0703
1955+ eb(app, except_to_errdict(e))
1956+ else:
1957+ eb(app, except_to_errdict(SampleException()))
1958+
1959+
1960+class FakedProxy(Recorder):
1961+ """A faked multiplatform proxy."""
1962+
1963+
1964+class FakedAccount(Recorder):
1965+ """A faked Account processor."""
1966+
1967+
1968+class FakedCredentialsFactory(Recorder):
1969+ """A very dummy Credentials object."""
1970+
1971+ credentials = FakedCredentials()
1972+
1973+ # pylint: disable=C0103
1974+
1975+ def Credentials(self, *a, **kw):
1976+ """Return always the same Credentials instance."""
1977+ return self.credentials
1978+
1979+ # pylint: enable=C0103
1980+
1981+
1982+class TestExceptToErrdictException(Exception):
1983+ """A dummy exception for the following testcase."""
1984+
1985+
1986+class ExceptToErrdictTestCase(TestCase):
1987+ """Tests for the except_to_errdict function."""
1988+
1989+ def test_first_arg_is_dict(self):
1990+ """If the first arg is a dict, use it as the base dict."""
1991+ sample_dict = {
1992+ "errorcode1": "error message 1",
1993+ "errorcode2": "error message 2",
1994+ "errorcode3": "error message 3",
1995+ }
1996+ e = TestExceptToErrdictException(sample_dict)
1997+ result = except_to_errdict(e)
1998+
1999+ self.assertEqual(result["errtype"], e.__class__.__name__)
2000+ for k in sample_dict.keys():
2001+ self.assertIn(k, result)
2002+ self.assertEqual(result[k], sample_dict[k])
2003+
2004+ def test_first_arg_is_str(self):
2005+ """If the first arg is a str, use it as the message."""
2006+ sample_string = "a sample string"
2007+ e = TestExceptToErrdictException(sample_string)
2008+ result = except_to_errdict(e)
2009+ self.assertEqual(result["errtype"], e.__class__.__name__)
2010+ self.assertEqual(result["message"], sample_string)
2011+
2012+ def test_first_arg_is_unicode(self):
2013+ """If the first arg is a unicode, use it as the message."""
2014+ sample_string = u"a sample string"
2015+ e = TestExceptToErrdictException(sample_string)
2016+ result = except_to_errdict(e)
2017+ self.assertEqual(result["errtype"], e.__class__.__name__)
2018+ self.assertEqual(result["message"], sample_string)
2019+
2020+ def test_no_args_at_all(self):
2021+ """If there are no args, use the class docstring."""
2022+ e = TestExceptToErrdictException()
2023+ result = except_to_errdict(e)
2024+ self.assertEqual(result["errtype"], e.__class__.__name__)
2025+ self.assertEqual(result["message"], e.__class__.__doc__)
2026+
2027+ def test_some_other_thing_as_first_arg(self):
2028+ """If first arg is not basestring nor dict, then repr all args."""
2029+ sample_args = (None, u"unicode2\ufffd", "errorcode3")
2030+ e = TestExceptToErrdictException(*sample_args)
2031+ result = except_to_errdict(e)
2032+ self.assertEqual(result["errtype"], e.__class__.__name__)
2033+
2034+
2035+class BaseTestCase(TestCase):
2036+ """Base test case."""
2037+
2038+ timeout = 2
2039+
2040+ @defer.inlineCallbacks
2041+ def setUp(self):
2042+ yield super(BaseTestCase, self).setUp()
2043+ self.proxy = FakedProxy()
2044+
2045+ def assert_recorder_called(self, fake, method, *args, **kwargs):
2046+ """Check that 'fake.method(*args, **kwargs)' was called."""
2047+ self.assertEqual(fake._called[method], [(args, kwargs)])
2048+
2049+
2050+class SSOLoginTestCase(BaseTestCase):
2051+ """Test the SSOLogin class."""
2052+
2053+ @defer.inlineCallbacks
2054+ def setUp(self):
2055+ yield super(SSOLoginTestCase, self).setUp()
2056+
2057+ def ksc(keyring, k, val):
2058+ """Assert over token and app_name."""
2059+ self.assertEqual(k, APP_NAME)
2060+ self.assertEqual(val, TOKEN)
2061+ self.keyring_was_set = True
2062+ self.keyring_values = k, val
2063+ return defer.succeed(None)
2064+
2065+ self.patch(main.Keyring, "set_credentials", ksc)
2066+ self.patch(main, 'Account', FakedAccount)
2067+ self.patch(main, "thread_execute", fake_ok_thread_execute)
2068+ self.keyring_was_set = False
2069+ self.keyring_values = None
2070+
2071+ self.obj = SSOLogin(self.proxy)
2072+
2073+ def test_creation(self):
2074+ """Test that the object creation is successful."""
2075+ self.assertIsInstance(self.obj.processor, main.Account)
2076+ self.assertTrue(self.obj.proxy is self.proxy)
2077+
2078+ @defer.inlineCallbacks
2079+ def test_generate_captcha(self):
2080+ """Test that the captcha method works ok."""
2081+ d = defer.Deferred()
2082+ filename = "sample filename"
2083+ expected_result = "expected result"
2084+
2085+ self.patch(self.obj, "CaptchaGenerated", lambda *a: d.callback(a))
2086+ self.patch(self.obj, "CaptchaGenerationError", d.errback)
2087+
2088+ self.obj.processor._next_result = expected_result
2089+ self.obj.generate_captcha(APP_NAME, filename)
2090+
2091+ app_name, result = yield d
2092+ self.assertEqual(result, expected_result)
2093+ self.assertEqual(app_name, APP_NAME)
2094+ self.assert_recorder_called(self.obj.processor, 'generate_captcha',
2095+ filename)
2096+
2097+ @defer.inlineCallbacks
2098+ def test_register_user(self):
2099+ """Test that the register_user method works ok."""
2100+ d = defer.Deferred()
2101+ expected_result = "expected result"
2102+
2103+ self.patch(self.obj, "UserRegistered", lambda *a: d.callback(a))
2104+ self.patch(self.obj, "UserRegistrationError", d.errback)
2105+
2106+ self.obj.processor._next_result = expected_result
2107+ self.obj.register_user(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID,
2108+ CAPTCHA_SOLUTION)
2109+
2110+ app_name, result = yield d
2111+ self.assertEqual(result, expected_result)
2112+ self.assertEqual(app_name, APP_NAME)
2113+ self.assert_recorder_called(self.obj.processor, 'register_user',
2114+ EMAIL, PASSWORD, NAME, CAPTCHA_ID, CAPTCHA_SOLUTION)
2115+
2116+ @defer.inlineCallbacks
2117+ def test_login(self):
2118+ """Test that the login method works ok."""
2119+ d = defer.Deferred()
2120+
2121+ self.patch(self.obj, "LoggedIn", lambda *a: d.callback(a))
2122+ self.patch(self.obj, "LoginError", d.errback)
2123+ self.patch(self.obj, "UserNotValidated", d.errback)
2124+
2125+ self.obj.processor._next_result = TOKEN
2126+ self.obj.login(APP_NAME, EMAIL, PASSWORD)
2127+
2128+ app_name, result = yield d
2129+ self.assertEqual(result, EMAIL)
2130+ self.assertEqual(app_name, APP_NAME)
2131+ self.assertTrue(self.keyring_was_set, "The keyring should be set")
2132+ self.assert_recorder_called(self.obj.processor, 'login',
2133+ EMAIL, PASSWORD, main.get_token_name(APP_NAME))
2134+
2135+ @defer.inlineCallbacks
2136+ def test_validate_email(self):
2137+ """Test that the validate_email method works ok."""
2138+ d = defer.Deferred()
2139+
2140+ self.patch(self.obj, "EmailValidated", lambda *a: d.callback(a))
2141+ self.patch(self.obj, "EmailValidationError", d.errback)
2142+
2143+ self.obj.processor._next_result = TOKEN
2144+ self.obj.validate_email(APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN)
2145+
2146+ app_name, result = yield d
2147+ self.assertEqual(result, EMAIL)
2148+ self.assertEqual(app_name, APP_NAME)
2149+ self.assertTrue(self.keyring_was_set, "The keyring should be set")
2150+ self.assert_recorder_called(self.obj.processor, 'validate_email',
2151+ EMAIL, PASSWORD, EMAIL_TOKEN, main.get_token_name(APP_NAME))
2152+
2153+ @defer.inlineCallbacks
2154+ def test_request_password_reset_token(self):
2155+ """Test that the request_password_reset_token method works ok."""
2156+ d = defer.Deferred()
2157+
2158+ self.patch(self.obj, "PasswordResetTokenSent",
2159+ lambda *a: d.callback(a))
2160+ self.patch(self.obj, "PasswordResetError", d.errback)
2161+
2162+ self.obj.processor._next_result = EMAIL
2163+ self.obj.request_password_reset_token(APP_NAME, EMAIL)
2164+
2165+ app_name, result = yield d
2166+ self.assertEqual(result, EMAIL)
2167+ self.assertEqual(app_name, APP_NAME)
2168+ self.assert_recorder_called(self.obj.processor,
2169+ 'request_password_reset_token', EMAIL)
2170+
2171+ @defer.inlineCallbacks
2172+ def test_set_new_password(self):
2173+ """Test that the set_new_password method works ok."""
2174+ d = defer.Deferred()
2175+
2176+ self.patch(self.obj, "PasswordChanged", lambda *a: d.callback(a))
2177+ self.patch(self.obj, "PasswordChangeError", d.errback)
2178+
2179+ self.obj.processor._next_result = EMAIL
2180+ self.obj.set_new_password(APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD)
2181+
2182+ app_name, result = yield d
2183+ self.assertEqual(result, EMAIL)
2184+ self.assertEqual(app_name, APP_NAME)
2185+ self.assert_recorder_called(self.obj.processor,
2186+ 'set_new_password', EMAIL, EMAIL_TOKEN, PASSWORD)
2187+
2188+
2189+class SSOLoginWithErrorTestCase(SSOLoginTestCase):
2190+ """Test the SSOLogin class."""
2191+
2192+ @defer.inlineCallbacks
2193+ def setUp(self):
2194+ yield super(SSOLoginWithErrorTestCase, self).setUp()
2195+ self.patch(main, "thread_execute", fake_err_thread_execute)
2196+
2197+ @defer.inlineCallbacks
2198+ def test_generate_captcha(self):
2199+ """Test that the captcha method fails as expected."""
2200+ d = defer.Deferred()
2201+ filename = "sample filename"
2202+
2203+ self.patch(self.obj, "CaptchaGenerated", d.errback)
2204+ self.patch(self.obj, "CaptchaGenerationError",
2205+ lambda *a: d.callback(a))
2206+ self.obj.generate_captcha(APP_NAME, filename)
2207+
2208+ app_name, errdict = yield d
2209+ self.assertEqual(errdict["errtype"], "SampleException")
2210+ self.assertEqual(app_name, APP_NAME)
2211+
2212+ @defer.inlineCallbacks
2213+ def test_register_user(self):
2214+ """Test that the register_user method fails as expected."""
2215+ d = defer.Deferred()
2216+
2217+ self.patch(self.obj, "UserRegistered", d.errback)
2218+ self.patch(self.obj, "UserRegistrationError", lambda *a: d.callback(a))
2219+ self.obj.register_user(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID,
2220+ CAPTCHA_SOLUTION)
2221+
2222+ app_name, errdict = yield d
2223+ self.assertEqual(errdict["errtype"], "SampleException")
2224+ self.assertEqual(app_name, APP_NAME)
2225+
2226+ @defer.inlineCallbacks
2227+ def test_login(self):
2228+ """The login method fails as expected when get_token_name fails."""
2229+ d = defer.Deferred()
2230+
2231+ def fake_gtn(*args):
2232+ """A fake get_token_name that fails."""
2233+ raise SampleException()
2234+
2235+ self.patch(main, "get_token_name", fake_gtn)
2236+ self.patch(self.obj, "LoggedIn", d.errback)
2237+ self.patch(self.obj, "LoginError", lambda *a: d.callback(a))
2238+ self.patch(self.obj, "UserNotValidated", d.errback)
2239+ self.obj.login(APP_NAME, EMAIL, PASSWORD)
2240+
2241+ app_name, errdict = yield d
2242+ self.assertEqual(app_name, APP_NAME)
2243+ self.assertEqual(errdict["errtype"], "SampleException")
2244+ self.assertFalse(self.keyring_was_set, "Keyring should not be set")
2245+
2246+ @defer.inlineCallbacks
2247+ def test_login_set_credentials(self):
2248+ """The login method fails as expected when set_credentials fails."""
2249+ d = defer.Deferred()
2250+
2251+ def fake_set_creds(*args):
2252+ """A fake Keyring.set_credentials that fails."""
2253+ return defer.fail(SampleException())
2254+
2255+ self.patch(main.Keyring, "set_credentials", fake_set_creds)
2256+ fail = lambda app, res: d.errback((app, res))
2257+ self.patch(self.obj, "LoggedIn", fail)
2258+ self.patch(self.obj, "LoginError", lambda *a: d.callback(a))
2259+ self.patch(self.obj, "UserNotValidated", fail)
2260+ self.obj.login(APP_NAME, EMAIL, PASSWORD)
2261+
2262+ app_name, errdict = yield d
2263+ self.assertEqual(app_name, APP_NAME)
2264+ self.assertEqual(errdict["errtype"], "SampleException")
2265+ self.assertFalse(self.keyring_was_set, "Keyring should not be set")
2266+
2267+ @defer.inlineCallbacks
2268+ def test_validate_email(self):
2269+ """Test that the validate_email method fails as expected."""
2270+ d = defer.Deferred()
2271+
2272+ def fake_gtn(*args):
2273+ """A fake get_token_name that fails."""
2274+ raise SampleException()
2275+
2276+ self.patch(main, "get_token_name", fake_gtn)
2277+
2278+ self.patch(self.obj, "EmailValidated", d.errback)
2279+ self.patch(self.obj, "EmailValidationError", lambda *a: d.callback(a))
2280+ self.obj.validate_email(APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN)
2281+
2282+ app_name, errdict = yield d
2283+ self.assertEqual(app_name, APP_NAME)
2284+ self.assertEqual(errdict["errtype"], "SampleException")
2285+ self.assertFalse(self.keyring_was_set, "Keyring should not be set")
2286+
2287+ @defer.inlineCallbacks
2288+ def test_request_password_reset_token(self):
2289+ """Test the request_password_reset_token method fails as expected."""
2290+ d = defer.Deferred()
2291+
2292+ self.patch(self.obj, "PasswordResetTokenSent", d.errback)
2293+ self.patch(self.obj, "PasswordResetError", lambda *a: d.callback(a))
2294+ self.obj.request_password_reset_token(APP_NAME, EMAIL)
2295+
2296+ app_name, errdict = yield d
2297+ self.assertEqual(errdict["errtype"], "SampleException")
2298+ self.assertEqual(app_name, APP_NAME)
2299+
2300+ @defer.inlineCallbacks
2301+ def test_set_new_password(self):
2302+ """Test that the set_new_password method fails as expected."""
2303+ d = defer.Deferred()
2304+
2305+ self.patch(self.obj, "PasswordChanged", d.errback)
2306+ self.patch(self.obj, "PasswordChangeError", lambda *a: d.callback(a))
2307+ self.obj.set_new_password(APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD)
2308+
2309+ app_name, errdict = yield d
2310+ self.assertEqual(errdict["errtype"], "SampleException")
2311+ self.assertEqual(app_name, APP_NAME)
2312+
2313+
2314+class BlockingFunctionTestCase(TestCase):
2315+ """Tests for the "thread_execute" function."""
2316+
2317+ timeout = 5
2318+
2319+ @defer.inlineCallbacks
2320+ def test_thread_execute(self):
2321+ """Test the normal behaviour."""
2322+ d = defer.Deferred()
2323+ expected_result = "expected result"
2324+
2325+ def f():
2326+ """No failure."""
2327+ return expected_result
2328+
2329+ thread_execute(f, APP_NAME, lambda *a: d.callback(a), d.errback)
2330+
2331+ app_name, result = yield d
2332+
2333+ self.assertEqual(result, expected_result)
2334+ self.assertEqual(app_name, APP_NAME)
2335+
2336+ @defer.inlineCallbacks
2337+ def test_thread_execute_error(self):
2338+ """Test the behaviour when an Exception is raised."""
2339+ d = defer.Deferred()
2340+ expected_error_message = "expected error message"
2341+ expected_exc = SampleException(expected_error_message)
2342+
2343+ def f():
2344+ """Failure."""
2345+ raise expected_exc
2346+
2347+ thread_execute(f, APP_NAME, d.errback, lambda *a: d.callback(a))
2348+
2349+ app_name, error = yield d
2350+
2351+ self.assertEqual(app_name, APP_NAME)
2352+ self.assertEqual(error, expected_exc)
2353+
2354+
2355+class CredentialsManagementTestCase(BaseTestCase):
2356+ """Tests for the CredentialsManagement DBus interface."""
2357+
2358+ base_args = {
2359+ HELP_TEXT_KEY: HELP_TEXT, PING_URL_KEY: PING_URL,
2360+ TC_URL_KEY: TC_URL, WINDOW_ID_KEY: WINDOW_ID,
2361+ UI_CLASS_KEY: 'SuperUI', UI_MODULE_KEY: 'foo.bar.baz',
2362+ }
2363+
2364+ @defer.inlineCallbacks
2365+ def setUp(self):
2366+ yield super(CredentialsManagementTestCase, self).setUp()
2367+
2368+ self.factory = FakedCredentialsFactory()
2369+ self.patch(main, 'Credentials', self.factory.Credentials)
2370+ self.obj = CredentialsManagement(timeout_func=lambda *a: None,
2371+ shutdown_func=lambda *a: None,
2372+ proxy=self.proxy)
2373+ self.args = {}
2374+ self.cred_args = {}
2375+
2376+ self.memento = MementoHandler()
2377+ self.memento.setLevel(logging.DEBUG)
2378+ main.logger.addHandler(self.memento)
2379+ self.addCleanup(main.logger.removeHandler, self.memento)
2380+
2381+
2382+class CredentialsManagementRefCountingTestCase(CredentialsManagementTestCase):
2383+ """Tests for the CredentialsManagement ref counting."""
2384+
2385+ @defer.inlineCallbacks
2386+ def assert_refcounter_increased(self, method_name, signal_name=None):
2387+ """Assert that calling 'method_name' increases the ref counter."""
2388+ if signal_name is not None:
2389+ d = defer.Deferred()
2390+ self.patch(self.obj, signal_name, lambda *a: d.callback(a))
2391+ else:
2392+ d = defer.succeed(None)
2393+
2394+ getattr(self.obj, method_name)(APP_NAME, self.args)
2395+
2396+ yield d
2397+ self.assertEqual(self.obj.ref_count, 1)
2398+
2399+ def assert_refcounter_decreased(self, signal_name, *a, **kw):
2400+ """Assert that calling 'signal_name' decreases the ref counter."""
2401+ self.obj.ref_count = 3
2402+
2403+ getattr(self.obj, signal_name)(*a, **kw)
2404+
2405+ self.assertEqual(self.obj.ref_count, 2)
2406+
2407+ def assert_refcounter_negative(self, signal_name, *a, **kw):
2408+ """Assert that calling 'signal_name' decreases the ref counter.
2409+
2410+ If decreased value is negative, assert that a warning log was made.
2411+
2412+ """
2413+ self.obj._ref_count = -3 # overwrite internal to force a negative
2414+
2415+ getattr(self.obj, signal_name)(*a, **kw)
2416+
2417+ self.assertEqual(self.obj.ref_count, 0)
2418+ msg = 'Attempting to decrease ref_count to a negative value (-4).'
2419+ self.assertTrue(self.memento.check_warning(msg))
2420+
2421+ def test_ref_counting(self):
2422+ """Ref counting is in place."""
2423+ self.assertEqual(self.obj.ref_count, 0)
2424+
2425+ @defer.inlineCallbacks
2426 def test_find_credentials(self):
2427- """Test that the call is relayed."""
2428- app_name = 'app'
2429- args = 'args'
2430- self.root.find_credentials(app_name, args, MATCH(callable),
2431- MATCH(callable))
2432- self.mocker.replay()
2433- self.cred.find_credentials(app_name, args)
2434-
2435+ """Keep proper track of ongoing requests."""
2436+ yield self.assert_refcounter_increased('find_credentials',
2437+ 'CredentialsFound')
2438+
2439+ @defer.inlineCallbacks
2440+ def test_find_credentials_with_success_cb(self):
2441+ """Keep proper track of ongoing requests."""
2442+ d = defer.Deferred()
2443+ counter = []
2444+
2445+ def in_the_middle():
2446+ """Store the ref_count value."""
2447+ counter.append(self.obj.ref_count)
2448+ return defer.succeed(TOKEN)
2449+
2450+ self.patch(self.factory.credentials, 'find_credentials', in_the_middle)
2451+ self.obj.find_credentials(APP_NAME, self.args,
2452+ success_cb=d.callback, error_cb=d.errback)
2453+ yield d
2454+ self.assertEqual(counter, [1])
2455+ self.assertEqual(self.obj.ref_count, 0)
2456+
2457+ @defer.inlineCallbacks
2458+ def test_find_credentials_with_error_cb(self):
2459+ """Keep proper track of ongoing requests."""
2460+ d = defer.Deferred()
2461+ counter = []
2462+
2463+ def in_the_middle():
2464+ """Store the ref_count value."""
2465+ counter.append(self.obj.ref_count)
2466+ return defer.fail('foo')
2467+
2468+ self.patch(self.factory.credentials, 'find_credentials', in_the_middle)
2469+ self.obj.find_credentials(APP_NAME, self.args,
2470+ success_cb=d.errback, error_cb=d.callback)
2471+
2472+ yield d
2473+ self.assertEqual(counter, [1])
2474+ self.assertEqual(self.obj.ref_count, 0)
2475+
2476+ @defer.inlineCallbacks
2477 def test_clear_credentials(self):
2478- """Test that the call is relayed."""
2479- app_name = 'app'
2480- args = 'args'
2481- self.root.clear_credentials(app_name, args, MATCH(callable),
2482- MATCH(callable))
2483- self.mocker.replay()
2484- self.cred.clear_credentials(app_name, args)
2485+ """Keep proper track of ongoing requests."""
2486+ yield self.assert_refcounter_increased('clear_credentials',
2487+ 'CredentialsCleared')
2488
2489+ @defer.inlineCallbacks
2490 def test_store_credentials(self):
2491- """Test that the call is relayed."""
2492- app_name = 'app'
2493- args = 'args'
2494- self.root.store_credentials(app_name, args, MATCH(callable),
2495- MATCH(callable))
2496- self.mocker.replay()
2497- self.cred.store_credentials(app_name, args)
2498-
2499- def test_register(self):
2500- """Test that the call is relayed."""
2501- app_name = 'app'
2502- args = 'args'
2503- self.root.register(app_name, args)
2504- self.mocker.replay()
2505- self.cred.register(app_name, args)
2506-
2507- def test_login(self):
2508- """Test that the call is relayed."""
2509- app_name = 'app'
2510- args = 'args'
2511- self.root.login(app_name, args)
2512- self.mocker.replay()
2513- self.cred.login(app_name, args)
2514+ """Keep proper track of ongoing requests."""
2515+ yield self.assert_refcounter_increased('store_credentials',
2516+ 'CredentialsStored')
2517+
2518+ def test_register(self):
2519+ """Keep proper track of ongoing requests."""
2520+ yield self.assert_refcounter_increased('register')
2521+
2522+ def test_login(self):
2523+ """Keep proper track of ongoing requests."""
2524+ yield self.assert_refcounter_increased('login')
2525+
2526+ def test_several_requests(self):
2527+ """Requests can be nested."""
2528+ self.obj.login(APP_NAME, self.args)
2529+ self.obj.register(APP_NAME, self.args)
2530+ self.obj.login(APP_NAME, self.args)
2531+ self.obj.register(APP_NAME, self.args)
2532+ self.obj.register(APP_NAME, self.args)
2533+
2534+ self.assertEqual(self.obj.ref_count, 5)
2535+
2536+ def test_credentials_found(self):
2537+ """Ref counter is decreased when a signal is sent."""
2538+ self.assert_refcounter_decreased('CredentialsFound', APP_NAME, TOKEN)
2539+
2540+ def test_credentials_not_found(self):
2541+ """Ref counter is decreased when a signal is sent."""
2542+ self.assert_refcounter_decreased('CredentialsNotFound', APP_NAME)
2543+
2544+ def test_credentials_cleared(self):
2545+ """Ref counter is decreased when a signal is sent."""
2546+ self.assert_refcounter_decreased('CredentialsCleared', APP_NAME)
2547+
2548+ def test_credentials_stored(self):
2549+ """Ref counter is decreased when a signal is sent."""
2550+ self.assert_refcounter_decreased('CredentialsStored', APP_NAME)
2551+
2552+ def test_credentials_error(self):
2553+ """Ref counter is decreased when a signal is sent."""
2554+ self.assert_refcounter_decreased('CredentialsError', APP_NAME,
2555+ SampleException('test'))
2556+
2557+ def test_authorization_denied(self):
2558+ """Ref counter is decreased when a signal is sent."""
2559+ self.assert_refcounter_decreased('AuthorizationDenied', APP_NAME)
2560+
2561+ def test_credentials_found_when_ref_count_is_not_positive(self):
2562+ """Ref counter is decreased when a signal is sent."""
2563+ self.assert_refcounter_negative('CredentialsFound', APP_NAME, TOKEN)
2564+
2565+ def test_credentials_not_found_when_ref_count_is_not_positive(self):
2566+ """Ref counter is decreased when a signal is sent."""
2567+ self.assert_refcounter_negative('CredentialsNotFound', APP_NAME)
2568+
2569+ def test_credentials_cleared_when_ref_count_is_not_positive(self):
2570+ """Ref counter is decreased when a signal is sent."""
2571+ self.assert_refcounter_negative('CredentialsCleared', APP_NAME)
2572+
2573+ def test_credentials_stored_when_ref_count_is_not_positive(self):
2574+ """Ref counter is decreased when a signal is sent."""
2575+ self.assert_refcounter_negative('CredentialsStored', APP_NAME)
2576+
2577+ def test_credentials_error_when_ref_count_is_not_positive(self):
2578+ """Ref counter is decreased when a signal is sent."""
2579+ self.assert_refcounter_negative('CredentialsError', APP_NAME,
2580+ SampleException('test'))
2581+
2582+ def test_autorization_denied_when_ref_count_is_not_positive(self):
2583+ """Ref counter is decreased when a signal is sent."""
2584+ self.assert_refcounter_negative('AuthorizationDenied', APP_NAME)
2585+
2586+ def test_on_zero_ref_count_shutdown(self):
2587+ """When ref count reaches 0, queue shutdown op."""
2588+ self.patch(self.obj, 'timeout_func', self._set_called)
2589+ self.obj.login(APP_NAME, self.args)
2590+ self.obj.CredentialsFound(APP_NAME, TOKEN)
2591+
2592+ self.assertEqual(self._called,
2593+ ((TIMEOUT_INTERVAL, self.obj.shutdown), {}))
2594+
2595+ def test_on_non_zero_ref_count_do_not_shutdown(self):
2596+ """If ref count is not 0, do not queue shutdown op."""
2597+ self.patch(self.obj, 'timeout_func', self._set_called)
2598+ self.obj.login(APP_NAME, self.args)
2599+
2600+ self.assertEqual(self._called, False)
2601+
2602+ def test_on_non_zero_ref_count_after_zero_do_not_shutdown(self):
2603+ """If the shutdown was queued, do not quit if counter is not zero."""
2604+
2605+ def fake_timeout_func(interval, func):
2606+ """Start a new request when the timer is started."""
2607+ self.obj.register(APP_NAME, self.args)
2608+ assert self.obj.ref_count > 0
2609+ func()
2610+
2611+ self.patch(self.obj, 'timeout_func', fake_timeout_func)
2612+ self.patch(self.obj, 'shutdown_func', self._set_called)
2613+
2614+ self.obj.login(APP_NAME, self.args)
2615+ self.obj.CredentialsFound(APP_NAME, TOKEN)
2616+ # counter reached 0, timeout_func was called
2617+
2618+ self.assertEqual(self._called, False, 'shutdown_func was not called')
2619+
2620+ def test_zero_ref_count_after_zero_do_shutdown(self):
2621+ """If the shutdown was queued, do quit if counter is zero."""
2622+
2623+ def fake_timeout_func(interval, func):
2624+ """Start a new request when the timer is started."""
2625+ assert self.obj.ref_count == 0
2626+ func()
2627+
2628+ self.patch(self.obj, 'timeout_func', fake_timeout_func)
2629+ self.patch(self.obj, 'shutdown_func', self._set_called)
2630+
2631+ self.obj.login(APP_NAME, self.args)
2632+ self.obj.CredentialsFound(APP_NAME, TOKEN)
2633+ # counter reached 0, timeout_func was called
2634+
2635+ self.assertEqual(self._called, ((), {}), 'shutdown_func was called')
2636+
2637+
2638+class CredentialsManagementClearTestCase(CredentialsManagementTestCase):
2639+ """Tests for the CredentialsManagement clear method."""
2640+
2641+ method = 'clear_credentials'
2642+ success_signal = 'CredentialsCleared'
2643+ success_result = (APP_NAME,)
2644+ others_should_errback = []
2645+
2646+ @defer.inlineCallbacks
2647+ def setUp(self):
2648+ yield super(CredentialsManagementClearTestCase, self).setUp()
2649+ self.call_method = getattr(self.obj, self.method)
2650+
2651+ def test_backend_called(self):
2652+ """The credentials backend is properly called."""
2653+ self.call_method(APP_NAME, self.args)
2654+ self.assert_recorder_called(self.factory, 'Credentials', APP_NAME)
2655+
2656+ @defer.inlineCallbacks
2657+ def test_does_not_block(self):
2658+ """Calling 'method' does not block but return thru signals."""
2659+ d = defer.Deferred()
2660+
2661+ self.patch(self.obj, self.success_signal, lambda *a: d.callback(a))
2662+ self.patch(self.obj, 'CredentialsError',
2663+ lambda *a: d.errback(SampleException(a)))
2664+ for signal in self.others_should_errback:
2665+ self.patch(self.obj, signal,
2666+ lambda *a: d.errback(AssertionError(a)))
2667+
2668+ self.call_method(APP_NAME, self.args)
2669+
2670+ result = yield d
2671+ self.assertEqual(result, self.success_result)
2672+
2673+ @defer.inlineCallbacks
2674+ def test_handles_error(self):
2675+ """If calling the backend fails, CredentialsError is sent."""
2676+ d = defer.Deferred()
2677+
2678+ self.patch(self.obj, self.success_signal, d.errback)
2679+ self.patch(self.obj, 'CredentialsError', lambda *a: d.callback(a))
2680+
2681+ exc = SampleException('baz')
2682+ self.patch(self.factory.credentials, self.method,
2683+ lambda *a: defer.fail(exc))
2684+ self.call_method(APP_NAME, self.args)
2685+
2686+ app_name, error = yield d
2687+ self.assertEqual(error, exc)
2688+ self.assertEqual(app_name, APP_NAME)
2689+
2690+
2691+class CredentialsManagementStoreTestCase(CredentialsManagementClearTestCase):
2692+ """Tests for the CredentialsManagement store method."""
2693+
2694+ method = 'store_credentials'
2695+ success_signal = 'CredentialsStored'
2696+
2697+
2698+class CredentialsManagementFindTestCase(CredentialsManagementClearTestCase):
2699+ """Tests for the CredentialsManagement find method."""
2700+
2701+ method = 'find_credentials'
2702+ success_signal = 'CredentialsFound'
2703+ success_result = (APP_NAME, TOKEN)
2704+ others_should_errback = ['CredentialsNotFound']
2705+
2706+ @defer.inlineCallbacks
2707+ def test_find_credentials_with_success_cb(self):
2708+ """The credentials are asked and returned in a thread_execute call."""
2709+ d = defer.Deferred()
2710+
2711+ self.obj.find_credentials(APP_NAME, self.args,
2712+ success_cb=d.callback, error_cb=d.errback)
2713+
2714+ creds = yield d
2715+ expected = yield self.factory.credentials.find_credentials()
2716+ self.assertEqual(creds, expected)
2717+ self.assert_recorder_called(self.factory, 'Credentials', APP_NAME)
2718+
2719+ @defer.inlineCallbacks
2720+ def test_find_credentials_with_error_cb(self):
2721+ """If find_credentials fails, error_cb is called."""
2722+ d = defer.Deferred()
2723+
2724+ self.patch(self.factory.credentials, 'find_credentials',
2725+ lambda *a: defer.fail(SampleException('baz')))
2726+ self.obj.find_credentials(APP_NAME, self.args,
2727+ success_cb=d.errback, error_cb=d.callback)
2728+
2729+ errdict = yield d
2730+ self.assertEqual(errdict["errtype"], "SampleException")
2731+ self.assert_recorder_called(self.factory, 'Credentials', APP_NAME)
2732+
2733+
2734+class CredentialsManagementNotFindTestCase(CredentialsManagementFindTestCase):
2735+ """Tests for the CredentialsManagement find method."""
2736+
2737+ success_signal = 'CredentialsNotFound'
2738+ success_result = (APP_NAME,)
2739+ others_should_errback = ['CredentialsFound']
2740+
2741+ @defer.inlineCallbacks
2742+ def setUp(self):
2743+ yield super(CredentialsManagementNotFindTestCase, self).setUp()
2744+ self.call_method = getattr(self.obj, self.method)
2745+ self.patch(self.factory.credentials, self.method,
2746+ lambda: defer.succeed({}))
2747+
2748+
2749+class CredentialsManagementOpsTestCase(CredentialsManagementTestCase):
2750+ """Tests for the CredentialsManagement login/register methods."""
2751+
2752+ @defer.inlineCallbacks
2753+ def setUp(self):
2754+ yield super(CredentialsManagementOpsTestCase, self).setUp()
2755+ self.args = dict((k, str(v)) for k, v in self.base_args.iteritems())
2756+ self.cred_args = self.base_args.copy()
2757+ self.cred_args[SUCCESS_CB_KEY] = self.obj.CredentialsFound
2758+ self.cred_args[ERROR_CB_KEY] = self.obj.CredentialsError
2759+ self.cred_args[DENIAL_CB_KEY] = self.obj.AuthorizationDenied
2760+
2761+ def test_register(self):
2762+ """The registration is correct."""
2763+ self.obj.register(APP_NAME, self.args)
2764+ self.assert_recorder_called(self.factory, 'Credentials',
2765+ APP_NAME, **self.cred_args)
2766+
2767+ def test_login(self):
2768+ """The login is correct."""
2769+ self.obj.login(APP_NAME, self.args)
2770+ self.assert_recorder_called(self.factory, 'Credentials',
2771+ APP_NAME, **self.cred_args)
2772
2773 def test_login_email_password(self):
2774- """Test that the call is relayed."""
2775- app_name = 'app'
2776- args = 'args'
2777- self.root.login_email_password(app_name, args)
2778- self.mocker.replay()
2779- self.cred.login_email_password(app_name, args)
2780+ """The login_email_password is correct."""
2781+ self.args['email'] = EMAIL
2782+ self.args['password'] = PASSWORD
2783+ self.obj.login_email_password(APP_NAME, self.args)
2784+ self.assert_recorder_called(self.factory, 'Credentials',
2785+ APP_NAME, **self.cred_args)
2786+
2787+
2788+class CredentialsManagementParamsTestCase(CredentialsManagementOpsTestCase):
2789+ """Tests for the CredentialsManagement extra parameters handling."""
2790+
2791+ @defer.inlineCallbacks
2792+ def setUp(self):
2793+ yield super(CredentialsManagementParamsTestCase, self).setUp()
2794+ self.args['dummy'] = 'nothing useful'
2795+
2796+
2797+class CredentialsManagementSignalsTestCase(CredentialsManagementTestCase):
2798+ """Tests for the CredentialsManagement DBus signals."""
2799+
2800+ def test_credentials_found(self):
2801+ """The CredentialsFound signal."""
2802+ self.obj.CredentialsFound(APP_NAME, TOKEN)
2803+ msgs = (self.obj.__class__.__name__,
2804+ self.obj.CredentialsFound.__name__, APP_NAME)
2805+ self.assertTrue(self.memento.check_info(*msgs))
2806+
2807+ msg = 'credentials must not be logged (found %r in log).'
2808+ for val in TOKEN.itervalues():
2809+ self.assertFalse(self.memento.check_info(val), msg % val)
2810+
2811+ def test_credentials_not_found(self):
2812+ """The CredentialsNotFound signal."""
2813+ self.obj.CredentialsNotFound(APP_NAME)
2814+ msgs = (self.obj.__class__.__name__,
2815+ self.obj.CredentialsNotFound.__name__, APP_NAME)
2816+ self.assertTrue(self.memento.check_info(*msgs))
2817+
2818+ def test_credentials_cleared(self):
2819+ """The CredentialsCleared signal."""
2820+ self.obj.CredentialsCleared(APP_NAME)
2821+ msgs = (self.obj.__class__.__name__,
2822+ self.obj.CredentialsCleared.__name__, APP_NAME)
2823+ self.assertTrue(self.memento.check_info(*msgs))
2824+
2825+ def test_credentials_stored(self):
2826+ """The CredentialsStored signal."""
2827+ self.obj.CredentialsStored(APP_NAME)
2828+ msgs = (self.obj.__class__.__name__,
2829+ self.obj.CredentialsStored.__name__, APP_NAME)
2830+ self.assertTrue(self.memento.check_info(*msgs))
2831+
2832+ def test_credentials_error(self):
2833+ """The CredentialsError signal."""
2834+ error = {'error_message': 'failed!', 'detailed error': 'yadda yadda'}
2835+ self.obj.CredentialsError(APP_NAME, error)
2836+ msgs = (self.obj.__class__.__name__,
2837+ self.obj.CredentialsError.__name__,
2838+ APP_NAME, str(error))
2839+ self.assertTrue(self.memento.check_error(*msgs))
2840+
2841+ def test_authorization_denied(self):
2842+ """The AuthorizationDenied signal."""
2843+ self.obj.AuthorizationDenied(APP_NAME)
2844+ msgs = (self.obj.__class__.__name__,
2845+ self.obj.AuthorizationDenied.__name__, APP_NAME)
2846+ self.assertTrue(self.memento.check_info(*msgs))
2847+
2848+
2849+class UbuntuSSOServiceTestCase(BaseTestCase):
2850+ """Test suite for the UbuntuSSOService class."""
2851+
2852+ @defer.inlineCallbacks
2853+ def setUp(self):
2854+ yield super(UbuntuSSOServiceTestCase, self).setUp()
2855+ self.proxy.sso_login = object()
2856+ self.proxy.cred_manager = object()
2857+ self.params = []
2858+ self.patch(main, 'UbuntuSSOProxy',
2859+ lambda _: self.params.append(_) or self.proxy)
2860+ self.obj = UbuntuSSOService()
2861+ yield self.obj.start()
2862+
2863+ def test_creation(self):
2864+ """Creation paremeter is properly used."""
2865+ self.assertEqual(self.params, [self.obj])
2866+
2867+ def test_sso_login(self):
2868+ """Attributes has the expected value."""
2869+ self.assertIsInstance(self.obj.sso_login, SSOLogin)
2870+ self.assertTrue(self.obj.sso_login.proxy is self.proxy.sso_login)
2871+
2872+ def test_cred_manager(self):
2873+ """Attributes has the expected value."""
2874+ self.assertIsInstance(self.obj.cred_manager, CredentialsManagement)
2875+ self.assertTrue(self.obj.cred_manager.proxy is self.proxy.cred_manager)
2876
2877=== removed file 'ubuntu_sso/main/tests/test_linux.py'
2878--- ubuntu_sso/main/tests/test_linux.py 2011-12-19 19:35:06 +0000
2879+++ ubuntu_sso/main/tests/test_linux.py 1970-01-01 00:00:00 +0000
2880@@ -1,1275 +0,0 @@
2881-# -*- coding: utf-8 -*-
2882-#
2883-# test_main - tests for ubuntu_sso.main
2884-#
2885-# Author: Natalia Bidart <natalia.bidart@canonical.com>
2886-# Author: Alejandro J. Cura <alecu@canonical.com>
2887-#
2888-# Copyright 2009-2010 Canonical Ltd.
2889-#
2890-# This program is free software: you can redistribute it and/or modify it
2891-# under the terms of the GNU General Public License version 3, as published
2892-# by the Free Software Foundation.
2893-#
2894-# This program is distributed in the hope that it will be useful, but
2895-# WITHOUT ANY WARRANTY; without even the implied warranties of
2896-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2897-# PURPOSE. See the GNU General Public License for more details.
2898-#
2899-# You should have received a copy of the GNU General Public License along
2900-# with this program. If not, see <http://www.gnu.org/licenses/>.
2901-"""Tests for the main SSO client code."""
2902-
2903-import logging
2904-
2905-from mocker import Mocker, ARGS, KWARGS
2906-from twisted.internet import defer
2907-from ubuntuone.devtools.handlers import MementoHandler
2908-
2909-import ubuntu_sso.keyring
2910-import ubuntu_sso.main
2911-import ubuntu_sso.main.linux
2912-
2913-from ubuntu_sso import DBUS_CREDENTIALS_IFACE
2914-from ubuntu_sso.main import (
2915- except_to_errdict,
2916- CredentialsManagement,
2917- SSOLogin,
2918- TIMEOUT_INTERVAL,
2919-)
2920-from ubuntu_sso.main.linux import blocking
2921-from ubuntu_sso.main import (HELP_TEXT_KEY, PING_URL_KEY,
2922- TC_URL_KEY, UI_CLASS_KEY, UI_MODULE_KEY, WINDOW_ID_KEY,
2923- SUCCESS_CB_KEY, ERROR_CB_KEY, DENIAL_CB_KEY)
2924-from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID,
2925- CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, PING_URL, TOKEN,
2926- TOKEN_NAME, WINDOW_ID, TestCase)
2927-
2928-
2929-# Access to a protected member 'yyy' of a client class
2930-# pylint: disable=W0212
2931-
2932-
2933-class BlockingSampleException(Exception):
2934- """The exception that will be thrown by the fake blocking."""
2935-
2936-
2937-def fake_ok_blocking(f, app, cb, eb):
2938- """A fake blocking function that succeeds."""
2939- cb(app, f())
2940-
2941-
2942-def fake_err_blocking(f, app, cb, eb):
2943- """A fake blocking function that fails."""
2944- try:
2945- f()
2946- except Exception, e: # pylint: disable=W0703
2947- eb(app, except_to_errdict(e))
2948- else:
2949- eb(app, except_to_errdict(BlockingSampleException()))
2950-
2951-
2952-class SsoDbusTestCase(TestCase):
2953- """Test the SSOLogin DBus interface."""
2954-
2955- timeout = 2
2956-
2957- @defer.inlineCallbacks
2958- def setUp(self):
2959- """Create the mocking bus."""
2960- yield super(SsoDbusTestCase, self).setUp()
2961- self.mocker = Mocker()
2962- self.mockbusname = self.mocker.mock()
2963- mockbus = self.mocker.mock()
2964- self.mockbusname.get_bus()
2965- self.mocker.result(mockbus)
2966- mockbus._register_object_path(ARGS)
2967- self.mockprocessorclass = None
2968-
2969- def ksc(keyring, k, val):
2970- """Assert over token and app_name."""
2971- self.assertEqual(k, APP_NAME)
2972- self.assertEqual(val, TOKEN)
2973- self.keyring_was_set = True
2974- self.keyring_values = k, val
2975- return defer.succeed(None)
2976-
2977- self.patch(ubuntu_sso.main.Keyring, "set_credentials", ksc)
2978- self.keyring_was_set = False
2979- self.keyring_values = None
2980-
2981- @defer.inlineCallbacks
2982- def tearDown(self):
2983- """Verify the mocking bus and shut it down."""
2984- self.mocker.verify()
2985- self.mocker.restore()
2986- yield super(SsoDbusTestCase, self).tearDown()
2987-
2988- def test_creation(self):
2989- """Test that the object creation is successful."""
2990- self.mocker.replay()
2991- SSOLogin(self.mockbusname)
2992-
2993- def create_mock_processor(self):
2994- """Create a mock processor from a dummy processor class."""
2995- self.mockprocessorclass = self.mocker.mock()
2996- mockprocessor = self.mocker.mock()
2997- self.mockprocessorclass(ARGS, KWARGS)
2998- self.mocker.result(mockprocessor)
2999- return mockprocessor
3000-
3001- def test_generate_captcha(self):
3002- """Test that the captcha method works ok."""
3003- d = defer.Deferred()
3004- filename = "sample filename"
3005- expected_result = "expected result"
3006- self.create_mock_processor().generate_captcha(filename)
3007- self.mocker.result(expected_result)
3008- self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking)
3009- self.mocker.replay()
3010-
3011- def verify(app_name, result):
3012- """The actual test."""
3013- self.assertEqual(result, expected_result)
3014- self.assertEqual(app_name, APP_NAME)
3015- d.callback(result)
3016-
3017- client = SSOLogin(self.mockbusname,
3018- sso_login_processor_class=self.mockprocessorclass)
3019- self.patch(client, "CaptchaGenerated", verify)
3020- self.patch(client, "CaptchaGenerationError", d.errback)
3021- client.generate_captcha(APP_NAME, filename)
3022- return d
3023-
3024- def test_generate_captcha_error(self):
3025- """Test that the captcha method fails as expected."""
3026- d = defer.Deferred()
3027- filename = "sample filename"
3028- expected_result = "expected result"
3029- self.create_mock_processor().generate_captcha(filename)
3030- self.mocker.result(expected_result)
3031- self.patch(ubuntu_sso.main, "thread_execute", fake_err_blocking)
3032- self.mocker.replay()
3033-
3034- def verify(app_name, errdict):
3035- """The actual test."""
3036- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3037- self.assertEqual(app_name, APP_NAME)
3038- d.callback("Ok")
3039-
3040- client = SSOLogin(self.mockbusname,
3041- sso_login_processor_class=self.mockprocessorclass)
3042- self.patch(client, "CaptchaGenerated", d.errback)
3043- self.patch(client, "CaptchaGenerationError", verify)
3044- client.generate_captcha(APP_NAME, filename)
3045- return d
3046-
3047- def test_register_user(self):
3048- """Test that the register_user method works ok."""
3049- d = defer.Deferred()
3050- expected_result = "expected result"
3051- self.create_mock_processor().register_user(EMAIL, PASSWORD, NAME,
3052- CAPTCHA_ID, CAPTCHA_SOLUTION)
3053- self.mocker.result(expected_result)
3054- self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking)
3055- self.mocker.replay()
3056-
3057- def verify(app_name, result):
3058- """The actual test."""
3059- self.assertEqual(result, expected_result)
3060- self.assertEqual(app_name, APP_NAME)
3061- d.callback(result)
3062-
3063- client = SSOLogin(self.mockbusname,
3064- sso_login_processor_class=self.mockprocessorclass)
3065- self.patch(client, "UserRegistered", verify)
3066- self.patch(client, "UserRegistrationError", d.errback)
3067- client.register_user(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID,
3068- CAPTCHA_SOLUTION)
3069- return d
3070-
3071- def test_register_user_error(self):
3072- """Test that the register_user method fails as expected."""
3073- d = defer.Deferred()
3074- expected_result = "expected result"
3075- self.create_mock_processor().register_user(EMAIL, PASSWORD, NAME,
3076- CAPTCHA_ID, CAPTCHA_SOLUTION)
3077- self.mocker.result(expected_result)
3078- self.patch(ubuntu_sso.main, "thread_execute", fake_err_blocking)
3079- self.mocker.replay()
3080-
3081- def verify(app_name, errdict):
3082- """The actual test."""
3083- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3084- self.assertEqual(app_name, APP_NAME)
3085- d.callback("Ok")
3086-
3087- client = SSOLogin(self.mockbusname,
3088- sso_login_processor_class=self.mockprocessorclass)
3089- self.patch(client, "UserRegistered", d.errback)
3090- self.patch(client, "UserRegistrationError", verify)
3091- client.register_user(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID,
3092- CAPTCHA_SOLUTION)
3093- return d
3094-
3095- def test_login(self):
3096- """Test that the login method works ok."""
3097- d = defer.Deferred()
3098- processor = self.create_mock_processor()
3099- processor.login(EMAIL, PASSWORD, TOKEN_NAME)
3100- self.mocker.result(TOKEN)
3101- processor.is_validated(TOKEN)
3102- self.mocker.result(True)
3103- self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking)
3104- self.mocker.replay()
3105-
3106- def verify(app_name, result):
3107- """The actual test."""
3108- self.assertEqual(result, EMAIL)
3109- self.assertEqual(app_name, APP_NAME)
3110- self.assertTrue(self.keyring_was_set, "The keyring should be set")
3111- d.callback(result)
3112-
3113- client = SSOLogin(self.mockbusname,
3114- sso_login_processor_class=self.mockprocessorclass)
3115- self.patch(client, "LoggedIn", verify)
3116- self.patch(client, "LoginError", d.errback)
3117- self.patch(client, "UserNotValidated", d.errback)
3118- client.login(APP_NAME, EMAIL, PASSWORD)
3119- return d
3120-
3121- def test_login_user_not_validated(self):
3122- """Test that the login sends EmailNotValidated signal."""
3123- d = defer.Deferred()
3124- processor = self.create_mock_processor()
3125- processor.login(EMAIL, PASSWORD, TOKEN_NAME)
3126- self.mocker.result(TOKEN)
3127- processor.is_validated(TOKEN)
3128- self.mocker.result(False)
3129- self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking)
3130- self.mocker.replay()
3131-
3132- def verify(app_name, email):
3133- """The actual test."""
3134- self.assertEqual(app_name, APP_NAME)
3135- self.assertEqual(email, EMAIL)
3136- self.assertFalse(self.keyring_was_set, "Keyring should not be set")
3137- d.callback("Ok")
3138-
3139- client = SSOLogin(self.mockbusname,
3140- sso_login_processor_class=self.mockprocessorclass)
3141- self.patch(client, "LoggedIn", d.errback)
3142- self.patch(client, "LoginError", d.errback)
3143- self.patch(client, "UserNotValidated", verify)
3144- client.login(APP_NAME, EMAIL, PASSWORD)
3145- return d
3146-
3147- def test_login_error_get_token_name(self):
3148- """The login method fails as expected when get_token_name fails."""
3149- d = defer.Deferred()
3150- self.create_mock_processor()
3151- self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking)
3152-
3153- def fake_gtn(*args):
3154- """A fake get_token_name that fails."""
3155- raise BlockingSampleException()
3156-
3157- self.patch(ubuntu_sso.main, "get_token_name", fake_gtn)
3158- self.mocker.replay()
3159-
3160- def verify(app_name, errdict):
3161- """The actual test."""
3162- self.assertEqual(app_name, APP_NAME)
3163- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3164- self.assertFalse(self.keyring_was_set, "Keyring should not be set")
3165- d.callback("Ok")
3166-
3167- client = SSOLogin(self.mockbusname,
3168- sso_login_processor_class=self.mockprocessorclass)
3169- self.patch(client, "LoggedIn", d.errback)
3170- self.patch(client, "LoginError", verify)
3171- self.patch(client, "UserNotValidated", d.errback)
3172- client.login(APP_NAME, EMAIL, PASSWORD)
3173- return d
3174-
3175- def test_login_error_set_credentials(self):
3176- """The login method fails as expected when set_credentials fails."""
3177- d = defer.Deferred()
3178- processor = self.create_mock_processor()
3179- processor.login(EMAIL, PASSWORD, TOKEN_NAME)
3180- self.mocker.result(TOKEN)
3181- processor.is_validated(TOKEN)
3182- self.mocker.result(True)
3183-
3184- self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking)
3185-
3186- def fake_set_creds(*args):
3187- """A fake Keyring.set_credentials that fails."""
3188- return defer.fail(BlockingSampleException())
3189-
3190- self.patch(ubuntu_sso.main.Keyring, "set_credentials", fake_set_creds)
3191- self.mocker.replay()
3192-
3193- def verify(app_name, errdict):
3194- """The actual test."""
3195- self.assertEqual(app_name, APP_NAME)
3196- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3197- self.assertFalse(self.keyring_was_set, "Keyring should not be set")
3198- d.callback("Ok")
3199-
3200- client = SSOLogin(self.mockbusname,
3201- sso_login_processor_class=self.mockprocessorclass)
3202- fail = lambda app, res: d.errback((app, res))
3203- self.patch(client, "LoggedIn", fail)
3204- self.patch(client, "LoginError", verify)
3205- self.patch(client, "UserNotValidated", fail)
3206- client.login(APP_NAME, EMAIL, PASSWORD)
3207- return d
3208-
3209- def test_validate_email(self):
3210- """Test that the validate_email method works ok."""
3211- d = defer.Deferred()
3212- self.create_mock_processor().validate_email(EMAIL, PASSWORD,
3213- EMAIL_TOKEN, TOKEN_NAME)
3214- self.mocker.result(TOKEN)
3215- self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking)
3216- self.mocker.replay()
3217-
3218- def verify(app_name, result):
3219- """The actual test."""
3220- self.assertEqual(result, EMAIL)
3221- self.assertEqual(app_name, APP_NAME)
3222- self.assertTrue(self.keyring_was_set, "The keyring should be set")
3223- d.callback(result)
3224-
3225- client = SSOLogin(self.mockbusname,
3226- sso_login_processor_class=self.mockprocessorclass)
3227- self.patch(client, "EmailValidated", verify)
3228- self.patch(client, "EmailValidationError", d.errback)
3229- client.validate_email(APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN)
3230- return d
3231-
3232- def test_validate_email_error(self):
3233- """Test that the validate_email method fails as expected."""
3234- d = defer.Deferred()
3235- self.create_mock_processor()
3236- self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking)
3237-
3238- def fake_gtn(*args):
3239- """A fake get_token_name that fails."""
3240- raise BlockingSampleException()
3241-
3242- self.patch(ubuntu_sso.main, "get_token_name", fake_gtn)
3243- self.mocker.replay()
3244-
3245- def verify(app_name, errdict):
3246- """The actual test."""
3247- self.assertEqual(app_name, APP_NAME)
3248- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3249- self.assertFalse(self.keyring_was_set, "Keyring should not be set")
3250- d.callback("Ok")
3251-
3252- client = SSOLogin(self.mockbusname,
3253- sso_login_processor_class=self.mockprocessorclass)
3254- self.patch(client, "EmailValidated", d.errback)
3255- self.patch(client, "EmailValidationError", verify)
3256- client.validate_email(APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN)
3257- return d
3258-
3259- def test_request_password_reset_token(self):
3260- """Test that the request_password_reset_token method works ok."""
3261- d = defer.Deferred()
3262- processor = self.create_mock_processor()
3263- processor.request_password_reset_token(EMAIL)
3264- self.patch(ubuntu_sso.main, "thread_execute", fake_ok_blocking)
3265- self.mocker.result(EMAIL)
3266- self.mocker.replay()
3267-
3268- def verify(app_name, result):
3269- """The actual test."""
3270- self.assertEqual(result, EMAIL)
3271- self.assertEqual(app_name, APP_NAME)
3272- d.callback(result)
3273-
3274- client = SSOLogin(self.mockbusname,
3275- sso_login_processor_class=self.mockprocessorclass)
3276- self.patch(client, "PasswordResetTokenSent", verify)
3277- self.patch(client, "PasswordResetError", d.errback)
3278- client.request_password_reset_token(APP_NAME, EMAIL)
3279- return d
3280-
3281- def test_request_password_reset_token_error(self):
3282- """Test the request_password_reset_token method fails as expected."""
3283- d = defer.Deferred()
3284-
3285- self.create_mock_processor().request_password_reset_token(EMAIL)
3286- self.mocker.result(EMAIL)
3287- self.patch(ubuntu_sso.main, "thread_execute", fake_err_blocking)
3288- self.mocker.replay()
3289-
3290- def verify(app_name, errdict):
3291- """The actual test."""
3292- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3293- self.assertEqual(app_name, APP_NAME)
3294- d.callback("Ok")
3295-
3296- client = SSOLogin(self.mockbusname,
3297- sso_login_processor_class=self.mockprocessorclass)
3298- self.patch(client, "PasswordResetTokenSent", d.errback)
3299- self.patch(client, "PasswordResetError", verify)
3300- client.request_password_reset_token(APP_NAME, EMAIL)
3301- return d
3302-
3303- def test_set_new_password(self):
3304- """Test that the set_new_password method works ok."""
3305- d = defer.Deferred()
3306- self.create_mock_processor().set_new_password(EMAIL, EMAIL_TOKEN,
3307- PASSWORD)
3308- self.mocker.result(EMAIL)
3309- self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking)
3310- self.mocker.replay()
3311-
3312- def verify(app_name, result):
3313- """The actual test."""
3314- self.assertEqual(result, EMAIL)
3315- self.assertEqual(app_name, APP_NAME)
3316- d.callback(result)
3317-
3318- client = SSOLogin(self.mockbusname,
3319- sso_login_processor_class=self.mockprocessorclass)
3320- self.patch(client, "PasswordChanged", verify)
3321- self.patch(client, "PasswordChangeError", d.errback)
3322- client.set_new_password(APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD)
3323- return d
3324-
3325- def test_set_new_password_error(self):
3326- """Test that the set_new_password method fails as expected."""
3327- d = defer.Deferred()
3328- expected_result = "expected result"
3329-
3330- self.create_mock_processor().set_new_password(EMAIL, EMAIL_TOKEN,
3331- PASSWORD)
3332- self.mocker.result(expected_result)
3333- self.patch(ubuntu_sso.main, "thread_execute", fake_err_blocking)
3334- self.mocker.replay()
3335-
3336- def verify(app_name, errdict):
3337- """The actual test."""
3338- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3339- self.assertEqual(app_name, APP_NAME)
3340- d.callback("Ok")
3341-
3342- client = SSOLogin(self.mockbusname,
3343- sso_login_processor_class=self.mockprocessorclass)
3344- self.patch(client, "PasswordChanged", d.errback)
3345- self.patch(client, "PasswordChangeError", verify)
3346- client.set_new_password(APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD)
3347- return d
3348-
3349-
3350-class BlockingFunctionTestCase(TestCase):
3351- """Tests for the "blocking" function."""
3352-
3353- timeout = 5
3354-
3355- def test_blocking(self):
3356- """Test the normal behaviour."""
3357- d = defer.Deferred()
3358- expected_result = "expected result"
3359-
3360- def f():
3361- """No failure."""
3362- return expected_result
3363-
3364- def verify(app_name, result):
3365- """The actual test."""
3366- self.assertEqual(result, expected_result)
3367- self.assertEqual(app_name, APP_NAME)
3368- d.callback(result)
3369-
3370- blocking(f, APP_NAME, verify, d.errback)
3371- return d
3372-
3373- def test_blocking_error(self):
3374- """Test the behaviour when an Exception is raised."""
3375- d = defer.Deferred()
3376- expected_error_message = "expected error message"
3377-
3378- def f():
3379- """Failure."""
3380- raise BlockingSampleException(expected_error_message)
3381-
3382- def verify(app_name, errdict):
3383- """The actual test."""
3384- self.assertEqual(app_name, APP_NAME)
3385- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3386- self.assertEqual(errdict["message"], expected_error_message)
3387- d.callback("Ok")
3388-
3389- blocking(f, APP_NAME, d.errback, verify)
3390- return d
3391-
3392-
3393-class TestExceptToErrdictException(Exception):
3394- """A dummy exception for the following testcase."""
3395-
3396-
3397-class ExceptToErrdictTestCase(TestCase):
3398- """Tests for the except_to_errdict function."""
3399-
3400- def test_first_arg_is_dict(self):
3401- """If the first arg is a dict, use it as the base dict."""
3402- sample_dict = {
3403- "errorcode1": "error message 1",
3404- "errorcode2": "error message 2",
3405- "errorcode3": "error message 3",
3406- }
3407- e = TestExceptToErrdictException(sample_dict)
3408- result = except_to_errdict(e)
3409-
3410- self.assertEqual(result["errtype"], e.__class__.__name__)
3411- for k in sample_dict.keys():
3412- self.assertIn(k, result)
3413- self.assertEqual(result[k], sample_dict[k])
3414-
3415- def test_first_arg_is_str(self):
3416- """If the first arg is a str, use it as the message."""
3417- sample_string = "a sample string"
3418- e = TestExceptToErrdictException(sample_string)
3419- result = except_to_errdict(e)
3420- self.assertEqual(result["errtype"], e.__class__.__name__)
3421- self.assertEqual(result["message"], sample_string)
3422-
3423- def test_first_arg_is_unicode(self):
3424- """If the first arg is a unicode, use it as the message."""
3425- sample_string = u"a sample string"
3426- e = TestExceptToErrdictException(sample_string)
3427- result = except_to_errdict(e)
3428- self.assertEqual(result["errtype"], e.__class__.__name__)
3429- self.assertEqual(result["message"], sample_string)
3430-
3431- def test_no_args_at_all(self):
3432- """If there are no args, use the class docstring."""
3433- e = TestExceptToErrdictException()
3434- result = except_to_errdict(e)
3435- self.assertEqual(result["errtype"], e.__class__.__name__)
3436- self.assertEqual(result["message"], e.__class__.__doc__)
3437-
3438- def test_some_other_thing_as_first_arg(self):
3439- """If first arg is not basestring nor dict, then repr all args."""
3440- sample_args = (None, u"unicode2\ufffd", "errorcode3")
3441- e = TestExceptToErrdictException(*sample_args)
3442- result = except_to_errdict(e)
3443- self.assertEqual(result["errtype"], e.__class__.__name__)
3444-
3445-
3446-class CredentialsManagementTestCase(TestCase):
3447- """Tests for the CredentialsManagement DBus interface."""
3448-
3449- timeout = 2
3450- base_args = {HELP_TEXT_KEY: HELP_TEXT, PING_URL_KEY: PING_URL,
3451- TC_URL_KEY: TC_URL, WINDOW_ID_KEY: WINDOW_ID,
3452- UI_CLASS_KEY: 'SuperUI', UI_MODULE_KEY: 'foo.bar.baz',
3453- }
3454-
3455- @defer.inlineCallbacks
3456- def setUp(self):
3457- yield super(CredentialsManagementTestCase, self).setUp()
3458-
3459- self.mocker = Mocker()
3460- self.client = CredentialsManagement(timeout_func=lambda *a: None,
3461- shutdown_func=lambda *a: None)
3462- self.args = {}
3463- self.cred_args = {}
3464-
3465- self.memento = MementoHandler()
3466- self.memento.setLevel(logging.DEBUG)
3467- ubuntu_sso.main.logger.addHandler(self.memento)
3468- self.addCleanup(ubuntu_sso.main.logger.removeHandler, self.memento)
3469-
3470- @defer.inlineCallbacks
3471- def tearDown(self):
3472- """Verify the mocking stuff and shut it down."""
3473- self.mocker.verify()
3474- self.mocker.restore()
3475- yield super(CredentialsManagementTestCase, self).tearDown()
3476-
3477- def assert_dbus_method_correct(self, method, out_signature=''):
3478- """Check that 'method' is a dbus method with proper signatures."""
3479- self.assertTrue(method._dbus_is_method)
3480- self.assertEqual(method._dbus_interface, DBUS_CREDENTIALS_IFACE)
3481- self.assertEqual(method._dbus_in_signature, 'sa{ss}')
3482- self.assertEqual(method._dbus_out_signature, out_signature)
3483-
3484- def create_mock_backend(self):
3485- """Create a mock backend."""
3486- mock_class = self.mocker.replace("ubuntu_sso.credentials.Credentials")
3487- mock_class(APP_NAME, **self.cred_args)
3488- creds_obj = self.mocker.mock()
3489- self.mocker.result(creds_obj)
3490-
3491- return creds_obj
3492-
3493- def test_is_dbus_object(self):
3494- """CredentialsManagement is a Dbus object."""
3495- self.assertIsInstance(self.client,
3496- ubuntu_sso.main.linux.dbus.service.Object)
3497-
3498-
3499-class FakeCredentials(object):
3500- """A very dummy Credentials object."""
3501-
3502- def __init__(self, *a, **kw):
3503- self.clear_credentials = lambda *a: defer.succeed(None)
3504- self.store_credentials = lambda *a: defer.succeed(None)
3505- self.login = self.register = lambda *a: None
3506-
3507- def find_credentials(self, *a, **kw):
3508- """Retrieve credentials."""
3509- return defer.succeed(TOKEN)
3510-
3511-
3512-class CredentialsManagementRefCountingTestCase(CredentialsManagementTestCase):
3513- """Tests for the CredentialsManagement ref counting."""
3514-
3515- @defer.inlineCallbacks
3516- def setUp(self):
3517- yield super(CredentialsManagementRefCountingTestCase, self).setUp()
3518- self.patch(ubuntu_sso.main, 'Credentials', FakeCredentials)
3519-
3520- def test_ref_counting(self):
3521- """Ref counting is in place."""
3522- self.assertEqual(self.client.root.ref_count, 0)
3523-
3524- def test_find_credentials(self):
3525- """Keep proper track of on going requests."""
3526- d = defer.Deferred()
3527-
3528- def verify(*args):
3529- """Make the check."""
3530- self.assertEqual(self.client.root.ref_count, 1)
3531- d.callback(True)
3532-
3533- self.patch(self.client, 'CredentialsFound', verify)
3534- self.client.find_credentials(APP_NAME, self.args)
3535-
3536- return d
3537-
3538- @defer.inlineCallbacks
3539- def test_find_credentials_sync(self):
3540- """Keep proper track of on going requests."""
3541- d = defer.Deferred()
3542-
3543- def verify(*args):
3544- """Make the check."""
3545- self.assertEqual(self.client.root.ref_count, 1)
3546- d.callback(True)
3547-
3548- self.client.find_credentials_sync(APP_NAME, self.args,
3549- reply_handler=verify,
3550- error_handler=d.errback)
3551- yield d
3552- self.assertEqual(self.client.root.ref_count, 0)
3553-
3554- @defer.inlineCallbacks
3555- def test_find_credentials_sync_error(self):
3556- """Keep proper track of on going requests."""
3557- d = defer.Deferred()
3558-
3559- def verify(*args):
3560- """Make the check."""
3561- self.assertEqual(self.client.root.ref_count, 1)
3562- d.callback(True)
3563-
3564- self.patch(ubuntu_sso.main.Credentials, 'find_credentials',
3565- lambda *a: defer.fail('foo'))
3566- self.client.find_credentials_sync(APP_NAME, self.args,
3567- reply_handler=d.errback,
3568- error_handler=verify)
3569-
3570- yield d
3571- self.assertEqual(self.client.root.ref_count, 0)
3572-
3573- def test_clear_credentials(self):
3574- """Keep proper track of on going requests."""
3575- d = defer.Deferred()
3576-
3577- def verify(*args):
3578- """Make the check."""
3579- self.assertEqual(self.client.root.ref_count, 1)
3580- d.callback(True)
3581-
3582- self.patch(self.client, 'CredentialsCleared', verify)
3583- self.client.clear_credentials(APP_NAME, self.args)
3584-
3585- return d
3586-
3587- def test_store_credentials(self):
3588- """Keep proper track of on going requests."""
3589- d = defer.Deferred()
3590-
3591- def verify(*args):
3592- """Make the check."""
3593- self.assertEqual(self.client.root.ref_count, 1)
3594- d.callback(True)
3595-
3596- self.patch(self.client, 'CredentialsStored', verify)
3597- self.client.store_credentials(APP_NAME, self.args)
3598-
3599- return d
3600-
3601- def test_register(self):
3602- """Keep proper track of on going requests."""
3603- self.client.register(APP_NAME, self.args)
3604-
3605- self.assertEqual(self.client.root.ref_count, 1)
3606-
3607- def test_login(self):
3608- """Keep proper track of on going requests."""
3609- self.client.login(APP_NAME, self.args)
3610-
3611- self.assertEqual(self.client.root.ref_count, 1)
3612-
3613- def test_several_requests(self):
3614- """Requests can be nested."""
3615- self.client.login(APP_NAME, self.args)
3616- self.client.register(APP_NAME, self.args)
3617- self.client.login(APP_NAME, self.args)
3618- self.client.register(APP_NAME, self.args)
3619- self.client.register(APP_NAME, self.args)
3620-
3621- self.assertEqual(self.client.root.ref_count, 5)
3622-
3623- def test_credentials_found(self):
3624- """Ref counter is decreased when a signal is sent."""
3625- self.client.root.ref_count = 3
3626- self.client.CredentialsFound(APP_NAME, TOKEN)
3627-
3628- self.assertEqual(self.client.root.ref_count, 2)
3629-
3630- def test_credentials_not_found(self):
3631- """Ref counter is decreased when a signal is sent."""
3632- self.client.root.ref_count = 3
3633- self.client.CredentialsNotFound(APP_NAME)
3634-
3635- self.assertEqual(self.client.root.ref_count, 2)
3636-
3637- def test_credentials_cleared(self):
3638- """Ref counter is decreased when a signal is sent."""
3639- self.client.root.ref_count = 3
3640- self.client.CredentialsCleared(APP_NAME)
3641-
3642- self.assertEqual(self.client.root.ref_count, 2)
3643-
3644- def test_credentials_stored(self):
3645- """Ref counter is decreased when a signal is sent."""
3646- self.client.root.ref_count = 3
3647- self.client.CredentialsStored(APP_NAME)
3648-
3649- self.assertEqual(self.client.root.ref_count, 2)
3650-
3651- def test_credentials_error(self):
3652- """Ref counter is decreased when a signal is sent."""
3653- self.client.root.ref_count = 3
3654- self.client.CredentialsError(APP_NAME, {'error_type': 'test'})
3655-
3656- self.assertEqual(self.client.root.ref_count, 2)
3657-
3658- def test_authorization_denied(self):
3659- """Ref counter is decreased when a signal is sent."""
3660- self.client.root.ref_count = 3
3661- self.client.AuthorizationDenied(APP_NAME)
3662-
3663- self.assertEqual(self.client.root.ref_count, 2)
3664-
3665- def test_credentials_found_when_ref_count_is_not_positive(self):
3666- """Ref counter is decreased when a signal is sent."""
3667- self.client.root._ref_count = -3
3668- self.client.CredentialsFound(APP_NAME, TOKEN)
3669-
3670- self.assertEqual(self.client.root.ref_count, 0)
3671- msg = 'Attempting to decrease ref_count to a negative value (-4).'
3672- self.assertTrue(self.memento.check_warning(msg))
3673-
3674- def test_credentials_not_found_when_ref_count_is_not_positive(self):
3675- """Ref counter is decreased when a signal is sent."""
3676- self.client.root._ref_count = -3
3677- self.client.CredentialsNotFound(APP_NAME)
3678-
3679- self.assertEqual(self.client.root.ref_count, 0)
3680- msg = 'Attempting to decrease ref_count to a negative value (-4).'
3681- self.assertTrue(self.memento.check_warning(msg))
3682-
3683- def test_credentials_cleared_when_ref_count_is_not_positive(self):
3684- """Ref counter is decreased when a signal is sent."""
3685- self.client.root._ref_count = -3
3686- self.client.CredentialsCleared(APP_NAME)
3687-
3688- self.assertEqual(self.client.root.ref_count, 0)
3689- msg = 'Attempting to decrease ref_count to a negative value (-4).'
3690- self.assertTrue(self.memento.check_warning(msg))
3691-
3692- def test_credentials_stored_when_ref_count_is_not_positive(self):
3693- """Ref counter is decreased when a signal is sent."""
3694- self.client.root._ref_count = -3
3695- self.client.CredentialsStored(APP_NAME)
3696-
3697- self.assertEqual(self.client.root.ref_count, 0)
3698- msg = 'Attempting to decrease ref_count to a negative value (-4).'
3699- self.assertTrue(self.memento.check_warning(msg))
3700-
3701- def test_credentials_error_when_ref_count_is_not_positive(self):
3702- """Ref counter is decreased when a signal is sent."""
3703- self.client.root._ref_count = -3
3704- self.client.CredentialsError(APP_NAME, {'error_type': 'test'})
3705-
3706- self.assertEqual(self.client.root.ref_count, 0)
3707- msg = 'Attempting to decrease ref_count to a negative value (-4).'
3708- self.assertTrue(self.memento.check_warning(msg))
3709-
3710- def test_autorization_denied_when_ref_count_is_not_positive(self):
3711- """Ref counter is decreased when a signal is sent."""
3712- self.client.root._ref_count = -3
3713- self.client.AuthorizationDenied(APP_NAME)
3714-
3715- self.assertEqual(self.client.root.ref_count, 0)
3716- msg = 'Attempting to decrease ref_count to a negative value (-4).'
3717- self.assertTrue(self.memento.check_warning(msg))
3718-
3719- def test_on_zero_ref_count_shutdown(self):
3720- """When ref count reaches 0, queue shutdown op."""
3721- self.patch(self.client.root, 'timeout_func', self._set_called)
3722- self.client.login(APP_NAME, self.args)
3723- self.client.CredentialsFound(APP_NAME, TOKEN)
3724-
3725- self.assertEqual(self._called,
3726- ((TIMEOUT_INTERVAL, self.client.root.shutdown), {}))
3727-
3728- def test_on_non_zero_ref_count_do_not_shutdown(self):
3729- """If ref count is not 0, do not queue shutdown op."""
3730- self.patch(self.client.root, 'timeout_func', self._set_called)
3731- self.client.login(APP_NAME, self.args)
3732-
3733- self.assertEqual(self._called, False)
3734-
3735- def test_on_non_zero_ref_count_after_zero_do_not_shutdown(self):
3736- """If the shutdown was queued, do not quit if counter is not zero."""
3737-
3738- def fake_timeout_func(interval, func):
3739- """Start a new request when the timer is started."""
3740- self.client.register(APP_NAME, self.args)
3741- assert self.client.root.ref_count > 0
3742- func()
3743-
3744- self.patch(self.client.root, 'timeout_func', fake_timeout_func)
3745- self.patch(self.client.root, 'shutdown_func', self._set_called)
3746-
3747- self.client.login(APP_NAME, self.args)
3748- self.client.CredentialsFound(APP_NAME, TOKEN)
3749- # counter reached 0, timeout_func was called
3750-
3751- self.assertEqual(self._called, False, 'shutdown_func was not called')
3752-
3753- def test_zero_ref_count_after_zero_do_shutdown(self):
3754- """If the shutdown was queued, do quit if counter is zero."""
3755-
3756- def fake_timeout_func(interval, func):
3757- """Start a new request when the timer is started."""
3758- assert self.client.root.ref_count == 0
3759- func()
3760-
3761- self.patch(self.client.root, 'timeout_func', fake_timeout_func)
3762- self.patch(self.client.root, 'shutdown_func', self._set_called)
3763-
3764- self.client.login(APP_NAME, self.args)
3765- self.client.CredentialsFound(APP_NAME, TOKEN)
3766- # counter reached 0, timeout_func was called
3767-
3768- self.assertEqual(self._called, ((), {}), 'shutdown_func was called')
3769-
3770-
3771-class CredentialsManagementFindTestCase(CredentialsManagementTestCase):
3772- """Tests for the CredentialsManagement find method."""
3773-
3774- def test_find_credentials(self):
3775- """The credentials are asked and returned in signals."""
3776- self.create_mock_backend().find_credentials()
3777- self.mocker.result(defer.succeed(None))
3778- self.mocker.replay()
3779-
3780- self.client.find_credentials(APP_NAME, self.args)
3781- self.assert_dbus_method_correct(self.client.find_credentials)
3782-
3783- def test_find_credentials_does_not_block_when_found(self):
3784- """Calling find_credentials does not block but return thru signals.
3785-
3786- If the creds are found, CredentialsFound is emitted.
3787-
3788- """
3789- d = defer.Deferred()
3790-
3791- def verify(app_name, creds):
3792- """The actual test."""
3793- try:
3794- self.assertEqual(app_name, APP_NAME)
3795- self.assertEqual(creds, TOKEN)
3796- except Exception, e: # pylint: disable=W0703
3797- d.errback(e)
3798- else:
3799- d.callback(creds)
3800-
3801- self.patch(self.client, 'CredentialsFound', verify)
3802- self.patch(self.client, 'CredentialsNotFound', d.errback)
3803-
3804- self.create_mock_backend().find_credentials()
3805- self.mocker.result(defer.succeed(TOKEN))
3806- self.mocker.replay()
3807-
3808- self.client.find_credentials(APP_NAME, self.args)
3809- return d
3810-
3811- def test_find_credentials_does_not_block_when_not_found(self):
3812- """Calling find_credentials does not block but return thru signals.
3813-
3814- If the creds are not found, CredentialsNotFound is emitted.
3815-
3816- """
3817- d = defer.Deferred()
3818-
3819- def verify(app_name):
3820- """The actual test."""
3821- try:
3822- self.assertEqual(app_name, APP_NAME)
3823- except Exception, e: # pylint: disable=W0703
3824- d.errback(e)
3825- else:
3826- d.callback(app_name)
3827-
3828- self.patch(self.client, 'CredentialsFound',
3829- lambda app, creds: d.errback(app))
3830- self.patch(self.client, 'CredentialsNotFound', verify)
3831-
3832- self.create_mock_backend().find_credentials()
3833- self.mocker.result(defer.succeed({}))
3834- self.mocker.replay()
3835-
3836- self.client.find_credentials(APP_NAME, self.args)
3837- return d
3838-
3839- def test_find_credentials_error(self):
3840- """If find_credentials fails, CredentialsError is sent."""
3841- d = defer.Deferred()
3842-
3843- def verify(app_name, errdict):
3844- """The actual test."""
3845- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3846- self.assertEqual(app_name, APP_NAME)
3847- d.callback("Ok")
3848-
3849- self.patch(self.client, 'CredentialsFound',
3850- lambda app, creds: d.errback(app))
3851- self.patch(self.client, 'CredentialsNotFound', d.errback)
3852- self.patch(self.client, 'CredentialsError', verify)
3853-
3854- self.create_mock_backend().find_credentials()
3855- self.mocker.result(defer.fail(BlockingSampleException()))
3856- self.mocker.replay()
3857-
3858- self.client.find_credentials(APP_NAME, self.args)
3859- return d
3860-
3861- def test_find_credentials_sync(self):
3862- """The credentials are asked and returned in a blocking call."""
3863- d = defer.Deferred()
3864-
3865- def verify(creds):
3866- """The actual test."""
3867- try:
3868- self.assertEqual(creds, TOKEN)
3869- except Exception, e: # pylint: disable=W0703
3870- d.errback(e)
3871- else:
3872- d.callback(creds)
3873-
3874- self.create_mock_backend().find_credentials()
3875- self.mocker.result(defer.succeed(TOKEN))
3876- self.mocker.replay()
3877-
3878- self.client.find_credentials_sync(APP_NAME, self.args,
3879- reply_handler=verify,
3880- error_handler=d.errback)
3881- self.assert_dbus_method_correct(self.client.find_credentials_sync,
3882- out_signature='a{ss}')
3883- return d
3884-
3885- def test_find_credentials_sync_error(self):
3886- """If find_credentials_sync fails, error_handler is called."""
3887- d = defer.Deferred()
3888-
3889- def verify(error):
3890- """The actual test."""
3891- try:
3892- errdict = error.args[0]
3893- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3894- except Exception, e: # pylint: disable=W0703
3895- d.errback(e)
3896- else:
3897- d.callback("Ok")
3898-
3899- self.create_mock_backend().find_credentials()
3900- self.mocker.result(defer.fail(BlockingSampleException()))
3901- self.mocker.replay()
3902-
3903- self.client.find_credentials_sync(APP_NAME, self.args,
3904- reply_handler=d.errback,
3905- error_handler=verify)
3906- return d
3907-
3908-
3909-class CredentialsManagementClearTestCase(CredentialsManagementTestCase):
3910- """Tests for the CredentialsManagement clear method."""
3911-
3912- def test_clear_credentials(self):
3913- """The credentials are removed."""
3914- self.create_mock_backend().clear_credentials()
3915- self.mocker.result(defer.succeed(APP_NAME))
3916- self.mocker.replay()
3917-
3918- self.client.clear_credentials(APP_NAME, self.args)
3919- self.assert_dbus_method_correct(self.client.clear_credentials)
3920-
3921- def test_clear_credentials_does_not_block(self):
3922- """Calling clear_credentials does not block but return thru signals."""
3923- d = defer.Deferred()
3924-
3925- def verify(app_name):
3926- """The actual test."""
3927- try:
3928- self.assertEqual(app_name, APP_NAME)
3929- except Exception, e: # pylint: disable=W0703
3930- d.errback(e)
3931- else:
3932- d.callback(app_name)
3933-
3934- self.patch(self.client, 'CredentialsCleared', verify)
3935- self.patch(self.client, 'CredentialsError',
3936- lambda app, err: d.errback(app))
3937-
3938- self.create_mock_backend().clear_credentials()
3939- self.mocker.result(defer.succeed(APP_NAME))
3940- self.mocker.replay()
3941-
3942- self.client.clear_credentials(APP_NAME, self.args)
3943- return d
3944-
3945- def test_clear_credentials_error(self):
3946- """If clear_credentials fails, CredentialsError is sent."""
3947- d = defer.Deferred()
3948-
3949- def verify(app_name, errdict):
3950- """The actual test."""
3951- self.assertEqual(errdict["errtype"], "BlockingSampleException")
3952- self.assertEqual(app_name, APP_NAME)
3953- d.callback("Ok")
3954-
3955- self.patch(self.client, 'CredentialsCleared', d.errback)
3956- self.patch(self.client, 'CredentialsError', verify)
3957-
3958- self.create_mock_backend().clear_credentials()
3959- self.mocker.result(defer.fail(BlockingSampleException()))
3960- self.mocker.replay()
3961-
3962- self.client.clear_credentials(APP_NAME, self.args)
3963- return d
3964-
3965-
3966-class CredentialsManagementStoreTestCase(CredentialsManagementTestCase):
3967- """Tests for the CredentialsManagement store method."""
3968-
3969- def test_store_credentials(self):
3970- """The credentials are stored and the outcome is a signal."""
3971- self.create_mock_backend().store_credentials(TOKEN)
3972- self.mocker.result(defer.succeed(APP_NAME))
3973- self.mocker.replay()
3974-
3975- self.client.store_credentials(APP_NAME, TOKEN)
3976- self.assert_dbus_method_correct(self.client.store_credentials)
3977-
3978- def test_store_credentials_does_not_block(self):
3979- """Calling store_credentials does not block but return thru signals.
3980-
3981- If the creds are stored, CredentialsStored is emitted.
3982-
3983- """
3984- d = defer.Deferred()
3985-
3986- def verify(app_name):
3987- """The actual test."""
3988- try:
3989- self.assertEqual(app_name, APP_NAME)
3990- except Exception, e: # pylint: disable=W0703
3991- d.errback(e)
3992- else:
3993- d.callback(app_name)
3994-
3995- self.patch(self.client, 'CredentialsStored', verify)
3996- self.patch(self.client, 'CredentialsError',
3997- lambda app, err: d.errback(app))
3998-
3999- self.create_mock_backend().store_credentials(TOKEN)
4000- self.mocker.result(defer.succeed(APP_NAME))
4001- self.mocker.replay()
4002-
4003- self.client.store_credentials(APP_NAME, TOKEN)
4004- return d
4005-
4006- def test_store_credentials_error(self):
4007- """If store_credentials fails, CredentialsError is sent."""
4008- d = defer.Deferred()
4009-
4010- def verify(app_name, errdict):
4011- """The actual test."""
4012- self.assertEqual(errdict["errtype"], "BlockingSampleException")
4013- self.assertEqual(app_name, APP_NAME)
4014- d.callback("Ok")
4015-
4016- self.patch(self.client, 'CredentialsStored', d.errback)
4017- self.patch(self.client, 'CredentialsError', verify)
4018-
4019- self.create_mock_backend().store_credentials(TOKEN)
4020- self.mocker.result(defer.fail(BlockingSampleException()))
4021- self.mocker.replay()
4022-
4023- self.client.store_credentials(APP_NAME, TOKEN)
4024- return d
4025-
4026-
4027-class CredentialsManagementOpsTestCase(CredentialsManagementTestCase):
4028- """Tests for the CredentialsManagement login/register methods."""
4029-
4030- @defer.inlineCallbacks
4031- def setUp(self):
4032- yield super(CredentialsManagementOpsTestCase, self).setUp()
4033- self.args = dict((k, str(v)) for k, v in self.base_args.iteritems())
4034- self.cred_args = self.base_args.copy()
4035- self.cred_args[SUCCESS_CB_KEY] = self.client.CredentialsFound
4036- self.cred_args[ERROR_CB_KEY] = self.client.CredentialsError
4037- self.cred_args[DENIAL_CB_KEY] = self.client.AuthorizationDenied
4038-
4039- def test_register(self):
4040- """The registration is correct."""
4041- self.create_mock_backend().register()
4042- self.mocker.replay()
4043-
4044- self.client.register(APP_NAME, self.args)
4045- self.assert_dbus_method_correct(self.client.register)
4046-
4047- def test_login(self):
4048- """The login is correct."""
4049- self.create_mock_backend().login()
4050- self.mocker.replay()
4051-
4052- self.client.login(APP_NAME, self.args)
4053- self.assert_dbus_method_correct(self.client.login)
4054-
4055- def test_login_email_password(self):
4056- """The login_email_password is correct."""
4057- self.create_mock_backend().login_email_password(email=EMAIL,
4058- password=PASSWORD)
4059- self.mocker.replay()
4060-
4061- self.args['email'] = EMAIL
4062- self.args['password'] = PASSWORD
4063- self.client.login_email_password(APP_NAME, self.args)
4064- self.assert_dbus_method_correct(self.client.login_email_password)
4065-
4066-
4067-class CredentialsManagementParamsTestCase(CredentialsManagementOpsTestCase):
4068- """Tests for the CredentialsManagement extra parameters handling."""
4069-
4070- @defer.inlineCallbacks
4071- def setUp(self):
4072- yield super(CredentialsManagementParamsTestCase, self).setUp()
4073- self.args['dummy'] = 'nothing useful'
4074-
4075-
4076-class CredentialsManagementSignalsTestCase(TestCase):
4077- """Tests for the CredentialsManagement DBus signals."""
4078-
4079- @defer.inlineCallbacks
4080- def setUp(self):
4081- """Set up."""
4082- yield super(CredentialsManagementSignalsTestCase, self).setUp()
4083- self.client = CredentialsManagement(timeout_func=lambda *a: None,
4084- shutdown_func=lambda *a: None)
4085-
4086- self.memento = MementoHandler()
4087- self.memento.setLevel(logging.DEBUG)
4088- ubuntu_sso.main.linux.logger.addHandler(self.memento)
4089- self.addCleanup(ubuntu_sso.main.linux.logger.removeHandler,
4090- self.memento)
4091-
4092- def assert_dbus_signal_correct(self, signal, signature):
4093- """Check that 'signal' is a dbus signal with proper 'signature'."""
4094- self.assertTrue(signal._dbus_is_signal)
4095- self.assertEqual(signal._dbus_interface, DBUS_CREDENTIALS_IFACE)
4096- self.assertEqual(signal._dbus_signature, signature)
4097-
4098- def test_credentials_found(self):
4099- """The CredentialsFound signal."""
4100- self.client.CredentialsFound(APP_NAME, TOKEN)
4101- msgs = (self.client.__class__.__name__,
4102- self.client.CredentialsFound.__name__, APP_NAME)
4103- self.assertTrue(self.memento.check_info(*msgs))
4104-
4105- msg = 'credentials must not be logged (found %r in log).'
4106- for val in TOKEN.itervalues():
4107- self.assertFalse(self.memento.check_info(val), msg % val)
4108-
4109- self.assert_dbus_signal_correct(self.client.CredentialsFound, 'sa{ss}')
4110-
4111- def test_credentials_not_found(self):
4112- """The CredentialsNotFound signal."""
4113- self.client.CredentialsNotFound(APP_NAME)
4114- msgs = (self.client.__class__.__name__,
4115- self.client.CredentialsNotFound.__name__, APP_NAME)
4116- self.assertTrue(self.memento.check_info(*msgs))
4117- self.assert_dbus_signal_correct(self.client.CredentialsNotFound, 's')
4118-
4119- def test_credentials_cleared(self):
4120- """The CredentialsCleared signal."""
4121- self.client.CredentialsCleared(APP_NAME)
4122- msgs = (self.client.__class__.__name__,
4123- self.client.CredentialsCleared.__name__, APP_NAME)
4124- self.assertTrue(self.memento.check_info(*msgs))
4125-
4126- self.assert_dbus_signal_correct(self.client.CredentialsCleared, 's')
4127-
4128- def test_credentials_stored(self):
4129- """The CredentialsStored signal."""
4130- self.client.CredentialsStored(APP_NAME)
4131- msgs = (self.client.__class__.__name__,
4132- self.client.CredentialsStored.__name__, APP_NAME)
4133- self.assertTrue(self.memento.check_info(*msgs))
4134-
4135- self.assert_dbus_signal_correct(self.client.CredentialsStored, 's')
4136-
4137- def test_credentials_error(self):
4138- """The CredentialsError signal."""
4139- error = {'error_message': 'failed!', 'detailed error': 'yadda yadda'}
4140- self.client.CredentialsError(APP_NAME, error)
4141- msgs = (self.client.__class__.__name__,
4142- self.client.CredentialsError.__name__,
4143- APP_NAME, str(error))
4144- self.assertTrue(self.memento.check_error(*msgs))
4145-
4146- self.assert_dbus_signal_correct(self.client.CredentialsError, 'sa{ss}')
4147-
4148- def test_authorization_denied(self):
4149- """The AuthorizationDenied signal."""
4150- self.client.AuthorizationDenied(APP_NAME)
4151- msgs = (self.client.__class__.__name__,
4152- self.client.AuthorizationDenied.__name__, APP_NAME)
4153- self.assertTrue(self.memento.check_info(*msgs))
4154-
4155- self.assert_dbus_signal_correct(self.client.AuthorizationDenied, 's')
4156
4157=== modified file 'ubuntu_sso/main/tests/test_windows.py'
4158--- ubuntu_sso/main/tests/test_windows.py 2011-12-19 19:35:06 +0000
4159+++ ubuntu_sso/main/tests/test_windows.py 2012-01-17 16:51:50 +0000
4160@@ -1,6 +1,4 @@
4161 # -*- coding: utf-8 -*-
4162-# Authors: Manuel de la Pena <manuel@canonical.com>
4163-# Alejandro J. Cura <alecu@canonical.com>
4164 #
4165 # Copyright 2011 Canonical Ltd.
4166 #
4167@@ -15,932 +13,16 @@
4168 #
4169 # You should have received a copy of the GNU General Public License along
4170 # with this program. If not, see <http://www.gnu.org/licenses/>.
4171-"""Windows tests."""
4172+"""Windows specific tests for the main module."""
4173
4174 # pylint: disable=F0401
4175 from _winreg import REG_SZ
4176
4177-from mocker import MATCH, Mocker, MockerTestCase
4178-
4179-from twisted.internet import defer, reactor
4180-from twisted.trial.unittest import TestCase
4181-from twisted.spread.pb import (
4182- DeadReferenceError,
4183- PBClientFactory,
4184- PBServerFactory,
4185- Broker,
4186-)
4187-from ubuntu_sso import main
4188 from ubuntu_sso.main import windows
4189-from ubuntu_sso.main.windows import (
4190- signal,
4191- CredentialsManagement,
4192- CredentialsManagementClient,
4193- LOCALHOST,
4194- SignalBroadcaster,
4195- SSOLogin,
4196- SSOLoginClient,
4197- UbuntuSSORoot,
4198- UbuntuSSOClient,
4199- get_sso_pb_port,
4200-)
4201-
4202-# because we are using twisted we have java like names C0103 and
4203-# the issues that mocker brings with it like W0104
4204-# pylint: disable=C0103,W0104,E1101,W0201
4205-
4206-
4207-class SaveProtocolServerFactory(PBServerFactory):
4208- """A PBServerFactory that saves the latest connected client."""
4209-
4210- protocolInstance = None
4211-
4212- def clientConnectionMade(self, protocol):
4213- """Keep track of the given protocol."""
4214- self.protocolInstance = protocol
4215-
4216-
4217-class SaveClientFactory(PBClientFactory):
4218- """Client Factory that knows when we disconnected."""
4219-
4220- def __init__(self, connected_d, disconnected_d):
4221- """Create a new instance."""
4222- PBClientFactory.__init__(self)
4223- self.connected_d = connected_d
4224- self.disconnected_d = disconnected_d
4225-
4226- def clientConnectionMade(self, broker):
4227- """Connection made."""
4228- PBClientFactory.clientConnectionMade(self, broker)
4229- self.connected_d.callback(True)
4230-
4231- def clientConnectionLost(self, connector, reason, reconnecting=0):
4232- """Connection lost."""
4233- self.disconnected_d.callback(True)
4234-
4235-
4236-class ServerProtocol(Broker):
4237- """Server protocol that allows us to clean the tests."""
4238-
4239- def connectionLost(self, *a):
4240- self.factory.onConnectionLost.callback(self)
4241-
4242-
4243-class ConnectedTestCase(TestCase):
4244- """Base test case with a client and a server."""
4245-
4246- @defer.inlineCallbacks
4247- def setUp(self):
4248- """Set up for the tests."""
4249- yield super(ConnectedTestCase, self).setUp()
4250- self.server_disconnected = defer.Deferred()
4251- self.client_disconnected = defer.Deferred()
4252- self.listener = None
4253- self.connector = None
4254- self.server_factory = None
4255- self.client_factory = None
4256-
4257- def setup_client_server(self, sso_root):
4258- """Set tests."""
4259- port = get_sso_pb_port()
4260- self.listener = self._listen_server(sso_root, self.server_disconnected,
4261- port)
4262- connected = defer.Deferred()
4263- self.connector = self._connect_client(connected,
4264- self.client_disconnected, port)
4265- self.addCleanup(self.teardown_client_server)
4266- return connected
4267-
4268- def _listen_server(self, sso_root, d, port):
4269- """Start listenting."""
4270- self.server_factory = SaveProtocolServerFactory(sso_root)
4271- self.server_factory.onConnectionLost = d
4272- self.server_factory.protocol = ServerProtocol
4273- return reactor.listenTCP(port, self.server_factory)
4274-
4275- def _connect_client(self, d1, d2, port):
4276- """Connect client."""
4277- self.client_factory = SaveClientFactory(d1, d2)
4278- return reactor.connectTCP(LOCALHOST, port, self.client_factory)
4279-
4280- def teardown_client_server(self):
4281- """Clean resources."""
4282- self.connector.disconnect()
4283- d = defer.maybeDeferred(self.listener.stopListening)
4284- return defer.gatherResults([d, self.client_disconnected,
4285- self.server_disconnected])
4286-
4287-
4288-class FakeDecoratedObject(object):
4289- """An object that has decorators."""
4290-
4291- def __init__(self):
4292- """Create a new instance."""
4293- super(FakeDecoratedObject, self).__init__()
4294-
4295- @signal
4296- def on_no_args(self):
4297- """Get no args passwed."""
4298-
4299- @signal
4300- def on_just_args(self, *args):
4301- """Just get args."""
4302-
4303- @signal
4304- def on_just_kwargs(self, **kwargs):
4305- """Just get kwargs."""
4306-
4307- @signal
4308- def on_both_args(self, *args, **kwargs):
4309- """Both args."""
4310-
4311-
4312-class TestException(Exception):
4313- """A test exception."""
4314-
4315- @classmethod
4316- def fail(cls, *args):
4317- """Raise this exception."""
4318- raise cls(*args)
4319-
4320-
4321-# pylint: disable=W0201
4322-class SignalTestCase(MockerTestCase):
4323- """Test the signal decorator."""
4324-
4325- @defer.inlineCallbacks
4326- def setUp(self):
4327- yield super(SignalTestCase, self).setUp()
4328- self.fake_object = FakeDecoratedObject()
4329- self.cb = self.mocker.mock()
4330-
4331- def test_no_args(self):
4332- """Test when the cb should have no args."""
4333- self.fake_object.on_no_args_cb = self.cb
4334- self.cb()
4335- self.mocker.replay()
4336- self.fake_object.on_no_args()
4337-
4338- def test_just_args(self):
4339- """Test when the cb just has *args"""
4340- first = 'first'
4341- second = 'second'
4342- self.fake_object.on_just_args_cb = self.cb
4343- self.cb(first, second)
4344- self.mocker.replay()
4345- self.fake_object.on_just_args(first, second)
4346-
4347- def test_just_kwargs(self):
4348- """Test when the cb just has kwargs."""
4349- first = 'first'
4350- second = 'second'
4351- self.fake_object.on_just_kwargs_cb = self.cb
4352- self.cb(first=first, second=second)
4353- self.mocker.replay()
4354- self.fake_object.on_just_kwargs(first=first, second=second)
4355-
4356- def test_just_kwargs_empty(self):
4357- """Test when the cb just has kwargs."""
4358- self.fake_object.on_just_kwargs_cb = self.cb
4359- self.cb()
4360- self.mocker.replay()
4361- self.fake_object.on_just_kwargs()
4362-
4363- def test_both_args(self):
4364- """Test with args and kwargs."""
4365- first = 'first'
4366- second = 'second'
4367- self.fake_object.on_both_args_cb = self.cb
4368- self.cb(first, second, first=first, second=second)
4369- self.mocker.replay()
4370- self.fake_object.on_both_args(first, second, first=first,
4371- second=second)
4372-
4373- def test_both_args_no_kwargs(self):
4374- """Test with args and kwargs."""
4375- first = 'first'
4376- second = 'second'
4377- self.fake_object.on_both_args_cb = self.cb
4378- self.cb(first, second)
4379- self.mocker.replay()
4380- self.fake_object.on_both_args(first, second)
4381-
4382- def test_both_args_no_args(self):
4383- """Test with args and kwargs."""
4384- first = 'first'
4385- second = 'second'
4386- self.fake_object.on_both_args_cb = self.cb
4387- self.cb(first=first, second=second)
4388- self.mocker.replay()
4389- self.fake_object.on_both_args(first=first, second=second)
4390-# pylint: enable=W0201
4391-
4392-
4393-class FakeDeadRemoteClient(object):
4394- """A fake dead remote client."""
4395-
4396- def callRemote(self, signal_name, *args, **kwargs):
4397- """Fails with DeadReferenceError."""
4398- raise DeadReferenceError("Calling Stale Broker")
4399-
4400-
4401-class FakeAliveRemoteClient(object):
4402- """A fake alive remote client."""
4403-
4404- def __init__(self):
4405- self.called = False
4406-
4407- def callRemote(self, signal_name, *args, **kwargs):
4408- """Returns a succeed."""
4409- self.called = True
4410- return defer.succeed(None)
4411-
4412-
4413-class SignalBroadcasterTestCase(TestCase):
4414- """Test the SignalBroadcaster class."""
4415-
4416- def test_emit_signal_dead_reference(self):
4417- """Test dead reference while emiting the signal."""
4418- fake_remote_client = FakeDeadRemoteClient()
4419- sb = SignalBroadcaster()
4420- sb.remote_register_to_signals(fake_remote_client)
4421- self.assertIn(fake_remote_client, sb.clients)
4422- sb.emit_signal("sample_signal")
4423- self.assertNotIn(fake_remote_client, sb.clients)
4424-
4425- def test_emit_signal_some_dead_some_not(self):
4426- """Test a clean reference after a dead one."""
4427- fake_dead_remote = FakeDeadRemoteClient()
4428- fake_alive_remote = FakeAliveRemoteClient()
4429- sb = SignalBroadcaster()
4430- sb.remote_register_to_signals(fake_dead_remote)
4431- sb.remote_register_to_signals(fake_alive_remote)
4432- sb.emit_signal("sample_signal")
4433- self.assertTrue(fake_alive_remote.called, "The alive must be called.")
4434-
4435-
4436-class SignalHandlingTestCase(TestCase):
4437- """Base for test suites that deal with IPC signal handling."""
4438-
4439- timeout = 3
4440- signal_names = []
4441- first_signal = None
4442-
4443- def create_handler(self, d):
4444- """Create a handler in a new namespace."""
4445- return lambda *args: d.callback(args)
4446-
4447- def install_handlers(self, client):
4448- """Install the signal handlers."""
4449- signal_handlers = []
4450-
4451- for signal_name in self.signal_names:
4452- d = defer.Deferred()
4453- handler = self.create_handler(d)
4454- handler_name = "on_%s_cb" % signal_name
4455- setattr(client, handler_name, handler)
4456- signal_handlers.append(d)
4457- self.first_signal = defer.DeferredList(signal_handlers,
4458- fireOnOneCallback=True,
4459- fireOnOneErrback=True)
4460-
4461- @defer.inlineCallbacks
4462- def assert_fired(self, name, *args):
4463- """Assert that the given signal was fired."""
4464- signal_args, signal_index = yield self.first_signal
4465- self.assertEqual(args, signal_args[:len(args)])
4466- self.assertEqual(name, self.signal_names[signal_index])
4467-
4468-
4469-class SSOLoginTestCase(ConnectedTestCase, SignalHandlingTestCase):
4470- """Test the login class."""
4471-
4472- signal_names = [
4473- 'captcha_generated',
4474- 'captcha_generation_error',
4475- 'user_registered',
4476- 'user_registration_error',
4477- 'logged_in',
4478- 'login_error',
4479- 'user_not_validated',
4480- 'email_validated',
4481- 'email_validation_error',
4482- 'password_reset_token_sent',
4483- 'password_reset_error',
4484- 'password_changed',
4485- 'password_change_error',
4486- ]
4487-
4488- @defer.inlineCallbacks
4489- def setUp(self):
4490- """Setup tests."""
4491- yield super(SSOLoginTestCase, self).setUp()
4492- self.mocker = Mocker()
4493- self.root = self.mocker.mock()
4494- self.login = SSOLogin(None)
4495- # start pb
4496- self.sso_root = UbuntuSSORoot(sso_login=self.login)
4497- # pylint: disable=E1101
4498- yield self.setup_client_server(self.sso_root)
4499- self.client = yield self._get_client()
4500- # pylint: enable=E1101
4501-
4502- @defer.inlineCallbacks
4503- def _get_client(self):
4504- """Get the client."""
4505- # request the remote object and create a client
4506- root = yield self.client_factory.getRootObject()
4507- remote = yield root.callRemote('get_sso_login')
4508- client = SSOLoginClient(remote)
4509- yield client.register_to_signals()
4510- self.addCleanup(client.unregister_to_signals)
4511-
4512- self.install_handlers(client)
4513- defer.returnValue(client)
4514-
4515- @defer.inlineCallbacks
4516- def test_emit_captcha_generated(self):
4517- """Test that the cb was called."""
4518- app_name = 'app'
4519- result = 'result'
4520-
4521- self.login.emit_captcha_generated(app_name, result)
4522- yield self.assert_fired("captcha_generated", app_name, result)
4523-
4524- @defer.inlineCallbacks
4525- def test_emit_captcha_generation_error(self):
4526- """Test that the cb was called."""
4527- app_name = 'app'
4528- filename = 'file'
4529-
4530- self.patch(self.login.root.processor, "generate_captcha",
4531- TestException.fail)
4532- self.login.generate_captcha(app_name, filename)
4533- yield self.assert_fired("captcha_generation_error", app_name)
4534-
4535- @defer.inlineCallbacks
4536- def test_generate_captcha(self):
4537- """Test the call from the client."""
4538- app_name = 'app'
4539- filename = 'file'
4540- self.login.root = self.root
4541-
4542- self.root.generate_captcha(app_name, filename,
4543- self.login.emit_captcha_generated,
4544- self.login.emit_captcha_generation_error)
4545- self.mocker.replay()
4546- yield self.client.generate_captcha(app_name, filename)
4547- yield self.client.unregister_to_signals()
4548- self.mocker.verify()
4549-
4550- @defer.inlineCallbacks
4551- def test_emit_user_registered(self):
4552- """Test that the cb was called."""
4553- app_name = 'app'
4554- result = 'result'
4555-
4556- self.login.emit_user_registered(app_name, result)
4557- yield self.assert_fired("user_registered", app_name, result)
4558-
4559- @defer.inlineCallbacks
4560- def test_emit_user_registration_error(self):
4561- """Test that the cb was called."""
4562- app_name = 'app'
4563-
4564- self.patch(self.login.root.processor, "register_user",
4565- TestException.fail)
4566- self.login.register_user(app_name, "email", "password", "name",
4567- "captcha_id", "captcha_solution")
4568- yield self.assert_fired("user_registration_error", app_name)
4569-
4570- @defer.inlineCallbacks
4571- def test_register_user(self):
4572- """Test the call from the client."""
4573- app_name = 'app'
4574- email = 'email'
4575- password = 'password'
4576- displayname = 'name'
4577- captcha_id = 'captcha_id'
4578- captcha_solution = 'captcha_solution'
4579- self.login.root = self.root
4580-
4581- self.root.register_user(app_name, email, password, displayname,
4582- captcha_id, captcha_solution,
4583- self.login.emit_user_registered,
4584- self.login.emit_user_registration_error)
4585- self.mocker.replay()
4586- yield self.client.register_user(app_name, email, password, displayname,
4587- captcha_id, captcha_solution)
4588- yield self.client.unregister_to_signals()
4589- self.mocker.verify()
4590-
4591- @defer.inlineCallbacks
4592- def test_emit_logged_in(self):
4593- """Test that the cb was called."""
4594- app_name = 'app'
4595- result = 'result'
4596-
4597- self.login.emit_logged_in(app_name, result)
4598- yield self.assert_fired("logged_in", app_name)
4599-
4600- @defer.inlineCallbacks
4601- def test_emit_login_error(self):
4602- """Test that the db was called."""
4603- app_name = 'app'
4604-
4605- self.patch(main, "get_token_name", lambda _: None)
4606- self.patch(self.login.root.processor, "login",
4607- TestException.fail)
4608- self.login.login(app_name, "email", "password")
4609- yield self.assert_fired("login_error", app_name)
4610-
4611- @defer.inlineCallbacks
4612- def test_emit_user_not_validated(self):
4613- """Test that the cb was called."""
4614- app_name = 'app'
4615- result = 'result'
4616-
4617- self.login.emit_user_not_validated(app_name, result)
4618- yield self.assert_fired("user_not_validated", app_name)
4619-
4620- @defer.inlineCallbacks
4621- def test_login(self):
4622- """Test the call from the client."""
4623- app_name = 'app'
4624- email = 'email'
4625- password = 'password'
4626- self.login.root = self.root
4627-
4628- self.root.login(app_name, email, password,
4629- self.login.emit_logged_in,
4630- self.login.emit_login_error,
4631- self.login.emit_user_not_validated)
4632- self.mocker.replay()
4633- yield self.client.login(app_name, email, password)
4634- yield self.client.unregister_to_signals()
4635- self.mocker.verify()
4636-
4637- @defer.inlineCallbacks
4638- def test_emit_email_validated(self):
4639- """Test the cb was called."""
4640- app_name = 'app'
4641- result = 'result'
4642-
4643- self.login.emit_email_validated(app_name, result)
4644- yield self.assert_fired("email_validated", app_name)
4645-
4646- @defer.inlineCallbacks
4647- def test_emit_email_validation_error(self):
4648- """Test the cb was called."""
4649- app_name = 'app'
4650-
4651- self.patch(main, "get_token_name", lambda _: None)
4652- self.patch(self.login.root.processor, "validate_email",
4653- TestException.fail)
4654- self.login.validate_email(app_name, "email", "password", "token")
4655- yield self.assert_fired("email_validation_error", app_name)
4656-
4657- @defer.inlineCallbacks
4658- def test_validate_email(self):
4659- """Test the client calll."""
4660- app_name = 'app'
4661- email = 'email'
4662- password = 'password'
4663- email_token = 'token'
4664- self.login.root = self.root
4665-
4666- self.root.validate_email(app_name, email, password, email_token,
4667- self.login.emit_email_validated,
4668- self.login.emit_email_validation_error)
4669- self.mocker.replay()
4670- yield self.client.validate_email(app_name, email, password,
4671- email_token)
4672- yield self.client.unregister_to_signals()
4673- self.mocker.verify()
4674-
4675- @defer.inlineCallbacks
4676- def test_emit_password_reset_token_sent(self):
4677- """Test the cb was called."""
4678- app_name = 'app'
4679- result = 'result'
4680-
4681- self.login.emit_password_reset_token_sent(app_name, result)
4682- yield self.assert_fired("password_reset_token_sent", app_name)
4683-
4684- @defer.inlineCallbacks
4685- def test_emit_password_reset_error(self):
4686- """Test the cb was called."""
4687- app_name = 'app'
4688-
4689- self.patch(self.login.root.processor, "request_password_reset_token",
4690- TestException.fail)
4691- self.login.request_password_reset_token(app_name, "email")
4692- yield self.assert_fired("password_reset_error", app_name)
4693-
4694- @defer.inlineCallbacks
4695- def test_request_password_reset_token(self):
4696- """Test the client call."""
4697- app_name = 'app'
4698- email = 'email'
4699- self.login.root = self.root
4700-
4701- self.root.request_password_reset_token(app_name, email,
4702- self.login.emit_password_reset_token_sent,
4703- self.login.emit_password_reset_error)
4704- self.mocker.replay()
4705- self.client.request_password_reset_token(app_name, email)
4706- yield self.client.unregister_to_signals()
4707- self.mocker.verify()
4708-
4709- @defer.inlineCallbacks
4710- def test_emit_password_changed(self):
4711- """Test the cb was called."""
4712- app_name = 'app'
4713- result = 'result'
4714-
4715- self.login.emit_password_changed(app_name, result)
4716- yield self.assert_fired("password_changed", app_name)
4717-
4718- @defer.inlineCallbacks
4719- def test_emit_password_change_error(self):
4720- """Test the cb was called."""
4721- app_name = 'app'
4722-
4723- self.patch(self.login.root.processor, "set_new_password",
4724- TestException.fail)
4725- self.login.set_new_password(app_name, "email", "token", "password")
4726- yield self.assert_fired("password_change_error", app_name)
4727-
4728- @defer.inlineCallbacks
4729- def test_set_new_password(self):
4730- """Test the client call."""
4731- app_name = 'app'
4732- email = 'email'
4733- token = 'token'
4734- new_password = 'password'
4735- self.login.root = self.root
4736-
4737- self.root.set_new_password(app_name, email, token, new_password,
4738- self.login.emit_password_changed,
4739- self.login.emit_password_change_error)
4740- self.mocker.replay()
4741- yield self.client.set_new_password(app_name, email, token,
4742- new_password)
4743- yield self.client.unregister_to_signals()
4744- self.mocker.verify()
4745-
4746-
4747-class CredentialsManagementTestCase(ConnectedTestCase, TestCase):
4748- """Test the management class."""
4749-
4750- @defer.inlineCallbacks
4751- def setUp(self):
4752- """Set up tests."""
4753- yield super(CredentialsManagementTestCase, self).setUp()
4754- self.mocker = Mocker()
4755- self.root = self.mocker.mock()
4756- self.except_to_errdict = self.mocker.replace(
4757- 'ubuntu_sso.main.except_to_errdict')
4758- self.creds = CredentialsManagement(None, None)
4759- self.creds.root = self.root
4760- # start pb
4761- self.sso_root = UbuntuSSORoot(cred_manager=self.creds)
4762- # pylint: disable=E1101
4763- yield self.setup_client_server(self.sso_root)
4764- self.client = yield self._get_client()
4765- # pylint: enable=E1101
4766-
4767- @defer.inlineCallbacks
4768- def _get_client(self):
4769- """Get the client."""
4770- # request the remote object and create a client
4771- root = yield self.client_factory.getRootObject()
4772- remote = yield root.callRemote('get_cred_manager')
4773- client = CredentialsManagementClient(remote)
4774- yield client.register_to_signals()
4775- self.addCleanup(client.unregister_to_signals)
4776- # set the cb
4777- for signal_name in ['on_authorization_denied_cb',
4778- 'on_credentials_found_cb',
4779- 'on_credentials_not_found_cb',
4780- 'on_credentials_cleared_cb',
4781- 'on_credentials_stored_cb',
4782- 'on_credentials_error_cb']:
4783- setattr(client, signal_name, self.mocker.mock())
4784- defer.returnValue(client)
4785-
4786- @defer.inlineCallbacks
4787- def test_shutdown(self):
4788- """Test that root is called."""
4789- # pylint: disable=W0104
4790- self.root.ref_count
4791- # pylint: enable=W0104
4792- self.mocker.result(1)
4793- self.root.shutdown()
4794- self.mocker.replay()
4795- yield self.client.shutdown()
4796- yield self.client.unregister_to_signals()
4797-
4798- @defer.inlineCallbacks
4799- def test_emit_authorization_denied(self):
4800- """Test the callback is called."""
4801- app_name = 'app'
4802-
4803- # pylint: disable=W0104
4804- self.root.ref_count
4805- # pylint: enable=W0104
4806- self.mocker.result(1)
4807- self.root.ref_count = 0
4808- self.client.on_authorization_denied_cb(app_name)
4809- self.mocker.replay()
4810- self.creds.emit_authorization_denied(app_name)
4811- yield self.client.unregister_to_signals()
4812- self.mocker.verify()
4813-
4814- @defer.inlineCallbacks
4815- def test_emit_credentials_found(self):
4816- """Test the callback is called."""
4817- app_name = 'app'
4818- creds = 'creds'
4819-
4820- # pylint: disable=W0104
4821- self.root.ref_count
4822- # pylint: enable=W0104
4823- self.mocker.result(1)
4824- self.root.ref_count = 0
4825- self.client.on_credentials_found_cb(app_name, creds)
4826- self.mocker.replay()
4827- self.creds.emit_credentials_found(app_name, creds)
4828- yield self.client.unregister_to_signals()
4829- self.mocker.verify()
4830-
4831- @defer.inlineCallbacks
4832- def test_emit_credentials_not_found(self):
4833- """Test the callback is called."""
4834- app_name = 'app'
4835-
4836- # pylint: disable=W0104
4837- self.root.ref_count
4838- # pylint: enable=W0104
4839- self.mocker.result(1)
4840- self.root.ref_count = 0
4841- self.client.on_credentials_not_found_cb(app_name)
4842- self.mocker.replay()
4843- self.creds.emit_credentials_not_found(app_name)
4844- yield self.client.unregister_to_signals()
4845- self.mocker.verify()
4846-
4847- @defer.inlineCallbacks
4848- def test_emit_credentials_cleared(self):
4849- """Test the callback is called."""
4850- app_name = 'app'
4851-
4852- # pylint: disable=W0104
4853- self.root.ref_count
4854- # pylint: enable=W0104
4855- self.mocker.result(1)
4856- self.root.ref_count = 0
4857- self.client.on_credentials_cleared_cb(app_name)
4858- self.mocker.replay()
4859- self.creds.emit_credentials_cleared(app_name)
4860- yield self.client.unregister_to_signals()
4861- self.mocker.verify()
4862-
4863- @defer.inlineCallbacks
4864- def test_emit_credentials_stored(self):
4865- """Test the callback is called."""
4866- app_name = 'app'
4867-
4868- # pylint: disable=W0104
4869- self.root.ref_count
4870- # pylint: enable=W0104
4871- self.mocker.result(1)
4872- self.root.ref_count = 0
4873- self.client.on_credentials_stored_cb(app_name)
4874- self.mocker.replay()
4875- self.creds.emit_credentials_stored(app_name)
4876- yield self.client.unregister_to_signals()
4877- self.mocker.verify()
4878-
4879- @defer.inlineCallbacks
4880- def test_emit_credentials_error(self):
4881- """Test the callback is called."""
4882- app_name = 'app'
4883- raised_error = 'error'
4884-
4885- # pylint: disable=W0104
4886- self.root.ref_count
4887- # pylint: enable=W0104
4888- self.mocker.result(1)
4889- self.root.ref_count = 0
4890- self.client.on_credentials_error_cb(app_name, raised_error)
4891- self.mocker.replay()
4892- self.creds.emit_credentials_error(app_name, raised_error)
4893- yield self.client.unregister_to_signals()
4894- self.mocker.verify()
4895-
4896- @defer.inlineCallbacks
4897- def test_find_credentials(self):
4898- """Test that root is called."""
4899- app_name = 'app'
4900- args = 'args'
4901-
4902- # pylint: disable=W0212
4903- self.root.find_credentials(app_name, args, MATCH(callable),
4904- self.creds._process_failure)
4905- # pylint: enable=W0212
4906- self.root.shutdown()
4907- yield self.client.find_credentials(app_name, args)
4908- yield self.client.unregister_to_signals()
4909-
4910- @defer.inlineCallbacks
4911- def test_clear_credentials(self):
4912- """Test that root is called."""
4913- app_name = 'app'
4914- args = 'args'
4915-
4916- # pylint: disable=W0212
4917- self.root.clear_credentials(app_name, args, MATCH(callable),
4918- self.creds._process_failure)
4919- # pylint: enable=W0212
4920- self.mocker.replay()
4921- yield self.client.clear_credentials(app_name, args)
4922- yield self.client.unregister_to_signals()
4923-
4924- @defer.inlineCallbacks
4925- def test_store_credentials(self):
4926- """Test that root is called."""
4927- app_name = 'app'
4928- args = 'args'
4929-
4930- # pylint: disable=W0212
4931- self.root.store_credentials(app_name, args, MATCH(callable),
4932- self.creds._process_failure)
4933- # pylint: enable=W0212
4934- self.mocker.replay()
4935- yield self.client.store_credentials(app_name, args)
4936- yield self.client.unregister_to_signals()
4937-
4938- @defer.inlineCallbacks
4939- def test_register(self):
4940- """Test that root is called."""
4941- app_name = 'app'
4942- args = 'args'
4943-
4944- self.root.register(app_name, args)
4945- self.mocker.replay()
4946- yield self.client.register(app_name, args)
4947- yield self.client.unregister_to_signals()
4948-
4949- @defer.inlineCallbacks
4950- def test_login(self):
4951- """Test that root is called."""
4952- app_name = 'app'
4953- args = 'args'
4954-
4955- self.root.login(app_name, args)
4956- self.mocker.replay()
4957- yield self.client.login(app_name, args)
4958- yield self.client.unregister_to_signals()
4959-
4960- @defer.inlineCallbacks
4961- def test_login_email_password(self):
4962- """Test that root is called."""
4963- app_name = 'app'
4964- args = 'args'
4965-
4966- self.root.login_email_password(app_name, args)
4967- self.mocker.replay()
4968- yield self.client.login_email_password(app_name, args)
4969- yield self.client.unregister_to_signals()
4970-
4971-
4972-class MockRemoteObject(object):
4973- """A mock RemoteObject."""
4974-
4975- def __init__(self):
4976- """A place to store MockRemoteObjects created by this."""
4977- self.children = []
4978-
4979- def callRemote(self, method_name, *args, **kwargs):
4980- """Any call to a remote object returns one of us."""
4981- new_child = MockRemoteObject()
4982- self.children.append(new_child)
4983- return defer.succeed(new_child)
4984-
4985-
4986-class MockPBClientFactory(object):
4987- """A mock PBClientFactory."""
4988-
4989- connected = False
4990- root_object = None
4991-
4992- def getRootObject(self):
4993- """Store that we were called; return a deferred."""
4994- assert self.connected == True
4995- self.root_object = MockRemoteObject()
4996- return defer.succeed(self.root_object)
4997-
4998-
4999-class MockReactorResult(object):
5000- """A mock result when calling twisted.reactor.connectTCP."""
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches