Merge lp:~nataliabidart/ubuntu-sso-client/no-more-gobject into lp:ubuntu-sso-client

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 666
Merged at revision: 666
Proposed branch: lp:~nataliabidart/ubuntu-sso-client/no-more-gobject
Merge into: lp:ubuntu-sso-client
Prerequisite: lp:~nataliabidart/ubuntu-sso-client/fix-693531
Diff against target: 956 lines (+263/-234)
5 files modified
ubuntu_sso/credentials.py (+21/-28)
ubuntu_sso/gtk/gui.py (+27/-32)
ubuntu_sso/gtk/tests/test_gui.py (+155/-86)
ubuntu_sso/tests/test_credentials.py (+49/-84)
ubuntu_sso/tests/test_main.py (+11/-4)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu-sso-client/no-more-gobject
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Manuel de la Peña (community) Approve
Review via email: mp+44988@code.launchpad.net

Commit message

No more gobject dependency on non-GUI classes! (LP: #695798).

Description of the change

To run the test suite, you can do:

./run-tests

To test IRL, you can run within this branch:

killall ubuntu-sso-login; DEBUG=True PYTHONPATH=. ./bin/ubuntu-sso-login

And delete your Ubuntu One token using d-feet (com.ubuntu.sso service, /com/ubuntu/sso/credentials object path, CredentialsManagemente interface, clear_credentials method) passing arguments:

'Ubuntu One', {}

Then, you can try re-adding your machine to Ubuntu One.

To post a comment you must log in.
Revision history for this message
Manuel de la Peña (mandel) wrote :

I cannot see any problem with this code. +1

review: Approve
Revision history for this message
Roberto Alsina (ralsina) wrote :

+1 from me too.

review: Approve
Revision history for this message
Alejandro J. Cura (alecu) wrote :

59 + # This code is blocking, we should change this.
60 + # I've tried with deferToThread an twisted.web.client.getPage
61 + # but the returned deferred will never be fired (nataliabidart).

Neither deferToThread nor getPage will work because the twisted reactor is not used anymore in ubuntu-sso-client. Only twisted Deferreds are used in this code, but only as a control structure in order to make the sequence of callbacks cleaner, and to ease testing with trial.

I'm thinking of a few ways to have safe async http calls here: using libsoup or putting back the twisted reactor.

Using libsoup (like the ubuntuone-control-panel backend uses) would make the code dependent once again on gobject and possibly gnome, but it will make supporting all kind of proxies a one liner.

Using twisted will make the code more portable, but the backend should be moved to its own process (also just like u1cp) to avoid the problems we had when we mixed gtkwebkit and the twisted reactor.

Yet another option would be to move the ping url code to the gui layer, where each toolkit would choose the best async way to do it. (a Gnome ui would use libsoup, etc).

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Alecu, thanks a lot for your input, it makes perfect sense.

Currently, with my not-very-good-connection, the ping will not last more than 2 seconds, so I think we can leave the code as is for now. I filed a bug to think/resolve this in the future (bug #697628).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_sso/credentials.py'
2--- ubuntu_sso/credentials.py 2010-11-24 12:48:28 +0000
3+++ ubuntu_sso/credentials.py 2011-01-02 03:31:12 +0000
4@@ -41,8 +41,6 @@
5
6 from functools import wraps
7
8-import gobject
9-
10 from oauth import oauth
11 from twisted.internet.defer import inlineCallbacks, returnValue
12
13@@ -187,13 +185,15 @@
14
15 @handle_failures(msg='Problem while retrieving credentials')
16 @inlineCallbacks
17- def _login_success_cb(self, dialog, app_name, email):
18+ def _login_success_cb(self, app_name, email):
19 """Store credentials when the login/registration succeeded.
20
21 Also, open self.ping_url/email to notify about this new token. If any
22 error occur, self.error_cb is called. Otherwise, self.success_cb is
23 called.
24
25+ Return 0 on success, and a non-zero value (or None) on error.
26+
27 """
28 logger.info('Login/registration was successful for app %r, email %r',
29 app_name, email)
30@@ -202,19 +202,21 @@
31 assert len(creds) > 0, 'Creds are empty! This should not happen'
32 # ping a server with the credentials if we were requested to
33 if self.ping_url is not None:
34- status = self._ping_url(app_name, email, creds)
35+ status = yield self._ping_url(app_name, email, creds)
36 if status is None:
37 yield self.clear_credentials()
38 return
39
40 self.success_cb(creds)
41+ returnValue(0)
42
43- def _auth_denial_cb(self, dialog, app_name):
44+ def _auth_denial_cb(self, app_name):
45 """The user decided not to allow the registration or login."""
46 logger.warning('Login/registration was denied to app %r', app_name)
47 self.denial_cb(app_name)
48
49- @handle_exceptions(msg='Problem opening the ping_url')
50+ @handle_failures(msg='Problem opening the ping_url')
51+ @inlineCallbacks
52 def _ping_url(self, app_name, email, credentials):
53 """Ping the self.ping_url with the email attached.
54
55@@ -237,9 +239,12 @@
56 request = urllib2.Request(url, headers=oauth_req.to_header())
57 logger.debug('Opening the url "%s" with urllib2.urlopen.',
58 request.get_full_url())
59+ # This code is blocking, we should change this.
60+ # I've tried with deferToThread an twisted.web.client.getPage
61+ # but the returned deferred will never be fired (nataliabidart).
62 response = urllib2.urlopen(request)
63 logger.debug('Url opened. Response: %s.', response.code)
64- return response.code
65+ returnValue(response.code)
66
67 @handle_exceptions(msg='Problem opening the Ubuntu SSO user interface')
68 def _show_ui(self, login_only):
69@@ -252,10 +257,9 @@
70 tc_url=self.tc_url, help_text=self.help_text,
71 window_id=self.window_id, login_only=login_only)
72
73- self.gui.connect(gui.SIG_LOGIN_SUCCEEDED, self._login_success_cb)
74- self.gui.connect(gui.SIG_REGISTRATION_SUCCEEDED,
75- self._login_success_cb)
76- self.gui.connect(gui.SIG_USER_CANCELATION, self._auth_denial_cb)
77+ self.gui.login_success_callback = self._login_success_cb
78+ self.gui.registration_success_callback = self._login_success_cb
79+ self.gui.user_cancellation_callback = self._auth_denial_cb
80
81 @handle_failures(msg='Problem while retrieving credentials')
82 @inlineCallbacks
83@@ -265,33 +269,22 @@
84 if token is not None and len(token) > 0:
85 self.success_cb(token)
86 elif token == {}:
87- gobject.idle_add(self._show_ui, login_only)
88+ self._show_ui(login_only)
89 else:
90 # something went wrong with find_credentials, already handled.
91 logger.info('_login_or_register: call to "find_credentials" went '
92 'wrong, and was already handled.')
93
94 def error_cb(self, error_dict):
95- """Handle error.
96-
97- Notify the caller and finish the GUI with error.
98-
99- """
100+ """Handle error and notify the caller."""
101+ logger.error('Calling error callback at %r (error is %r).',
102+ self._error_cb, error_dict)
103 self._error_cb(self.app_name, error_dict)
104- if self.gui is not None:
105- self.gui.finish_error(error=error_dict)
106- self.gui = None
107
108 def success_cb(self, creds):
109- """Handle success.
110-
111- Notify the caller and finish the GUI with success.
112-
113- """
114+ """Handle success and notify the caller."""
115+ logger.debug('Calling success callback at %r.', self._success_cb)
116 self._success_cb(self.app_name, creds)
117- if self.gui is not None:
118- self.gui.finish_success()
119- self.gui = None
120
121 @handle_failures(msg='Problem while retrieving credentials')
122 @inlineCallbacks
123
124=== modified file 'ubuntu_sso/gtk/gui.py'
125--- ubuntu_sso/gtk/gui.py 2010-11-29 16:04:26 +0000
126+++ ubuntu_sso/gtk/gui.py 2011-01-02 03:31:12 +0000
127@@ -30,11 +30,11 @@
128
129 import dbus
130 import gettext
131-import gobject
132 import gtk
133 import xdg
134
135 from dbus.mainloop.glib import DBusGMainLoop
136+from twisted.internet.defer import inlineCallbacks
137
138 from ubuntu_sso import (DBUS_ACCOUNT_PATH, DBUS_BUS_NAME, DBUS_IFACE_USER_NAME,
139 NO_OP)
140@@ -72,20 +72,6 @@
141 HELP_TEXT_COLOR = gtk.gdk.Color("#bfbfbf")
142 WARNING_TEXT_COLOR = gtk.gdk.Color("red")
143
144-SIG_LOGIN_SUCCEEDED = 'login-succeeded'
145-SIG_REGISTRATION_SUCCEEDED = 'registration-succeeded'
146-SIG_USER_CANCELATION = 'user-cancelation'
147-
148-SIGNAL_ARGUMENTS = [
149- (SIG_LOGIN_SUCCEEDED, (gobject.TYPE_STRING, gobject.TYPE_STRING,)),
150- (SIG_REGISTRATION_SUCCEEDED, (gobject.TYPE_STRING, gobject.TYPE_STRING,)),
151- (SIG_USER_CANCELATION, (gobject.TYPE_STRING,)),
152-]
153-
154-for sig, sig_args in SIGNAL_ARGUMENTS:
155- gobject.signal_new(sig, gtk.Window, gobject.SIGNAL_RUN_FIRST,
156- gobject.TYPE_NONE, sig_args)
157-
158
159 def get_data_dir():
160 """Build absolute path to the 'data' directory."""
161@@ -198,7 +184,7 @@
162
163
164 class UbuntuSSOClientGUI(object):
165- """Ubuntu single sign on GUI."""
166+ """Ubuntu single sign-on GUI."""
167
168 CAPTCHA_SOLUTION_ENTRY = _('Type the characters above')
169 CAPTCHA_LOAD_ERROR = _('There was a problem getting the captcha, '
170@@ -256,7 +242,7 @@
171 CAPTCHA_RELOAD_TOOLTIP = _('Reload')
172
173 def __init__(self, app_name, tc_url='', help_text='',
174- window_id=0, login_only=False, close_callback=None):
175+ window_id=0, login_only=False):
176 """Create the GUI and initialize widgets."""
177 gtk.link_button_set_uri_hook(NO_OP)
178
179@@ -270,7 +256,12 @@
180 self.tc_url = tc_url
181 self.help_text = help_text
182 self.login_only = login_only
183- self.close_callback = close_callback
184+
185+ self.close_callback = NO_OP
186+ self.login_success_callback = NO_OP
187+ self.registration_success_callback = NO_OP
188+ self.user_cancellation_callback = NO_OP
189+
190 self.user_email = None
191 self.user_password = None
192
193@@ -730,17 +721,12 @@
194 signal_name, handler, args, kwargs)
195 self.window.connect(signal_name, handler, *args, **kwargs)
196
197- def emit(self, *args, **kwargs):
198- """Emit a signal proxing the main window."""
199- logger.debug('emit: args %r, kwargs, %r', args, kwargs)
200- self.window.emit(*args, **kwargs)
201-
202 def finish_success(self):
203 """The whole process was completed succesfully. Show success page."""
204 self._done = True
205 self._set_current_page(self.success_vbox)
206
207- def finish_error(self, error):
208+ def finish_error(self):
209 """The whole process was not completed succesfully. Show error page."""
210 self._done = True
211 self._set_current_page(self.error_vbox)
212@@ -766,18 +752,17 @@
213 if self.window is not None:
214 self.window.hide()
215
216- # process any pending events before emitting signals
217+ # process any pending events before callbacking with result
218 while gtk.events_pending():
219 gtk.main_iteration()
220
221 if not self._done:
222- self.emit(SIG_USER_CANCELATION, self.app_name)
223+ self.user_cancellation_callback(self.app_name)
224
225 # call user defined callback
226- if self.close_callback is not None:
227- logger.info('Calling custom close_callback %r with params %r, %r',
228- self.close_callback, args, kwargs)
229- self.close_callback(*args, **kwargs)
230+ logger.info('Calling custom close_callback %r with params %r, %r',
231+ self.close_callback, args, kwargs)
232+ self.close_callback(*args, **kwargs)
233
234 def on_sign_in_button_clicked(self, *args, **kwargs):
235 """User wants to sign in, present the Login page."""
236@@ -1096,10 +1081,15 @@
237 self._set_current_page(self.enter_details_vbox, warning_text=msg)
238
239 @log_call
240+ @inlineCallbacks
241 def on_email_validated(self, app_name, email, *args, **kwargs):
242 """User email was successfully verified."""
243 self._done = True
244- self.emit(SIG_REGISTRATION_SUCCEEDED, self.app_name, email)
245+ result = yield self.registration_success_callback(self.app_name, email)
246+ if result == 0:
247+ self.finish_success()
248+ else:
249+ self.finish_error()
250
251 @log_call
252 def on_email_validation_error(self, app_name, error, *args, **kwargs):
253@@ -1112,10 +1102,15 @@
254 self._set_current_page(self.verify_email_vbox, warning_text=msg)
255
256 @log_call
257+ @inlineCallbacks
258 def on_logged_in(self, app_name, email, *args, **kwargs):
259 """User was successfully logged in."""
260 self._done = True
261- self.emit(SIG_LOGIN_SUCCEEDED, self.app_name, email)
262+ result = yield self.login_success_callback(self.app_name, email)
263+ if result == 0:
264+ self.finish_success()
265+ else:
266+ self.finish_error()
267
268 @log_call
269 def on_login_error(self, app_name, error, *args, **kwargs):
270
271=== modified file 'ubuntu_sso/gtk/tests/test_gui.py'
272--- ubuntu_sso/gtk/tests/test_gui.py 2010-12-09 21:04:59 +0000
273+++ ubuntu_sso/gtk/tests/test_gui.py 2011-01-02 03:31:12 +0000
274@@ -517,6 +517,11 @@
275 self.assertTrue(self._called,
276 'close_callback was called when window was closed.')
277
278+ def test_close_callback_if_not_set(self):
279+ """The close_callback is a no op if not set."""
280+ self.ui.on_close_clicked()
281+ # no crash when close_callback is not set
282+
283 def test_app_name_is_stored(self):
284 """The app_name is stored for further use."""
285 self.assertIn(APP_NAME, self.ui.app_name)
286@@ -555,18 +560,6 @@
287
288 self.assertEqual(self.ui.bus.callbacks, {})
289
290- def test_close_callback(self):
291- """A close_callback parameter is called when closing the window."""
292- ui = self.gui_class(close_callback=self._set_called, **self.kwargs)
293- ui.on_close_clicked()
294- self.assertTrue(self._called, 'close_callback was called on close.')
295-
296- def test_close_callback_if_none(self):
297- """A close_callback parameter is not called if is None."""
298- ui = self.gui_class(close_callback=None, **self.kwargs)
299- ui.on_close_clicked()
300- # no crash when close_callback is None
301-
302 def test_pages_are_packed_into_container(self):
303 """All the pages are packed in the main container."""
304 children = self.ui.content.get_children()
305@@ -620,8 +613,7 @@
306 buttons = filter(lambda name: 'cancel_button' in name or
307 'close_button' in name, self.ui.widgets)
308 for name in buttons:
309- self.ui = self.gui_class(close_callback=self._set_called,
310- **self.kwargs)
311+ self.ui.close_callback = self._set_called
312 widget = getattr(self.ui, name)
313 widget.clicked()
314 self.assertEqual(self._called, ((widget,), {}), msg % name)
315@@ -661,7 +653,7 @@
316
317 def test_finish_error_shows_error_page(self):
318 """When calling 'finish_error' the error page is shown."""
319- self.ui.finish_error(error=self.error)
320+ self.ui.finish_error()
321 self.assert_pages_visibility(finish=True)
322 self.assertEqual(self.ui.ERROR, self.ui.finish_vbox.label.get_text())
323
324@@ -1164,10 +1156,10 @@
325 self.click_verify_email_with_valid_data()
326 self.assert_warnings_visibility()
327
328- def test_on_email_validated_shows_processing_page(self):
329- """On email validated the procesing page is still shown."""
330+ def test_on_email_validated_shows_finish_page(self):
331+ """On email validated the finish page is shown."""
332 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
333- self.assert_pages_visibility(processing=True)
334+ self.assert_pages_visibility(finish=True)
335
336 def test_on_email_validated_does_not_clear_the_help_text(self):
337 """On email validated the help text is not removed."""
338@@ -1227,6 +1219,32 @@
339 self.ui.verify_token_button.clicked()
340 self.assertTrue(self._called)
341
342+ def test_after_registration_success_finish_success(self):
343+ """After REGISTRATION_SUCCESS is called, finish_success is called.
344+
345+ Only when REGISTRATION_SUCCESS returns 0.
346+
347+ """
348+ self.patch(self.ui, 'finish_success', self._set_called)
349+ self.ui.registration_success_callback = lambda app, email: 0
350+
351+ self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
352+
353+ self.assertEqual(self._called, ((), {}))
354+
355+ def test_after_registration_error_finish_error(self):
356+ """After REGISTRATION_SUCCESS is called, finish_error is called.
357+
358+ Only when REGISTRATION_SUCCESS returns a non-zero value.
359+
360+ """
361+ self.patch(self.ui, 'finish_error', self._set_called)
362+ self.ui.registration_success_callback = lambda app, email: -1
363+
364+ self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
365+
366+ self.assertEqual(self._called, ((), {}))
367+
368
369 class VerifyEmailValidationTestCase(UbuntuSSOClientTestCase):
370 """Test suite for the user registration validation (verify email page)."""
371@@ -1480,11 +1498,11 @@
372 self.click_connect_with_valid_data()
373 self.assert_pages_visibility(processing=True)
374
375- def test_on_logged_in_morphs_to_processing_page(self):
376- """When user logged in the processing page is still shown."""
377+ def test_on_logged_in_morphs_to_finish_page(self):
378+ """When user logged in the finish page is shown."""
379 self.click_connect_with_valid_data()
380 self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
381- self.assert_pages_visibility(processing=True)
382+ self.assert_pages_visibility(finish=True)
383
384 def test_on_login_error_morphs_to_login_page(self):
385 """On user login error, the previous page is shown."""
386@@ -1541,6 +1559,32 @@
387 self.assertEqual(self.ui.user_email, EMAIL)
388 self.assertEqual(self.ui.user_password, PASSWORD)
389
390+ def test_after_login_success_finish_success(self):
391+ """After LOGIN_SUCCESSFULL is called, finish_success is called.
392+
393+ Only when LOGIN_SUCCESSFULL returns 0.
394+
395+ """
396+ self.patch(self.ui, 'finish_success', self._set_called)
397+ self.ui.login_success_callback = lambda app, email: 0
398+
399+ self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
400+
401+ self.assertEqual(self._called, ((), {}))
402+
403+ def test_after_login_error_finish_error(self):
404+ """After LOGIN_SUCCESSFULL is called, finish_error is called.
405+
406+ Only when LOGIN_SUCCESSFULL returns a non-zero value.
407+
408+ """
409+ self.patch(self.ui, 'finish_error', self._set_called)
410+ self.ui.login_success_callback = lambda app, email: -1
411+
412+ self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
413+
414+ self.assertEqual(self._called, ((), {}))
415+
416
417 class LoginValidationTestCase(UbuntuSSOClientTestCase):
418 """Test suite for the user login validation."""
419@@ -2060,119 +2104,144 @@
420 self.assertEqual(self.ui.help_label.get_text(), HELP_TEXT)
421
422
423-class SignalsTestCase(UbuntuSSOClientTestCase):
424- """Test the GTK signal emission."""
425+class CallbacksTestCase(UbuntuSSOClientTestCase):
426+ """Test the GTK callback calls."""
427+
428+ LOGIN_SUCCESSFULL = 'login_success_callback'
429+ REGISTRATION_SUCCESS = 'registration_success_callback'
430+ USER_CANCELLATION = 'user_cancellation_callback'
431
432 def setUp(self):
433 """Init."""
434- super(SignalsTestCase, self).setUp()
435+ super(CallbacksTestCase, self).setUp()
436 self._called = {}
437- for sig_name, _ in gui.SIGNAL_ARGUMENTS:
438- self.ui.connect(sig_name, self._set_called, sig_name)
439-
440- def _set_called(self, widget, *args, **kwargs):
441- """Keep trace of signals emition."""
442+ for name in (self.LOGIN_SUCCESSFULL,
443+ self.REGISTRATION_SUCCESS,
444+ self.USER_CANCELLATION):
445+ setattr(self.ui, name, self._set_called(name))
446+
447+ def _set_called(self, callback_name):
448+ """Keep trace of callbacks calls."""
449+
450 # pylint: disable=W0221
451- self._called[args[-1]] = (widget, args[:-1], kwargs)
452-
453- def test_closing_main_window_sends_outcome_as_signal(self):
454- """A signal is sent when closing the main window."""
455+
456+ def inner(*args, **kwargs):
457+ """Store arguments used in this call."""
458+ self._called[callback_name] = args
459+
460+ return inner
461+
462+ def test_closing_main_window(self):
463+ """When closing the main window, USER_CANCELLATION is called."""
464 self.ui.window.emit('delete-event', gtk.gdk.Event(gtk.gdk.DELETE))
465- expected = (self.ui.window, (APP_NAME,), {})
466- self.assertEqual(self._called[gui.SIG_USER_CANCELATION], expected)
467+ self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
468
469- def test_every_cancel_emits_proper_signal(self):
470- """Clicking on any cancel button, 'user-cancelation' signal is sent."""
471- sig = gui.SIG_USER_CANCELATION
472- msg = 'user-cancelation is emitted when "%s" is clicked.'
473+ def test_every_cancel_calls_proper_callback(self):
474+ """When any cancel button is clicked, USER_CANCELLATION is called."""
475+ msg = 'user_cancellation_callback is called when "%s" is clicked.'
476 buttons = filter(lambda name: 'cancel_button' in name, self.ui.widgets)
477 for name in buttons:
478- self.ui = self.gui_class(**self.kwargs)
479- self.ui.connect(sig, self._set_called, sig)
480 widget = getattr(self.ui, name)
481 widget.clicked()
482- expected_args = (self.ui.window, (APP_NAME,), {})
483- self.assertEqual(self._called[sig], expected_args, msg % name)
484+ self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,),
485+ msg % name)
486 self._called = {}
487
488- def test_on_user_registration_error_proper_signal_is_emitted(self):
489- """On UserRegistrationError, SIG_USER_CANCELATION signal is sent."""
490+ def test_on_user_registration_error_proper_callback_is_called(self):
491+ """On UserRegistrationError, USER_CANCELLATION is called."""
492 self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
493 self.ui.on_close_clicked()
494- expected = (self.ui.window, (APP_NAME,), {})
495- self.assertEqual(expected,
496- self._called[gui.SIG_USER_CANCELATION])
497-
498- def test_on_email_validated_proper_signals_is_emitted(self):
499- """On EmailValidated, 'registration-succeeded' signal is sent."""
500+
501+ self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
502+
503+ def test_on_email_validated_proper_callback_is_called(self):
504+ """On EmailValidated, REGISTRATION_SUCCESS is called."""
505 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
506 self.ui.on_close_clicked()
507- self.assertEqual((self.ui.window, (APP_NAME, EMAIL), {}),
508- self._called[gui.SIG_REGISTRATION_SUCCEEDED])
509-
510- def test_on_email_validation_error_proper_signals_is_emitted(self):
511- """On EmailValidationError, SIG_USER_CANCELATION signal is sent."""
512+
513+ self.assertEqual(self._called[self.REGISTRATION_SUCCESS],
514+ (APP_NAME, EMAIL))
515+
516+ def test_on_email_validation_error_proper_callback_is_called(self):
517+ """On EmailValidationError, USER_CANCELLATION is called."""
518 self.ui.on_email_validation_error(app_name=APP_NAME, error=self.error)
519 self.ui.on_close_clicked()
520- expected = (self.ui.window, (APP_NAME,), {})
521- self.assertEqual(expected,
522- self._called[gui.SIG_USER_CANCELATION])
523-
524- def test_on_logged_in_proper_signals_is_emitted(self):
525- """On LoggedIn, 'login-succeeded' signal is sent."""
526+
527+ self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
528+
529+ def test_on_logged_in_proper_callback_is_called(self):
530+ """On LoggedIn, LOGIN_SUCCESSFULL is called."""
531 self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
532 self.ui.on_close_clicked()
533- self.assertEqual((self.ui.window, (APP_NAME, EMAIL), {}),
534- self._called[gui.SIG_LOGIN_SUCCEEDED])
535-
536- def test_on_login_error_proper_signals_is_emitted(self):
537- """On LoginError, SIG_USER_CANCELATION signal is sent."""
538+
539+ self.assertEqual(self._called[self.LOGIN_SUCCESSFULL],
540+ (APP_NAME, EMAIL))
541+
542+ def test_on_login_error_proper_callback_is_called(self):
543+ """On LoginError, USER_CANCELLATION is called."""
544 self.click_connect_with_valid_data()
545 self.ui.on_login_error(app_name=APP_NAME, error=self.error)
546 self.ui.on_close_clicked()
547- expected = (self.ui.window, (APP_NAME,), {})
548- self.assertEqual(expected,
549- self._called[gui.SIG_USER_CANCELATION])
550-
551- def test_registration_successfull_even_if_prior_registration_error(self):
552- """Only one signal is sent with the final outcome."""
553+
554+ self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
555+
556+ def test_registration_success_even_if_prior_registration_error(self):
557+ """Only one callback is called with the final outcome.
558+
559+ When the user successfully registers, REGISTRATION_SUCCESS is
560+ called even if there were errors before.
561+
562+ """
563 self.click_join_with_valid_data()
564 self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
565 self.click_join_with_valid_data()
566 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
567 self.ui.on_close_clicked()
568
569- self.assertEqual(len(self._called), 1)
570- self.assertTrue(gui.SIG_REGISTRATION_SUCCEEDED in self._called)
571-
572- def test_login_successfull_even_if_prior_login_error(self):
573- """Only one signal is sent with the final outcome."""
574+ self.assertEqual(self._called[self.REGISTRATION_SUCCESS],
575+ (APP_NAME, EMAIL))
576+
577+ def test_login_success_even_if_prior_login_error(self):
578+ """Only one callback is called with the final outcome.
579+
580+ When the user successfully logins, LOGIN_SUCCESSFULL is called even if
581+ there were errors before.
582+
583+ """
584 self.click_connect_with_valid_data()
585 self.ui.on_login_error(app_name=APP_NAME, error=self.error)
586 self.click_connect_with_valid_data()
587 self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
588 self.ui.on_close_clicked()
589
590- self.assertEqual(len(self._called), 1)
591- self.assertTrue(gui.SIG_LOGIN_SUCCEEDED in self._called)
592+ self.assertEqual(self._called[self.LOGIN_SUCCESSFULL],
593+ (APP_NAME, EMAIL))
594
595 def test_user_cancelation_even_if_prior_registration_error(self):
596- """Only one signal is sent with the final outcome."""
597+ """Only one callback is called with the final outcome.
598+
599+ When the user closes the window, USER_CANCELLATION is called even if
600+ there were registration errors before.
601+
602+ """
603 self.click_join_with_valid_data()
604 self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
605 self.ui.join_cancel_button.clicked()
606
607- self.assertEqual(len(self._called), 1)
608- self.assertTrue(gui.SIG_USER_CANCELATION in self._called)
609+ self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
610
611 def test_user_cancelation_even_if_prior_login_error(self):
612- """Only one signal is sent with the final outcome."""
613+ """Only one callback is called with the final outcome.
614+
615+ When the user closes the window, USER_CANCELLATION is called even if
616+ there were login errors before.
617+
618+ """
619 self.click_connect_with_valid_data()
620 self.ui.on_login_error(app_name=APP_NAME, error=self.error)
621 self.ui.login_cancel_button.clicked()
622
623- self.assertEqual(len(self._called), 1)
624- self.assertTrue(gui.SIG_USER_CANCELATION in self._called)
625+ self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
626
627
628 class DefaultButtonsTestCase(UbuntuSSOClientTestCase):
629
630=== modified file 'ubuntu_sso/tests/test_credentials.py'
631--- ubuntu_sso/tests/test_credentials.py 2010-12-20 14:29:18 +0000
632+++ ubuntu_sso/tests/test_credentials.py 2011-01-02 03:31:12 +0000
633@@ -21,8 +21,6 @@
634 import logging
635 import urllib
636
637-import gobject
638-
639 from twisted.internet import defer
640 from twisted.internet.defer import inlineCallbacks
641 from twisted.trial.unittest import TestCase, FailTest
642@@ -60,10 +58,6 @@
643 WINDOW_ID_KEY: WINDOW_ID,
644 }
645
646-SIG_LOGIN_SUCCEEDED = '1'
647-SIG_REGISTRATION_SUCCEEDED = '2'
648-SIG_USER_CANCELATION = '3'
649-
650
651 class SampleMiscException(Exception):
652 """An error to be used while testing."""
653@@ -82,17 +76,9 @@
654 def __init__(self, *args, **kwargs):
655 self.args = args
656 self.kwargs = kwargs
657- self.signals = []
658- self.methods = []
659- self.connect = lambda *a: self.signals.append(a)
660-
661- def finish_success(self):
662- """Fake the success finish."""
663- self.methods.append('finish_success')
664-
665- def finish_error(self, error):
666- """Fake the error finish."""
667- self.methods.append(('finish_error', error))
668+ self.login_success_callback = None
669+ self.registration_success_callback = None
670+ self.user_cancellation_callback = None
671
672
673 class BasicTestCase(TestCase):
674@@ -100,7 +86,6 @@
675
676 def setUp(self):
677 """Init."""
678- self.patch(gobject, 'idle_add', lambda f, *a, **kw: f(*a, **kw))
679 self._called = False # helper
680
681 self.memento = MementoHandler()
682@@ -226,45 +211,21 @@
683
684 self.assertEqual(getattr(self.obj, UI_MODULE_KEY), GTK_GUI_MODULE)
685
686- def test_success_cb_gui_none(self):
687- """Success callback calls the caller but not the GUI."""
688- self.obj.gui = None
689- self.obj.success_cb(creds=TOKEN)
690-
691- self.assertEqual(self._called, (('success', APP_NAME, TOKEN), {}),
692- 'caller _success_cb was called.')
693-
694- def test_success_cb_gui_not_none(self):
695- """Success callback calls the caller and finish the GUI."""
696- self.obj.gui = ui = FakedClientGUI(APP_NAME)
697- self.obj.success_cb(creds=TOKEN)
698-
699- self.assertEqual(self._called, (('success', APP_NAME, TOKEN), {}),
700- 'caller _success_cb was called.')
701- self.assertEqual(ui.methods, ['finish_success'],
702- 'GUI finish_success was called.')
703- self.assertTrue(self.obj.gui is None, 'gui is reset to None.')
704-
705- def test_error_cb_gui_none(self):
706- """Error callback calls the caller but not the GUI."""
707- self.obj.gui = None
708- error_dict = {'foo': 'bar'}
709- self.obj.error_cb(error_dict=error_dict)
710-
711- self.assertEqual(self._called, (('error', APP_NAME, error_dict), {}),
712- 'caller _error_cb was called.')
713-
714- def test_error_cb_gui_not_none(self):
715- """Error callback calls the caller and finish the GUI."""
716- self.obj.gui = ui = FakedClientGUI(APP_NAME)
717- error_dict = {'foo': 'bar'}
718- self.obj.error_cb(error_dict=error_dict)
719-
720- self.assertEqual(self._called, (('error', APP_NAME, error_dict), {}),
721- 'caller _error_cb was called.')
722- self.assertEqual(ui.methods, [('finish_error', error_dict)],
723- 'GUI finish_error was called.')
724- self.assertTrue(self.obj.gui is None, 'gui is reset to None.')
725+ def test_success_cb(self):
726+ """Success callback calls the caller."""
727+ self.obj.gui = None
728+ self.obj.success_cb(creds=TOKEN)
729+
730+ self.assertEqual(self._called, (('success', APP_NAME, TOKEN), {}),
731+ 'caller _success_cb was called.')
732+
733+ def test_error_cb(self):
734+ """Error callback calls the caller."""
735+ error_dict = {'foo': 'bar'}
736+ self.obj.error_cb(error_dict=error_dict)
737+
738+ self.assertEqual(self._called, (('error', APP_NAME, error_dict), {}),
739+ 'caller _error_cb was called.')
740
741
742 class CredentialsLoginSuccessTestCase(CredentialsTestCase):
743@@ -276,12 +237,12 @@
744 self.patch(credentials.Keyring, 'get_credentials',
745 lambda kr, app: defer.succeed(None))
746
747- yield self.obj._login_success_cb(dialog=None, app_name=APP_NAME,
748- email=EMAIL)
749+ result = yield self.obj._login_success_cb(APP_NAME, EMAIL)
750
751 msg = 'Creds are empty! This should not happen'
752 self.assert_error_cb_called(msg='Problem while retrieving credentials',
753 detailed_error=AssertionError(msg))
754+ self.assertEqual(result, None)
755
756 @inlineCallbacks
757 def test_cred_error(self):
758@@ -290,11 +251,11 @@
759 self.patch(credentials.Keyring, 'get_credentials',
760 lambda kr, app: defer.fail(expected_error))
761
762- yield self.obj._login_success_cb(dialog=None, app_name=APP_NAME,
763- email=EMAIL)
764+ result = yield self.obj._login_success_cb(APP_NAME, EMAIL)
765
766 msg = 'Problem while retrieving credentials'
767 self.assert_error_cb_called(msg=msg, detailed_error=expected_error)
768+ self.assertEqual(result, None)
769 self.assertTrue(self.memento.check_exception(SampleMiscException))
770
771 @inlineCallbacks
772@@ -304,10 +265,10 @@
773 lambda kr, app: defer.succeed(TOKEN))
774 self.patch(self.obj, '_ping_url', lambda *a, **kw: 200)
775
776- yield self.obj._login_success_cb(dialog=None, app_name=APP_NAME,
777- email=EMAIL)
778+ result = yield self.obj._login_success_cb(APP_NAME, EMAIL)
779
780 self.assertEqual(self._called, (('success', APP_NAME, TOKEN), {}))
781+ self.assertEqual(result, 0)
782
783 @inlineCallbacks
784 def test_ping_error(self):
785@@ -324,12 +285,12 @@
786 self.patch(self.obj, 'clear_credentials',
787 lambda: setattr(self, '_cred_cleared', True))
788
789- yield self.obj._login_success_cb(dialog=None, app_name=APP_NAME,
790- email=EMAIL)
791+ result = yield self.obj._login_success_cb(APP_NAME, EMAIL)
792
793 # error cb called correctly
794 msg = 'Problem opening the ping_url'
795 self.assert_error_cb_called(msg=msg, detailed_error=FailTest(error))
796+ self.assertEqual(result, None)
797
798 # credentials cleared
799 self.assertTrue(self._cred_cleared)
800@@ -349,8 +310,7 @@
801 self.patch(self.obj, '_ping_url',
802 lambda *a, **kw: setattr(self, '_url_pinged', (a, kw)))
803
804- yield self.obj._login_success_cb(dialog=None, app_name=APP_NAME,
805- email=EMAIL)
806+ yield self.obj._login_success_cb(APP_NAME, EMAIL)
807
808 self.assertEqual(self._url_pinged, ((APP_NAME, EMAIL, TOKEN), {}))
809
810@@ -366,10 +326,10 @@
811 self.patch(self.obj, 'clear_credentials', self._set_called)
812 self.obj.ping_url = None
813
814- yield self.obj._login_success_cb(dialog=None, app_name=APP_NAME,
815- email=EMAIL)
816+ result = yield self.obj._login_success_cb(APP_NAME, EMAIL)
817
818 self.assertEqual(self._called, (('success', APP_NAME, TOKEN), {}))
819+ self.assertEqual(result, 0)
820
821
822 class CredentialsAuthDeniedTestCase(CredentialsTestCase):
823@@ -377,10 +337,9 @@
824
825 def test_auth_denial_cb(self):
826 """On auth denied, self.denial_cb is called."""
827- self.obj.gui = ui = FakedClientGUI(APP_NAME)
828- self.obj._auth_denial_cb(dialog=None, app_name=APP_NAME)
829+ self.obj._auth_denial_cb(app_name=APP_NAME)
830+
831 self.assertEqual(self._called, (('denial', APP_NAME), {}))
832- self.assertEqual(ui.methods, [], 'no GUI method was called.')
833
834
835 class PingUrlTestCase(CredentialsTestCase):
836@@ -401,24 +360,29 @@
837
838 self.patch(credentials.urllib2, 'urlopen', faked_urlopen)
839
840+ @inlineCallbacks
841 def test_ping_url_if_url_is_none(self):
842 """self.ping_url is opened."""
843 self.patch(credentials.urllib2, 'urlopen', self.fail)
844 self.obj.ping_url = None
845- self.obj._ping_url(app_name=APP_NAME, email=EMAIL, credentials=TOKEN)
846+ yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL,
847+ credentials=TOKEN)
848 # no failure
849
850+ @inlineCallbacks
851 def test_ping_url(self):
852 """On auth success, self.ping_url is opened."""
853- self.obj._ping_url(app_name=APP_NAME, email=EMAIL, credentials=TOKEN)
854+ yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL,
855+ credentials=TOKEN)
856
857 self.assertIsInstance(self._request, credentials.urllib2.Request)
858 self.assertEqual(self._request.get_full_url(),
859 self.obj.ping_url + EMAIL)
860
861+ @inlineCallbacks
862 def test_request_is_signed_with_credentials(self):
863 """The http request to self.ping_url is signed with the credentials."""
864- result = self.obj._ping_url(APP_NAME, EMAIL, TOKEN)
865+ result = yield self.obj._ping_url(APP_NAME, EMAIL, TOKEN)
866 headers = self._request.headers
867
868 self.assertIn('Authorization', headers)
869@@ -431,12 +395,13 @@
870 self.assertIn(expected, oauth_stuff)
871 self.assertEqual(result, 200)
872
873+ @inlineCallbacks
874 def test_ping_url_error(self):
875 """Exception is handled if ping fails."""
876 error = 'Blu'
877 self.patch(credentials.urllib2, 'urlopen', lambda r: self.fail(error))
878
879- self.obj._ping_url(APP_NAME, EMAIL, TOKEN)
880+ yield self.obj._ping_url(APP_NAME, EMAIL, TOKEN)
881
882 msg = 'Problem opening the ping_url'
883 self.assert_error_cb_called(msg=msg, detailed_error=FailTest(error))
884@@ -592,17 +557,17 @@
885
886 @inlineCallbacks
887 def test_connects_gui_signals(self):
888- """Outcome signals from GUI are connected to callbacks."""
889+ """GUI callbacks are properly connected."""
890 self.patch(credentials.Keyring, 'get_credentials',
891 lambda kr, app: defer.succeed(None))
892-
893- expected = [
894- (SIG_LOGIN_SUCCEEDED, self.obj._login_success_cb),
895- (SIG_REGISTRATION_SUCCEEDED, self.obj._login_success_cb),
896- (SIG_USER_CANCELATION, self.obj._auth_denial_cb),
897- ]
898 yield getattr(self.obj, self.operation)()
899- self.assertEqual(self.obj.gui.signals, expected)
900+
901+ self.assertEqual(self.obj.gui.login_success_callback,
902+ self.obj._login_success_cb)
903+ self.assertEqual(self.obj.gui.registration_success_callback,
904+ self.obj._login_success_cb)
905+ self.assertEqual(self.obj.gui.user_cancellation_callback,
906+ self.obj._auth_denial_cb)
907
908 @inlineCallbacks
909 def test_gui_is_created(self):
910
911=== modified file 'ubuntu_sso/tests/test_main.py'
912--- ubuntu_sso/tests/test_main.py 2011-01-02 03:31:12 +0000
913+++ ubuntu_sso/tests/test_main.py 2011-01-02 03:31:12 +0000
914@@ -702,6 +702,7 @@
915 class CredentialsManagementTestCase(TestCase):
916 """Tests for the CredentialsManagement DBus interface."""
917
918+ timeout = 2
919 base_args = {HELP_TEXT_KEY: HELP_TEXT, PING_URL_KEY: PING_URL,
920 TC_URL_KEY: TC_URL, WINDOW_ID_KEY: WINDOW_ID,
921 UI_CLASS_KEY: 'SuperUI', UI_MODULE_KEY: 'foo.bar.baz',
922@@ -739,10 +740,8 @@
923 self.assertIsInstance(self.client, ubuntu_sso.main.dbus.service.Object)
924
925
926-class CredentialsManagementFindClearTestCase(CredentialsManagementTestCase):
927- """Tests for the CredentialsManagement find/clear/store methods."""
928-
929- timeout = 5
930+class CredentialsManagementFindTestCase(CredentialsManagementTestCase):
931+ """Tests for the CredentialsManagement find method."""
932
933 def test_find_credentials(self):
934 """The credentials are asked and returned in signals."""
935@@ -808,6 +807,10 @@
936 self.client.find_credentials(APP_NAME, self.args)
937 return d
938
939+
940+class CredentialsManagementClearTestCase(CredentialsManagementTestCase):
941+ """Tests for the CredentialsManagement clear method."""
942+
943 def test_clear_credentials(self):
944 """The credentials are removed."""
945 self.create_mock_backend().clear_credentials()
946@@ -840,6 +843,10 @@
947 self.client.clear_credentials(APP_NAME, self.args)
948 return d
949
950+
951+class CredentialsManagementStoreTestCase(CredentialsManagementTestCase):
952+ """Tests for the CredentialsManagement store method."""
953+
954 def test_store_credentials(self):
955 """The credentials are stored and the outcome is a signal."""
956 self.create_mock_backend().store_credentials(TOKEN)

Subscribers

People subscribed via source and target branches