Merge lp:~nataliabidart/ubuntu-sso-client/unify-signal-broadcaster into lp:ubuntu-sso-client

Proposed by Natalia Bidart on 2012-01-06
Status: Merged
Approved by: Alejandro J. Cura on 2012-01-11
Approved revision: 851
Merged at revision: 830
Proposed branch: lp:~nataliabidart/ubuntu-sso-client/unify-signal-broadcaster
Merge into: lp:ubuntu-sso-client
Diff against target: 7477 lines (+3074/-3439)
21 files modified
ubuntu_sso/account.py (+11/-14)
ubuntu_sso/credentials.py (+61/-25)
ubuntu_sso/logger.py (+20/-0)
ubuntu_sso/main/__init__.py (+257/-111)
ubuntu_sso/main/linux.py (+181/-164)
ubuntu_sso/main/tests/__init__.py (+37/-0)
ubuntu_sso/main/tests/test_clients.py (+373/-0)
ubuntu_sso/main/tests/test_common.py (+909/-215)
ubuntu_sso/main/tests/test_linux.py (+0/-1249)
ubuntu_sso/main/tests/test_windows.py (+18/-926)
ubuntu_sso/main/windows.py (+185/-663)
ubuntu_sso/qt/controllers.py (+2/-1)
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/tests/__init__.py (+41/-0)
ubuntu_sso/tests/test_account.py (+17/-3)
ubuntu_sso/tests/test_credentials.py (+11/-14)
ubuntu_sso/utils/ipc.py (+361/-0)
ubuntu_sso/utils/tests/test_common.py (+5/-1)
ubuntu_sso/utils/tests/test_ipc.py (+505/-0)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu-sso-client/unify-signal-broadcaster
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) 2012-01-06 Approve on 2012-01-11
Manuel de la Peña (community) Approve on 2012-01-11
Review via email: mp+87759@code.launchpad.net

Commit message

- Remove duplications and unify code for IPC using Twisted's Perspective Broker between ubuntu-sso-client and ubuntuone-client (LP: #834730).

Description of the change

In order to run syncdaemon with this branch, you need to use this branch:

https://code.launchpad.net/~nataliabidart/ubuntuone-client/make-it-work/+merge/87767

To post a comment you must log in.
Alejandro J. Cura (alecu) wrote :

== Python Lint Notices ==

ubuntu_sso/main/linux.py:
    29: [W0611] Unused import sys

ubuntu_sso/main/tests/test_windows.py:
    21: [W0611] Unused import defer
    23: [W0611] Unused import UbuntuSSOService

review: Needs Fixing
842. By Natalia Bidart on 2012-01-10

Merged trunk in.

Alejandro J. Cura (alecu) wrote :

Why do we have an Elf here?

            def __init__(elf):
                elf.LoggedIn = lambda app_name, result: d.callback(result)
                elf.LoginError = lambda app_name, error: d.errback(error)
                elf.UserNotValidated = lambda app_name, email: d.callback(None)

I think __init__ should be deleted, and each of those lambda methods could be written as:
           def LoggedIn(self, app_name, result):
               d.callback(result)
---

"Provides an utility" -> "Provides a utility"

---

I find that the refcounting code in CredentialsManagement is confusing.
Perhaps we should open a bug to fix this so it's not spread in so many functions, and also we should make so that when the refcount increases above zero any pending timeout should be cancelled.
I don't consider this to be a "needs-fixing" since it's code that was already present in this branch.

Manuel de la Peña (mandel) wrote :
Download full text (5.4 KiB)

Major branch!!! here are some things I'v noticed:

* We do the following several times:
'SSOLogin: emitting CaptchaGenerated with app_name "%s" and result %r', app_name, result
'SSOLogin: emitting CaptchaGenerationError with app_name "%s" and error %r', app_name, error

is it possible to be smarter regarding the logging of the DBusSignals? an idea would be to use a decorator that way we know the function name and the args. I think it will remove some noise form the code.

* I'm confused, why do we have the following?:

1218 + d.addCallback(lambda a: reply_handler(*a))

If a is not a sequence we are going to have a runtime error..

* There are several tests of the type:

2869 + def test_credentials_cleared(self):
2870 + """The CredentialsCleared signal."""
2871 + self.obj.CredentialsCleared(APP_NAME)
2872 + msgs = (self.obj.__class__.__name__,
2873 + self.obj.CredentialsCleared.__name__, APP_NAME)
2874 + self.assertTrue(self.memento.check_info(*msgs))

Where we call a signal, build the msg and assert, can those be merged?

There seems to be some repetitive code here:

2583 + def test_credentials_found_when_ref_count_is_not_positive(self):
2584 + """Ref counter is decreased when a signal is sent."""
2585 + self.obj._ref_count = -3
2586 + self.obj.CredentialsFound(APP_NAME, TOKEN)
2587 +
2588 + self.assertEqual(self.obj.ref_count, 0)
2589 + msg = 'Attempting to decrease ref_count to a negative value (-4).'
2590 + self.assertTrue(self.memento.check_warning(msg))
2591 +
2592 + def test_credentials_not_found_when_ref_count_is_not_positive(self):
2593 + """Ref counter is decreased when a signal is sent."""
2594 + self.obj._ref_count = -3
2595 + self.obj.CredentialsNotFound(APP_NAME)
2596 +
2597 + self.assertEqual(self.obj.ref_count, 0)
2598 + msg = 'Attempting to decrease ref_count to a negative value (-4).'
2599 + self.assertTrue(self.memento.check_warning(msg))
2600 +
2601 + def test_credentials_cleared_when_ref_count_is_not_positive(self):
2602 + """Ref counter is decreased when a signal is sent."""
2603 + self.obj._ref_count = -3
2604 + self.obj.CredentialsCleared(APP_NAME)
2605 +
2606 + self.assertEqual(self.obj.ref_count, 0)
2607 + msg = 'Attempting to decrease ref_count to a negative value (-4).'
2608 + self.assertTrue(self.memento.check_warning(msg))
2609 +
2610 + def test_credentials_stored_when_ref_count_is_not_positive(self):
2611 + """Ref counter is decreased when a signal is sent."""
2612 + self.obj._ref_count = -3
2613 + self.obj.CredentialsStored(APP_NAME)
2614 +
2615 + self.assertEqual(self.obj.ref_count, 0)
2616 + msg = 'Attempting to decrease ref_count to a negative value (-4).'
2617 + self.assertTrue(self.memento.check_warning(msg))
2618 +
2619 + def test_credentials_error_when_ref_count_is_not_positive(self):
2620 + """Ref counter is decreased when a signal is sent."""
2621 + self.obj._ref_count = -3
2622 + self.obj.CredentialsError(APP_NAME, SampleException('test'))
2623 ...

Read more...

review: Needs Fixing
Natalia Bidart (nataliabidart) wrote :

> Why do we have an Elf here?

Fixed.

> I think __init__ should be deleted, and each of those lambda methods could be
> written as:
> def LoggedIn(self, app_name, result):
> d.callback(result)

Fixed.

> "Provides an utility" -> "Provides a utility"

Fixed.

> I find that the refcounting code in CredentialsManagement is confusing.
> Perhaps we should open a bug to fix this so it's not spread in so many
> functions, and also we should make so that when the refcount increases above
> zero any pending timeout should be cancelled.

I agree. Filled bug #914381.

843. By Natalia Bidart on 2012-01-10

- Fixes from alecu's review.

844. By Natalia Bidart on 2012-01-10

Adding a log_call decorator to remove logging code duplication.

Alejandro J. Cura (alecu) wrote :

This is a great branch: a lot of redundancy removed, a lot of needed cleanup on the part of our code that has always managed to make grown men cry.

Here are few more comments:
----

"about which signal send when" ->
"about when to send which signal"

----
This line seems that should be gone:
            #return defer.fail(AlreadyStartedError())

----
        """Disconnect from the process."""
        # do something?
        ->
        """No need to disconnect DBus proxy objects."""
----

"The self.patchable_backend will be patch with" ->
"The self.patchable_backend will be patched with"

----

test_clients.py is magnificent. I'm shedding tears because of how concise and beautiful it is.

----

