Merge lp:~chipaca/ubuntu-system-settings/push-helper-to-spec into lp:ubuntu-system-settings

Proposed by John Lenton
Status: Merged
Approved by: Sebastien Bacher
Approved revision: 1034
Merged at revision: 1047
Proposed branch: lp:~chipaca/ubuntu-system-settings/push-helper-to-spec
Merge into: lp:ubuntu-system-settings
Diff against target: 417 lines (+264/-75)
3 files modified
CMakeLists.txt (+1/-1)
push-helper/software_updates_helper.py (+149/-64)
tests/test_push_helper.py.in (+114/-10)
To merge this branch: bzr merge lp:~chipaca/ubuntu-system-settings/push-helper-to-spec
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Sebastien Bacher (community) Approve
Ken VanDine Needs Fixing
Roberto Alsina (community) Approve
Review via email: mp+234311@code.launchpad.net

Commit message

Take the system settings push helper closer to implementing the spec at https://wiki.ubuntu.com/SoftwareUpdates#Prompting

Description of the change

This brings the push helper for system settings closer to implementing https://wiki.ubuntu.com/SoftwareUpdates#Prompting

Missing still is the bit about click package updates counter. Also the emblem doesn't appear everywhere the icon for system settings is, so the fallback to a notification in the software centre exists instead.

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) :
review: Approve
Revision history for this message
Roberto Alsina (ralsina) wrote :

To test:

* Downgrade to an older image
* Configure for automatic update
* gdbus call -e -d com.ubuntu.Postal -o /com/ubuntu/Postal/_ -m com.ubuntu.Postal.Post _ubuntu-system-settings '"null"'
* Image will download automatically
* Notification on notification centre about update will appear

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1031
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~chipaca/ubuntu-system-settings/push-helper-to-spec/+merge/234311/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-ci/1454/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/4726/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/3476
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-i386-ci/646
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/4497/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5978
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5978/artifact/work/output/*zip*/output.zip
    None: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/13067/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2877
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3781
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3781/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-system-settings-ci/1454/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1033
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~chipaca/ubuntu-system-settings/push-helper-to-spec/+merge/234311/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-ci/1459/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/4742/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/3482/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-i386-ci/651
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/4512/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5994
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/5994/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/13083
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2883/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3787
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3787/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-system-settings-ci/1459/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Sebastien Bacher (seb128) wrote :

Thanks, does that address bug #1363972 (notification should replace previous ones)? if not could that be done in the same changeset?

could you also summarize what should change compared to the previous version, from an user perspective, so we can confirm that the update works as it should?

review: Needs Information
Revision history for this message
Sebastien Bacher (seb128) wrote :

oh, also the title is confusing since it suggest you can tap anywhere on the entry where it's not the case, see bug #1366288 (not a new issue but I want to point it out while we refactor/update to match design)

Revision history for this message
John Lenton (chipaca) wrote :

> Thanks, does that address bug #1363972 (notification should replace previous
> ones)? if not could that be done in the same changeset?

yes, it fixes that bug. I'll tie the bug to the branch now.

> could you also summarize what should change compared to the previous version,
> from an user perspective, so we can confirm that the update works as it
> should?

yes. Before, when a broadcast notification came in (or if you faked it with gdbus as per roberto's suggestion) you'd get a notification immediately every time, and they'd stack up.

Now, you only get a notification immediately if you have turned off auto downloads; otherwise, you'll get a notification after it's been downloaded.

Revision history for this message
John Lenton (chipaca) wrote :

> oh, also the title is confusing since it suggest you can tap anywhere on the
> entry where it's not the case, see bug #1366288 (not a new issue but I want to
> point it out while we refactor/update to match design)

as far as i know i'm per design on this, if not please point me at it?

the tap-to-expand-then-tap-this-little-icon-to-activate thing is a bit of a pet peeve of mine that i hope to address with design at some point, tho.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1034
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~chipaca/ubuntu-system-settings/push-helper-to-spec/+merge/234311/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-ci/1464/
Executed test runs:
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/4756
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/3493
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-i386-ci/656
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/4525
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/6008
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/6008/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/13096
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2893
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3798
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/3798/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-system-settings-ci/1464/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

Looks good, it does properly supersede the notification instead of stacking with normal use. You can trigger a race if you make several dbus calls to simulate the notification, it'll still give you multiple notifications. But that's a corner case, the postal service shouldn't do that.

review: Approve
Revision history for this message
Ted Gould (ted) wrote :

This relies on a bug in lifecycle confinement that will be fixed shortly:

https://bugs.launchpad.net/ubuntu-app-launch/+bug/1358753

At that point all children will also have the same lifecycle as their parents.

Revision history for this message
Ken VanDine (ken-vandine) wrote :

I dropped the top approval based on ted's comment, I'd rather not just end up with this broken next week and have to scramble to get a fix.

review: Needs Fixing
Revision history for this message
John Lenton (chipaca) wrote :

The system settings push helper is not an untrusted helper.

Revision history for this message
John Lenton (chipaca) wrote :

That is: the system settings push helper is not an untrusted helper, so it does not run confined. It is not launched by ubuntu-app-launch. That bug and its fix does not apply; you can't have untrusted helpers for "legacy" applications, so for legacy applications (which are trusted) we just do an exec "old style".

Revision history for this message
Ted Gould (ted) wrote :

Legacy applications can have untrusted helpers you just need to have an AppArmor profile or set them explicitly to unconfined. I'd recommend that they have an AppArmor profile as there are dpkg tools that make that relatively easy to maintain.

I'm really surprised that there's two separate mechanisms to run helpers, but I guess that does make this MR safe, so I retract my objection.

Revision history for this message
John Lenton (chipaca) wrote :

> Legacy applications can have untrusted helpers you just need to have an
> AppArmor profile or set them explicitly to unconfined. I'd recommend that they
> have an AppArmor profile as there are dpkg tools that make that relatively
> easy to maintain.

Hm! I'm pretty sure this failed in interesting ways when I tested it. I've just tested it again and it fails in a sane way that looks suspiciously like a missing apparmor profile. I guess we can revisit this split then (not now though).

I'll catch you on IRC to go over whether it'll possible to implement the design for update notifications without escaping the lifecycle nor special-casing system settings.

> I'm really surprised that there's two separate mechanisms to run helpers,

yeah, i wasn't too happy having to write that code either. Not having to maintain it long term would be good.

> but
> I guess that does make this MR safe, so I retract my objection.

ok

Revision history for this message
Sebastien Bacher (seb128) wrote :

