Merge lp:~chris.gagnon/ubuntu-system-settings/barebones-autopilot-emulator-for-updates-page into lp:ubuntu-system-settings

Proposed by Chris Gagnon
Status: Work in progress
Proposed branch: lp:~chris.gagnon/ubuntu-system-settings/barebones-autopilot-emulator-for-updates-page
Merge into: lp:ubuntu-system-settings
Diff against target: 444 lines (+224/-73)
5 files modified
plugins/system-update/PageComponent.qml (+2/-0)
tests/autopilot/ubuntu_system_settings/emulators.py (+155/-0)
tests/autopilot/ubuntu_system_settings/tests/__init__.py (+61/-58)
tests/autopilot/ubuntu_system_settings/tests/test_about.py (+0/-1)
tests/autopilot/ubuntu_system_settings/tests/test_system_updates.py (+6/-14)
To merge this branch: bzr merge lp:~chris.gagnon/ubuntu-system-settings/barebones-autopilot-emulator-for-updates-page
Reviewer Review Type Date Requested Status
Sebastien Bacher (community) Needs Fixing
PS Jenkins bot continuous-integration Approve
Review via email: mp+210036@code.launchpad.net

Commit message

barebones emulator for system updates page, add objectNames that were needed for testing

Description of the change

I am trying to use the updates page in the system settings app to do upgrade testing for images that have not been released.

We use a custom proxy object/autopilot emulator in the autopilot tests so all projects can reuse the custom proxy objects.

This will be used to save time when there are changes made to the system-settings app, as we will not have also make changes to all the tests that might check or use something in system-settings with autopilot.

To test the changes there should be an in memory backend that changes the state of the updates page.

There should also be a mock update that does not reboot the phone.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Sebastien Bacher (seb128) wrote :

Thank you for the work, I've no reviewed it yet but could you write a short summary of the intend of those changes? What issues are you trying to resolve? How do you recommend testing the changes?

review: Needs Information
Revision history for this message
Chris Gagnon (chris.gagnon) wrote :

I am trying to use the updates page in the system settings app to do upgrade testing for images that have not been released.

We use a custom proxy object/autopilot emulator in the autopilot tests so all projects can reuse the custom proxy objects.

This will be used to save time when there are changes made to the system-settings app, as we will not have also make changes to all the tests that might check or use something in system-settings with autopilot.

To test the changes there should be an in memory backend that changes the state of the updates page.

There should also be a mock update that does not reboot the phone.

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

> To test the changes there should be an in memory backend that changes the state of the updates page.
> There should also be a mock update that does not reboot the phone.

Those are not done there though, so it means the current changes should be tested by just checking that tests keep being green with the update?

Some extra changes landed to trunk since you submitted that work and they create conflicts, could you rebase on trunk?

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

setting to "work in progress", change it back to "needs review" when the issue pointed before are resolved

Unmerged revisions

641. By Chris Gagnon

barebones start on emulator for system-settings

640. By Chris Gagnon

add objectNames, create emulator for system updates

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/system-update/PageComponent.qml'
2--- plugins/system-update/PageComponent.qml 2014-02-26 11:39:47 +0000
3+++ plugins/system-update/PageComponent.qml 2014-03-08 00:11:20 +0000
4@@ -369,6 +369,7 @@
5
6 ListItem.Standard {
7 id: notification
8+ objectName: "notification"
9 visible: false
10 anchors.bottom: configuration.top
11 }
12@@ -391,6 +392,7 @@
13
14 Rectangle {
15 id: updatedNotification
16+ objectName: "updateNotification"
17 anchors {
18 left: parent.left
19 right: parent.right
20
21=== added file 'tests/autopilot/ubuntu_system_settings/emulators.py'
22--- tests/autopilot/ubuntu_system_settings/emulators.py 1970-01-01 00:00:00 +0000
23+++ tests/autopilot/ubuntu_system_settings/emulators.py 2014-03-08 00:11:20 +0000
24@@ -0,0 +1,155 @@
25+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
26+# Copyright 2013, 2014 Canonical
27+#
28+# This program is free software: you can redistribute it and/or modify it
29+# under the terms of the GNU General Public License version 3, as published
30+# by the Free Software Foundation.
31+
32+""" Autopilot for Ubuntu System Settings """
33+
34+from __future__ import absolute_import
35+
36+import time
37+
38+from ubuntuuitoolkit import emulators as toolkit_emulators
39+
40+
41+class SystemSettingsEmulatorException(Exception):
42+ """Exception raised when there is an error with the emulator."""
43+ pass
44+
45+
46+class MainWindow(toolkit_emulators.MainView):
47+ def click_obj(self, obj):
48+ """Generic way to click an object
49+
50+ :param obj: The object to click
51+ """
52+ obj.visible.wait_for(True)
53+ self.pointing_device.click_object(obj)
54+ return obj
55+
56+ def _click_entry_component(self, object_name):
57+ """Click and return an entry component
58+
59+ :param object_name: the object name to click and return
60+ """
61+ return self.click_obj(
62+ self.wait_select_single('EntryComponent',
63+ objectName=object_name))
64+
65+ def click_updates(self):
66+ """Click updates and return the updates page"""
67+ self._click_entry_component('entryComponent-system-update')
68+ return self.wait_select_single(PageComponent,
69+ objectName='systemUpdatesPage')
70+
71+
72+class PageComponent(MainWindow):
73+ # TODO refactor QT code with page names, so better page object pattern
74+ # can be used
75+
76+ # updates pages
77+ def click_install_all_button(self):
78+ """click install all button"""
79+ button = self._get_install_all_button()
80+ self.click_obj(button)
81+ return button
82+
83+ def get_state(self):
84+ """Return state of app"""
85+ # TODO make sure this matches design doc
86+ # from qml code for system updates page:
87+ #
88+ # state SEARCHING
89+ # notification visible false
90+ # installAllButton visible false
91+ # checkForUpdatesArea visible true
92+ # updateNotification visiible false
93+ # state NOUPDATES
94+ # updateNotification text i18n.tr("Software is up to date")
95+ # updateNotification visible true
96+ # updateList visible false
97+ # installAllButton visible false
98+ # checkForUpdatesArea visible false
99+ # state SYSTEMUPDATEFAILED
100+ # notification text i18n.tr("System update has failed.")
101+ # notification progression false
102+ # notification visible true
103+ # installingImageUpdate visible false
104+ # checkForUpdatesArea visible false
105+ # updatedNotifiation visible false
106+ # state UPDATE
107+ # notification visible false
108+ # updateList visible true
109+ # installAllButton visible true
110+ # checkForUpdatesArea visible true
111+ # updatedNotification visible false
112+
113+ updateNotification = self._get_update_notification()
114+ if updateNotification.text == 'Software is up to date':
115+ return 'NOUPDATES'
116+ install_all_button = self._get_install_all_button()
117+ if install_all_button.visible:
118+ return 'UPDATE'
119+ notification = self._get_notification()
120+ if notification.text == 'System update has failed.':
121+ return 'SYSTEMUPDATEFAILED'
122+ return 'SEARCHING'
123+
124+ def wait_for_state(self, state, timeout=60):
125+ """Wait for expected state
126+
127+ :returns: state
128+ :raises: SystemSettingsEmulatorException
129+ """
130+ for wait in range(timeout):
131+ if state == self.get_state():
132+ return state
133+ time.sleep(1)
134+
135+ raise SystemSettingsEmulatorException('State not found before '
136+ 'timeout')
137+
138+ def _get_update_notification(self):
139+ """Return update notification"""
140+ return self.wait_select_single(objectName='updateNotification')
141+
142+ def _get_install_all_button(self):
143+ """Return install all button"""
144+ return self.wait_select_single('Button',
145+ objectName='installAllButton')
146+
147+ def _get_notification(self):
148+ """Return notification"""
149+ return self.wait_select_single(objectName='notification')
150+
151+ def _get_update_list(self):
152+ """Return update list"""
153+ return self.wait_select_single(objectName='updateList')
154+
155+ def _get_check_for_updates_area(self):
156+ """Return the check for updates area"""
157+ return self.wait_select_single('QQuickItem',
158+ objectName='checkForUpdatesArea')
159+
160+ def _get_check_for_updates_label(self):
161+ """return label for check updates area"""
162+ area = self.get_check_for_updates_area()
163+ return area.select_single('Label')
164+
165+
166+class Dialog(MainWindow):
167+ # updates page dialog
168+ # TODO add object names to qml file
169+ def click_not_now(self):
170+ """Click not now button"""
171+ button = self.click_obj(
172+ self.wait_select_single('Button', text='Not Now'))
173+ button.wait_for_destroyed()
174+
175+ def click_install_and_restart(self):
176+ """Click install and restart button"""
177+ button = self.click_obj(
178+ self.wait_select_single('Button', text='Install & Restart'))
179+ button.wait_for_destroyed()
180
181=== modified file 'tests/autopilot/ubuntu_system_settings/tests/__init__.py'
182--- tests/autopilot/ubuntu_system_settings/tests/__init__.py 2014-02-18 15:42:24 +0000
183+++ tests/autopilot/ubuntu_system_settings/tests/__init__.py 2014-03-08 00:11:20 +0000
184@@ -10,29 +10,31 @@
185 from __future__ import absolute_import
186
187 from ubuntu_system_settings.utils.i18n import ugettext as _
188+from ubuntu_system_settings import emulators
189
190 from autopilot.input import Mouse, Touch, Pointer
191 from autopilot.platform import model
192+from autopilot.matchers import Eventually
193 from autopilot.testcase import AutopilotTestCase
194-from autopilot.matchers import Eventually
195-from testtools.matchers import Equals, NotEquals, GreaterThan
196
197-from ubuntuuitoolkit.base import UbuntuUIToolkitAppTestCase
198 from ubuntuuitoolkit import emulators as toolkit_emulators
199+from testtools.matchers import Equals, GreaterThan
200
201 import dbus
202 import dbusmock
203 import subprocess
204 from time import sleep
205
206-class UbuntuSystemSettingsTestCase(UbuntuUIToolkitAppTestCase):
207+
208+class UbuntuSystemSettingsTestCase(AutopilotTestCase):
209 """ Base class for Ubuntu System Settings """
210 if model() == 'Desktop':
211- scenarios = [ ('with mouse', dict(input_device_class=Mouse)) ]
212+ scenarios = [('with mouse', dict(input_device_class=Mouse))]
213 else:
214- scenarios = [ ('with touch', dict(input_device_class=Touch)) ]
215+ scenarios = [('with touch', dict(input_device_class=Touch))]
216
217 def setUp(self, panel=None):
218+ self.panel = panel
219 super(UbuntuSystemSettingsTestCase, self).setUp()
220 self.launch_system_settings(panel=panel)
221 self.assertThat(self.main_view.visible, Eventually(Equals(True)))
222@@ -40,7 +42,8 @@
223 def launch_system_settings(self, panel=None):
224 params = ['/usr/bin/system-settings']
225 if (model() != 'Desktop'):
226- params.append('--desktop_file_hint=/usr/share/applications/ubuntu-system-settings.desktop')
227+ params.append('--desktop_file_hint=/usr/share/applications/'
228+ 'ubuntu-system-settings.desktop')
229
230 # Launch to a specific panel
231 if panel is not None:
232@@ -54,7 +57,7 @@
233 @property
234 def main_view(self):
235 """ Return main view """
236- return self.app.select_single("QQuickView")
237+ return self.app.select_single(emulators.MainWindow)
238
239 @property
240 def pointer(self):
241@@ -69,8 +72,8 @@
242 page_center_x = int(page_right / 2)
243 page_center_y = int(page_bottom / 2)
244 while obj.globalRect[1] + obj.height > page_bottom:
245- self.pointer.drag(page_center_x, page_center_y,
246- page_center_x, page_center_y - obj.height * 2)
247+ self.pointer.drag(page_center_x, page_center_y,
248+ page_center_x, page_center_y - obj.height * 2)
249 # avoid a flick
250 sleep(0.5)
251
252@@ -96,7 +99,12 @@
253
254 def add_mock_battery(self):
255 """ Make sure we have a battery """
256- self.dbusmock.AddDischargingBattery('mock_BATTERY', 'Battery', 50.0, 10)
257+ self.dbusmock.AddDischargingBattery(
258+ 'mock_BATTERY',
259+ 'Battery',
260+ 50.0,
261+ 10
262+ )
263
264
265 class UbuntuSystemSettingsBatteryTestCase(UbuntuSystemSettingsUpowerTestCase):
266@@ -123,37 +131,47 @@
267 def setUp(self, panel=None):
268 self.obj_ofono.Reset()
269 # Add an available carrier
270- self.dbusmock.AddObject('/ril_0/operator/op2',
271- 'org.ofono.NetworkOperator',
272- {
273- 'Name': 'my.cool.telco',
274- 'Status': 'available',
275- 'MobileCountryCode': '777',
276- 'MobileNetworkCode': '22',
277- 'Technologies': ['gsm'],
278- },
279- [
280- ('GetProperties', '', 'a{sv}',
281- 'ret = self.GetAll("org.ofono.NetworkOperator")'),
282- ('Register', '', '', ''),
283- ]
284- )
285+ self.dbusmock.AddObject(
286+ '/ril_0/operator/op2',
287+ 'org.ofono.NetworkOperator',
288+ {
289+ 'Name': 'my.cool.telco',
290+ 'Status': 'available',
291+ 'MobileCountryCode': '777',
292+ 'MobileNetworkCode': '22',
293+ 'Technologies': ['gsm'],
294+ },
295+ [
296+ (
297+ 'GetProperties',
298+ '',
299+ 'a{sv}',
300+ 'ret = self.GetAll("org.ofono.NetworkOperator")'
301+ ),
302+ ('Register', '', '', ''),
303+ ]
304+ )
305 # Add a forbidden carrier
306- self.dbusmock.AddObject('/ril_0/operator/op3',
307- 'org.ofono.NetworkOperator',
308- {
309- 'Name': 'my.bad.telco',
310- 'Status': 'forbidden',
311- 'MobileCountryCode': '777',
312- 'MobileNetworkCode': '22',
313- 'Technologies': ['gsm'],
314- },
315- [
316- ('GetProperties', '', 'a{sv}',
317- 'ret = self.GetAll("org.ofono.NetworkOperator")'),
318- ('Register', '', '', ''),
319- ]
320- )
321+ self.dbusmock.AddObject(
322+ '/ril_0/operator/op3',
323+ 'org.ofono.NetworkOperator',
324+ {
325+ 'Name': 'my.bad.telco',
326+ 'Status': 'forbidden',
327+ 'MobileCountryCode': '777',
328+ 'MobileNetworkCode': '22',
329+ 'Technologies': ['gsm'],
330+ },
331+ [
332+ (
333+ 'GetProperties',
334+ '',
335+ 'a{sv}',
336+ 'ret = self.GetAll("org.ofono.NetworkOperator")'
337+ ),
338+ ('Register', '', '', ''),
339+ ]
340+ )
341 super(UbuntuSystemSettingsOfonoTestCase, self).setUp('cellular')
342
343 @property
344@@ -187,19 +205,15 @@
345 super(StorageBaseTestCase, self).setUp()
346 # Click on 'Storage' option
347 button = self.about_page.select_single(objectName='storageItem')
348- self.assertThat(button, NotEquals(None))
349 self.scroll_to_and_click(button)
350
351 def assert_space_item(self, object_name, text):
352 """ Checks whether an space item exists and returns a value """
353 item = self.storage_page.select_single(objectName=object_name)
354- self.assertThat(item, NotEquals(None))
355- label = item.label # Label
356- space = item.value # Disk space (bytes)
357+ label = item.label # Label
358 self.assertThat(label, Equals(text))
359 # Get item's label
360 size_label = item.select_single(objectName='sizeLabel')
361- self.assertThat(size_label, NotEquals(None))
362 values = size_label.text.split(' ') # Format: "00.0 (bytes|MB|GB)"
363 self.assertThat(len(values), GreaterThan(1))
364
365@@ -217,7 +231,6 @@
366 super(LicenseBaseTestCase, self).setUp()
367 # Click on 'Software licenses' option
368 button = self.main_view.select_single(objectName='licenseItem')
369- self.assertThat(button, NotEquals(None))
370 self.assertThat(button.text, Equals(_('Software licenses')))
371 self.scroll_to_and_click(button)
372
373@@ -234,14 +247,4 @@
374 """ Go to SystemUpdates Page """
375 super(SystemUpdatesBaseTestCase, self).setUp()
376 # Click on 'System Updates' option
377- button = self.main_view.select_single(
378- objectName='entryComponent-system-update')
379- self.assertThat(button, NotEquals(None))
380- self.pointer.move_to_object(button)
381- self.pointer.click()
382-
383- @property
384- def updates_page(self):
385- """ Return 'System Update' page """
386- return self.main_view.select_single(
387- objectName='entryComponent-system-update')
388+ self.updates_page = self.main_view.click_updates()
389
390=== modified file 'tests/autopilot/ubuntu_system_settings/tests/test_about.py'
391--- tests/autopilot/ubuntu_system_settings/tests/test_about.py 2014-01-27 16:17:57 +0000
392+++ tests/autopilot/ubuntu_system_settings/tests/test_about.py 2014-03-08 00:11:20 +0000
393@@ -20,7 +20,6 @@
394
395 def test_about_page(self):
396 """ Checks whether About page is available """
397- self.assertThat(self.about_page, NotEquals(None))
398 self.assertThat(self.about_page.title, Equals(_('About this phone')))
399
400 def test_device(self):
401
402=== modified file 'tests/autopilot/ubuntu_system_settings/tests/test_system_updates.py'
403--- tests/autopilot/ubuntu_system_settings/tests/test_system_updates.py 2014-02-04 21:05:28 +0000
404+++ tests/autopilot/ubuntu_system_settings/tests/test_system_updates.py 2014-03-08 00:11:20 +0000
405@@ -5,17 +5,12 @@
406 # under the terms of the GNU General Public License version 3, as published
407 # by the Free Software Foundation.
408
409-from time import sleep
410-
411-from autopilot.matchers import Eventually
412 from autopilot.introspection.dbus import StateNotFoundError
413-from testtools.matchers import Contains, Equals, NotEquals, GreaterThan, raises
414-from unittest import skip
415+from testtools.matchers import Equals, raises
416
417 from ubuntu_system_settings.tests import SystemUpdatesBaseTestCase
418 from ubuntu_system_settings.utils.i18n import ugettext as _
419
420-
421 """ Tests for Ubuntu System Settings """
422
423
424@@ -27,15 +22,12 @@
425 # Set environment variables
426 super(SystemUpdatesTestCases, self).setUp()
427
428- def test_show_updates(self):
429- """ Checks whether Search box actually filters the results """
430- updates = self.updates_page
431- self.assertThat(updates, NotEquals(None))
432- # Move to text field
433- self.pointer.move_to_object(updates)
434- self.pointer.click()
435+ def test_page_title(self):
436+ """Check the page title is Updates """
437+ self.assertThat(self.updates_page.title, Equals(_('Updates')))
438
439 def test_updates_not_in_main(self):
440 """Check that the updates notification is shown in main."""
441 self.assertThat(lambda: self.main_view.select_single(
442- objectName='entryComponent-updates'), raises(StateNotFoundError))
443+ objectName='entryComponent-updates'),
444+ raises(StateNotFoundError))

Subscribers

People subscribed via source and target branches