Merge lp:~elopio/u1-test-utils/in-dash-sso into lp:~canonical-isd-hackers/u1-test-utils/test-in-dash-payments

Proposed by Leo Arias
Status: Merged
Approved by: Vincent Ladeuil
Approved revision: 62
Merged at revision: 25
Proposed branch: lp:~elopio/u1-test-utils/in-dash-sso
Merge into: lp:~canonical-isd-hackers/u1-test-utils/test-in-dash-payments
Diff against target: 457 lines (+339/-23)
7 files modified
selftest.py (+2/-1)
tests/schema.py (+13/-0)
tests/settings.py (+16/-0)
tests/sso.py (+230/-0)
tests/test_purchase_good.py (+15/-13)
tests/test_users.py (+31/-0)
tests/users.py (+32/-9)
To merge this branch: bzr merge lp:~elopio/u1-test-utils/in-dash-sso
Reviewer Review Type Date Requested Status
Vincent Ladeuil (community) Approve
Review via email: mp+162636@code.launchpad.net

This proposal supersedes a proposal from 2013-05-06.

Commit message

Added the code to create a user and log in to Ubuntu SSO.

Description of the change

To post a comment you must log in.
Revision history for this message
Leo Arias (elopio) wrote : Posted in a previous version of this proposal
Revision history for this message
Vincent Ladeuil (vila) wrote :
Download full text (3.3 KiB)

41 +import os.path

Just 'import os' and then use os.path in code.

'path' is not a regular module and mentioning it in a import clause is propagating the wrong idea that it is a regular module (look at os.py for details).

76 +DBusQtMainLoop(set_as_default=True)

IMHO, you shouldn't call this when importing the module, instead, tests that
requires it should call it and cleanup accordingly.

79 +def create_new_account_with_server_api(user):
80 + api_client = api.APIClient(conf.settings.OPENID_SSO_SERVER_URL)

This is an example of the anti-pattern we discussed earlier, referring to a
config setting without any way for the caller to override it smells
wrong. I'm ok to leave it for now as discussed but I would feel better with
a FIXME there ;)

275 - # User not logged in
276 - self.user.logout()

I'm fine with your change of logout() semantic but the comment should be
updated to say something like: "We assume the user is not logged in" or
better an assertion to verify this. But just deleting the comment is
removing some important knowledge from the test.

286 + sactions.wait_for(
287 + sactions.assert_url_contains, conf.settings.OPENID_SSO_SERVER_URL)

Fallout from hiding the config in create_new_account_with_server_api ;)

Still ok to go this route for now, even more now that it's clearly
reflected in the test.

291 - # User has no payment method
292 + self.addCleanup(self.user.logout)

Please keep the comment ;)

I'm a bit surprised to see two self.addCleanup(self.user.logout), can't this
be made part of self.user.login() instead ? And if that means passing 'self'
to self.user.login(), I'm fine with that too ;)

337 + user.is_logged_in(self.assertTrue)

That's a weird syntax 8-/

371 + def is_logged_in(self, callback):
372 + app = QtCore.QCoreApplication([])
373 + deferred = defer.Deferred()
374 + deferred.addCallback(callback)
375 + def _are_credentials_stored(credentials):
376 + deferred.callback(credentials != {})
377 +
378 + credentials_deferred = sso.get_credentials()
379 + credentials_deferred.addCallback(_are_credentials_stored)
380 + credentials_deferred.addBoth(lambda _: app.quit())
381 + app.exec_()

Ouch, I see why the syntax was weird.

I'd rather go with two helpers in this case (keeping the code but adding
comments explaining the intent, 'callback' is not explained here even if you
can only use it with test assertions): is_logged_in() and
is_not_logged_in().

That may even be the ones I'm asking for in the tests themselves.

Note that in this case I would pass the test object itself again which will
make it clearer than relying on 'callback' being a test method (which is an
indirect way to pass the test object anyway).

383 def login(self):
384 - self.logged_in = True
385 + sso.create_new_account_with_server_api(self.user_data)
386 + sso.log_in_with_desktop_client(self.user_data)

I've got a feeling we may want to separate user creation and user login in
the future. No objection to land this as is for now.

388 def logout(self):
389 - self.logged_in = False
390 + sso.log_out_with_dbus_api()

We don't delete the created user here, may be you should add a comment
explaining your reasoning in login/logout.

Overall, awesome work,...

