Merge lp:~nataliabidart/ubuntu/precise/ubuntu-sso-client/ubuntu-sso-client-2.99.2 into lp:ubuntu/precise/ubuntu-sso-client

Proposed by Natalia Bidart
Status: Merged
Merged at revision: 42
Proposed branch: lp:~nataliabidart/ubuntu/precise/ubuntu-sso-client/ubuntu-sso-client-2.99.2
Merge into: lp:ubuntu/precise/ubuntu-sso-client
Diff against target: 9351 lines (+4205/-3711)
42 files modified
PKG-INFO (+1/-1)
debian/changelog (+7/-0)
debian/watch (+1/-1)
setup.py (+1/-1)
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 (+264/-0)
ubuntu_sso/utils/tests/test_ipc.py (+505/-0)
ubuntu_sso/utils/tests/test_oauth_headers.py (+0/-260)
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/precise/ubuntu-sso-client/ubuntu-sso-client-2.99.2
Reviewer Review Type Date Requested Status
Sebastien Bacher Approve
Review via email: mp+88926@code.launchpad.net

Commit message

* New upstream release.
* debian/watch: updated to latest active milestone.

To post a comment you must log in.
Revision history for this message
Sebastien Bacher (seb128) wrote :

Thank you for your work there

review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches