Merge lp:~om26er/ubuntu-system-image/add_upgrade_test into lp:~registry/ubuntu-system-image/client

Proposed by Omer Akram
Status: Needs review
Proposed branch: lp:~om26er/ubuntu-system-image/add_upgrade_test
Merge into: lp:~registry/ubuntu-system-image/client
Diff against target: 475 lines (+436/-0)
7 files modified
systemimage/tests/autopilot/system_upgrade/__init__.py (+6/-0)
systemimage/tests/autopilot/system_upgrade/helpers/__init__.py (+6/-0)
systemimage/tests/autopilot/system_upgrade/helpers/system_image_setup.py (+167/-0)
systemimage/tests/autopilot/system_upgrade/helpers/unlock_screen.py (+55/-0)
systemimage/tests/autopilot/system_upgrade/tests/test_system_updates.py (+46/-0)
systemimage/tests/autopilot/system_upgrade/variables.py (+14/-0)
systemimage/tests/autopilot/upgrade_test_runner (+142/-0)
To merge this branch: bzr merge lp:~om26er/ubuntu-system-image/add_upgrade_test
Reviewer Review Type Date Requested Status
Registry Administrators Pending
Review via email: mp+215028@code.launchpad.net

Commit message

Add a test runner that flashes the touch device and then runs the upgrade test on the Device under test.

Description of the change

Add a test runner that flashes the touch device and then runs the upgrade test on the Device under test.

Needs packaging work so that we have a separate package system-image-autopilot ?

depends on: lp:~om26er/ubuntu-system-settings/upgrade_testing_prerequisite

For Barry regarding packaging, the newly created test package that you create out of these tests should depend on.
  * ubuntu-system-settings-autopilot
  * unity8-autopilot

To post a comment you must log in.
259. By Omer Akram

remove hard-coded package list

Unmerged revisions

259. By Omer Akram

remove hard-coded package list

258. By Omer Akram

add docstrings to elaborate helpers

257. By Omer Akram

update header, arrange import

256. By Omer Akram

minor cleaning, make some methods private, remove unsued variables

255. By Omer Akram

don't try to push tests to the device if the test runner is installed system-wide

254. By Omer Akram

fix pep8

253. By Omer Akram

adapt to helper class changes

252. By Omer Akram

import update helpers from system-settings

251. By Omer Akram

clean code a bit

250. By Omer Akram