Read more...

review: Needs Fixing
Revision history for this message
Leo Arias (elopio) wrote :
Download full text (3.6 KiB)

> 41 +import os.path
>
> Just 'import os' and then use os.path in code.

Fixed. Thanks for pointing this out.

> 76 +DBusQtMainLoop(set_as_default=True)
>
> IMHO, you shouldn't call this when importing the module, instead, tests that
> requires it should call it and cleanup accordingly.

I moved it to the functions that use the dbus loop. On all the code I've read, they never stop the loop. I'll ask alecu about that.

> 79 +def create_new_account_with_server_api(user):
> 80 + api_client = api.APIClient(conf.settings.OPENID_SSO_SERVER_URL)
>
> This is an example of the anti-pattern we discussed earlier, referring to a
> config setting without any way for the caller to override it smells
> wrong. I'm ok to leave it for now as discussed but I would feel better with
> a FIXME there ;)

I added a server parameter, and moved the config call to the test.
Also, added a FIXME on the django import.

> 275 - # User not logged in
> 276 - self.user.logout()
>
> I'm fine with your change of logout() semantic but the comment should be
> updated to say something like: "We assume the user is not logged in" or
> better an assertion to verify this. But just deleting the comment is
> removing some important knowledge from the test.

I've put an assertion there.

> 291 - # User has no payment method
> 292 + self.addCleanup(self.user.logout)
>
> Please keep the comment ;)

I've put a better one.

> I'm a bit surprised to see two self.addCleanup(self.user.logout), can't this
> be made part of self.user.login() instead ? And if that means passing 'self'
> to self.user.login(), I'm fine with that too ;)

Done.

> 337 + user.is_logged_in(self.assertTrue)
>
> That's a weird syntax 8-/
>
>
> 371 + def is_logged_in(self, callback):
> 372 + app = QtCore.QCoreApplication([])
> 373 + deferred = defer.Deferred()
> 374 + deferred.addCallback(callback)
> 375 + def _are_credentials_stored(credentials):
> 376 + deferred.callback(credentials != {})
> 377 +
> 378 + credentials_deferred = sso.get_credentials()
> 379 + credentials_deferred.addCallback(_are_credentials_stored)
> 380 + credentials_deferred.addBoth(lambda _: app.quit())
> 381 + app.exec_()
>
> Ouch, I see why the syntax was weird.
>
> I'd rather go with two helpers in this case (keeping the code but adding
> comments explaining the intent, 'callback' is not explained here even if you
> can only use it with test assertions): is_logged_in() and
> is_not_logged_in().
>
> That may even be the ones I'm asking for in the tests themselves.
>
> Note that in this case I would pass the test object itself again which will
> make it clearer than relying on 'callback' being a test method (which is an
> indirect way to pass the test object anyway).

I think that now I can improve this a little. Let me try.

> 383 def login(self):
> 384 - self.logged_in = True
> 385 + sso.create_new_account_with_server_api(self.user_data)
> 386 + sso.log_in_with_desktop_client(self.user_data)
>
> I've got a feeling we may want to separate user creation and user login in
> the future. No objection to land this as is for now.

On the pay ...

Read more...

Revision history for this message
Leo Arias (elopio) wrote :

I've made the asynchronous call synchronous, as twisted and dbus were hiding the errors. The alternative would be to use twisted trial.

lp:~elopio/u1-test-utils/in-dash-sso updated
62. By Leo Arias

Set the urls as instance attributes.

Revision history for this message
Vincent Ladeuil (vila) wrote :

First thought after reading the mail containing your newest proposal: wow, that's at least an order of magnitude clearer \o/

> Please wait while I try to refactor the is_logged_in method.

Oh my, that was worth the wait :-D

Thanks for taking my remarks into account and making your code so much more easier to understand for me.

The minor details I've come across while reading are not even worth mentioning here, most of them are already covered by FIXME or TODO comments and I'm confident that the remaining ones will be addressed at the same time.

> I've made the asynchronous call synchronous, as twisted and dbus were hiding the errors. The alternative would be to use twisted trial.

That was a key point, well done.

Asynchronous is harder than synchronous. If we can keep tests free of any asynchronous stuff without reducing their usefulness or stability, we should do that. (IME asynchronous *always* introduce complexity and/hence reduce stability, I'm happy to be proven wrong on exceptions to this rule though).

In short, this proposal provides significant enhancements vastly outweighing any objection I can think of ;)

