Merge lp:~mterry/update-manager/stop-update into lp:update-manager
- stop-update
- Merge into main
Proposed by
Michael Terry
Status: | Merged |
---|---|
Merged at revision: | 2538 |
Proposed branch: | lp:~mterry/update-manager/stop-update |
Merge into: | lp:update-manager |
Diff against target: |
389 lines (+173/-65) 7 files modified
UpdateManager/Dialogs.py (+12/-0) UpdateManager/UpdateManager.py (+31/-14) UpdateManager/UpdateProgress.py (+1/-6) UpdateManager/UpdatesAvailable.py (+36/-36) UpdateManager/backend/InstallBackendAptdaemon.py (+3/-0) data/gtkbuilder/UpdateManager.ui (+40/-9) tests/test_stop_update.py (+50/-0) |
To merge this branch: | bzr merge lp:~mterry/update-manager/stop-update |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Vogt (community) | Approve | ||
Review via email: mp+120318@code.launchpad.net |
Commit message
Description of the change
mpt added some dialogs recently to the update-manager experience if the user stops an apt update. See https:/
This branch implements those changes (and does a little minor cleanup in UpdatesAvailable by dropping some support for num_updates == 0 which shouldn't ever happen now, after the redesign).
To post a comment you must log in.
Revision history for this message
Michael Vogt (mvo) wrote : | # |
Revision history for this message
Michael Vogt (mvo) : | # |
review:
Approve
- 2536. By Michael Terry
-
merge from trunk
- 2537. By Michael Terry
-
merge from trunk again
- 2538. By Michael Terry
-
fix pep8 and pyflakes issues
- 2539. By Michael Terry
-
adjust code to make the UpdateManager class slightly easier to unit test; add tests for stop-update code
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'UpdateManager/Dialogs.py' |
2 | --- UpdateManager/Dialogs.py 2012-07-06 19:30:02 +0000 |
3 | +++ UpdateManager/Dialogs.py 2012-08-24 23:06:30 +0000 |
4 | @@ -106,6 +106,18 @@ |
5 | self.label_desc.set_visible(bool(label)) |
6 | |
7 | |
8 | +class StoppedUpdatesDialog(Dialog): |
9 | + def __init__(self, window_main): |
10 | + Dialog.__init__(self, window_main) |
11 | + self.set_header(_("You stopped the check for updates.")) |
12 | + self.add_settings_button() |
13 | + self.add_button(_("_Check Again"), self.check) |
14 | + self.focus_button = self.add_button(Gtk.STOCK_OK, self.close) |
15 | + |
16 | + def check(self): |
17 | + self.window_main.start_update() |
18 | + |
19 | + |
20 | class NoUpdatesDialog(Dialog): |
21 | def __init__(self, window_main): |
22 | Dialog.__init__(self, window_main) |
23 | |
24 | === modified file 'UpdateManager/UpdateManager.py' |
25 | --- UpdateManager/UpdateManager.py 2012-08-24 20:20:11 +0000 |
26 | +++ UpdateManager/UpdateManager.py 2012-08-24 23:06:30 +0000 |
27 | @@ -46,6 +46,7 @@ |
28 | ErrorDialog, |
29 | NeedRestartDialog, |
30 | NoUpdatesDialog, |
31 | + StoppedUpdatesDialog, |
32 | PartialUpgradeDialog, |
33 | UnsupportedDialog) |
34 | from .InstallProgress import InstallProgress |
35 | @@ -162,18 +163,36 @@ |
36 | |
37 | self._start_pane(UpdateProgress(self)) |
38 | |
39 | - def start_available(self): |
40 | + def start_available(self, cancelled_update=False): |
41 | self._look_busy() |
42 | self.refresh_cache() |
43 | |
44 | - if self.cache.install_count == 0: |
45 | + pane = self._make_available_pane(self.cache.install_count, |
46 | + os.path.exists(REBOOT_REQUIRED_FILE), |
47 | + cancelled_update) |
48 | + self._start_pane(pane) |
49 | + |
50 | + def _make_available_pane(self, install_count, need_reboot, |
51 | + cancelled_update): |
52 | + if install_count == 0: |
53 | # Need Restart > New Release > No Updates |
54 | - if os.path.exists(REBOOT_REQUIRED_FILE): |
55 | - self._start_pane(NeedRestartDialog(self)) |
56 | - elif not self._check_meta_release(): |
57 | - self._start_pane(NoUpdatesDialog(self)) |
58 | + if need_reboot: |
59 | + return NeedRestartDialog(self) |
60 | + pane = self._check_meta_release() |
61 | + if pane: |
62 | + return pane |
63 | + elif cancelled_update: |
64 | + return StoppedUpdatesDialog(self) |
65 | + else: |
66 | + return NoUpdatesDialog(self) |
67 | else: |
68 | - self._start_pane(UpdatesAvailable(self)) |
69 | + header = None |
70 | + desc = None |
71 | + if cancelled_update: |
72 | + header = _("You stopped the check for updates.") |
73 | + desc = _("Updated software is available from " |
74 | + "a previous check.") |
75 | + return UpdatesAvailable(self, header, desc) |
76 | |
77 | def start_install(self): |
78 | self._start_pane(InstallProgress(self)) |
79 | @@ -192,7 +211,7 @@ |
80 | |
81 | def _check_meta_release(self): |
82 | if self.meta_release is None: |
83 | - return False |
84 | + return None |
85 | |
86 | if self.meta_release.downloading: |
87 | # Block until we get an answer |
88 | @@ -202,22 +221,20 @@ |
89 | # Check if there is anything to upgrade to or a known-broken upgrade |
90 | next = self.meta_release.upgradable_to |
91 | if not next or next.upgrade_broken: |
92 | - return False |
93 | + return None |
94 | |
95 | # Check for end-of-life |
96 | if self.meta_release.no_longer_supported: |
97 | - self._start_pane(UnsupportedDialog(self, self.meta_release)) |
98 | - return True |
99 | + return UnsupportedDialog(self, self.meta_release) |
100 | |
101 | # Check for new fresh release |
102 | settings = Gio.Settings("com.ubuntu.update-manager") |
103 | if (self.meta_release.new_dist and |
104 | (self.options.check_dist_upgrades or |
105 | settings.get_boolean("check-dist-upgrades"))): |
106 | - self._start_pane(DistUpgradeDialog(self, self.meta_release)) |
107 | - return True |
108 | + return DistUpgradeDialog(self, self.meta_release) |
109 | |
110 | - return False |
111 | + return None |
112 | |
113 | def _meta_release_wait_idle(self): |
114 | # 'downloading' is changed in a thread, but the signal |
115 | |
116 | === modified file 'UpdateManager/UpdateProgress.py' |
117 | --- UpdateManager/UpdateProgress.py 2012-06-27 22:01:50 +0000 |
118 | +++ UpdateManager/UpdateProgress.py 2012-08-24 23:06:30 +0000 |
119 | @@ -35,7 +35,6 @@ |
120 | DeprecationWarning) |
121 | |
122 | import os |
123 | -import sys |
124 | |
125 | from .backend import get_backend |
126 | |
127 | @@ -72,11 +71,7 @@ |
128 | allow_sleep(self.sleep_dev, self.sleep_cookie) |
129 | self.sleep_cookie = self.sleep_dev = None |
130 | |
131 | - # Either launch main dialog and continue or quit altogether |
132 | - if success: |
133 | - self.window_main.start_available() |
134 | - else: |
135 | - sys.exit(0) |
136 | + self.window_main.start_available(not success) |
137 | |
138 | def main(self): |
139 | self.invoke_manager() |
140 | |
141 | === modified file 'UpdateManager/UpdatesAvailable.py' |
142 | --- UpdateManager/UpdatesAvailable.py 2012-06-28 00:09:03 +0000 |
143 | +++ UpdateManager/UpdatesAvailable.py 2012-08-24 23:06:30 +0000 |
144 | @@ -81,7 +81,7 @@ |
145 | |
146 | class UpdatesAvailable(SimpleGtkbuilderApp): |
147 | |
148 | - def __init__(self, app): |
149 | + def __init__(self, app, header=None, desc=None): |
150 | self.window_main = app |
151 | self.datadir = app.datadir |
152 | self.options = app.options |
153 | @@ -99,6 +99,9 @@ |
154 | # workaround for LP: #945536 |
155 | self.clearing_store = False |
156 | |
157 | + self.custom_header = header |
158 | + self.custom_desc = desc |
159 | + |
160 | self.button_close.grab_focus() |
161 | self.dl_size = 0 |
162 | self.connected = True |
163 | @@ -360,9 +363,6 @@ |
164 | Gtk.MenuItem.new_with_mnemonic(_("_Deselect All")) |
165 | item_select_none.connect("activate", self.select_none_updgrades) |
166 | menu.append(item_select_none) |
167 | - num_updates = self.cache.install_count |
168 | - if num_updates == 0: |
169 | - item_select_none.set_property("sensitive", False) |
170 | item_select_all = Gtk.MenuItem.new_with_mnemonic(_("Select _All")) |
171 | item_select_all.connect("activate", self.select_all_updgrades) |
172 | menu.append(item_select_all) |
173 | @@ -462,38 +462,38 @@ |
174 | """activate or disable widgets and show dialog texts correspoding to |
175 | the number of available updates""" |
176 | self.refresh_updates_count() |
177 | - num_updates = self.cache.install_count |
178 | - |
179 | - if num_updates == 0: |
180 | - text_header = _("The software on this computer is up to date.") |
181 | - self.label_downsize.set_text("\n") |
182 | - if self.cache.keep_count() == 0: |
183 | - self.notebook_details.set_sensitive(False) |
184 | - self.treeview_update.set_sensitive(False) |
185 | - self.button_install.set_sensitive(False) |
186 | - self.unity.set_install_menuitem_visible(False) |
187 | - self.button_close.grab_default() |
188 | - self.textview_changes.get_buffer().set_text("") |
189 | - self.textview_descr.get_buffer().set_text("") |
190 | - else: |
191 | - # show different text on first run (UX team suggestion) |
192 | - firstrun = self.settings.get_boolean("first-run") |
193 | - if firstrun: |
194 | - flavor = self.window_main.meta_release.flavor_name |
195 | - version = self.window_main.meta_release.current_dist_version |
196 | - text_header = _("Updated software has been issued since %s %s " |
197 | - "was released. Do you want to install " |
198 | - "it now?") % (flavor, version) |
199 | - self.settings.set_boolean("first-run", False) |
200 | - else: |
201 | - text_header = _("Updated software is available for this " |
202 | - "computer. Do you want to install it now?") |
203 | - self.notebook_details.set_sensitive(True) |
204 | - self.treeview_update.set_sensitive(True) |
205 | - self.button_install.grab_default() |
206 | - self.treeview_update.set_cursor(Gtk.TreePath.new_from_string("1"), |
207 | - None, False) |
208 | - self.label_header.set_markup(text_header) |
209 | + |
210 | + text_header = None |
211 | + text_desc = None |
212 | + |
213 | + if self.custom_header is not None: |
214 | + text_header = self.custom_header |
215 | + text_desc = self.custom_desc |
216 | + # show different text on first run (UX team suggestion) |
217 | + elif self.settings.get_boolean("first-run"): |
218 | + flavor = self.window_main.meta_release.flavor_name |
219 | + version = self.window_main.meta_release.current_dist_version |
220 | + text_header = _("Updated software has been issued since %s %s " |
221 | + "was released. Do you want to install " |
222 | + "it now?") % (flavor, version) |
223 | + self.settings.set_boolean("first-run", False) |
224 | + else: |
225 | + text_header = _("Updated software is available for this " |
226 | + "computer. Do you want to install it now?") |
227 | + |
228 | + self.notebook_details.set_sensitive(True) |
229 | + self.treeview_update.set_sensitive(True) |
230 | + self.button_install.grab_default() |
231 | + self.treeview_update.set_cursor(Gtk.TreePath.new_from_string("1"), |
232 | + None, False) |
233 | + self.label_header.set_label(text_header) |
234 | + |
235 | + if text_desc is not None: |
236 | + self.label_desc.set_label(text_desc) |
237 | + self.label_desc.show() |
238 | + else: |
239 | + self.label_desc.hide() |
240 | + |
241 | return True |
242 | |
243 | # Before we shrink the window, capture the size |
244 | |
245 | === modified file 'UpdateManager/backend/InstallBackendAptdaemon.py' |
246 | --- UpdateManager/backend/InstallBackendAptdaemon.py 2012-06-28 00:10:23 +0000 |
247 | +++ UpdateManager/backend/InstallBackendAptdaemon.py 2012-08-24 23:06:30 +0000 |
248 | @@ -125,6 +125,8 @@ |
249 | progressbar_slot.add(progressbar) |
250 | |
251 | self.button_cancel = AptCancelButton(trans) |
252 | + if action == self.UPDATE: |
253 | + self.button_cancel.set_label(Gtk.STOCK_STOP) |
254 | self.button_cancel.show() |
255 | button_cancel_slot = builder.get_object("button_cancel_slot") |
256 | button_cancel_slot.add(self.button_cancel) |
257 | @@ -225,6 +227,7 @@ |
258 | err_dia = AptErrorDialog(trans.error, self.window_main) |
259 | err_dia.run() |
260 | err_dia.hide() |
261 | + sys.exit(0) |
262 | elif status == EXIT_SUCCESS and close_on_done: |
263 | sys.exit(0) |
264 | # tell unity to hide the progress again |
265 | |
266 | === modified file 'data/gtkbuilder/UpdateManager.ui' |
267 | --- data/gtkbuilder/UpdateManager.ui 2012-06-18 21:14:43 +0000 |
268 | +++ data/gtkbuilder/UpdateManager.ui 2012-08-24 23:06:30 +0000 |
269 | @@ -40,16 +40,47 @@ |
270 | </packing> |
271 | </child> |
272 | <child> |
273 | - <object class="GtkLabel" id="label_header"> |
274 | + <object class="GtkGrid" id="grid1"> |
275 | <property name="visible">True</property> |
276 | <property name="can_focus">False</property> |
277 | - <property name="xalign">0</property> |
278 | - <property name="use_underline">True</property> |
279 | - <property name="wrap">True</property> |
280 | - <attributes> |
281 | - <attribute name="weight" value="bold"/> |
282 | - <attribute name="scale" value="1.25"/> |
283 | - </attributes> |
284 | + <property name="margin_bottom">6</property> |
285 | + <property name="row_spacing">6</property> |
286 | + <property name="column_spacing">12</property> |
287 | + <child> |
288 | + <object class="GtkLabel" id="label_header"> |
289 | + <property name="visible">True</property> |
290 | + <property name="can_focus">False</property> |
291 | + <property name="hexpand">True</property> |
292 | + <property name="xalign">0</property> |
293 | + <property name="wrap">True</property> |
294 | + <attributes> |
295 | + <attribute name="weight" value="bold"/> |
296 | + <attribute name="scale" value="1.25"/> |
297 | + </attributes> |
298 | + </object> |
299 | + <packing> |
300 | + <property name="left_attach">0</property> |
301 | + <property name="top_attach">0</property> |
302 | + <property name="width">1</property> |
303 | + <property name="height">1</property> |
304 | + </packing> |
305 | + </child> |
306 | + <child> |
307 | + <object class="GtkLabel" id="label_desc"> |
308 | + <property name="can_focus">False</property> |
309 | + <property name="no_show_all">True</property> |
310 | + <property name="hexpand">True</property> |
311 | + <property name="xalign">0</property> |
312 | + <property name="wrap">True</property> |
313 | + <property name="max_width_chars">20</property> |
314 | + </object> |
315 | + <packing> |
316 | + <property name="left_attach">0</property> |
317 | + <property name="top_attach">1</property> |
318 | + <property name="width">1</property> |
319 | + <property name="height">1</property> |
320 | + </packing> |
321 | + </child> |
322 | </object> |
323 | <packing> |
324 | <property name="expand">False</property> |
325 | @@ -462,8 +493,8 @@ |
326 | <property name="receives_default">True</property> |
327 | <property name="use_action_appearance">False</property> |
328 | <property name="use_stock">True</property> |
329 | + <accelerator key="Q" signal="clicked" modifiers="GDK_CONTROL_MASK"/> |
330 | <accelerator key="W" signal="clicked" modifiers="GDK_CONTROL_MASK"/> |
331 | - <accelerator key="Q" signal="clicked" modifiers="GDK_CONTROL_MASK"/> |
332 | </object> |
333 | <packing> |
334 | <property name="expand">False</property> |
335 | |
336 | === added file 'tests/test_stop_update.py' |
337 | --- tests/test_stop_update.py 1970-01-01 00:00:00 +0000 |
338 | +++ tests/test_stop_update.py 2012-08-24 23:06:30 +0000 |
339 | @@ -0,0 +1,50 @@ |
340 | +#!/usr/bin/python3 |
341 | +# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*- |
342 | + |
343 | +import logging |
344 | +import sys |
345 | +import unittest |
346 | +from mock import patch |
347 | + |
348 | +from UpdateManager.UpdateManager import UpdateManager |
349 | +from UpdateManager.UpdatesAvailable import UpdatesAvailable |
350 | +from UpdateManager import Dialogs |
351 | + |
352 | +import os |
353 | +CURDIR = os.path.dirname(os.path.abspath(__file__)) |
354 | + |
355 | + |
356 | +class TestStopUpdate(unittest.TestCase): |
357 | + |
358 | + def setUp(self): |
359 | + patcher = patch('UpdateManager.UpdateManager.UpdateManager') |
360 | + self.addCleanup(patcher.stop) |
361 | + self.manager = patcher.start() |
362 | + self.manager._check_meta_release.return_value = False |
363 | + self.manager.datadir = os.path.join(CURDIR, '..', 'data') |
364 | + |
365 | + def test_stop_no_updates(self): |
366 | + # install_count, need_reboot, cancelled_update |
367 | + p = UpdateManager._make_available_pane(self.manager, 0, False, True) |
368 | + self.assertIsInstance(p, Dialogs.StoppedUpdatesDialog) |
369 | + |
370 | + def test_no_stop_no_updates(self): |
371 | + # install_count, need_reboot, cancelled_update |
372 | + p = UpdateManager._make_available_pane(self.manager, 0, False, False) |
373 | + self.assertNotIsInstance(p, Dialogs.StoppedUpdatesDialog) |
374 | + |
375 | + def test_stop_updates(self): |
376 | + # install_count, need_reboot, cancelled_update |
377 | + p = UpdateManager._make_available_pane(self.manager, 1, False, True) |
378 | + self.assertIsInstance(p, UpdatesAvailable) |
379 | + self.assertIsNotNone(p.custom_header) |
380 | + |
381 | + def test_no_stop_updates(self): |
382 | + p = UpdateManager._make_available_pane(self.manager, 1, False, False) |
383 | + self.assertIsInstance(p, UpdatesAvailable) |
384 | + self.assertIsNone(p.custom_header) |
385 | + |
386 | +if __name__ == '__main__': |
387 | + if len(sys.argv) > 1 and sys.argv[1] == "-v": |
388 | + logging.basicConfig(level=logging.DEBUG) |
389 | + unittest.main() |
On Mon, Aug 20, 2012 at 02:45:38AM -0000, Michael Terry wrote: pane(UpdateProg ress(self) ) (self): (self, cancelled_update = False): cache() UpdatesAvailabl e.py' UpdatesAvailabl e.py 2012-06-28 00:09:03 +0000 UpdatesAvailabl e.py 2012-08-20 02:44:22 +0000 e(SimpleGtkbuil derApp) :
[..]
> @@ -162,7 +163,7 @@
>
> self._start_
>
> - def start_available
> + def start_available
> self._look_busy()
> self.refresh_
[..]
> === modified file 'UpdateManager/
> --- UpdateManager/
> +++ UpdateManager/
> @@ -81,7 +81,7 @@
>
> class UpdatesAvailabl
>
> - def __init__(self, app):
> + def __init__(self, app, header = None, desc = None):
> self.window_main = app
> self.datadir = app.datadir
> self.options = app.options
pep8 suggests to use "cancelled_ update= False" (no space around the
"=").
Otherwise this looks good, would be nice to have a test, but to get it
in before UIF I'm fine with merging it now.
Thanks,
Michael