since the issue got resolved setting back to approved, we can land it ;-)

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-08-05 10:12:14 +0000
3+++ CMakeLists.txt 2014-09-11 20:53:25 +0000
4@@ -76,7 +76,7 @@
5 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ubuntu-system-settings.desktop DESTINATION share/applications)
6 install(FILES ubuntu-system-settings.url-dispatcher DESTINATION share/url-dispatcher/urls)
7 install(FILES screenshot.png DESTINATION ${SETTINGS_SHARE_DIR})
8-install(PROGRAMS push-helper/software-updates-helper.py DESTINATION ${PUSH_HELPER_DIR} RENAME ubuntu-system-settings)
9+install(PROGRAMS push-helper/software_updates_helper.py DESTINATION ${PUSH_HELPER_DIR} RENAME ubuntu-system-settings)
10
11 if(cmake_build_type_lower MATCHES coverage)
12 ENABLE_COVERAGE_REPORT(TARGETS system-settings FILTER /usr/include ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/*)
13
14=== renamed file 'push-helper/software-updates-helper.py' => 'push-helper/software_updates_helper.py'
15--- push-helper/software-updates-helper.py 2014-08-28 00:45:29 +0000
16+++ push-helper/software_updates_helper.py 2014-09-11 20:53:25 +0000
17@@ -16,74 +16,159 @@
18 # 2. yes, this is rather convoluted. Most push helpers don't have to deal with
19 # this stuff.
20
21+import os
22 import json
23 import sys
24 import time
25 import gettext
26
27-if len(sys.argv) != 3:
28- print("File in and out expected via argv", file=sys.stderr)
29- sys.exit(1)
30-
31-f1, f2 = sys.argv[1:3]
32 _ = gettext.translation('ubuntu-system-settings', fallback=True).gettext
33
34-# here you should look at the input (the contents of the file whose
35-# name is in f1, which are guaranteed to be json). If it's a broadcast
36-# it will be the most recent we've received, and will have passed a
37-# minimum amount of sanity checking, but you can probably do more. As
38-# per the design on https://wiki.ubuntu.com/SoftwareUpdates#Prompting
39-# if things are set to auto-download you should go download them (in a
40-# child process -- this helper process itself has 4 more seconds to
41-# live).
42-#
43-# the broadcast payload will be a single json object looking like
44-# { image-channel/device-model": [build-number, channel-alias]}
45-#
46-# e.g.,
47-#
48-# {"ubuntu-touch/utopic-proposed/hammerhead":[265,""]}
49-#
50-#
51-# When the click server starts sending notifications of packages a user can
52-# update, you should probably describe that payload here. What to do with it
53-# is described in some detail in the wiki page above.
54-#
55-#
56-# Once you've downloaded things and need to actually notify the user, you'd
57-# send a notification as below, over dbus to Post. That's the third payload
58-# this script will be called with; for that case you'd just pass the payload
59-# through.
60-#
61-# For cases when you don't want to notify the user (yet), the correct
62-# output (to be written to a file whose name is f2) is “{}”, i.e. an
63-# empty json object.
64-#
65-# For now, this script assumes everything that comes in is a valid
66-# broadcast notification, and notifies the user directly:
67-
68-
69-icon = "/usr/share/ubuntu/settings/system/icons/settings-system-update.svg"
70-obj = {
71- "notification": {
72- "emblem-counter": {
73- "count": 1,
74- "visible": True,
75- },
76- "vibrate": {
77- "pattern": [50, 150],
78- "repeat": 3,
79- },
80- "card": {
81- "summary": _("There's an updated system image."),
82- "body": _("Tap to open the system updater."),
83- "actions": ["settings:///system/system-update"],
84- "icon": icon,
85- "timestamp": int(time.time()),
86- "persist": True,
87- "popup": True,
88- },
89- },
90-}
91-
92-json.dump(obj, open(f2, "w"))
93+SYS_UPDATE = "system-image-update"
94+
95+
96+class SystemImage:
97+ def __init__(self):
98+ self.loop = None
99+ self.sysimg = None
100+ self.postal = None
101+ self.notify = False
102+
103+ def setup(self):
104+ import dbus
105+ from dbus.mainloop.glib import DBusGMainLoop
106+ from gi.repository import GLib
107+
108+ gloop = DBusGMainLoop()
109+ sys_bus = dbus.SystemBus(mainloop=gloop)
110+ ses_bus = dbus.SessionBus(mainloop=gloop)
111+ self.loop = GLib.MainLoop()
112+ self.sysimg = dbus.Interface(
113+ sys_bus.get_object("com.canonical.SystemImage", "/Service"),
114+ dbus_interface="com.canonical.SystemImage")
115+ self.postal = dbus.Interface(
116+ ses_bus.get_object("com.ubuntu.Postal", "/com/ubuntu/Postal/_"),
117+ dbus_interface="com.ubuntu.Postal")
118+ self.notify = False
119+
120+ def quit(self):
121+ if self.notify:
122+ # remove any older notifications about this
123+ self.postal.ClearPersistent("_ubuntu-system-settings", SYS_UPDATE)
124+ # send ours. This will of course come back to this same script.
125+ self.postal.Post("_ubuntu-system-settings", json.dumps(SYS_UPDATE))
126+ self.loop.quit()
127+
128+ def available_cb(self, available, downloading, *ignored):
129+ if available:
130+ if downloading:
131+ # handled in the UpdateDownloaded or UpdateFailed handlers
132+ return
133+ # available and not downloading: auto downloads are turned
134+ # off; notify the user right now.
135+ self.notify = True
136+ else:
137+ # if not available, we were called spuriously. No notification.
138+ self.notify = False
139+ self.quit()
140+
141+ def downloaded_cb(self):
142+ self.notify = True
143+ self.quit()
144+
145+ def failed_cb(self, *ignored):
146+ # give up
147+ self.notify = False
148+ self.quit()
149+
150+ def run(self):
151+ self.sysimg.connect_to_signal("UpdateAvailableStatus",
152+ self.available_cb)
153+ self.sysimg.connect_to_signal("UpdateDownloaded", self.downloaded_cb)
154+ self.sysimg.connect_to_signal("UpdateFailed", self.failed_cb)
155+ self.sysimg.CheckForUpdate()
156+ self.loop.run()
157+
158+
159+def main():
160+ if len(sys.argv) != 3:
161+ print("File in and out expected via argv", file=sys.stderr)
162+ sys.exit(1)
163+
164+ f1, f2 = sys.argv[1:3]
165+
166+ # here you should look at the input (the contents of the file whose
167+ # name is in f1, which are guaranteed to be json). If it's a broadcast
168+ # it will be the most recent we've received, and will have passed a
169+ # minimum amount of sanity checking, but you can probably do more. As
170+ # per the design on https://wiki.ubuntu.com/SoftwareUpdates#Prompting
171+ # if things are set to auto-download you should go download them (in a
172+ # child process -- this helper process itself has 4 more seconds to
173+ # live).
174+ #
175+ # the broadcast payload will be a single json object looking like
176+ # { image-channel/device-model": [build-number, channel-alias]}
177+ #
178+ # e.g.,
179+ #
180+ # {"ubuntu-touch/utopic-proposed/hammerhead":[265,""]}
181+ #
182+ #
183+ # When the click server starts sending notifications of packages a user
184+ # can update, you should probably describe that payload here. What to
185+ # do with it is described in some detail in the wiki page above.
186+ #
187+ #
188+ # Once you've downloaded things and need to actually notify the user,
189+ # you'd send a notification as below, over dbus to Post. That's the
190+ # third payload this script will be called with; for that case you'd
191+ # just pass the payload through.
192+ #
193+ # For cases when you don't want to notify the user (yet), the correct
194+ # output (to be written to a file whose name is f2) is “{}”, i.e. an
195+ # empty json object.
196+
197+ with open(f1) as f:
198+ arg = json.load(f)
199+
200+ obj = {}
201+ if arg == "system-image-update":
202+ icon = "/usr/share/ubuntu/settings/system/icons/" + \
203+ "settings-system-update.svg"
204+ obj = {
205+ "notification": {
206+ "tag": SYS_UPDATE,
207+ "emblem-counter": {
208+ "count": 1,
209+ "visible": True,
210+ },
211+ "vibrate": {
212+ "pattern": [50, 150],
213+ "repeat": 3,
214+ },
215+ "card": {
216+ "summary": _("There's an updated system image."),
217+ "body": _("Tap to open the system updater."),
218+ "actions": ["settings:///system/system-update"],
219+ "icon": icon,
220+ "timestamp": int(time.time()),
221+ "persist": True,
222+ },
223+ },
224+ }
225+ elif arg == "testing":
226+ # for tests
227+ obj = {"testing": True}
228+ else:
229+ # assume it's a broadcast
230+ if os.fork() == 0:
231+ os.setsid()
232+ s = SystemImage()
233+ s.setup()
234+ s.run()
235+ return
236+
237+ json.dump(obj, open(f2, "w"))
238+
239+if __name__ == '__main__':
240+ main()
241
242=== modified file 'tests/test_push_helper.py.in'
243--- tests/test_push_helper.py.in 2014-09-03 14:12:52 +0000
244+++ tests/test_push_helper.py.in 2014-09-11 20:53:25 +0000
245@@ -1,5 +1,4 @@
246-#!/usr/bin/python
247-
248+#!/usr/bin/python3
249 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
250 # Copyright 2014 Canonical
251 #
252@@ -14,6 +13,16 @@
253 import sys
254 import tempfile
255 import unittest
256+from unittest import mock
257+
258+HELPER_DIR = '@CMAKE_CURRENT_SOURCE_DIR@/../push-helper/'
259+sys.path.append(HELPER_DIR)
260+import software_updates_helper
261+
262+
263+class TestingSystemImage(software_updates_helper.SystemImage):
264+ def setup(self):
265+ pass
266
267
268 class PushHelperTests(unittest.TestCase):
269@@ -22,15 +31,14 @@
270 def setUp(self):
271 super(PushHelperTests, self).setUp()
272 self.tmp_dir = tempfile.mkdtemp(suffix='push-helper', prefix='tests')
273- self.helper_path = '@CMAKE_CURRENT_SOURCE_DIR@/../push-helper/' + \
274- 'software-updates-helper.py'
275+ self.helper_path = HELPER_DIR + 'software_updates_helper.py'
276
277 def tearDown(self):
278 super(PushHelperTests, self).tearDown()
279 shutil.rmtree(self.tmp_dir)
280
281 def run_push_helper(self, input_fname, output_fname):
282- subprocess.call([self.helper_path, input_fname, output_fname],
283+ subprocess.call(["python3", self.helper_path, input_fname, output_fname],
284 stdout=subprocess.PIPE)
285
286 def create_input_file(self, filename, content):
287@@ -49,7 +57,7 @@
288 self.assertEqual(card['actions'], ['settings:///system/system-update'])
289 self.assertEqual(card['persist'], True)
290 self.assertEqual(card['body'], 'Tap to open the system updater.')
291- self.assertEqual(card['popup'], True)
292+ self.assertEqual(card.get('popup', False), False)
293 emblem_counter = notif['notification']['emblem-counter']
294 self.assertEqual(emblem_counter, {'visible': True, 'count': 1})
295 vibrate = notif['notification']['vibrate']
296@@ -58,7 +66,7 @@
297 def test_update_broadcast(self):
298 """Default system-update broadcast."""
299 input_f = self.create_input_file('bcast_in',
300- '{"daily/mako": [200, ""]}')
301+ '"system-image-update"')
302 output_f = os.path.join(self.tmp_dir, 'bcast_out')
303 self.run_push_helper(input_f, output_f)
304 with open(output_f, 'r') as fd:
305@@ -67,13 +75,109 @@
306
307 def test_valid_json(self):
308 """Handle a valid json input."""
309- input_f = self.create_input_file('valid_json_in', '"null"')
310+ input_f = self.create_input_file('valid_json_in', '"testing"')
311 output_f = os.path.join(self.tmp_dir, 'valid_json_out')
312 self.run_push_helper(input_f, output_f)
313 with open(output_f, 'r') as fd:
314 output = json.load(fd)
315- self.assertSystemUpdateNotification(output)
316-
317+ self.assertEqual(output, {"testing": True})
318+
319+ def test_system_image_run(self):
320+ """Check that run looks sane"""
321+ s = TestingSystemImage()
322+ s.sysimg = mock.Mock(name="sysimg")
323+ s.loop = mock.Mock(name="loop")
324+ s.run()
325+ # check the main loop was run
326+ s.loop.run.assert_called_once_with()
327+ # check CheckForUpdate was called
328+ s.sysimg.CheckForUpdate.assert_called_once_with()
329+ # and connect_to_signal
330+ s.sysimg.connect_to_signal.assert_any_call("UpdateDownloaded",
331+ s.downloaded_cb)
332+ s.sysimg.connect_to_signal.assert_any_call("UpdateFailed",
333+ s.failed_cb)
334+ s.sysimg.connect_to_signal.assert_any_call("UpdateAvailableStatus",
335+ s.available_cb)
336+ self.assertEqual(s.notify, False)
337+
338+ def test_available_and_downloading(self):
339+ """check that available_cb when available and d'loading just returns"""
340+ s = TestingSystemImage()
341+ s.quit = mock.Mock(name="quit")
342+
343+ self.assertEqual(s.notify, False)
344+ # available and downloading; returns without calling quit
345+ s.available_cb(True, True)
346+ self.assertEqual(s.notify, False)
347+ self.assertEqual(s.quit.called, False)
348+
349+ def test_available_not_downloading(self):
350+ """check that available_cb when available and not downloading
351+ sets notify and quits"""
352+ s = TestingSystemImage()
353+ s.quit = mock.Mock(name="quit")
354+
355+ self.assertEqual(s.notify, False)
356+ # available and not downloading; quits with notification
357+ s.available_cb(True, False)
358+ self.assertEqual(s.notify, True)
359+ s.quit.assert_called_once_with()
360+
361+ def test_not_available(self):
362+ """check that available_cb quits when not available"""
363+ s = TestingSystemImage()
364+ s.quit = mock.Mock(name="quit")
365+
366+ self.assertEqual(s.notify, False)
367+ # not available; quits without notifying
368+ s.available_cb(False, False)
369+ self.assertEqual(s.notify, False)
370+ s.quit.assert_called_once_with()
371+
372+ def test_downloaded_cb(self):
373+ """check that on download, notify is set to True and quit is called"""
374+ s = TestingSystemImage()
375+ s.quit = mock.Mock(name="quit")
376+
377+ self.assertEqual(s.notify, False)
378+ s.downloaded_cb()
379+ self.assertEqual(s.notify, True)
380+ s.quit.assert_called_once_with()
381+
382+ def test_failed_cb(self):
383+ """check that on failure, notify is set to False and quit is called"""
384+ s = TestingSystemImage()
385+ s.quit = mock.Mock(name="quit")
386+
387+ self.assertEqual(s.notify, False)
388+ s.failed_cb()
389+ self.assertEqual(s.notify, False)
390+ s.quit.assert_called_once_with()
391+
392+ def test_quit_no_notify(self):
393+ """Check that quit withlooks sane"""
394+ s = TestingSystemImage()
395+ s.postal = mock.Mock(name="sysimg")
396+ s.loop = mock.Mock(name="loop")
397+ s.notify = False
398+ s.quit()
399+ self.assertEqual(s.postal.Post.called, False)
400+ self.assertEqual(s.postal.ClearPersistent.called, False)
401+ s.loop.quit.assert_called_once_with()
402+
403+ def test_quit_with_notify(self):
404+ """Check that quit withlooks sane"""
405+ s = TestingSystemImage()
406+ s.postal = mock.Mock(name="sysimg")
407+ s.loop = mock.Mock(name="loop")
408+ s.notify = True
409+ s.quit()
410+ s.postal.Post.assert_called_once_with("_ubuntu-system-settings",
411+ '"system-image-update"')
412+ s.postal.ClearPersistent.assert_called_once_with(
413+ "_ubuntu-system-settings", "system-image-update")
414+ s.loop.quit.assert_called_once_with()
415
416 if __name__ == '__main__':
417 unittest.main(

Subscribers

People subscribed via source and target branches