"on going" -> "ongoing" (it's repeated a few times)

----
what's a namaner?

    def test_cred_namaner(self):

----

In ubuntu_sso/main/windows.py:

    service_cmdlie = UbuntuSSOProxy.cmdline

(lie instead of line)
----
Authentication or Authorization?

    client.cred_manager.connect_to_signal('AuthenrizationDenied', found)

(this is present twice)
----
"Kepp track of calls" -> "Keep track of calls"
----
test_service_url_as_paremeter -> test_service_url_as_parameter
"If the paremeter service url" -> "If the parameter service url"
----
"exeucte" - > "execute
----

review: Needs Fixing
Natalia Bidart (nataliabidart) wrote :

> "about which signal send when" ->
> "about when to send which signal"

Fixed.

> This line seems that should be gone:
> #return defer.fail(AlreadyStartedError())

Yes, thanks for catching that. Removed!

> """Disconnect from the process."""
> # do something?
> ->
> """No need to disconnect DBus proxy objects."""

Changed.

> "The self.patchable_backend will be patch with" ->
> "The self.patchable_backend will be patched with"

Fixed.

> test_clients.py is magnificent. I'm shedding tears because of how concise and
> beautiful it is.

Thanks. If it wasn't for you, I would not have been able to complete it.

> "on going" -> "ongoing" (it's repeated a few times)

All fixed.

> what's a namaner?

A manager written by a gal really tired :-)

> In ubuntu_sso/main/windows.py:
>
> service_cmdlie = UbuntuSSOProxy.cmdline
>
> (lie instead of line)

Good catch!

> Authentication or Authorization?
>
> client.cred_manager.connect_to_signal('AuthenrizationDenied', found)

Twice fixed.

> "Kepp track of calls" -> "Keep track of calls"
> ----
> test_service_url_as_paremeter -> test_service_url_as_parameter
> "If the paremeter service url" -> "If the parameter service url"
> ----
> "exeuexeuctecte" - > "execute

All fixed!
Thanks!!!

845. By Natalia Bidart on 2012-01-10

Tons of grammar errors and typos fixed.

846. By Natalia Bidart on 2012-01-10

Fixed failing tests.

Natalia Bidart (nataliabidart) wrote :

> * We do the following several times:
> 'SSOLogin: emitting CaptchaGenerated with app_name "%s" and result %r',
> app_name, result
> 'SSOLogin: emitting CaptchaGenerationError with app_name "%s" and error %r',
> app_name, error
>
> is it possible to be smarter regarding the logging of the DBusSignals? an idea
> would be to use a decorator that way we know the function name and the args. I
> think it will remove some noise form the code.

Yes, I added a lg_call function to the logger module to facilitate this.

> * I'm confused, why do we have the following?:
>
> 1218 + d.addCallback(lambda a: reply_handler(*a))
>
> If a is not a sequence we are going to have a runtime error..

There will be no runtime error because of the following. That code is inside the implementation of "call_method", which makes an async call thru dbus.

So, as you can see here:

    390 self.bus.call_async(
    391 bus_name=self.bus_name, object_path=self.path,
    392 dbus_interface=self.interface, method=method_name,
    393 signature=None, args=args,
    394 reply_handler=lambda *a: d.callback(a),
    395 error_handler=d.errback)

the dbus async called will call d.callback(a) when the call is finished, and because of the lamda *a, 'a' will always be a tuple (perhaps the empty one).

This method also has to call reply_handler if it was passed as named parameter, so that's why we add this code:

    382 reply_handler = kwargs.get('reply_handler', None)
    383 if reply_handler is not None:
    384 d.addCallback(lambda a: reply_handler(*a))

Since d is callback'd with a tuple, we "desrefence" that tuple for reply_handler, which expects separated arguments.

> * There are several tests of the type:
>
> 2869 + def test_credentials_cleared(self):
> 2870 + """The CredentialsCleared signal."""
> 2871 + self.obj.CredentialsCleared(APP_NAME)
> 2872 + msgs = (self.obj.__class__.__name__,
> 2873 + self.obj.CredentialsCleared.__name__, APP_NAME)
> 2874 + self.assertTrue(self.memento.check_info(*msgs))
>
> Where we call a signal, build the msg and assert, can those be merged?

Yes, I added a method called assert_refcounter_increased which unifies them all.

> There seems to be some repetitive code here:
>
> 2583 + def test_credentials_found_when_ref_count_is_not_positive(self):
...

Yes, also unified all tests you listed using assert_refcounter_decreased and assert_refcounter_negative.

Thanks for the great review!

847. By Natalia Bidart on 2012-01-10

Avoid code duplication in main/test_common.py.

848. By Natalia Bidart on 2012-01-10

Fixed broken tests.

849. By Natalia Bidart on 2012-01-10

Merged trunk in.

850. By Natalia Bidart on 2012-01-10

- Make cmdline use it's classes' name instead of it's instance.

851. By Natalia Bidart on 2012-01-11

- Fixed copyright year.

Manuel de la Peña (mandel) wrote :

Looks great to me, things are cleaner an unified. Superb!

review: Approve
Alejandro J. Cura (alecu) wrote :

And the "Fowler lifetime achievement" goes to.... nessita!
Keep those huge refactorings coming, but let's hope we can do them in smaller branches :-)

review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches