Merge lp:~ralsina/ubuntuone-windows-installer/fix-810053 into lp:ubuntuone-windows-installer

Proposed by Roberto Alsina
Status: Merged
Approved by: Alejandro J. Cura
Approved revision: 33
Merged at revision: 20
Proposed branch: lp:~ralsina/ubuntuone-windows-installer/fix-810053
Merge into: lp:ubuntuone-windows-installer
Diff against target: 481 lines (+239/-69)
8 files modified
bin/ubuntuone-installer-qt (+3/-0)
run-tests.bat (+1/-1)
ubuntuone_installer/gui/qt/embedded_sso.py (+30/-4)
ubuntuone_installer/gui/qt/gui.py (+5/-13)
ubuntuone_installer/gui/qt/main/tests/__init__.py (+19/-0)
ubuntuone_installer/gui/qt/main/tests/test_main.py (+97/-0)
ubuntuone_installer/gui/qt/main/windows.py (+28/-24)
ubuntuone_installer/gui/qt/tests/test_gui.py (+56/-27)
To merge this branch: bzr merge lp:~ralsina/ubuntuone-windows-installer/fix-810053
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Approve
Natalia Bidart (community) Approve
Roberto Alsina (community) Abstain
Review via email: mp+67894@code.launchpad.net

Commit message

Use SSO the right way, make the UI be in the ussoc process, connect callbacks so the ping url is used.

Description of the change

Use SSO the right way, make the UI be in the ussoc process, connect callbacks so the ping url is used.

To test IRL(windows):

This works by making ussoc import the UI from the installer (which imports from control panel), you need to run ussoc with the installer in the PYTHONPATH, something like this (maybe you need other things):

set PYTHONPATH=..\fix-810053;..\ubuntuone-control-panel;.
python bin\windows-ubuntu-sso-client

And then you run the installer:

set PYTHONPATH=..\ubuntuone-client;..\ubuntuone-control-panel;..\ubuntu-sso-client;.
python bin\ubuntuone-installer-qt

To post a comment you must log in.
18. By Roberto Alsina

Re-add appname, which SSO uses

19. By Roberto Alsina

More tests

20. By Roberto Alsina

missing docstrings

21. By Roberto Alsina

another test

22. By Roberto Alsina

stylefix

23. By Roberto Alsina

removed unused imports

24. By Roberto Alsina

test fixes

25. By Roberto Alsina

removed unused code, fixed comment

26. By Roberto Alsina

Add help_text, emit userCancellation

27. By Roberto Alsina

Close the app when the user cancels

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

A couple of needs fixing:

* the 'found' callback should be called 'stop' or something similar.
* the signal CredentialsError should be connected as well, probably by defining a callback for on_credentials_error.
* the code needs to unregister_from_signals, otherwise the SSO process shows something like this when the installer finishes:

2011-07-15 22:53:43,746:746.000051498 - ubuntu_sso.main.windows - WARNING - Could not emit signal due to [Failure instance: Traceback (failure with no frames):
<class 'twisted.spread.pb.PBConnectionLost'>: [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionDone'>: Connection was closed cleanly.]
]
*--- Failure #6 ---
Failure: twisted.spread.pb.PBConnectionLost: [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionDone'>: Connectionwas closed cleanly.]
*--- End of Failure #6 ---
2011-07-15 22:53:43,760:760.999917984 - ubuntu_sso.main.windows - WARNING - Traceback is:
None

* I had the following issue, but this may be related to SSO itself and not to the installer:
 - I ran SSO as specified
 - I ran the installer as specified
 - I got the wizard UI and I clicked 'Disagree and cancel' -> 'Yes I want to cancel'
 - SSO printed out the trace above and re-opened the wizard UI...

Let me know if you can reproduce what I detailed in the last point.
Thanks!

review: Needs Fixing
28. By Roberto Alsina

more reasonable name

29. By Roberto Alsina

Connect to credentials error signal

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

Pushed revision 29.

* I can reproduce that error (BTW, what kind of error is "connection closed cleanly"? ;-)
* I can't reproduce the wizard restarting before or after revision 29
* I added the connection to on_credentials_error (does nothing interesting yet, I can add that in another
  branch)
* I added the unregister_to_signals call (hope I did it right)

After these changes, the trace is still there. Looks harmless enough, even if it's annoying.

review: Approve
Revision history for this message
Roberto Alsina (ralsina) :
review: Abstain
30. By Roberto Alsina

Use the right constants in main

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

Test are not passing in windows.

review: Needs Fixing
31. By Roberto Alsina

test fixes

32. By Roberto Alsina

pep8 fix

33. By Roberto Alsina

oops

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

All green now.

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

In order to review, I had to add the ubuntuone-client to the sso path, and I needed to remove the credentials from the windows credentials manager.

The branch works as expected, but I still don't understand why we are making the UI be a part of the ussoc process. The dependency graph of module import looks very weird after this change.

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

> In order to review, I had to add the ubuntuone-client to the sso path, and I
> needed to remove the credentials from the windows credentials manager.
>
> The branch works as expected, but I still don't understand why we are making
> the UI be a part of the ussoc process. The dependency graph of module import
> looks very weird after this change.

This is how ussoc is meant to be used, and using it differently did not work.
I agree it complicates things.

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

I finally understood how this all is supposed to work, and while I'm not thrilled about it, I think it works and the UI looks perfect.
Thanks for taking the time to explain it on IRC :-)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/ubuntuone-installer-qt'
2--- bin/ubuntuone-installer-qt 2011-06-22 14:53:52 +0000
3+++ bin/ubuntuone-installer-qt 2011-07-18 18:59:29 +0000
4@@ -27,6 +27,8 @@
5
6 from optparse import OptionParser
7
8+from twisted.internet import reactor
9+
10 from ubuntuone_installer import TRANSLATION_DOMAIN
11
12 gettext.textdomain(TRANSLATION_DOMAIN)
13@@ -46,3 +48,4 @@
14 parser = parser_options()
15 (options, args) = parser.parse_args(sys.argv)
16 main.main()
17+ reactor.run()
18
19=== modified file 'run-tests.bat'
20--- run-tests.bat 2011-07-05 20:07:40 +0000
21+++ run-tests.bat 2011-07-18 18:59:29 +0000
22@@ -55,7 +55,7 @@
23 set USE_PYFLAKES=1
24 "%PYTHONEXEPATH%\python.exe" "%PYTHONEXEPATH%\Scripts\u1lint" ubuntuone_installer
25 :: test for style if we can, if pep8 is not present, move to the end
26-IF EXIST "%PYTHONEXEPATH%\Scripts\pep8.exe" "%PYTHONEXEPATH%\Scripts\pep8.exe" --repeat ubuntuone_installer
27+IF EXIST "%PYTHONEXEPATH%\Scripts\pep8.exe" "%PYTHONEXEPATH%\Scripts\pep8.exe" --repeat --exclude="*_ui.py,*_rc.py" .
28 :: Delete the temp folders
29 RMDIR /s /q _trial_temp
30 RMDIR /s /q .coverage
31
32=== modified file 'ubuntuone_installer/gui/qt/embedded_sso.py'
33--- ubuntuone_installer/gui/qt/embedded_sso.py 2011-06-27 09:59:34 +0000
34+++ ubuntuone_installer/gui/qt/embedded_sso.py 2011-07-18 18:59:29 +0000
35@@ -18,10 +18,10 @@
36
37 """The user interface for the Ubuntu One Installer's SSO client."""
38
39-from PyQt4 import QtCore
40-
41 import ubuntu_sso.qt.gui as sso_gui
42
43+from ubuntuone_installer.gui.qt.gui import MainWindow
44+
45
46 class UbuntuSSOClientGUI(object):
47 """A custom Client GUI for SSO."""
48@@ -31,5 +31,31 @@
49
50 # create the controller and the ui, then set the cb and call the show
51 # method so that we can work
52- self.controller = sso_gui.UbuntuSSOWizardController(app_name)
53- self.view = QtCore.QCoreApplication.instance().window
54+ self.controller = sso_gui.UbuntuSSOWizardController(
55+ app_name,
56+ user_cancellation_callback=self._user_cancellation_callback
57+ )
58+ self.view = MainWindow()
59+
60+ self.login_success_callback = lambda x, y: None
61+ self.registration_success_callback = lambda x, y: None
62+ self.user_cancellation_callback = lambda x: None
63+
64+ self.view.loginSuccess.connect(self._login_success_callback)
65+ self.view.registrationSuccess.connect(
66+ self._registration_success_callback)
67+ self.view.userCancellation.connect(self._user_cancellation_callback)
68+
69+ self.view.show()
70+
71+ def _login_success_callback(self, app_name, email):
72+ """Call the real callback, set by the Credentials object"""
73+ self.login_success_callback(str(app_name), str(email))
74+
75+ def _registration_success_callback(self, app_name, email):
76+ """Call the real callback, set by the Credentials object"""
77+ self.registration_success_callback(str(app_name), str(email))
78+
79+ def _user_cancellation_callback(self, app_name):
80+ """Call the real callback, set by the Credentials object"""
81+ self.user_cancellation_callback(str(app_name))
82
83=== modified file 'ubuntuone_installer/gui/qt/gui.py'
84--- ubuntuone_installer/gui/qt/gui.py 2011-07-15 18:45:17 +0000
85+++ ubuntuone_installer/gui/qt/gui.py 2011-07-18 18:59:29 +0000
86@@ -25,8 +25,6 @@
87
88 from PyQt4 import QtGui, QtCore
89
90-from ubuntu_sso.credentials import Credentials
91-
92 from ubuntu_sso.qt.gui import (
93 EmailVerificationPage,
94 ErrorPage,
95@@ -57,9 +55,8 @@
96
97 from ubuntuone.credentials import (
98 APP_NAME,
99+ DESCRIPTION,
100 TC_URL,
101- PING_URL,
102- DESCRIPTION,
103 )
104
105 from ubuntuone_installer.logger import setup_logging
106@@ -276,6 +273,7 @@
107 recoverableError = QtCore.pyqtSignal('QString', 'QString')
108 loginSuccess = QtCore.pyqtSignal('QString', 'QString')
109 registrationSuccess = QtCore.pyqtSignal('QString', 'QString')
110+ userCancellation = QtCore.pyqtSignal('QString')
111
112 def __init__(self, close_callback=None):
113 """Initialize this instance."""
114@@ -284,9 +282,9 @@
115
116 # Data for SSO
117 self.app_name = APP_NAME
118- self.help_text = DESCRIPTION
119 self.tc_url = TC_URL
120- self.ping_url = PING_URL
121+ # help_text is not displayed in the UI from design
122+ self.help_text = ""
123
124 super(MainWindow, self).__init__()
125 self.setWizardStyle(self.ModernStyle)
126@@ -294,13 +292,6 @@
127
128 self.setSideWidget(SideWidget())
129
130- self.creds = Credentials(
131- app_name=self.app_name,
132- ui_module="ubuntuone_installer.gui.qt.embedded_sso",
133- tc_url=self.tc_url,
134- ping_url=self.ping_url,
135- )
136-
137 self.setOption(self.NoBackButtonOnStartPage, True)
138
139 # PyQt doesn't support the (int, page) version of addPage, so
140@@ -400,6 +391,7 @@
141 if self.close_callback is not None:
142 self.close_callback()
143 QtGui.QWizard.done(self, result)
144+ self.userCancellation.emit(self.app_name)
145
146 def on_currentIdChanged(self, page_id):
147 """The current page changed."""
148
149=== added directory 'ubuntuone_installer/gui/qt/main/tests'
150=== added file 'ubuntuone_installer/gui/qt/main/tests/__init__.py'
151--- ubuntuone_installer/gui/qt/main/tests/__init__.py 1970-01-01 00:00:00 +0000
152+++ ubuntuone_installer/gui/qt/main/tests/__init__.py 2011-07-18 18:59:29 +0000
153@@ -0,0 +1,19 @@
154+# -*- coding: utf-8 -*-
155+
156+# Authors: Roberto Alsina <roberto.alsina@canonical.com>
157+#
158+# Copyright 2011 Canonical Ltd.
159+#
160+# This program is free software: you can redistribute it and/or modify it
161+# under the terms of the GNU General Public License version 3, as published
162+# by the Free Software Foundation.
163+#
164+# This program is distributed in the hope that it will be useful, but
165+# WITHOUT ANY WARRANTY; without even the implied warranties of
166+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
167+# PURPOSE. See the GNU General Public License for more details.
168+#
169+# You should have received a copy of the GNU General Public License along
170+# with this program. If not, see <http://www.gnu.org/licenses/>.
171+
172+"""The test suite for the Qt UI of the Ubuntu One Installer."""
173
174=== added file 'ubuntuone_installer/gui/qt/main/tests/test_main.py'
175--- ubuntuone_installer/gui/qt/main/tests/test_main.py 1970-01-01 00:00:00 +0000
176+++ ubuntuone_installer/gui/qt/main/tests/test_main.py 2011-07-18 18:59:29 +0000
177@@ -0,0 +1,97 @@
178+# -*- coding: utf-8 -*-
179+
180+# Authors: Roberto Alsina <roberto.alsina@canonical.com>
181+#
182+# Copyright 2011 Canonical Ltd.
183+#
184+# This program is free software: you can redistribute it and/or modify it
185+# under the terms of the GNU General Public License version 3, as published
186+# by the Free Software Foundation.
187+#
188+# This program is distributed in the hope that it will be useful, but
189+# WITHOUT ANY WARRANTY; without even the implied warranties of
190+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
191+# PURPOSE. See the GNU General Public License for more details.
192+#
193+# You should have received a copy of the GNU General Public License along
194+# with this program. If not, see <http://www.gnu.org/licenses/>.
195+
196+"""The test suite for the Qt UI of the Ubuntu One Installer."""
197+
198+from ubuntuone_installer.tests import TestCase
199+
200+import ubuntuone_installer.gui.qt.main.windows as main_module
201+
202+# pylint: disable=W0212
203+
204+from ubuntuone.credentials import (
205+ APP_NAME,
206+ DESCRIPTION,
207+ TC_URL,
208+)
209+
210+
211+class FakeSSOCred(object):
212+ """A fake sso_cred."""
213+
214+ _registered_to_signal = False
215+ _login_args = ()
216+ on_credentials_found = None
217+
218+ def register_to_signals(self):
219+ """Fake method."""
220+ self._registered_to_signal = True
221+
222+ def login_or_register_to_get_credentials(self, *args, **kwargs):
223+ """Fake method."""
224+ self._login_args = (args, kwargs)
225+
226+
227+class FakeSSOClient(object):
228+
229+ """Fake UbuntuSSOClient."""
230+
231+ sso_cred = FakeSSOCred()
232+ testcase = None
233+
234+ def connect(self):
235+ """Fake method."""
236+ self.testcase._connected = True
237+ return self
238+
239+
240+class MainTestCase(TestCase):
241+
242+ """Tests for the main() function."""
243+
244+ _connected = False
245+
246+ def setUp(self):
247+ """Prepare tests."""
248+ self.patch(main_module, "UbuntuSSOClient", FakeSSOClient)
249+ main_module.UbuntuSSOClient.testcase = self
250+
251+ def test_connect(self):
252+ """Test that we instantiate the SSO Client and connect."""
253+ main_module.main()
254+ self.assertEqual(FakeSSOClient.testcase._connected, True)
255+
256+ def test_credentials_found_cb(self):
257+ """Test that the credentials found signal is connected."""
258+ main_module.main()
259+ self.assertEqual(
260+ FakeSSOClient.sso_cred.on_credentials_found_cb.__name__,
261+ "stop")
262+
263+ def test_registered(self):
264+ """Test that the credentials registers to signals."""
265+ main_module.main()
266+ self.assertEqual(FakeSSOClient.sso_cred._registered_to_signal,
267+ True)
268+
269+ def test_check_register_args(self):
270+ """Test that the credentials registers to signals."""
271+ main_module.main()
272+ self.assertEqual(FakeSSOClient.sso_cred._login_args,
273+ ((APP_NAME, TC_URL, DESCRIPTION, 0),
274+ {'ui_module': 'ubuntuone_installer.gui.qt.embedded_sso'}))
275
276=== modified file 'ubuntuone_installer/gui/qt/main/windows.py'
277--- ubuntuone_installer/gui/qt/main/windows.py 2011-06-30 17:53:23 +0000
278+++ ubuntuone_installer/gui/qt/main/windows.py 2011-07-18 18:59:29 +0000
279@@ -17,32 +17,36 @@
280 # with this program. If not, see <http://www.gnu.org/licenses/>.
281 """Reactor installation for windows."""
282
283-import sys
284-
285-from PyQt4 import Qt
286-
287 # pylint: disable=E1101, F0401, W0404
288
289-
290+from twisted.internet.defer import inlineCallbacks
291+from ubuntu_sso.main.windows import UbuntuSSOClient
292+
293+from ubuntuone.credentials import (
294+ APP_NAME,
295+ DESCRIPTION,
296+ TC_URL,
297+)
298+
299+
300+@inlineCallbacks
301 def main():
302- """Start the txnamedpipes reactor and open the main window."""
303-
304- # The following cannot be imported outside this function
305- # because u1trial already provides a reactor.
306- # pylint: disable=W0404, F0401
307-
308- app = Qt.QApplication(sys.argv)
309- import qtreactor.qt4reactor
310- qtreactor.qt4reactor.install()
311-
312- from ubuntuone_installer.gui.qt.gui import MainWindow
313- from twisted.internet import reactor
314-
315- def really_quit():
316- """Close the application and cleanup."""
317+ """Perform a client request to be logged in."""
318+ client = UbuntuSSOClient()
319+ client = yield client.connect()
320+
321+ @inlineCallbacks
322+ def stop(*args):
323+ """End the process."""
324+ from twisted.internet import reactor
325+ yield client.sso_cred.unregister_to_signals()
326 reactor.stop()
327
328- window = MainWindow(close_callback=really_quit)
329- app.window = window
330- window.show()
331- reactor.run()
332+ client.sso_cred.on_credentials_found_cb = stop
333+ client.sso_cred.on_authorization_denied_cb = stop
334+ client.sso_cred.on_credentials_error_cb = stop
335+ yield client.sso_cred.register_to_signals()
336+ yield client.sso_cred.login_or_register_to_get_credentials(APP_NAME,
337+ TC_URL,
338+ DESCRIPTION, 0,
339+ ui_module="ubuntuone_installer.gui.qt.embedded_sso")
340
341=== modified file 'ubuntuone_installer/gui/qt/tests/test_gui.py'
342--- ubuntuone_installer/gui/qt/tests/test_gui.py 2011-07-15 18:47:24 +0000
343+++ ubuntuone_installer/gui/qt/tests/test_gui.py 2011-07-18 18:59:29 +0000
344@@ -29,14 +29,13 @@
345 from ubuntuone.credentials import (
346 APP_NAME,
347 TC_URL,
348- PING_URL,
349 )
350
351 import ubuntu_sso
352
353 from ubuntuone_installer.gui.qt import gui
354 from ubuntuone_installer.gui.qt.tests import BaseTestCase
355-from ubuntuone_installer.gui.qt.embedded_sso import UbuntuSSOClientGUI
356+from ubuntuone_installer.gui.qt import embedded_sso
357 from ubuntuone_installer.gui.qt import local_folders
358 from ubuntuone_installer.gui.qt import setup_account
359 from ubuntuone_installer.gui.qt.ui import (
360@@ -51,15 +50,6 @@
361 u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'}
362
363
364-class FakeCredentials(object):
365-
366- """A very dummy Credentials object."""
367-
368- def __init__(self, *a, **kw):
369- self.args = a
370- self.kwargs = kw
371-
372-
373 class FakeController(object):
374
375 """A fake controller for the tests."""
376@@ -99,7 +89,6 @@
377 """Initialize this test instance."""
378 # Faking each SSO object instead of doing it lower
379 # so we don't rely on any SSO behaviour
380- self.patch(gui, "Credentials", FakeCredentials)
381 self.patch(gui, "SetUpAccountController", FakeController)
382 self.patch(gui, "TosController", FakeController)
383 self.patch(gui, "EmailVerificationController", FakeController)
384@@ -196,15 +185,6 @@
385 self.assertEqual(
386 congrats_page.ui.progressContainer.isVisible(), False)
387
388- def test_credential_parameters(self):
389- """Compare credential parameters with control panel's."""
390- self.assertEqual(self.ui.creds.kwargs, {
391- 'app_name': APP_NAME,
392- 'ui_module': "ubuntuone_installer.gui.qt.embedded_sso",
393- 'tc_url': TC_URL,
394- 'ping_url': PING_URL,
395- })
396-
397 def test_current_user_controller_parameters(self):
398 """Compare controller parameters with expected values."""
399 self.assertEqual(self.ui.current_user_controller.args,
400@@ -283,10 +263,37 @@
401 self.assertEqual(self.ui.result(), self.ui.Rejected)
402
403
404+class FakeSignal(object):
405+
406+ """A fake PyQt signal."""
407+
408+ def connect(self, target):
409+ """Fake connect."""
410+ self.target = target
411+
412+ def emit(self, *args):
413+ """Fake emit."""
414+ self.target(*args)
415+
416+
417+class FakeMainWindow(object):
418+
419+ """A fake MainWindow."""
420+
421+ loginSuccess = FakeSignal()
422+ registrationSuccess = FakeSignal()
423+ userCancellation = FakeSignal()
424+ shown = False
425+
426+ def show(self):
427+ """Fake method."""
428+ self.shown = True
429+
430+
431 class SSOGuiTestCase(BaseTestCase):
432 """Test the custom SSO GUI."""
433
434- class_ui = UbuntuSSOClientGUI
435+ class_ui = embedded_sso.UbuntuSSOClientGUI
436
437 def setUp(self):
438 """Initialize this test instance."""
439@@ -295,15 +302,37 @@
440 'tc_url': TC_URL,
441 'help_text': '',
442 }
443- QtCore.QCoreApplication.instance().window = True
444+ self.patch(embedded_sso, "MainWindow", FakeMainWindow)
445 super(SSOGuiTestCase, self).setUp()
446
447 def test_sso_client_gui(self):
448- """Ensure the class stores the right parameters."""
449- self.assertEqual(isinstance(
450+ """Ensure the class instantiates correctly."""
451+ self.assertIsInstance(
452 self.ui.controller,
453- ubuntu_sso.qt.controllers.UbuntuSSOWizardController), True)
454- self.assertEqual(self.ui.view, True)
455+ ubuntu_sso.qt.controllers.UbuntuSSOWizardController)
456+ self.assertIsInstance(self.ui.view, FakeMainWindow)
457+
458+ def test_login_callback(self):
459+ """Test that the login success callback is correctly handled."""
460+ self.ui.login_success_callback = self._set_called
461+ self.ui.view.loginSuccess.emit("app", "email")
462+ self.assertEqual(self._called, (('app', 'email'), {}))
463+
464+ def test_registration_callback(self):
465+ """Test that the registration success callback is correctly handled."""
466+ self.ui.registration_success_callback = self._set_called
467+ self.ui.view.registrationSuccess.emit("app", "email")
468+ self.assertEqual(self._called, (('app', 'email'), {}))
469+
470+ def test_cancellation_callback(self):
471+ """Test that the cancellation callback is called right."""
472+ self.ui.user_cancellation_callback = self._set_called
473+ self.ui.view.userCancellation.emit("app")
474+ self.assertEqual(self._called, (('app',), {}))
475+
476+ def test_view_is_shown(self):
477+ """Test that the view is actually shown."""
478+ self.assertTrue(self.ui.view.shown)
479
480
481 class LocalFoldersTestCase(BaseTestCase):

Subscribers

People subscribed via source and target branches