Merge lp:~nataliabidart/ubuntu-sso-client/gtk-ui-executable into lp:ubuntu-sso-client

Proposed by Natalia Bidart on 2012-01-16
Status: Merged
Approved by: Natalia Bidart on 2012-01-18
Approved revision: 841
Merged at revision: 834
Proposed branch: lp:~nataliabidart/ubuntu-sso-client/gtk-ui-executable
Merge into: lp:ubuntu-sso-client
Diff against target: 1326 lines (+467/-358)
8 files modified
bin/ubuntu-sso-login-gtk (+31/-0)
run-tests.bat (+70/-70)
setup.py (+7/-4)
ubuntu_sso/gtk/gui.py (+66/-76)
ubuntu_sso/gtk/main.py (+49/-0)
ubuntu_sso/gtk/tests/test_gui.py (+113/-191)
ubuntu_sso/gtk/tests/test_main.py (+123/-0)
ubuntu_sso/keyring/linux.py (+8/-17)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu-sso-client/gtk-ui-executable
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve on 2012-01-18
Manuel de la Peña (community) 2012-01-16 Approve on 2012-01-18
Review via email: mp+88777@code.launchpad.net

Commit Message

- Make the GTK UI a separated executable script (LP: #917373).

To post a comment you must log in.
838. By Natalia Bidart on 2012-01-17

Rename LOGIN_SUCCESSFULL to LOGIN_SUCCESS for consistency sake.

839. By Natalia Bidart on 2012-01-18

- Removing unused setUp.

840. By Natalia Bidart on 2012-01-18

Remove unused import.

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

I'm getting the following when I run the tests on windows:

C:\Users\Mandel\Projects\Canonical\ubuntu-sso-client\gtk-ui-executable>run-tests

Checking if python 2.7 is in the system
ERROR: The system was unable to find the specified registry key or value.
Checking if python 2.6 is in the system
ERROR: The system was unable to find the specified registry key or value.
Checking if python 2.7 32 is in the system
Python found, building auto-generated modules...
running build
Compiled data\qt\choose_sign_in.ui into ubuntu_sso\qt\choose_sign_in_ui.py
Compiled data\qt\current_user_sign_in.ui into ubuntu_sso\qt\current_user_sign_in
_ui.py
Compiled data\qt\email_verification.ui into ubuntu_sso\qt\email_verification_ui.
py
Compiled data\qt\error_message.ui into ubuntu_sso\qt\error_message_ui.py
Compiled data\qt\forgotten_password.ui into ubuntu_sso\qt\forgotten_password_ui.
py
Compiled data\qt\reset_password.ui into ubuntu_sso\qt\reset_password_ui.py
Compiled data\qt\setup_account.ui into ubuntu_sso\qt\setup_account_ui.py
Compiled data\qt\success_message.ui into ubuntu_sso\qt\success_message_ui.py
running build_py
creating build
creating build\lib
creating build\lib\ubuntu_sso
copying ubuntu_sso\account.py -> build\lib\ubuntu_sso
copying ubuntu_sso\credentials.py -> build\lib\ubuntu_sso
copying ubuntu_sso\logger.py -> build\lib\ubuntu_sso
copying ubuntu_sso\__init__.py -> build\lib\ubuntu_sso
creating build\lib\ubuntu_sso\utils
copying ubuntu_sso\utils\ipc.py -> build\lib\ubuntu_sso\utils
copying ubuntu_sso\utils\tcpactivation.py -> build\lib\ubuntu_sso\utils
copying ubuntu_sso\utils\txsecrets.py -> build\lib\ubuntu_sso\utils
copying ubuntu_sso\utils\ui.py -> build\lib\ubuntu_sso\utils
copying ubuntu_sso\utils\__init__.py -> build\lib\ubuntu_sso\utils
creating build\lib\ubuntu_sso\keyring
copying ubuntu_sso\keyring\linux.py -> build\lib\ubuntu_sso\keyring
copying ubuntu_sso\keyring\windows.py -> build\lib\ubuntu_sso\keyring
copying ubuntu_sso\keyring\__init__.py -> build\lib\ubuntu_sso\keyring
creating build\lib\ubuntu_sso\networkstate
copying ubuntu_sso\networkstate\linux.py -> build\lib\ubuntu_sso\networkstate
copying ubuntu_sso\networkstate\windows.py -> build\lib\ubuntu_sso\networkstate
copying ubuntu_sso\networkstate\__init__.py -> build\lib\ubuntu_sso\networkstate

creating build\lib\ubuntu_sso\main
copying ubuntu_sso\main\linux.py -> build\lib\ubuntu_sso\main
copying ubuntu_sso\main\windows.py -> build\lib\ubuntu_sso\main
copying ubuntu_sso\main\__init__.py -> build\lib\ubuntu_sso\main
creating build\lib\ubuntu_sso\gtk
copying ubuntu_sso\gtk\gui.py -> build\lib\ubuntu_sso\gtk
copying ubuntu_sso\gtk\main.py -> build\lib\ubuntu_sso\gtk
copying ubuntu_sso\gtk\__init__.py -> build\lib\ubuntu_sso\gtk
creating build\lib\ubuntu_sso\qt
copying ubuntu_sso\qt\choose_sign_in_ui.py -> build\lib\ubuntu_sso\qt
copying ubuntu_sso\qt\common.py -> build\lib\ubuntu_sso\qt
copying ubuntu_sso\qt\controllers.py -> build\lib\ubuntu_sso\qt
copying ubuntu_sso\qt\current_user_sign_in_ui.py -> build\lib\ubuntu_sso\qt
copying ubuntu_sso\qt\email_verification_ui.py -> build\lib\ubuntu_sso\qt
copying ubuntu_sso\qt\error_message_ui.py -> build\lib\ubuntu_sso\qt
copying ubun...

Read more...

review: Needs Fixing
841. By Natalia Bidart on 2012-01-18

- Not running any test related to GTK on windows.

Manuel de la Peña (mandel) wrote :

Fixed, nice branch!

review: Approve
Roberto Alsina (ralsina) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'bin/ubuntu-sso-login-gtk'
2--- bin/ubuntu-sso-login-gtk 1970-01-01 00:00:00 +0000
3+++ bin/ubuntu-sso-login-gtk 2012-01-18 13:20:30 +0000
4@@ -0,0 +1,31 @@
5+#!/usr/bin/env python
6+# -*- coding: utf-8 -*-
7+#
8+# Copyright 2012 Canonical Ltd.
9+#
10+# This program is free software: you can redistribute it and/or modify it
11+# under the terms of the GNU General Public License version 3, as published
12+# by the Free Software Foundation.
13+#
14+# This program is distributed in the hope that it will be useful, but
15+# WITHOUT ANY WARRANTY; without even the implied warranties of
16+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
17+# PURPOSE. See the GNU General Public License for more details.
18+#
19+# You should have received a copy of the GNU General Public License along
20+# with this program. If not, see <http://www.gnu.org/licenses/>.
21+
22+"""Start the sso GTK UI."""
23+
24+# Invalid name "ubuntu-sso-login-gtk", pylint: disable=C0103
25+# Access to a protected member, pylint: disable=W0212
26+
27+from ubuntu_sso.gtk.main import parse_args, main
28+
29+from dbus.mainloop.glib import DBusGMainLoop
30+DBusGMainLoop(set_as_default=True)
31+
32+
33+if __name__ == "__main__":
34+ args = parse_args()
35+ main(**dict(args._get_kwargs()))
36
37=== modified file 'run-tests.bat'
38--- run-tests.bat 2011-12-01 15:02:38 +0000
39+++ run-tests.bat 2012-01-18 13:20:30 +0000
40@@ -1,70 +1,70 @@
41-:: Author: Manuel de la Pena <manuel@canonical.com>
42-::
43-:: Copyright 2010 Canonical Ltd.
44-::
45-:: This program is free software: you can redistribute it and/or modify it
46-:: under the terms of the GNU General Public License version 3, as published
47-:: by the Free Software Foundation.
48-::
49-:: This program is distributed in the hope that it will be useful, but
50-:: WITHOUT ANY WARRANTY; without even the implied warranties of
51-:: MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
52-:: PURPOSE. See the GNU General Public License for more details.
53-::
54-:: You should have received a copy of the GNU General Public License along
55-:: with this program. If not, see <http://www.gnu.org/licenses/>.
56-@ECHO off
57-:: We could have Python 2.6 or 2.7 on Windows. In order to check availability,
58-:: we should first check for 2.7, and run the tests, otherwise fall back to 2.6.
59-SET PYTHONEXEPATH=""
60-:: This is very annoying; FOR /F will work differently depending on the output
61-:: of reg which is not consistent between OS versions (XP, 7). We must choose
62-:: the tokens according to OS version.
63-SET PYTHONPATHTOKENS=3
64-VER | FIND "XP" > nul
65-IF %ERRORLEVEL% == 0 SET PYTHONPATHTOKENS=4
66-ECHO Checking if python 2.7 is in the system
67-:: Look for python 2.7
68-FOR /F "tokens=%PYTHONPATHTOKENS%" %%A IN ('REG QUERY HKLM\Software\Python\PythonCore\2.7\InstallPath /ve') DO @SET PYTHONEXEPATH=%%A
69-IF NOT %PYTHONEXEPATH% == "" GOTO :PYTHONPRESENT
70-ECHO Checking if python 2.6 is in the system
71-:: we do not have python 2.7 in the system, try to find 2.6
72-FOR /F "tokens=%PYTHONPATHTOKENS%" %%A IN ('REG QUERY HKLM\Software\Python\PythonCore\2.6\InstallPath /ve') DO @SET PYTHONEXEPATH=%%A
73-IF NOT %PYTHONEXEPATH% == "" GOTO :PYTHONPRESENT
74-
75-:: we do not have python (2.6 or 2.7) this could hapen in the case that the
76-:: user installed the 32version in a 64 machine, let check if the software was installed in the wow key
77-
78-:: Look for python 2.7 in WoW64
79-ECHO Checking if python 2.7 32 is in the system
80-FOR /F "tokens=%PYTHONPATHTOKENS%" %%A IN ('REG QUERY HKLM\Software\Wow6432Node\Python\PythonCore\2.7\InstallPath /ve') DO @SET PYTHONEXEPATH=%%A
81-IF NOT %PYTHONEXEPATH% == "" GOTO :PYTHONPRESENT
82-ECHO Checking if python 2.6 32 is in the system
83-:: we do not have python 2.7 in the system, try to find 2.6
84-FOR /F "tokens=%PYTHONPATHTOKENS%" %%A IN ('REG QUERY HKLM\Software\Wow6432Node\Python\PythonCore\2.6\InstallPath /ve') DO @SET PYTHONEXEPATH=%%A
85-IF NOT %PYTHONEXEPATH% == "" GOTO :PYTHONPRESENT
86-
87-ECHO Please ensure you have python installed
88-GOTO :END
89-
90-
91-:PYTHONPRESENT
92-ECHO Python found, building auto-generated modules...
93-:: call setup.py build so that the qt uic is called
94-::START "Build code" /D%CD% /WAIT "%PYTHONEXEPATH%\python.exe" setup.py build
95-"%PYTHONEXEPATH%\python.exe" setup.py build
96-ECHO Running tests
97-:: execute the tests with a number of ignored linux only modules
98-"%PYTHONEXEPATH%\python.exe" "%PYTHONEXEPATH%\Scripts\u1trial" -c -i "test_gui.py, test_linux.py, test_txsecrets.py" --reactor=qt4 --gui %* ubuntu_sso
99-:: Clean the build from the setupt.py
100-ECHO Cleaning the generated code before running the style checks...
101-"%PYTHONEXEPATH%\python.exe" setup.py clean
102-ECHO Performing style checks...
103-SET IGNORE_LINT="ubuntu_sso\gtk,ubuntu_sso\networkstate\linux.py,ubuntu_sso\main\linux.py,ubuntu_sso\main\tests\test_linux.py,ubuntu_sso\utils\txsecrets.py,ubuntu_sso\utils\tests\test_txsecrets.py,ubuntu_sso\tests\bin,bin\ubuntu-sso-login"
104-"%PYTHONEXEPATH%\python.exe" "%PYTHONEXEPATH%\Scripts\u1lint" -i "%IGNORE_LINT%" ubuntu_sso
105-:: test for style if we can, if pep8 is not present, move to the end
106-"%PYTHONEXEPATH%\Scripts\pep8.exe" --repeat .
107-:: Delete the temp folders
108-RMDIR /q /s _trial_temp
109-DEL /q .coverage
110-:END
111+:: Author: Manuel de la Pena <manuel@canonical.com>
112+::
113+:: Copyright 2010 Canonical Ltd.
114+::
115+:: This program is free software: you can redistribute it and/or modify it
116+:: under the terms of the GNU General Public License version 3, as published
117+:: by the Free Software Foundation.
118+::
119+:: This program is distributed in the hope that it will be useful, but
120+:: WITHOUT ANY WARRANTY; without even the implied warranties of
121+:: MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
122+:: PURPOSE. See the GNU General Public License for more details.
123+::
124+:: You should have received a copy of the GNU General Public License along
125+:: with this program. If not, see <http://www.gnu.org/licenses/>.
126+@ECHO off
127+:: We could have Python 2.6 or 2.7 on Windows. In order to check availability,
128+:: we should first check for 2.7, and run the tests, otherwise fall back to 2.6.
129+SET PYTHONEXEPATH=""
130+:: This is very annoying; FOR /F will work differently depending on the output
131+:: of reg which is not consistent between OS versions (XP, 7). We must choose
132+:: the tokens according to OS version.
133+SET PYTHONPATHTOKENS=3
134+VER | FIND "XP" > nul
135+IF %ERRORLEVEL% == 0 SET PYTHONPATHTOKENS=4
136+ECHO Checking if python 2.7 is in the system
137+:: Look for python 2.7
138+FOR /F "tokens=%PYTHONPATHTOKENS%" %%A IN ('REG QUERY HKLM\Software\Python\PythonCore\2.7\InstallPath /ve') DO @SET PYTHONEXEPATH=%%A
139+IF NOT %PYTHONEXEPATH% == "" GOTO :PYTHONPRESENT
140+ECHO Checking if python 2.6 is in the system
141+:: we do not have python 2.7 in the system, try to find 2.6
142+FOR /F "tokens=%PYTHONPATHTOKENS%" %%A IN ('REG QUERY HKLM\Software\Python\PythonCore\2.6\InstallPath /ve') DO @SET PYTHONEXEPATH=%%A
143+IF NOT %PYTHONEXEPATH% == "" GOTO :PYTHONPRESENT
144+
145+:: we do not have python (2.6 or 2.7) this could hapen in the case that the
146+:: user installed the 32version in a 64 machine, let check if the software was installed in the wow key
147+
148+:: Look for python 2.7 in WoW64
149+ECHO Checking if python 2.7 32 is in the system
150+FOR /F "tokens=%PYTHONPATHTOKENS%" %%A IN ('REG QUERY HKLM\Software\Wow6432Node\Python\PythonCore\2.7\InstallPath /ve') DO @SET PYTHONEXEPATH=%%A
151+IF NOT %PYTHONEXEPATH% == "" GOTO :PYTHONPRESENT
152+ECHO Checking if python 2.6 32 is in the system
153+:: we do not have python 2.7 in the system, try to find 2.6
154+FOR /F "tokens=%PYTHONPATHTOKENS%" %%A IN ('REG QUERY HKLM\Software\Wow6432Node\Python\PythonCore\2.6\InstallPath /ve') DO @SET PYTHONEXEPATH=%%A
155+IF NOT %PYTHONEXEPATH% == "" GOTO :PYTHONPRESENT
156+
157+ECHO Please ensure you have python installed
158+GOTO :END
159+
160+
161+:PYTHONPRESENT
162+ECHO Python found, building auto-generated modules...
163+:: call setup.py build so that the qt uic is called
164+::START "Build code" /D%CD% /WAIT "%PYTHONEXEPATH%\python.exe" setup.py build
165+"%PYTHONEXEPATH%\python.exe" setup.py build
166+ECHO Running tests
167+:: execute the tests with a number of ignored linux only modules
168+"%PYTHONEXEPATH%\python.exe" "%PYTHONEXEPATH%\Scripts\u1trial" -i "test_linux.py, test_txsecrets.py" -p "ubuntu_sso\gtk" --reactor=qt4 --gui %* ubuntu_sso
169+:: Clean the build from the setupt.py
170+ECHO Cleaning the generated code before running the style checks...
171+"%PYTHONEXEPATH%\python.exe" setup.py clean
172+ECHO Performing style checks...
173+SET IGNORE_LINT="ubuntu_sso\gtk,ubuntu_sso\networkstate\linux.py,ubuntu_sso\main\linux.py,ubuntu_sso\main\tests\test_linux.py,ubuntu_sso\utils\txsecrets.py,ubuntu_sso\utils\tests\test_txsecrets.py,ubuntu_sso\tests\bin,bin\ubuntu-sso-login"
174+"%PYTHONEXEPATH%\python.exe" "%PYTHONEXEPATH%\Scripts\u1lint" -i "%IGNORE_LINT%" ubuntu_sso
175+:: test for style if we can, if pep8 is not present, move to the end
176+"%PYTHONEXEPATH%\Scripts\pep8.exe" --repeat .
177+:: Delete the temp folders
178+RMDIR /q /s _trial_temp
179+DEL /q .coverage
180+:END
181
182=== modified file 'setup.py'
183--- setup.py 2011-11-14 17:40:13 +0000
184+++ setup.py 2012-01-18 13:20:30 +0000
185@@ -349,7 +349,7 @@
186 },
187 },
188 # add the console script so that py2exe compiles it
189- 'console': ['bin/windows-ubuntu-sso-login'],
190+ 'console': ['bin/ubuntu-sso-login'],
191 'zipfile': None,
192 }
193 return _data_files, _extra
194@@ -357,9 +357,12 @@
195 if sys.platform == 'win32':
196 data_files, extra = setup_windows()
197 else:
198- data_files = [('share/dbus-1/services', ['data/com.ubuntu.sso.service']),
199- ('lib/ubuntu-sso-client', ['bin/ubuntu-sso-login']),
200- ('share/ubuntu-sso-client/data/gtk', ['data/gtk/ui.glade'])]
201+ data_files = [
202+ ('lib/ubuntu-sso-client', ['bin/ubuntu-sso-login']),
203+ ('lib/ubuntu-sso-client', ['bin/ubuntu-sso-login-gtk']),
204+ ('share/dbus-1/services', ['data/com.ubuntu.sso.service']),
205+ ('share/ubuntu-sso-client/data/gtk', ['data/gtk/ui.glade']),
206+ ]
207 extra = {}
208
209 DistUtilsExtra.auto.setup(
210
211=== modified file 'ubuntu_sso/gtk/gui.py'
212--- ubuntu_sso/gtk/gui.py 2011-12-15 20:40:20 +0000
213+++ ubuntu_sso/gtk/gui.py 2012-01-18 13:20:30 +0000
214@@ -1,9 +1,5 @@
215 # -*- coding: utf-8 -*-
216 #
217-# ubuntu_sso.gui - GUI for login and registration
218-#
219-# Author: Natalia Bidart <natalia.bidart@canonical.com>
220-#
221 # Copyright 2010 Canonical Ltd.
222 #
223 # This program is free software: you can redistribute it and/or modify it
224@@ -22,20 +18,18 @@
225
226 import logging
227 import os
228+import sys
229 import tempfile
230 import webbrowser
231
232 from functools import wraps
233
234-import dbus
235 import gtk
236
237-from twisted.internet.defer import inlineCallbacks
238+from twisted.internet import defer
239
240 from ubuntu_sso import (
241- DBUS_ACCOUNT_PATH,
242- DBUS_BUS_NAME,
243- DBUS_IFACE_USER_NAME,
244+ main,
245 NO_OP,
246 utils,
247 )
248@@ -101,6 +95,9 @@
249 HELP_TEXT_COLOR = gtk.gdk.Color("#bfbfbf")
250 WARNING_TEXT_COLOR = gtk.gdk.Color("red")
251
252+USER_CANCELLATION = 10
253+LOGIN_SUCCESS = REGISTRATION_SUCCESS = 0
254+
255
256 def log_call(f):
257 """Decorator to log call funtions."""
258@@ -183,9 +180,11 @@
259 class UbuntuSSOClientGUI(object):
260 """Ubuntu single sign-on GUI."""
261
262- def __init__(self, app_name, tc_url='', help_text='',
263- window_id=0, login_only=False):
264+ def __init__(self, app_name, **kwargs):
265 """Create the GUI and initialize widgets."""
266+ logger.debug('UbuntuSSOClientGUI: app_name %r, kwargs %r.',
267+ app_name, kwargs)
268+
269 gtk.link_button_set_uri_hook(NO_OP)
270
271 self._captcha_filename = tempfile.mktemp()
272@@ -195,11 +194,15 @@
273
274 self.app_name = app_name
275 self.app_label = '<b>%s</b>' % self.app_name
276- self.tc_url = tc_url
277- self.help_text = help_text
278- self.login_only = login_only
279-
280- self.close_callback = NO_OP
281+ self.ping_url = kwargs.get('ping_url', '')
282+ self.tc_url = kwargs.get('tc_url', '')
283+ self.help_text = kwargs.get('help_text', '')
284+ self.login_only = kwargs.get('login_only', False)
285+ window_id = kwargs.get('window_id', 0)
286+ self.close_callback = kwargs.get('close_callback', NO_OP)
287+ self.backend = None
288+ # the following 3 callbacks will be removed as soon as
289+ # the backend stop using them
290 self.login_success_callback = NO_OP
291 self.registration_success_callback = NO_OP
292 self.user_cancellation_callback = NO_OP
293@@ -252,23 +255,12 @@
294
295 self.window.set_icon_name('ubuntu-logo')
296
297- self.bus = dbus.SessionBus()
298- obj = self.bus.get_object(bus_name=DBUS_BUS_NAME,
299- object_path=DBUS_ACCOUNT_PATH,
300- follow_name_owner_changes=True)
301- self.iface_name = DBUS_IFACE_USER_NAME
302- self.backend = dbus.Interface(object=obj,
303- dbus_interface=self.iface_name)
304- logger.debug('UbuntuSSOClientGUI: backend created: %r', self.backend)
305-
306 self.pages = (self.enter_details_vbox, self.processing_vbox,
307 self.verify_email_vbox, self.finish_vbox,
308 self.tc_browser_vbox, self.login_vbox,
309 self.request_password_token_vbox,
310 self.set_new_password_vbox)
311
312- self._append_pages()
313-
314 self._signals = {
315 'CaptchaGenerated':
316 self._filter_by_app_name(self.on_captcha_generated),
317@@ -297,7 +289,6 @@
318 'PasswordChangeError':
319 self._filter_by_app_name(self.on_password_change_error),
320 }
321- self._setup_signals()
322
323 if window_id != 0:
324 # be as robust as possible:
325@@ -314,7 +305,18 @@
326 logger.exception(msg, window_id)
327
328 self.yes_to_updates_checkbutton.hide()
329-
330+ self.start_backend()
331+
332+ @defer.inlineCallbacks
333+ def start_backend(self):
334+ """Start the backend, show the window when ready."""
335+ client = yield main.get_sso_client()
336+ self.backend = client.sso_login
337+
338+ logger.debug('UbuntuSSOClientGUI: backend created: %r', self.backend)
339+
340+ self._setup_signals()
341+ self._append_pages()
342 self.window.show()
343
344 @property
345@@ -353,22 +355,14 @@
346
347 def _setup_signals(self):
348 """Bind signals to callbacks to be able to test the pages."""
349- iface = self.iface_name
350 for signal, method in self._signals.iteritems():
351- actual = self._signals_receivers.get((iface, signal))
352+ actual = self._signals_receivers.get(signal)
353 if actual is not None:
354- msg = 'Signal %r is already connected with %r at iface %r.'
355- logger.warning(msg, signal, actual, iface)
356-
357- match = self.bus.add_signal_receiver(method, signal_name=signal,
358- dbus_interface=iface)
359- logger.debug('Connecting signal %r with method %r at iface %r.' \
360- 'Match: %r', signal, method, iface, match)
361- self._signals_receivers[(iface, signal)] = method
362-
363- def _debug(self, *args, **kwargs):
364- """Do some debugging."""
365- print args, kwargs
366+ msg = 'Signal %r is already connected with %r.'
367+ logger.warning(msg, signal, actual)
368+
369+ match = self.backend.connect_to_signal(signal, method)
370+ self._signals_receivers[signal] = match
371
372 def _add_spinner_to_container(self, container, legend=None):
373 """Add a spinner to 'container'."""
374@@ -651,10 +645,6 @@
375
376 # GTK callbacks
377
378- def run(self):
379- """Run the application."""
380- gtk.main()
381-
382 def connect(self, signal_name, handler, *args, **kwargs):
383 """Connect 'signal_name' with 'handler'."""
384 logger.debug('connect: signal %r, handler %r, args %r, kwargs, %r',
385@@ -680,13 +670,8 @@
386 if os.path.exists(self._captcha_filename):
387 os.remove(self._captcha_filename)
388
389- # remove the signals from DBus
390- remove = self.bus.remove_signal_receiver
391- for (iface, signal) in self._signals_receivers.keys():
392- method = self._signals_receivers.pop((iface, signal))
393- logger.debug('Removing signal %r with method %r at iface %r.',
394- signal, method, iface)
395- remove(method, signal_name=signal, dbus_interface=iface)
396+ for signal, match in self._signals_receivers.iteritems():
397+ self.backend.disconnect_from_signal(signal, match)
398
399 # hide the main window
400 if self.window is not None:
401@@ -696,14 +681,18 @@
402 while gtk.events_pending():
403 gtk.main_iteration()
404
405+ return_code = LOGIN_SUCCESS
406 if not self._done:
407 self.user_cancellation_callback(self.app_name)
408+ return_code = USER_CANCELLATION
409
410 # call user defined callback
411 logger.info('Calling custom close_callback %r with params %r, %r',
412 self.close_callback, args, kwargs)
413 self.close_callback(*args, **kwargs)
414
415+ sys.exit(return_code)
416+
417 def on_sign_in_button_clicked(self, *args, **kwargs):
418 """User wants to sign in, present the Login page."""
419 self._set_current_page(self.login_vbox)
420@@ -780,12 +769,18 @@
421
422 email = self.user_email
423 password = self.user_password
424- f = self.backend.validate_email
425- logger.info('Calling validate_email with email %r, password <hidden>' \
426- ', app_name %r and email_token %r.', email, self.app_name,
427+
428+ args = (self.app_name, email, password, email_token)
429+ if self.ping_url:
430+ f = self.backend.validate_email_with_ping
431+ args = args + (self.ping_url,)
432+ else:
433+ f = self.backend.validate_email
434+
435+ logger.info('Calling validate_email with email %r, password <hidden>, '
436+ 'app_name %r and email_token %r.', email, self.app_name,
437 email_token)
438- f(self.app_name, email, password, email_token,
439- reply_handler=NO_OP, error_handler=NO_OP)
440+ f(*args, reply_handler=NO_OP, error_handler=NO_OP)
441
442 self._set_current_page(self.processing_vbox)
443
444@@ -812,9 +807,14 @@
445 if error:
446 return
447
448- f = self.backend.login
449- f(self.app_name, email, password,
450- reply_handler=NO_OP, error_handler=NO_OP)
451+ args = (self.app_name, email, password)
452+ if self.ping_url:
453+ f = self.backend.login_with_ping
454+ args = args + (self.ping_url,)
455+ else:
456+ f = self.backend.login
457+
458+ f(*args, reply_handler=NO_OP, error_handler=NO_OP)
459
460 self._set_current_page(self.processing_vbox)
461 self.user_email = email
462@@ -1021,15 +1021,10 @@
463 self._set_current_page(self.enter_details_vbox, warning_text=msg)
464
465 @log_call
466- @inlineCallbacks
467 def on_email_validated(self, app_name, email, *args, **kwargs):
468 """User email was successfully verified."""
469- self._done = True
470- result = yield self.registration_success_callback(self.app_name, email)
471- if result == 0:
472- self.finish_success()
473- else:
474- self.finish_error()
475+ self.registration_success_callback(self.app_name, email)
476+ self.finish_success()
477
478 @log_call
479 def on_email_validation_error(self, app_name, error, *args, **kwargs):
480@@ -1042,15 +1037,10 @@
481 self._set_current_page(self.verify_email_vbox, warning_text=msg)
482
483 @log_call
484- @inlineCallbacks
485 def on_logged_in(self, app_name, email, *args, **kwargs):
486 """User was successfully logged in."""
487- self._done = True
488- result = yield self.login_success_callback(self.app_name, email)
489- if result == 0:
490- self.finish_success()
491- else:
492- self.finish_error()
493+ self.login_success_callback(self.app_name, email)
494+ self.finish_success()
495
496 @log_call
497 def on_login_error(self, app_name, error, *args, **kwargs):
498
499=== added file 'ubuntu_sso/gtk/main.py'
500--- ubuntu_sso/gtk/main.py 1970-01-01 00:00:00 +0000
501+++ ubuntu_sso/gtk/main.py 2012-01-18 13:20:30 +0000
502@@ -0,0 +1,49 @@
503+# -*- coding: utf-8 -*-
504+#
505+# Copyright 2012 Canonical Ltd.
506+#
507+# This program is free software: you can redistribute it and/or modify it
508+# under the terms of the GNU General Public License version 3, as published
509+# by the Free Software Foundation.
510+#
511+# This program is distributed in the hope that it will be useful, but
512+# WITHOUT ANY WARRANTY; without even the implied warranties of
513+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
514+# PURPOSE. See the GNU General Public License for more details.
515+#
516+# You should have received a copy of the GNU General Public License along
517+# with this program. If not, see <http://www.gnu.org/licenses/>.
518+
519+"""Main module to open the GTK UI."""
520+
521+import argparse
522+
523+import gtk
524+
525+from ubuntu_sso.gtk.gui import UbuntuSSOClientGUI
526+
527+
528+def parse_args():
529+ """Parse sys.argv options."""
530+ parser = argparse.ArgumentParser(description='Open the GTK SSO UI.')
531+ parser.add_argument('--app_name', required=True,
532+ help='the name of the application to retrieve credentials for')
533+ parser.add_argument('--ping_url', default='',
534+ help='a link to be used as the ping url (to notify about new tokens)')
535+ parser.add_argument('--tc_url', default='',
536+ help='a link to be used as Terms & Conditions url')
537+ parser.add_argument('--help_text', default='',
538+ help='extra text that will be shown below the headers')
539+ parser.add_argument('--window_id', type=int, default=0,
540+ help='the window id to be set transient for the SSO GTK dialogs')
541+ parser.add_argument('--login_only', action='store_true', default=False,
542+ help='whether the SSO GTK UI should only offer login or not')
543+
544+ args = parser.parse_args()
545+ return args
546+
547+
548+def main(**kwargs):
549+ """Start the GTK mainloop and open the main window."""
550+ UbuntuSSOClientGUI(close_callback=gtk.main_quit, **kwargs)
551+ gtk.main()
552
553=== modified file 'ubuntu_sso/gtk/tests/test_gui.py'
554--- ubuntu_sso/gtk/tests/test_gui.py 2011-12-15 20:40:20 +0000
555+++ ubuntu_sso/gtk/tests/test_gui.py 2012-01-18 13:20:30 +0000
556@@ -1,10 +1,6 @@
557 # -*- coding: utf-8 -*-
558 #
559-# test_gui - tests for ubuntu_sso.gui
560-#
561-# Author: Natalia Bidart <natalia.bidart@canonical.com>
562-#
563-# Copyright 2010 Canonical Ltd.
564+# Copyright 2010-2012 Canonical Ltd.
565 #
566 # This program is free software: you can redistribute it and/or modify it
567 # under the terms of the GNU General Public License version 3, as published
568@@ -25,7 +21,6 @@
569
570 from collections import defaultdict
571
572-import dbus
573 import gtk
574 import webkit
575
576@@ -36,7 +31,9 @@
577 from ubuntu_sso.utils.ui import UNKNOWN_ERROR
578 from ubuntu_sso.gtk import gui
579 from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID,
580- CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, RESET_PASSWORD_TOKEN)
581+ CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, PING_URL,
582+ RESET_PASSWORD_TOKEN,
583+)
584
585
586 # Access to a protected member 'yyy' of a client class
587@@ -47,17 +44,32 @@
588
589
590 class FakedSSOBackend(object):
591- """Fake a SSO Backend (acts as a dbus.Interface as well)."""
592+ """Fake a SSO Backend."""
593
594 def __init__(self, *args, **kwargs):
595 self._args = args
596 self._kwargs = kwargs
597 self._called = {}
598- for i in ('generate_captcha', 'login', 'register_user',
599- 'validate_email', 'request_password_reset_token',
600+ self.callbacks = defaultdict(list)
601+
602+ for i in ('generate_captcha', 'login', 'login_with_ping',
603+ 'register_user', 'validate_email',
604+ 'validate_email_with_ping',
605+ 'request_password_reset_token',
606 'set_new_password'):
607 setattr(self, i, self._record_call(i))
608
609+ def connect_to_signal(self, signal_name, callback):
610+ """Connect a callback to a given signal."""
611+ self.callbacks[signal_name].append(callback)
612+ return callback
613+
614+ def disconnect_from_signal(self, signal_name, match):
615+ """Disconnect from a given signal."""
616+ self.callbacks[signal_name].remove(match)
617+ if len(self.callbacks[signal_name]) == 0:
618+ self.callbacks.pop(signal_name)
619+
620 def _record_call(self, func_name):
621 """Store values when calling 'func_name'."""
622
623@@ -68,42 +80,11 @@
624 return inner
625
626
627-class FakedDbusObject(object):
628- """Fake a dbus."""
629-
630- def __init__(self, *args, **kwargs):
631- """Init."""
632- self._args = args
633- self._kwargs = kwargs
634-
635-
636-class FakedSessionBus(object):
637- """Fake the session bus."""
638+class FakedSSOService(object):
639+ """A faked SSO service."""
640
641 def __init__(self):
642- """Init."""
643- self.obj = None
644- self.callbacks = {}
645-
646- def add_signal_receiver(self, method, signal_name, dbus_interface):
647- """Add a signal receiver."""
648- self.callbacks[(dbus_interface, signal_name)] = method
649-
650- def remove_signal_receiver(self, match, signal_name, dbus_interface):
651- """Remove the signal receiver."""
652- assert (dbus_interface, signal_name) in self.callbacks
653- del self.callbacks[(dbus_interface, signal_name)]
654-
655- def get_object(self, bus_name, object_path, introspect=True,
656- follow_name_owner_changes=False, **kwargs):
657- """Return a faked proxy for the given remote object."""
658- if bus_name == gui.DBUS_BUS_NAME and \
659- object_path == gui.DBUS_ACCOUNT_PATH:
660- assert self.obj is None
661- kwargs = dict(object_path=object_path,
662- bus_name=bus_name, follow_name_owner_changes=True)
663- self.obj = FakedDbusObject(**kwargs)
664- return self.obj
665+ self.sso_login = FakedSSOBackend()
666
667
668 class Settings(dict):
669@@ -165,6 +146,8 @@
670 yield super(BasicTestCase, self).setUp()
671 self._called = False # helper
672
673+ self.patch(gui.sys, 'exit', lambda *a: None)
674+
675 self.memento = MementoHandler()
676 self.memento.setLevel(logging.DEBUG)
677 gui.logger.addHandler(self.memento)
678@@ -382,20 +365,14 @@
679 def setUp(self):
680 """Init."""
681 yield super(UbuntuSSOClientTestCase, self).setUp()
682- self.patch(dbus, 'SessionBus', FakedSessionBus)
683- self.patch(dbus, 'Interface', FakedSSOBackend)
684+ self.patch(gui.main, 'get_sso_client',
685+ lambda: defer.succeed(FakedSSOService()))
686 self.pages = ('enter_details', 'processing', 'verify_email', 'finish',
687 'tc_browser', 'login', 'request_password_token',
688 'set_new_password')
689 self.ui = self.gui_class(**self.kwargs)
690 self.error = {'message': UNKNOWN_ERROR}
691
692- @defer.inlineCallbacks
693- def tearDown(self):
694- self.ui.bus.callbacks = {}
695- self.ui = None
696- yield super(UbuntuSSOClientTestCase, self).tearDown()
697-
698 def assert_entries_are_packed_to_ui(self, container_name, entries):
699 """Every entry is properly packed in the ui 'container_name'."""
700 msg = 'Entry "%s" must be packed in "%s" but is not.'
701@@ -524,39 +501,13 @@
702 """The app_name is stored for further use."""
703 self.assertIn(APP_NAME, self.ui.app_name)
704
705- def test_session_bus_is_correct(self):
706- """The session bus is created and is correct."""
707- self.assertIsInstance(self.ui.bus, FakedSessionBus)
708-
709- def test_iface_name_is_correct(self):
710- """The session bus is created and is correct."""
711- self.assertEqual(self.ui.iface_name, gui.DBUS_IFACE_USER_NAME)
712-
713- def test_bus_object_is_created(self):
714- """SessionBus.get_object is called properly."""
715- self.assertIsInstance(self.ui.bus.obj, FakedDbusObject)
716- self.assertEqual(self.ui.bus.obj._args, ())
717- expected = dict(object_path=gui.DBUS_ACCOUNT_PATH,
718- bus_name=gui.DBUS_BUS_NAME,
719- follow_name_owner_changes=True)
720- self.assertEqual(expected, self.ui.bus.obj._kwargs)
721-
722- def test_bus_interface_is_created(self):
723- """dbus.Interface is called properly."""
724- self.assertIsInstance(self.ui.backend, FakedSSOBackend)
725- self.assertEqual(self.ui.backend._args, ())
726- expected = dict(object=self.ui.bus.obj,
727- dbus_interface=gui.DBUS_IFACE_USER_NAME)
728- self.assertEqual(expected, self.ui.backend._kwargs)
729-
730- def test_dbus_signals_are_removed(self):
731+ def test_signals_are_removed(self):
732 """The hooked signals are removed at shutdown time."""
733- self.ui._setup_signals()
734- assert len(self.ui.bus.callbacks) > 0 # at least one callback
735+ assert len(self.ui.backend.callbacks) > 0 # at least one callback
736
737 self.ui.on_close_clicked()
738
739- self.assertEqual(self.ui.bus.callbacks, {})
740+ self.assertEqual(self.ui.backend.callbacks, {})
741
742 def test_pages_are_packed_into_container(self):
743 """All the pages are packed in the main container."""
744@@ -607,6 +558,7 @@
745
746 def test_cancel_buttons_close_window(self):
747 """Every cancel button should close the window when clicked."""
748+ self.patch(self.ui.backend, 'disconnect_from_signal', lambda *a: None)
749 msg = '"%s" should close the window when clicked.'
750 buttons = filter(lambda name: 'cancel_button' in name or
751 'close_button' in name, self.ui.widgets)
752@@ -923,15 +875,12 @@
753 self.patch(webkit, 'WebView', FakedEmbeddedBrowser)
754
755 self.ui.tc_button.clicked()
756+ self.addCleanup(self.ui.tc_browser_vbox.hide)
757+
758 children = self.ui.tc_browser_window.get_children()
759 assert len(children) == 1
760 self.browser = children[0]
761
762- @defer.inlineCallbacks
763- def tearDown(self):
764- self.ui.tc_browser_vbox.hide()
765- yield super(TermsAndConditionsBrowserTestCase, self).tearDown()
766-
767 def test_tc_browser_is_created_when_tc_page_is_shown(self):
768 """The browser is created when the TC button is clicked."""
769 self.ui.on_tc_browser_notify_load_status(self.browser)
770@@ -1124,12 +1073,14 @@
771 class VerifyEmailTestCase(UbuntuSSOClientTestCase):
772 """Test suite for the user registration (verify email page)."""
773
774+ method = 'validate_email'
775+ method_args = (APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN)
776+
777 @defer.inlineCallbacks
778 def setUp(self):
779 """Init."""
780 yield super(VerifyEmailTestCase, self).setUp()
781 self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
782- self.click_verify_email_with_valid_data()
783
784 def test_registration_successful_shows_verify_email_vbox(self):
785 """Receiving 'registration_successful' shows the verify email vbox."""
786@@ -1144,13 +1095,13 @@
787 'email': EMAIL}
788 self.assertEqual(expected, actual, msg % (expected, actual))
789
790- def test_on_verify_token_button_clicked_calls_validate_email(self):
791+ def test_on_verify_token_button_clicked_calls_backend(self):
792 """Verify token button triggers call to backend."""
793 self.click_verify_email_with_valid_data()
794- expected = 'validate_email'
795+ expected = self.method
796 self.assertIn(expected, self.ui.backend._called)
797 self.assertEqual(self.ui.backend._called[expected],
798- ((APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN),
799+ (self.method_args,
800 dict(reply_handler=gui.NO_OP,
801 error_handler=gui.NO_OP)))
802
803@@ -1158,10 +1109,16 @@
804 """Verify token uses cached user_email and user_password."""
805 self.ui.user_email = 'test@me.com'
806 self.ui.user_password = 'yadda-yedda'
807+ method_args = list(self.method_args)
808+ method_args[1] = self.ui.user_email
809+ method_args[2] = self.ui.user_password
810+
811+ # resolve email token properly
812+ self.ui.email_token_entry.set_text(EMAIL_TOKEN)
813+
814 self.ui.on_verify_token_button_clicked()
815- self.assertEqual(self.ui.backend._called['validate_email'],
816- ((APP_NAME, self.ui.user_email,
817- self.ui.user_password, EMAIL_TOKEN),
818+ self.assertEqual(self.ui.backend._called[self.method],
819+ (tuple(method_args),
820 dict(reply_handler=gui.NO_OP,
821 error_handler=gui.NO_OP)))
822
823@@ -1240,31 +1197,22 @@
824 self.ui.verify_token_button.clicked()
825 self.assertTrue(self._called)
826
827- def test_after_registration_success_finish_success(self):
828- """After REGISTRATION_SUCCESS is called, finish_success is called.
829-
830- Only when REGISTRATION_SUCCESS returns 0.
831-
832- """
833+ def test_after_email_validated_finish_success(self):
834+ """After email_validated is called, finish_success is called."""
835 self.patch(self.ui, 'finish_success', self._set_called)
836- self.ui.registration_success_callback = lambda app, email: 0
837-
838- self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
839-
840- self.assertEqual(self._called, ((), {}))
841-
842- def test_after_registration_error_finish_error(self):
843- """After REGISTRATION_SUCCESS is called, finish_error is called.
844-
845- Only when REGISTRATION_SUCCESS returns a non-zero value.
846-
847- """
848- self.patch(self.ui, 'finish_error', self._set_called)
849- self.ui.registration_success_callback = lambda app, email: -1
850-
851- self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
852-
853- self.assertEqual(self._called, ((), {}))
854+
855+ self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
856+
857+ self.assertEqual(self._called, ((), {}))
858+
859+
860+class VerifyEmailWithPingTestCase(VerifyEmailTestCase):
861+ """Test suite for the user registration (verify email page)."""
862+
863+ kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
864+ ping_url=PING_URL)
865+ method = 'validate_email_with_ping'
866+ method_args = (APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN, PING_URL)
867
868
869 class VerifyEmailValidationTestCase(UbuntuSSOClientTestCase):
870@@ -1455,6 +1403,9 @@
871 class LoginTestCase(UbuntuSSOClientTestCase):
872 """Test suite for the user login pages."""
873
874+ method = 'login'
875+ method_args = (APP_NAME, EMAIL, PASSWORD)
876+
877 @defer.inlineCallbacks
878 def setUp(self):
879 """Init."""
880@@ -1505,10 +1456,10 @@
881 """Clicking login_ok_button calls backend.login."""
882 self.click_connect_with_valid_data()
883
884- expected = 'login'
885+ expected = self.method
886 self.assertIn(expected, self.ui.backend._called)
887 self.assertEqual(self.ui.backend._called[expected],
888- ((APP_NAME, EMAIL, PASSWORD),
889+ (self.method_args,
890 dict(reply_handler=gui.NO_OP,
891 error_handler=gui.NO_OP)))
892
893@@ -1579,30 +1530,21 @@
894 self.assertEqual(self.ui.user_password, PASSWORD)
895
896 def test_after_login_success_finish_success(self):
897- """After LOGIN_SUCCESSFULL is called, finish_success is called.
898-
899- Only when LOGIN_SUCCESSFULL returns 0.
900-
901- """
902+ """After logged_in is called, finish_success is called."""
903 self.patch(self.ui, 'finish_success', self._set_called)
904- self.ui.login_success_callback = lambda app, email: 0
905-
906- self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
907-
908- self.assertEqual(self._called, ((), {}))
909-
910- def test_after_login_error_finish_error(self):
911- """After LOGIN_SUCCESSFULL is called, finish_error is called.
912-
913- Only when LOGIN_SUCCESSFULL returns a non-zero value.
914-
915- """
916- self.patch(self.ui, 'finish_error', self._set_called)
917- self.ui.login_success_callback = lambda app, email: -1
918-
919- self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
920-
921- self.assertEqual(self._called, ((), {}))
922+
923+ self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
924+
925+ self.assertEqual(self._called, ((), {}))
926+
927+
928+class LoginWithPingTestCase(LoginTestCase):
929+ """Test suite for the login when the ping_url is set."""
930+
931+ kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
932+ ping_url=PING_URL)
933+ method = 'login_with_ping'
934+ method_args = (APP_NAME, EMAIL, PASSWORD, PING_URL)
935
936
937 class LoginValidationTestCase(UbuntuSSOClientTestCase):
938@@ -1978,8 +1920,8 @@
939 self.assert_warnings_visibility()
940
941
942-class DbusTestCase(UbuntuSSOClientTestCase):
943- """Test suite for the dbus calls."""
944+class SignalsTestCase(UbuntuSSOClientTestCase):
945+ """Test suite for the backend signals."""
946
947 def test_all_the_signals_are_listed(self):
948 """All the backend signals are listed to be binded."""
949@@ -1993,13 +1935,12 @@
950
951 def test_signal_receivers_are_connected(self):
952 """Callbacks are connected to signals of interest."""
953- msg1 = 'callback %r for signal %r must be added to the internal bus.'
954+ msg1 = 'callback %r for signal %r must be added to the backend.'
955 msg2 = 'callback %r for signal %r must be added to the ui log.'
956- dbus_iface = self.ui.iface_name
957 for signal, method in self.ui._signals.iteritems():
958- actual = self.ui.bus.callbacks.get((dbus_iface, signal))
959- self.assertEqual(method, actual, msg1 % (method, signal))
960- actual = self.ui._signals_receivers.get((dbus_iface, signal))
961+ actual = self.ui.backend.callbacks.get(signal)
962+ self.assertEqual([method], actual, msg1 % (method, signal))
963+ actual = self.ui._signals_receivers.get(signal)
964 self.assertEqual(method, actual, msg2 % (method, signal))
965
966 def test_callbacks_only_log_when_app_name_doesnt_match(self):
967@@ -2126,79 +2067,62 @@
968 self.assertEqual(self.ui.help_label.get_text(), HELP_TEXT)
969
970
971-class CallbacksTestCase(UbuntuSSOClientTestCase):
972- """Test the GTK callback calls."""
973+class ReturnCodeTestCase(UbuntuSSOClientTestCase):
974+ """Test the return codes."""
975
976- LOGIN_SUCCESSFULL = 'login_success_callback'
977- REGISTRATION_SUCCESS = 'registration_success_callback'
978- USER_CANCELLATION = 'user_cancellation_callback'
979+ LOGIN_SUCCESS = gui.LOGIN_SUCCESS
980+ REGISTRATION_SUCCESS = gui.REGISTRATION_SUCCESS
981+ USER_CANCELLATION = gui.USER_CANCELLATION
982
983 @defer.inlineCallbacks
984 def setUp(self):
985- """Init."""
986- yield super(CallbacksTestCase, self).setUp()
987- self._called = {}
988- for name in (self.LOGIN_SUCCESSFULL,
989- self.REGISTRATION_SUCCESS,
990- self.USER_CANCELLATION):
991- setattr(self.ui, name, self._set_called(name))
992-
993- def _set_called(self, callback_name):
994- """Keep trace of callbacks calls."""
995-
996- # pylint: disable=W0221
997-
998- def inner(*args, **kwargs):
999- """Store arguments used in this call."""
1000- self._called[callback_name] = args
1001-
1002- return inner
1003+ yield super(ReturnCodeTestCase, self).setUp()
1004+ self.patch(gui.sys, 'exit', self._set_called)
1005
1006 def test_closing_main_window(self):
1007 """When closing the main window, USER_CANCELLATION is called."""
1008 self.ui.window.emit('delete-event', gtk.gdk.Event(gtk.gdk.DELETE))
1009- self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
1010+ self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
1011
1012 def test_every_cancel_calls_proper_callback(self):
1013 """When any cancel button is clicked, USER_CANCELLATION is called."""
1014- msg = 'user_cancellation_callback is called when "%s" is clicked.'
1015+ self.patch(self.ui.backend, 'disconnect_from_signal', lambda *a: None)
1016+ msg = 'USER_CANCELLATION should be returned when "%s" is clicked.'
1017 buttons = filter(lambda name: 'cancel_button' in name, self.ui.widgets)
1018 for name in buttons:
1019 widget = getattr(self.ui, name)
1020 widget.clicked()
1021- self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,),
1022+ self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}),
1023 msg % name)
1024- self._called = {}
1025+ self._called = False
1026
1027 def test_on_user_registration_error_proper_callback_is_called(self):
1028 """On UserRegistrationError, USER_CANCELLATION is called."""
1029 self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
1030 self.ui.on_close_clicked()
1031
1032- self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
1033+ self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
1034
1035 def test_on_email_validated_proper_callback_is_called(self):
1036 """On EmailValidated, REGISTRATION_SUCCESS is called."""
1037 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
1038 self.ui.on_close_clicked()
1039
1040- self.assertEqual(self._called[self.REGISTRATION_SUCCESS],
1041- (APP_NAME, EMAIL))
1042+ self.assertEqual(self._called, ((gui.REGISTRATION_SUCCESS,), {}))
1043
1044 def test_on_email_validation_error_proper_callback_is_called(self):
1045 """On EmailValidationError, USER_CANCELLATION is called."""
1046 self.ui.on_email_validation_error(app_name=APP_NAME, error=self.error)
1047 self.ui.on_close_clicked()
1048
1049- self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
1050+ self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
1051
1052 def test_on_logged_in_proper_callback_is_called(self):
1053- """On LoggedIn, LOGIN_SUCCESSFULL is called."""
1054+ """On LoggedIn, LOGIN_SUCCESS is called."""
1055 self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
1056 self.ui.on_close_clicked()
1057
1058- self.assertEqual(self._called[self.LOGIN_SUCCESSFULL],
1059- (APP_NAME, EMAIL))
1060+ self.assertEqual(self._called, ((gui.LOGIN_SUCCESS,), {}))
1061
1062 def test_on_login_error_proper_callback_is_called(self):
1063 """On LoginError, USER_CANCELLATION is called."""
1064@@ -2206,7 +2130,7 @@
1065 self.ui.on_login_error(app_name=APP_NAME, error=self.error)
1066 self.ui.on_close_clicked()
1067
1068- self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
1069+ self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
1070
1071 def test_registration_success_even_if_prior_registration_error(self):
1072 """Only one callback is called with the final outcome.
1073@@ -2221,13 +2145,12 @@
1074 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
1075 self.ui.on_close_clicked()
1076
1077- self.assertEqual(self._called[self.REGISTRATION_SUCCESS],
1078- (APP_NAME, EMAIL))
1079+ self.assertEqual(self._called, ((gui.REGISTRATION_SUCCESS,), {}))
1080
1081 def test_login_success_even_if_prior_login_error(self):
1082 """Only one callback is called with the final outcome.
1083
1084- When the user successfully logins, LOGIN_SUCCESSFULL is called even if
1085+ When the user successfully logins, LOGIN_SUCCESS is called even if
1086 there were errors before.
1087
1088 """
1089@@ -2237,8 +2160,7 @@
1090 self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
1091 self.ui.on_close_clicked()
1092
1093- self.assertEqual(self._called[self.LOGIN_SUCCESSFULL],
1094- (APP_NAME, EMAIL))
1095+ self.assertEqual(self._called, ((gui.LOGIN_SUCCESS,), {}))
1096
1097 def test_user_cancelation_even_if_prior_registration_error(self):
1098 """Only one callback is called with the final outcome.
1099@@ -2251,7 +2173,7 @@
1100 self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
1101 self.ui.join_cancel_button.clicked()
1102
1103- self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
1104+ self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
1105
1106 def test_user_cancelation_even_if_prior_login_error(self):
1107 """Only one callback is called with the final outcome.
1108@@ -2264,7 +2186,7 @@
1109 self.ui.on_login_error(app_name=APP_NAME, error=self.error)
1110 self.ui.login_cancel_button.clicked()
1111
1112- self.assertEqual(self._called[self.USER_CANCELLATION], (APP_NAME,))
1113+ self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
1114
1115
1116 class DefaultButtonsTestCase(UbuntuSSOClientTestCase):
1117
1118=== added file 'ubuntu_sso/gtk/tests/test_main.py'
1119--- ubuntu_sso/gtk/tests/test_main.py 1970-01-01 00:00:00 +0000
1120+++ ubuntu_sso/gtk/tests/test_main.py 2012-01-18 13:20:30 +0000
1121@@ -0,0 +1,123 @@
1122+# -*- coding: utf-8 -*-
1123+#
1124+# Copyright 2012 Canonical Ltd.
1125+#
1126+# This program is free software: you can redistribute it and/or modify it
1127+# under the terms of the GNU General Public License version 3, as published
1128+# by the Free Software Foundation.
1129+#
1130+# This program is distributed in the hope that it will be useful, but
1131+# WITHOUT ANY WARRANTY; without even the implied warranties of
1132+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1133+# PURPOSE. See the GNU General Public License for more details.
1134+#
1135+# You should have received a copy of the GNU General Public License along
1136+# with this program. If not, see <http://www.gnu.org/licenses/>.
1137+
1138+"""Tests for the GUI main module."""
1139+
1140+import sys
1141+
1142+from StringIO import StringIO
1143+
1144+from twisted.trial.unittest import TestCase
1145+
1146+from ubuntu_sso.gtk import main
1147+
1148+
1149+APP_NAME = 'Foo Bar'
1150+DEFAULTS = dict(
1151+ app_name=APP_NAME,
1152+ help_text='',
1153+ login_only=False,
1154+ ping_url='',
1155+ tc_url='',
1156+ window_id=0,
1157+)
1158+DUMMY_TEXT = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
1159+risus orci, lacinia ac tincidunt fermentum, vestibulum suscipit orci. Aliquam
1160+quis aliquet magna. Morbi vitae ligula ut libero porttitor imperdiet.
1161+Vestibulum et ipsum sapien, pellentesque ultricies risus. Aenean in lectus
1162+orci. Cras a lorem sollicitudin dui mollis varius. In hac habitasse platea
1163+dictumst."""
1164+PROGRAM = 'foo-bar'
1165+SOME_URL = 'http://example.com/foo/bar'
1166+
1167+
1168+class BasicTestCase(TestCase):
1169+ """Test case with a helper tracker."""
1170+
1171+ def assert_parse_args_custom_field(self, option_name=None,
1172+ option_value=None):
1173+ """The args are correctly set when using a custom value."""
1174+ expected = dict(DEFAULTS)
1175+ argv = [PROGRAM, '--app_name=%s' % APP_NAME]
1176+
1177+ if option_name is not None:
1178+ if option_value is not None:
1179+ argv.append('--%s=%s' % (option_name, option_value))
1180+ expected[option_name] = option_value
1181+ else:
1182+ argv.append('--%s' % (option_name,))
1183+ expected[option_name] = True
1184+
1185+ self.patch(sys, 'argv', argv)
1186+
1187+ result = main.parse_args()
1188+
1189+ # Access to a protected member, pylint: disable=W0212
1190+ self.assertEqual(expected, dict(result._get_kwargs()))
1191+
1192+ def test_main(self):
1193+ """Calling main.main() a UI instance is created."""
1194+ called = []
1195+ self.patch(main, 'UbuntuSSOClientGUI',
1196+ lambda **kw: called.append(('GUI', kw)))
1197+ self.patch(main.gtk, 'main',
1198+ lambda: called.append('gtk.main'))
1199+
1200+ kwargs = dict(foo='foo', bar='bar', baz='yadda', yadda=0)
1201+ main.main(**kwargs)
1202+
1203+ kwargs['close_callback'] = main.gtk.main_quit
1204+ self.assertEqual(called, [('GUI', kwargs), 'gtk.main'])
1205+
1206+ def test_parse_args_app_name_is_required(self):
1207+ """If no app_name, show help and exit."""
1208+ called = []
1209+ stderr = StringIO()
1210+ self.addCleanup(stderr.close)
1211+
1212+ self.patch(sys, 'argv', [PROGRAM])
1213+ self.patch(sys, 'exit', called.append)
1214+ self.patch(sys, 'stderr', stderr)
1215+
1216+ main.parse_args()
1217+
1218+ self.assertEqual(called, [2])
1219+ self.assertIn('usage: %s' % PROGRAM, stderr.getvalue())
1220+ self.assertIn('app_name is required', stderr.getvalue())
1221+
1222+ def test_parse_args_defaults(self):
1223+ """The args are correctly set when using defaults."""
1224+ self.assert_parse_args_custom_field()
1225+
1226+ def test_parse_args_help_text(self):
1227+ """The args are correctly set when using a custom help_text."""
1228+ self.assert_parse_args_custom_field('help_text', DUMMY_TEXT)
1229+
1230+ def test_parse_args_ping_url(self):
1231+ """The args are correctly set when using a custom ping_url."""
1232+ self.assert_parse_args_custom_field('ping_url', SOME_URL)
1233+
1234+ def test_parse_args_tc_url(self):
1235+ """The args are correctly set when using a custom tc_url."""
1236+ self.assert_parse_args_custom_field('tc_url', SOME_URL)
1237+
1238+ def test_parse_args_window_id(self):
1239+ """The args are correctly set when using a custom window_id."""
1240+ self.assert_parse_args_custom_field('window_id', 42)
1241+
1242+ def test_parse_args_login_only(self):
1243+ """The args are correctly set when using a custom login_only."""
1244+ self.assert_parse_args_custom_field('login_only')
1245
1246=== modified file 'ubuntu_sso/keyring/linux.py'
1247--- ubuntu_sso/keyring/linux.py 2011-03-03 15:19:29 +0000
1248+++ ubuntu_sso/keyring/linux.py 2012-01-18 13:20:30 +0000
1249@@ -37,7 +37,7 @@
1250 try_old_credentials)
1251
1252
1253-logger = setup_logging("ubuntu_sso.keyring")
1254+logger = setup_logging("ubuntu_sso.keyring.linux")
1255
1256
1257 class Keyring(object):
1258@@ -51,16 +51,15 @@
1259 def _find_keyring_item(self, app_name, attr=None):
1260 """Return the keyring item or None if not found."""
1261 if attr is None:
1262- logger.debug("getting attr")
1263 attr = self._get_keyring_attr(app_name)
1264- logger.debug("finding all items")
1265+ logger.debug("Finding all items for app_name %r.", app_name)
1266 items = yield self.service.search_items(attr)
1267 if len(items) == 0:
1268 # if no items found, return None
1269- logger.debug("No items found")
1270+ logger.debug("No items found!")
1271 returnValue(None)
1272
1273- logger.debug("Returning first item found")
1274+ logger.debug("Returning first item found.")
1275 returnValue(items[0])
1276
1277 def _get_keyring_attr(self, app_name):
1278@@ -84,44 +83,36 @@
1279 @inlineCallbacks
1280 def _migrate_old_token_name(self, app_name):
1281 """Migrate credentials with old name, store them with new name."""
1282- logger.debug("getting keyring attr")
1283+ logger.debug("Migrating old token name.")
1284 attr = self._get_keyring_attr(app_name)
1285- logger.debug("getting old token name")
1286 attr['token-name'] = get_old_token_name(app_name)
1287- logger.debug("finding keyring item")
1288 item = yield self._find_keyring_item(app_name, attr=attr)
1289 if item is not None:
1290- logger.debug("setting credentials")
1291 yield self.set_credentials(app_name,
1292 dict(urlparse.parse_qsl(item.secret)))
1293- logger.debug("deleting old item")
1294 yield item.delete()
1295
1296- logger.debug("finding keyring item")
1297 result = yield self._find_keyring_item(app_name)
1298- logger.debug("returning result value")
1299 returnValue(result)
1300
1301 @inlineCallbacks
1302 def get_credentials(self, app_name):
1303 """A deferred with the secret of the SSO item in a dictionary."""
1304 # If we have no attributes, return None
1305- logger.debug("getting credentials")
1306+ logger.debug("Getting credentials for %r.", app_name)
1307 yield self.service.open_session()
1308- logger.debug("calling find item")
1309 item = yield self._find_keyring_item(app_name)
1310 if item is None:
1311- logger.debug("migrating token")
1312 item = yield self._migrate_old_token_name(app_name)
1313
1314 if item is not None:
1315- logger.debug("parsing secret")
1316+ logger.debug("Parsing secret.")
1317 secret = yield item.get_value()
1318 returnValue(dict(urlparse.parse_qsl(secret)))
1319 else:
1320 # if no item found, try getting the old credentials
1321 if app_name == U1_APP_NAME:
1322- logger.debug("trying old credentials")
1323+ logger.debug("Trying old credentials for %r.", app_name)
1324 old_creds = yield try_old_credentials(app_name)
1325 returnValue(old_creds)
1326 # nothing was found

Subscribers

People subscribed via source and target branches