bring in upgrade tests.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'systemimage/tests/autopilot'
=== added directory 'systemimage/tests/autopilot/system_upgrade'
=== added file 'systemimage/tests/autopilot/system_upgrade/__init__.py'
--- systemimage/tests/autopilot/system_upgrade/__init__.py 1970-01-01 00:00:00 +0000
+++ systemimage/tests/autopilot/system_upgrade/__init__.py 2014-04-09 20:44:26 +0000
@@ -0,0 +1,6 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2014 Canonical
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
07
=== added directory 'systemimage/tests/autopilot/system_upgrade/helpers'
=== added file 'systemimage/tests/autopilot/system_upgrade/helpers/__init__.py'
--- systemimage/tests/autopilot/system_upgrade/helpers/__init__.py 1970-01-01 00:00:00 +0000
+++ systemimage/tests/autopilot/system_upgrade/helpers/__init__.py 2014-04-09 20:44:26 +0000
@@ -0,0 +1,6 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2014 Canonical
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
07
=== added file 'systemimage/tests/autopilot/system_upgrade/helpers/system_image_setup.py'
--- systemimage/tests/autopilot/system_upgrade/helpers/system_image_setup.py 1970-01-01 00:00:00 +0000
+++ systemimage/tests/autopilot/system_upgrade/helpers/system_image_setup.py 2014-04-09 20:44:26 +0000
@@ -0,0 +1,167 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2014 Canonical
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7
8import logging
9import subprocess
10import time
11
12logger = logging.getLogger(__name__)
13
14
15class DeviceImageFlash(object):
16
17 def setup_device(self, ppa=None, package_list=None, **kwargs):
18 """Sets up the device for upgrade testing
19
20 :param ppa: A launchpad ppa to add to the DUT before
21 apt-get update.
22
23 :param package_list: Names of the packages to be installed
24 on the DUT before running tests.
25
26 :param channel: Ubuntu system image update channel like
27 ubuntu-touch/devel.
28
29 :param revision_number: System image revision number.
30
31 :param bootstrap: clear everything on DUT before flashing
32 the image.
33
34 """
35 self.flash_device(**kwargs)
36 self.wait_for_device()
37 self.set_device_read_write()
38 self.reboot_device()
39 self.disable_intro_screen()
40 self.reboot_device()
41
42 self.setup_network()
43
44 if ppa is not None:
45 self.add_apt_repository(ppa)
46
47 self.apt_get_update()
48
49 if package_list is not None:
50 self.apt_get_install(package_list)
51
52 def str_to_lst(self, string):
53 result = []
54 for char in string.split():
55 result.append(char)
56
57 return result
58
59 def run_command_on_host(self, command, str_to_list=True, **kwargs):
60 logger.info('Running command: {}'.format(command))
61 if str_to_list is True:
62 command = self.str_to_lst(command)
63
64 subprocess.call(command, **kwargs)
65
66 def flash_device(
67 self,
68 channel='ubuntu-touch/trusty-proposed',
69 revision_number=None,
70 bootstrap=False
71 ):
72
73 command = []
74 command.append('phablet-flash')
75 command.append('ubuntu-system')
76 command.append('--serial')
77 command.append(self.dut_serial)
78 command.append('--channel')
79 command.append(channel)
80 if revision_number is not None:
81 command.append('--revision')
82 command.append(revision_number)
83 if bootstrap is True:
84 command.append('--bootstrap')
85
86 self.run_command_on_host(command, str_to_list=False)
87
88 def run_command_on_target(self, command, user=None, shell=False):
89 if user == 'phablet':
90 command = 'adb -s {} shell sudo -iu {} bash -ic "{}"'.format(
91 self.dut_serial, user, command
92 )
93 self.run_command_on_host(command)
94 elif user is None:
95 command = 'adb -s {} shell {}'.format(self.dut_serial, command)
96 self.run_command_on_host(command)
97
98 def wait_for_device(self, timeout=60):
99 """wait for the DUT to restart
100
101 :param timeout: number of seconds to wait for the system
102 to settle.
103 """
104 command = 'adb -s {} wait-for-device'.format(self.dut_serial)
105 self.run_command_on_host(command)
106 logger.info(
107 'Waiting {} seconds for the device to settle before '
108 'proceeding further.'.format(timeout)
109 )
110 time.sleep(timeout)
111 logger.info('Just to be sure')
112 self.run_command_on_host(command)
113
114 def set_device_read_write(self):
115 command = 'touch /userdata/.writable_image'
116 logger.info('Changing system to read/write.')
117 self.run_command_on_target(command)
118
119 def reboot_device(self):
120 wait = 10
121 self.run_command_on_target('reboot')
122 logger.info(
123 'Wating {} seconds to ensure target goes away from adb before '
124 'waiting for it to come back'.format(wait)
125 )
126 time.sleep(wait)
127 self.wait_for_device()
128
129 def disable_intro_screen(self):
130 self.run_command_on_target(
131 'dbus-send --system --print-reply '
132 '--dest=org.freedesktop.Accounts '
133 '/org/freedesktop/Accounts/User32011 '
134 'org.freedesktop.DBus.Properties.Set '
135 'string:com.canonical.unity.AccountsService '
136 'string:demo-edges variant:boolean:false')
137 self.run_command_on_target('touch /.intro_off')
138
139 def setup_network(self):
140 if self.running_in_ci:
141 command = ('adb -s {} shell nmcli dev wifi '
142 'connect ubuntu-qa-g-wpa-d password '
143 'qalabwireless'.format(self.dut_serial))
144 logger.info('Setting up network on target.')
145 self.run_command_on_host(command)
146 else:
147 command = 'phablet-network -s {}'.format(self.dut_serial)
148 logger.warning(
149 'Setting up network on target. you may need to '
150 'enter your password for "{}" to complete.'.format(command)
151 )
152 self.run_command_on_host(command)
153
154 def add_apt_repository(self, ppa):
155 command = 'add-apt-repository -y {}'.format(ppa)
156 logger.info('Adding ppa {} on target.'.format(ppa))
157 self.run_command_on_target(command)
158
159 def apt_get_update(self):
160 command = 'apt-get update'
161 logger.info('Updating apt database.')
162 self.run_command_on_target(command)
163
164 def apt_get_install(self, packages):
165 command = 'apt-get -y -f --force-yes install {}'.format(packages)
166 logger.info('Installing required packages on target.')
167 self.run_command_on_target(command)
0168
=== added file 'systemimage/tests/autopilot/system_upgrade/helpers/unlock_screen.py'
--- systemimage/tests/autopilot/system_upgrade/helpers/unlock_screen.py 1970-01-01 00:00:00 +0000
+++ systemimage/tests/autopilot/system_upgrade/helpers/unlock_screen.py 2014-04-09 20:44:26 +0000
@@ -0,0 +1,55 @@
1#!/usr/bin/env python
2
3import logging
4import sys
5
6from unity8 import process_helpers as helpers
7
8import dbus
9
10logging.basicConfig(level=logging.INFO)
11
12
13class UnlockScreen(object):
14
15 def __init__(self):
16 self.powerd = self._get_powerd_interface()
17
18 def _get_powerd_interface(self):
19 bus = dbus.SystemBus()
20 return bus.get_object(
21 'com.canonical.powerd', '/com/canonical/powerd'
22 )
23
24 def _powerd_request_state_active(self):
25 logging.info('Setting system state "Active"')
26 return self.powerd.requestSysState(
27 'autopilot-lock', 1, dbus_interface='com.canonical.powerd'
28 )
29
30 def _powerd_release_state_active(self):
31 logging.info('Releasing system state "Active"')
32 self.powerd.clearSysState(
33 self.active_cookie, dbus_interface='com.canonical.powerd'
34 )
35
36 def restart_with_testability(self):
37 self.active_cookie = self._powerd_request_state_active()
38 helpers.restart_unity_with_testability()
39 self._powerd_release_state_active()
40
41 def unlock_screen(self):
42 self.restart_with_testability()
43 helpers.unlock_unity()
44
45
46def help():
47 print("Usage:")
48 print("Run the script without any argument to unlock with assertion.")
49
50
51if len(sys.argv) >= 2 and sys.argv[1] == '-h':
52 help()
53else:
54 unlocker = UnlockScreen()
55 unlocker.unlock_screen()
056
=== added directory 'systemimage/tests/autopilot/system_upgrade/tests'
=== added file 'systemimage/tests/autopilot/system_upgrade/tests/__init__.py'
=== added file 'systemimage/tests/autopilot/system_upgrade/tests/test_system_updates.py'
--- systemimage/tests/autopilot/system_upgrade/tests/test_system_updates.py 1970-01-01 00:00:00 +0000
+++ systemimage/tests/autopilot/system_upgrade/tests/test_system_updates.py 2014-04-09 20:44:26 +0000
@@ -0,0 +1,46 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2014 Canonical
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7
8import logging
9
10from autopilot.matchers import Eventually
11from testtools.matchers import Equals
12
13from ubuntu_system_settings.emulators import UpdatesPage
14from ubuntu_system_settings.tests import SystemUpdatesBaseTestCase
15
16logging.basicConfig(level=logging.INFO)
17
18
19class SystemUpdatesTestCases(SystemUpdatesBaseTestCase):
20
21 """Tests for System Updates."""
22
23 def setUp(self):
24 self.patch_environment('IGNORE_CREDENTIALS', 'True')
25 super(SystemUpdatesTestCases, self).setUp()
26
27 @property
28 def updates_page(self):
29 return UpdatesPage(self.app, self.pointer)
30
31 def test_image_update_from_ui_works(self):
32 """Install latest system update available."""
33 self.updates_page.wait_for_state('UPDATE')
34 logging.info('Waiting for update to download.')
35 self.updates_page.wait_for_updates_to_download()
36 logging.info('finished waiting for update')
37 self.updates_page.click_install_and_restart_button()
38 installing_screen = self.updates_page.get_installing_update_screen()
39 self.assertThat(
40 installing_screen.visible, Eventually(Equals(True))
41 )
42
43 def test_state_noupdates(self):
44 """Check if system is fully updated."""
45 state = self.updates_page.wait_for_state('NOUPDATES')
46 self.assertThat(state, Equals('NOUPDATES'))
047
=== added file 'systemimage/tests/autopilot/system_upgrade/variables.py'
--- systemimage/tests/autopilot/system_upgrade/variables.py 1970-01-01 00:00:00 +0000
+++ systemimage/tests/autopilot/system_upgrade/variables.py 2014-04-09 20:44:26 +0000
@@ -0,0 +1,14 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2014 Canonical
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7
8
9TEST1_PART1 = 'system_upgrade.tests.test_system_updates.SystemUpdatesTestCases\
10.test_image_update_from_ui_works'
11
12
13TEST1_PART2 = 'system_upgrade.tests.test_system_updates.SystemUpdatesTestCases\
14.test_state_noupdates'
015
=== added file 'systemimage/tests/autopilot/upgrade_test_runner'
--- systemimage/tests/autopilot/upgrade_test_runner 1970-01-01 00:00:00 +0000
+++ systemimage/tests/autopilot/upgrade_test_runner 2014-04-09 20:44:26 +0000
@@ -0,0 +1,142 @@
1#!/usr/bin/python3
2
3import argparse
4import logging
5import os
6import subprocess
7import sys
8import tempfile
9import time
10import unittest
11
12from system_upgrade.helpers.system_image_setup import DeviceImageFlash
13from system_upgrade.variables import TEST1_PART1, TEST1_PART2
14
15
16class SystemImageUpgrader(unittest.TestCase, DeviceImageFlash):
17
18 source = 'system_upgrade'
19 test_suite = 'system_upgrade'
20 device_test_path = '/home/phablet/{}'.format(test_suite)
21 unlocker = 'unlock_screen.py'
22 unlocker_location = 'system_upgrade/helpers/' + unlocker
23
24 def setUp(self):
25 self.dut_serial = arguments.serial
26 self.running_in_ci = arguments.ci
27 self.setup_device(
28 ppa=arguments.ppa,
29 package_list=arguments.packages,
30 bootstrap=True,
31 revision_number='-1'
32 )
33
34 def test_system_update(self):
35 if self.installed_system_wide():
36 self.run_tests(test_suite=TEST1_PART1)
37 else:
38 self.push_and_run_tests(test_suite=TEST1_PART1)
39 log_file = self.open_log_file()
40 self.assertTrue(log_file.endswith('OK\n'))
41
42 self.wait_for_device(timeout=150)
43
44 self.run_tests(test_suite=TEST1_PART2)
45 log_file = self.open_log_file()
46 self.assertTrue(log_file.endswith('OK\n'))
47
48 def open_log_file(self, retries=10):
49 for retry in range(retries):
50 if os.path.exists(self.result_log):
51 break
52 time.sleep(1)
53 retry = retry - 1
54 else:
55 raise IOError('file {} not found'.format(self.result_log))
56
57 log_file = open(self.result_log, 'r')
58 self.addCleanup(log_file.close)
59 return log_file.read()
60
61 def push_and_run_tests(self, test_suite):
62 self._push_files_to_device(self.source, self.device_test_path)
63 self.run_tests(test_suite)
64
65 def run_tests(self, test_suite):
66 self._unlock_device_screen()
67 self._run_autopilot_test_on_target(test_suite)
68
69 def _push_files_to_device(self, source, location):
70 self.addCleanup(self._delete_autopilot_test)
71 command = 'adb -s {} push {} {}'.format(
72 self.dut_serial, source, location
73 )
74
75 logger.info('Pushing files to target with command:')
76 logger.info(command)
77 self.run_command_on_host(command)
78
79 def _delete_autopilot_test(self):
80 command = 'rm -rf {}'.format(self.device_test_path)
81 self.run_command_on_target(command)
82
83 def _unlock_device_screen(self):
84 command = 'python3 -m system_upgrade.helpers.unlock_screen'
85 self.run_command_on_target(command, user='phablet')
86
87 def _run_autopilot_test_on_target(self, test_suite, user='phablet'):
88 self.result_log = tempfile.mkstemp()[1]
89 autopilot_log_target = '/tmp/upgrade.log'
90 test_command = \
91 "autopilot run -f text -o {} -v {}".format(
92 autopilot_log_target, test_suite)
93 command = 'adb -s {} shell sudo -iu {} bash -ic "{}"'.format(
94 self.dut_serial, user, test_command)
95
96 subprocess.call(command, shell=True)
97
98 self.run_command_on_host('adb -s {} pull {} {}'.format(
99 self.dut_serial, autopilot_log_target, self.result_log)
100 )
101 self.addCleanup(os.remove, self.result_log)
102
103 def installed_system_wide(self):
104 """Returns true if the test runner is installed in /usr
105 or any of its subdirectories.
106 """
107 return os.path.abspath('.').startswith('/usr')
108
109
110def _parse_command_line_arguments():
111 parser = argparse.ArgumentParser(
112 description='Ubuntu system update test runner'
113 )
114 parser.add_argument(
115 'serial', metavar='SERIAL_NUMBER', type=str,
116 help='Serial numer of the device to run tests on.'
117 )
118 parser.add_argument(
119 '--ppa', type=str,
120 help='Launchpad ppa to add to the device before installing '
121 'test packages. example: ppa:test/ppa'
122 )
123 parser.add_argument(
124 '--packages', type=str,
125 help='Names of extra packages to be installed on the test device '
126 'before running the tests.'
127 )
128 parser.add_argument(
129 '--ci', metavar='BOOLEAN', default=False,
130 help='Specify whether the test suite is running in the Canonical '
131 'CI lab so that the network connection could be established '
132 'with the appropriate Wi-Fi network.'
133 )
134
135 return parser.parse_args()
136
137
138if __name__ == '__main__':
139 arguments = _parse_command_line_arguments()
140 logging.basicConfig(level=logging.INFO)
141 logger = logging.getLogger(__name__)
142 unittest.main(argv=[sys.argv[0]])

Subscribers

People subscribed via source and target branches