Let's land it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'selftest.py'
2--- selftest.py 2013-02-20 15:42:29 +0000
3+++ selftest.py 2013-05-15 21:02:26 +0000
4@@ -1,5 +1,6 @@
5 #!/usr/bin/env python
6
7+import os
8 import sys
9
10 import tests
11@@ -15,5 +16,5 @@
12 discover_args = ['discover',
13 '--start-directory', './tests',
14 ]
15+os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'
16 tests.TestProgram(__name__, argv=[sys.argv[0]] + discover_args + sys.argv[1:])
17-
18
19=== added file 'tests/schema.py'
20--- tests/schema.py 1970-01-01 00:00:00 +0000
21+++ tests/schema.py 2013-05-15 21:02:26 +0000
22@@ -0,0 +1,13 @@
23+from configglue import schema
24+
25+
26+class InDashPaymentsSchema(schema.Schema):
27+
28+ class openid(schema.Section):
29+ openid_sso_server_url = schema.StringOption()
30+
31+ class ubuntuone(schema.Section):
32+ ubuntuone_server_url = schema.StringOption()
33+
34+
35+schema = InDashPaymentsSchema
36
37=== added file 'tests/settings.py'
38--- tests/settings.py 1970-01-01 00:00:00 +0000
39+++ tests/settings.py 2013-05-15 21:02:26 +0000
40@@ -0,0 +1,16 @@
41+import os
42+
43+from django_configglue.utils import configglue
44+
45+from tests.schema import schema
46+
47+
48+# Get location of local cfg files.
49+local_configs = os.environ.get('CONFIGGLUE_LOCAL_CONFIG', ['local.cfg'])
50+
51+# Get absolute path for config files
52+current_dir = os.path.dirname(os.path.abspath(__file__))
53+config_files = map(lambda x: os.path.join(current_dir, x), local_configs)
54+
55+# Glue everything together.
56+configglue(schema, config_files, __name__)
57
58=== added file 'tests/sso.py'
59--- tests/sso.py 1970-01-01 00:00:00 +0000
60+++ tests/sso.py 2013-05-15 21:02:26 +0000
61@@ -0,0 +1,230 @@
62+import os
63+import subprocess
64+import threading
65+import time
66+
67+import dbus
68+import dbus.mainloop.glib
69+import gobject
70+
71+from autopilot.emulators import input
72+from autopilot import introspection
73+from u1testutils.sso import api
74+
75+
76+def create_new_account_with_server_api(user, sso_server_url):
77+ api_client = api.APIClient(sso_server_url)
78+ api_client.create_new_account(
79+ user, captcha_id='Not used', captcha_solution='Not used')
80+
81+
82+def log_in_with_desktop_client(user, sso_server_url, ubuntuone_server_url):
83+ desktop_client = UbuntuSSODesktopClient(
84+ sso_server_url, ubuntuone_server_url)
85+ desktop_client.log_in(user)
86+
87+
88+def _start_ubuntu_sso_service_on_desktop(sso_server_url):
89+ os.environ['USSOC_SERVICE_URL'] = '{0}/api/1.0/'.format(sso_server_url)
90+ os.system('pkill ubuntu-sso')
91+ subprocess.Popen('/usr/lib/ubuntu-sso-client/ubuntu-sso-login')
92+
93+
94+def log_out_with_dbus_api(sso_server_url):
95+ _start_ubuntu_sso_service_on_desktop(sso_server_url)
96+ interface = _get_sso_dbus_interface()
97+ interface.clear_credentials('Ubuntu One', {})
98+
99+
100+def is_logged_in_on_desktop(sso_server_url):
101+ _start_ubuntu_sso_service_on_desktop(sso_server_url)
102+ credentials = _get_credentials()
103+ return credentials is not None
104+
105+
106+def _get_credentials():
107+ signaled = threading.Event()
108+ # XXX is there a way to share a variable without the globals? Without that
109+ # the signal callbacks don't set the value on the right variable.
110+ # -- elopio - 2013-05-15
111+ global _credentials
112+ _credentials = None
113+ global _credentials_error
114+ _credentials_error = None
115+
116+ def credentials_found(app_name, credentials=None):
117+ if app_name == 'Ubuntu One':
118+ global _credentials
119+ _credentials = credentials
120+ loop.quit()
121+ signaled.set()
122+
123+ def credentials_error(app_name, error):
124+ if app_name == 'Ubuntu One':
125+ global _credentials_error
126+ _credentials_error = error
127+ loop.quit()
128+ signaled.set()
129+
130+ interface = _get_sso_dbus_interface()
131+ interface.connect_to_signal('CredentialsFound', credentials_found)
132+ interface.connect_to_signal('CredentialsNotFound', credentials_found)
133+ interface.connect_to_signal('CredentialsError', credentials_error)
134+ interface.find_credentials('Ubuntu One', {})
135+ loop = gobject.MainLoop()
136+ loop.run()
137+ signaled.wait()
138+ if _credentials_error:
139+ raise dbus.DBusException(_credentials_error['message'])
140+ else:
141+ return _credentials
142+
143+
144+def _get_sso_dbus_interface():
145+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
146+ session_bus = dbus.SessionBus()
147+ proxy = session_bus.get_object(
148+ 'com.ubuntu.sso', '/com/ubuntu/sso/credentials')
149+ return dbus.Interface(
150+ proxy, dbus_interface='com.ubuntu.sso.CredentialsManagement')
151+
152+
153+class UbuntuSSODesktopClient(object):
154+
155+ def __init__(self, sso_server_url, ubuntuone_server_url):
156+ self.sso_server_url = sso_server_url
157+ self.ubuntuone_server_url = ubuntuone_server_url
158+
159+ def launch_application(self):
160+ self._prepare_environment()
161+ process = subprocess.Popen(
162+ '/usr/lib/ubuntu-sso-client/ubuntu-sso-login-qt')
163+ self.application = (
164+ introspection.get_autopilot_proxy_object_for_process(process))
165+
166+ def _prepare_environment(self):
167+ os.environ['TESTABILITY'] = '1'
168+ os.environ['SSO_AUTH_BASE_URL'] = self.sso_server_url
169+ os.environ['SSO_UONE_BASE_URL'] = self.ubuntuone_server_url
170+ _start_ubuntu_sso_service_on_desktop(self.sso_server_url)
171+
172+ def log_in(self, user):
173+ self.launch_application()
174+ log_in_page = self._go_to_log_in_page()
175+ success_page = log_in_page.log_in_with_successful_authentication(user)
176+ assert (success_page.get_message() ==
177+ 'You are now logged into Ubuntu One.')
178+ success_page.finish()
179+
180+ def _go_to_log_in_page(self):
181+ sign_up_page = SignUpPage(self.application)
182+ sign_up_page.wait_for_action_to_complete()
183+ return sign_up_page.go_to_log_in()
184+
185+
186+class Page(object):
187+
188+ def __init__(self, application, page_type):
189+ self.application = application
190+ self.page = self.application.select_single(page_type)
191+ self.keyboard = input.get_keyboard()
192+ self.mouse = input.get_mouse()
193+
194+ def wait_for_action_to_complete(self):
195+ """Wait for the last action to complete.
196+
197+ This method waits for the loading overlay to dissapear.
198+
199+ """
200+ # If autopilot executes too fast, we might look for the loading overlay
201+ # before it is shown. We should wait a little to give it time to
202+ # appear, but it's not possible to wait for it, because if autopilot
203+ # executes too slow, we might look for it after it dissapears.
204+ # Thus, the only solution that comes to mind is a sleep.
205+ # -- elopio - 2013-05-03
206+ time.sleep(3)
207+ loading_overlay = self.application.select_single('LoadingOverlay')
208+ loading_overlay.visible.wait_for(False)
209+
210+ # TODO propose this to autopilot. -- elopio - 2013-05-03
211+ def get_child_by_type(self, desired_type, **kwargs):
212+ children = self.page.get_children_by_type(desired_type, **kwargs)
213+ assert len(children) > 0, 'No child found.'
214+ assert len(children) == 1, 'More than one child found.'
215+ return children[0]
216+
217+
218+class SignUpPage(Page):
219+
220+ def __init__(self, application):
221+ super(SignUpPage, self).__init__(application, 'SetupAccountPage')
222+
223+ def go_to_log_in(self):
224+ self.wait_for_action_to_complete()
225+ sign_in_label = self.get_child_by_type(
226+ 'QLabel', objectName='sign_in_label')
227+ self.mouse.move_to_object(sign_in_label)
228+ self.mouse.click()
229+ self.wait_for_action_to_complete()
230+ return LogInPage(self.application)
231+
232+
233+class LogInPage(Page):
234+
235+ def __init__(self, application):
236+ super(LogInPage, self).__init__(application, 'CurrentUserSignInPage')
237+
238+ def log_in_with_successful_authentication(self, user):
239+ self._log_in(user.email, user.password)
240+ assert self.get_form_errors() is None
241+ return SuccessPage(self.application)
242+
243+ def _log_in(self, email, password):
244+ self._fill_log_in_form(email, password)
245+ sign_in_button = self.get_child_by_type(
246+ 'QPushButton', objectName='sign_in_button')
247+ self.mouse.move_to_object(sign_in_button)
248+ self.mouse.click()
249+ self.wait_for_action_to_complete()
250+
251+ def _fill_log_in_form(self, email, password):
252+ email_edit = self.get_child_by_type(
253+ 'QLineEdit', objectName='email_edit')
254+ self.mouse.move_to_object(email_edit)
255+ self.mouse.click()
256+ self.keyboard.type(email)
257+ password_edit = self.get_child_by_type(
258+ 'QLineEdit', objectName='password_edit')
259+ self.mouse.move_to_object(password_edit)
260+ self.mouse.click()
261+ self.keyboard.type(password)
262+
263+ def get_form_errors(self):
264+ form_errors = self.get_child_by_type(
265+ 'QLabel', objectName='form_errors')
266+ if form_errors.visible:
267+ return form_errors.text
268+ else:
269+ return None
270+
271+
272+class SuccessPage(Page):
273+
274+ def __init__(self, application):
275+ super(SuccessPage, self).__init__(application, 'SuccessPage')
276+
277+ def get_message(self):
278+ label = self.get_child_by_type(
279+ 'QLabel', objectName='success_message_body')
280+ return label.text
281+
282+ def finish(self):
283+ # TODO when we have the emulator, this should be on the wizard, not the
284+ # page. -- elopio - 2013-05-03
285+ # Bug reported to QWizard because the buttons don't have objectName:
286+ # https://bugreports.qt-project.org/browse/QTBUG-29924
287+ # TODO update when the bug is fixed. -- elopio - 2013-05-03
288+ finish_button = self.application.select_single(
289+ 'QPushButton', text='&Finish')
290+ self.mouse.move_to_object(finish_button)
291+ self.mouse.click()
292
293=== modified file 'tests/test_purchase_good.py'
294--- tests/test_purchase_good.py 2013-04-25 08:18:50 +0000
295+++ tests/test_purchase_good.py 2013-05-15 21:02:26 +0000
296@@ -1,7 +1,9 @@
297-import unity.tests as utests
298-
299 from sst import actions as sactions
300
301+# FIXME do not use django config to get the server settings.
302+# -- elopio - 2013-05-15
303+from django import conf
304+
305 import tests
306 from tests import (
307 browsers,
308@@ -19,11 +21,14 @@
309
310 class TestPurchaseGood(tests.UnitySSTestcase):
311
312-
313 def setUp(self):
314 super(TestPurchaseGood, self).setUp()
315 # A user will purchase a good
316 self.user = users.User.make_unique()
317+ # Set the urls of the servers that will be used by the user.
318+ self.user.set_urls(
319+ sso_server_url=conf.settings.OPENID_SSO_SERVER_URL,
320+ ubuntuone_server_url=conf.settings.UBUNTUONE_SERVER_URL)
321 # Arbitrarily using a good that is known to work
322 self.good = goods.Good(u'Jimi Hendrix', 'hendrix')
323
324@@ -43,8 +48,7 @@
325 scenarios = [(name, {'lens_name': name}) for name in ('home', 'music',)]
326
327 def test_user_not_logged_in(self):
328- # User not logged in
329- self.user.logout()
330+ self.assertFalse(self.user.is_logged_in())
331 self.preview_good()
332 if self.lens_name == 'music':
333 # There is a workaround in place for the music lens only that
334@@ -59,13 +63,13 @@
335 browser = browsers.Browser(self)
336 # We've been redirected to the web
337 browser.switch_to_new_window(self)
338- sactions.wait_for(sactions.assert_url_contains,
339- 'https://login.ubuntu.com')
340+ sactions.wait_for(
341+ sactions.assert_url_contains, conf.settings.OPENID_SSO_SERVER_URL)
342 self.assertEqual('Log in', browser.title)
343
344 def test_no_payment_method_stored(self):
345- # User has no payment method
346- self.user.login()
347+ # Log in with a newly created user, and set no payment method to him.
348+ self.user.login(self)
349 self.user.payment_method = None
350 self.preview_good()
351 goto_u1 = self.purchase_good()
352@@ -78,8 +82,8 @@
353 self.assertEqual('https://pay.ubuntu.com', browser.current_url)
354
355 def test_pay_method_expired(self):
356- # Uesrs's payment has expired
357- self.user.login()
358+ # Users's payment has expired
359+ self.user.login(self)
360 self.user.payment_method = 'expired'
361 self.preview_good()
362 goto_u1 = self.purchase_good()
363@@ -90,5 +94,3 @@
364 # User is sent to the web
365 self.assertTrue(browser.is_running())
366 self.assertEqual('https://pay.ubuntu.com', browser.current_url)
367-
368-
369
370=== added file 'tests/test_users.py'
371--- tests/test_users.py 1970-01-01 00:00:00 +0000
372+++ tests/test_users.py 2013-05-15 21:02:26 +0000
373@@ -0,0 +1,31 @@
374+# FIXME do not use django config to get the server settings.
375+# -- elopio - 2013-05-15
376+from django import conf
377+
378+import tests
379+from tests import users
380+
381+
382+class UsersTest(tests.TestCase):
383+
384+ def test_make_unique(self):
385+ user = users.User.make_unique(unique_id='test_uuid')
386+ user.set_urls(sso_server_url=conf.settings.OPENID_SSO_SERVER_URL)
387+
388+ self.assertEqual(user.user_data.full_name, 'Test user test_uuid')
389+ self.assertEqual(
390+ user.user_data.email, 'test+test_uuid@example.com')
391+ self.assertEqual(
392+ user.user_data.password, 'Hola123*')
393+ self.assertFalse(user.is_logged_in())
394+ self.assertEqual(user.payment_method, None)
395+
396+ def test_log_in_and_out(self):
397+ user = users.User.make_unique()
398+ user.set_urls(
399+ sso_server_url=conf.settings.OPENID_SSO_SERVER_URL,
400+ ubuntuone_server_url=conf.settings.UBUNTUONE_SERVER_URL)
401+ user.login(self)
402+ self.assertTrue(user.is_logged_in())
403+ user.logout()
404+ self.assertFalse(user.is_logged_in())
405
406=== modified file 'tests/users.py'
407--- tests/users.py 2012-12-12 15:05:25 +0000
408+++ tests/users.py 2013-05-15 21:02:26 +0000
409@@ -1,16 +1,39 @@
410+from u1testutils.sso import data
411+
412+from tests import sso
413+
414+
415 class User(object):
416
417- def __init__(self):
418- self.logged_in = None
419+ def __init__(self, user_data):
420+ self.user_data = user_data
421 self.payment_method = None
422
423+ def set_urls(self, sso_server_url=None, ubuntuone_server_url=None):
424+ self.sso_server_url = sso_server_url
425+ self.ubuntuone_server_url = ubuntuone_server_url
426+
427 @classmethod
428- def make_unique(kls):
429- user = User()
430- return user
431-
432- def login(self):
433- self.logged_in = True
434+ def make_unique(cls, unique_id=None):
435+ user_data = data.User.make_unique(unique_id)
436+ # If the user has a name longer than 30 characters, the log in will
437+ # fail. TODO is this a bug? -- elopio - 2013-05-07
438+ user_data.full_name = user_data.full_name[:30]
439+ return cls(user_data)
440+
441+ def is_logged_in(self):
442+ return sso.is_logged_in_on_desktop(self.sso_server_url)
443+
444+ def login(self, test):
445+ # Currently we are using a local server, so we don't need to delete
446+ # the user after the test. The same applies to the staging server.
447+ # When we start testing against the production server, we should either
448+ # use a stock user, or delete it after the test. -- elopio - 2013-05-15
449+ sso.create_new_account_with_server_api(
450+ self.user_data, self.sso_server_url)
451+ test.addCleanup(self.logout)
452+ sso.log_in_with_desktop_client(
453+ self.user_data, self.sso_server_url, self.ubuntuone_server_url)
454
455 def logout(self):
456- self.logged_in = False
457+ sso.log_out_with_dbus_api(self.sso_server_url)

Subscribers

People subscribed via source and target branches