Merge lp:~mandel/ubuntuone-control-panel/autoupdate into lp:ubuntuone-control-panel

Proposed by Manuel de la Peña on 2012-04-02
Status: Merged
Merged at revision: 310
Proposed branch: lp:~mandel/ubuntuone-control-panel/autoupdate
Merge into: lp:ubuntuone-control-panel
Diff against target: 227 lines (+121/-1)
7 files modified
ubuntuone/controlpanel/gui/qt/gui.py (+11/-0)
ubuntuone/controlpanel/gui/qt/main/windows.py (+4/-0)
ubuntuone/controlpanel/gui/qt/tests/__init__.py (+7/-0)
ubuntuone/controlpanel/gui/qt/tests/test_gui.py (+20/-1)
ubuntuone/controlpanel/utils/__init__.py (+2/-0)
ubuntuone/controlpanel/utils/tests/test_windows.py (+60/-0)
ubuntuone/controlpanel/utils/windows.py (+17/-0)
To merge this branch: bzr merge lp:~mandel/ubuntuone-control-panel/autoupdate
Reviewer Review Type Date Requested Status
Natalia Bidart Needs Fixing on 2012-04-05
Leo Arias (community) Needs Information on 2012-04-02
Brian Curtin (community) 2012-04-02 Approve on 2012-04-02
Review via email: mp+100387@code.launchpad.net

Commit Message

- Added simple autoupdate implementation based on the code found in the ubutnuone-windows-installer project (LP: #971455).

Description of the Change

- Added simple autoupdate implementation based on the code found in the ubutnuone-windows-installer project (LP: #971455).

To post a comment you must log in.
309. By Manuel de la Peña on 2012-04-02

Pass the gui module to check updates.

Brian Curtin (brian.curtin) wrote :

I think the UI that gets displayed when an update is found needs some work. Rather than "There is a new update available", we need to ask a question if they want to install it since we have yes/no buttons.

Something like "There is a new update available. Would you like to install it?"

review: Needs Fixing
310. By Manuel de la Peña on 2012-04-02

Ask a question when you do as a question.

Brian Curtin (brian.curtin) wrote :

That's better. The code looks fine and the IRL tests have been working.

review: Approve
Leo Arias (elopio) wrote :

I followed your instructions and got the update message. As Brian said, the message is better as a question and seems to be working fine.
Were we just testing that the message appears or the actual upgrade? If it was just the first, make this a +1. If it was the upgrade, I got an error saying that it couldn't copy a file. Tomorrow morning I'm available for more testing if needed, just ping me.

review: Needs Information
Manuel de la Peña (mandel) wrote :

The most probable cause of the error is that the pacakage found on the server side is not an update or that no package was found, lets make sure whenever you are in IRC.

Natalia Bidart (nataliabidart) wrote :

The module utils can not depend on the module gui.qt, since utils is being distributed in the base package which is in the Ubuntu default install. The allowed dependency is the other way around: gui.qt depends (already) on utils.
Also, this branch generates a circular dependency between utils -> gui.qt -> utils.

So, the solution to this is having the code in MainWindow should what check_updates does, importing utils and calling utils.are_updates_present() and utils.perform_update(), showing the confirmation dialog in between.

As soon as this is fixed, I'm happy to re-review.

review: Needs Fixing
Manuel de la Peña (mandel) wrote :

Please take another look because there is no dependency between utils and the gui modules. The utils.check_updates takes a parameter which is the gui module to use that has the ask question method, the check_updates method is used the main.windows module which has a dependency with the qt.gui module.

This means that:

* Utils does not depend on any gui module. The only dependency is found in the windows tests which can be remove by not using patch and just passing and object that implements the required gui method.
* All logic is in the utils module.
* There is a dependency between windows.main and qt.gui.

Do I make sense?

Natalia Bidart (nataliabidart) wrote :

> * Utils does not depend on any gui module. The only dependency is found in the
> windows tests which can be remove by not using patch and just passing and
> object that implements the required gui method.
> * All logic is in the utils module.
> * There is a dependency between windows.main and qt.gui.
>
> Do I make sense?

Yes, sorry, I stated the dependency wrong. Anyways, the ubuntu_sso.main module can not depend on the gui.qt module, since the former is part of the base package, like I mentioned before.
So, the way to solve this is having the gui.qt module doing the check and raising any needed confirmation dialog, so we keep having the gui.qt module depending on utils, like we do so far.

Since you're in holidays and we need this ASAP, I'm proposing a fix for this at:

https://code.launchpad.net/~nataliabidart/ubuntuone-control-panel/autoupdate/+merge/100989

you can review it if you like :-P (but no need to do so).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntuone/controlpanel/gui/qt/gui.py'
2--- ubuntuone/controlpanel/gui/qt/gui.py 2012-03-29 19:05:02 +0000
3+++ ubuntuone/controlpanel/gui/qt/gui.py 2012-04-02 17:43:21 +0000
4@@ -31,6 +31,10 @@
5 # pylint: enable=E0611
6
7 U1_DOTDESKTOP = "ubuntuone-installer.desktop"
8+# TODO: make strings translatable after the linux deadlines
9+UPDATE_TITLE = 'Ubuntu One'
10+UPDATE_SOFTWARE = ('There is a new update available.'
11+ ' Would you like to install it?')
12
13
14 class MainWindow(QtGui.QMainWindow):
15@@ -111,3 +115,10 @@
16 else:
17 icon = None
18 return icon, window
19+
20+
21+def user_wants_to_update():
22+ """Ask the user if he really wants to update the software."""
23+ result = QtGui.QMessageBox.question(None, UPDATE_TITLE, UPDATE_SOFTWARE,
24+ QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
25+ return result == QtGui.QMessageBox.Yes
26
27=== modified file 'ubuntuone/controlpanel/gui/qt/main/windows.py'
28--- ubuntuone/controlpanel/gui/qt/main/windows.py 2012-03-15 01:25:08 +0000
29+++ ubuntuone/controlpanel/gui/qt/main/windows.py 2012-04-02 17:43:21 +0000
30@@ -17,6 +17,8 @@
31 """Main method to be used on windows."""
32
33 from PyQt4 import QtGui
34+from ubuntuone.controlpanel.gui.qt import gui
35+from ubuntuone.controlpanel.utils import check_updates
36
37
38 def main(app):
39@@ -32,6 +34,8 @@
40 def main_start(app):
41 """Start the mainloop."""
42 from twisted.internet import reactor
43+ # add the check of the updates to be executed asap
44+ reactor.callWhenRunning(check_updates, gui)
45 reactor.run()
46
47
48
49=== modified file 'ubuntuone/controlpanel/gui/qt/tests/__init__.py'
50--- ubuntuone/controlpanel/gui/qt/tests/__init__.py 2012-03-28 12:06:28 +0000
51+++ ubuntuone/controlpanel/gui/qt/tests/__init__.py 2012-04-02 17:43:21 +0000
52@@ -210,6 +210,13 @@
53 cls.kwargs = kw
54 return cls.response
55
56+ @classmethod
57+ def question(cls, *a, **kw):
58+ """Simulate a question message."""
59+ cls.args = a
60+ cls.kwargs = kw
61+ return cls.response
62+
63 # Invalid name "setDetailedText", "setDefaultButton"
64 # pylint: disable=C0103
65
66
67=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_gui.py'
68--- ubuntuone/controlpanel/gui/qt/tests/test_gui.py 2012-03-26 13:30:07 +0000
69+++ ubuntuone/controlpanel/gui/qt/tests/test_gui.py 2012-04-02 17:43:21 +0000
70@@ -18,10 +18,11 @@
71
72 """Tests for the Qt UI."""
73
74+from PyQt4 import QtGui
75 from twisted.internet import defer
76
77 from ubuntuone.controlpanel.gui.qt import gui
78-from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase
79+from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase, FakedDialog
80
81
82 class FakeEntry(object):
83@@ -131,3 +132,21 @@
84 def test_add_to_autostart(self):
85 """Test that the add_to_autostart function is called when CP opens."""
86 self.assertTrue(self._called)
87+
88+
89+class UserWantsUpdateTestCase(BaseTestCase):
90+ """Test the function used to ask the user if he wants to update."""
91+
92+ def test_user_accepts(self):
93+ """Assert the result when the user accepts."""
94+ FakedDialog.response = QtGui.QMessageBox.Yes
95+ self.assertTrue(gui.user_wants_to_update())
96+ self.assertIn(gui.UPDATE_TITLE, FakedDialog.args)
97+ self.assertIn(gui.UPDATE_SOFTWARE, FakedDialog.args)
98+
99+ def test_user_rejects(self):
100+ """Assert the result when the user rejects."""
101+ FakedDialog.response = QtGui.QMessageBox.No
102+ self.assertFalse(gui.user_wants_to_update())
103+ self.assertIn(gui.UPDATE_TITLE, FakedDialog.args)
104+ self.assertIn(gui.UPDATE_SOFTWARE, FakedDialog.args)
105
106=== modified file 'ubuntuone/controlpanel/utils/__init__.py'
107--- ubuntuone/controlpanel/utils/__init__.py 2012-03-29 17:58:28 +0000
108+++ ubuntuone/controlpanel/utils/__init__.py 2012-04-02 17:43:21 +0000
109@@ -39,6 +39,7 @@
110 from ubuntuone.controlpanel.utils import windows
111 add_to_autostart = windows.add_to_autostart
112 are_updates_present = windows.are_updates_present
113+ check_updates = windows.check_updates
114 default_folders = windows.default_folders
115 perform_update = windows.perform_update
116 uninstall_application = windows.uninstall_application
117@@ -46,6 +47,7 @@
118 from ubuntuone.controlpanel.utils import linux
119 add_to_autostart = no_op
120 are_updates_present = no_op
121+ check_updates = no_op
122 default_folders = linux.default_folders
123 perform_update = no_op
124 uninstall_application = no_op
125
126=== modified file 'ubuntuone/controlpanel/utils/tests/test_windows.py'
127--- ubuntuone/controlpanel/utils/tests/test_windows.py 2012-04-02 17:41:54 +0000
128+++ ubuntuone/controlpanel/utils/tests/test_windows.py 2012-04-02 17:43:21 +0000
129@@ -22,6 +22,8 @@
130 from twisted.internet import defer
131
132 from ubuntuone.controlpanel import utils
133+from ubuntuone.controlpanel.gui.qt import gui
134+from ubuntuone.controlpanel.utils import windows
135 from ubuntuone.controlpanel.tests import TestCase
136
137 # let me use protected methods
138@@ -341,3 +343,61 @@
139 """Test the uninstall_application helper when sys is not frozen."""
140
141 frozen = None
142+
143+
144+class CheckUpdatesTestCase(TestCase):
145+ """Test the code that is used for the auto update check process."""
146+
147+ @defer.inlineCallbacks
148+ def setUp(self):
149+ """Set the different tests."""
150+ yield super(CheckUpdatesTestCase, self).setUp()
151+ self.are_present = False
152+ self.user_wants_update = False
153+ self.called = []
154+
155+ def fake_are_present():
156+ """Fake are updates present."""
157+ self.called.append('are_updates_present')
158+ return self.are_present
159+
160+ def fake_perform_update():
161+ """Fake perform update."""
162+ self.called.append('perform_update')
163+
164+ def fake_user_wants_to_update():
165+ """Fake user_wants_to_update."""
166+ self.called.append('user_wants_to_update')
167+ return self.user_wants_update
168+
169+ self.patch(windows, 'are_updates_present', fake_are_present)
170+ self.patch(windows, 'perform_update', fake_perform_update)
171+ self.patch(gui, 'user_wants_to_update', fake_user_wants_to_update)
172+
173+ @defer.inlineCallbacks
174+ def test_check_updates_not_present(self):
175+ """Test the method that interacts with the user."""
176+ self.are_present = False
177+ yield utils.check_updates(gui)
178+ self.assertTrue('are_updates_present' in self.called)
179+ self.assertFalse('perform_update' in self.called)
180+
181+ @defer.inlineCallbacks
182+ def test_check_updates_present_not_user(self):
183+ """Test the method that interacts with the user."""
184+ self.are_present = True
185+ self.user_wants_update = False
186+ yield utils.check_updates(gui)
187+ self.assertTrue('are_updates_present' in self.called)
188+ self.assertTrue('user_wants_to_update' in self.called)
189+ self.assertFalse('perform_update' in self.called)
190+
191+ @defer.inlineCallbacks
192+ def test_check_updates_present_user(self):
193+ """Test the method that interacts with the user."""
194+ self.are_present = True
195+ self.user_wants_update = True
196+ yield utils.check_updates(gui)
197+ self.assertTrue('are_updates_present' in self.called)
198+ self.assertTrue('user_wants_to_update' in self.called)
199+ self.assertTrue('perform_update' in self.called)
200
201=== modified file 'ubuntuone/controlpanel/utils/windows.py'
202--- ubuntuone/controlpanel/utils/windows.py 2012-04-02 17:41:54 +0000
203+++ ubuntuone/controlpanel/utils/windows.py 2012-04-02 17:43:21 +0000
204@@ -87,6 +87,23 @@
205 defer.returnValue(result)
206
207
208+@defer.inlineCallbacks
209+def check_updates(gui):
210+ """Check for updates."""
211+ logger.info('Checking for updates.')
212+ # the idea is simple, check if there are updates, ask user if we perform
213+ # the update and continue.
214+ are_present = yield are_updates_present()
215+ logger.debug('Updates present: %r', are_updates_present)
216+ if are_present:
217+ logger.info('Updates are present.')
218+ if gui.user_wants_to_update():
219+ logger.info('Performing auto-update in diff process.')
220+ perform_update()
221+ else:
222+ logger.info('No updates are present.')
223+
224+
225 def default_folders(user_home=None):
226 """Return a list of the folders to add by default."""
227 # as per http://msdn.microsoft.com/en-us/library/windows/desktop/bb762181,

Subscribers

People subscribed via source and target branches

to all changes: