blob: f9b1e44bb60377f674aeb8c3cb1bc882c05892dc [file] [log] [blame]
Chris Sosada9632e2013-03-04 12:28:06 -08001#!/usr/bin/python
2#
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Integration test to test the basic functionality of dev-install and gmerge.
8
9This module contains a test that runs some sanity integration tests against
10a VM. First it starts a VM test image and turns it into a base image by wiping
11all of the stateful partition. Once done, runs dev_install to restore the
12stateful partition and then runs gmerge.
13"""
14
15import logging
16import optparse
17import os
18import shutil
Chris Sosada9632e2013-03-04 12:28:06 -080019import sys
20import tempfile
21
22import constants
23sys.path.append(constants.SOURCE_ROOT)
24sys.path.append(constants.CROS_PLATFORM_ROOT)
25
26from chromite.lib import cros_build_lib
Yu-Ju Hong29111982013-12-20 15:04:41 -080027from chromite.lib import dev_server_wrapper
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080028from chromite.lib import osutils
Chris Sosa928085e2013-03-08 17:25:30 -080029from chromite.lib import remote_access
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080030from chromite.lib import vm
31
Chris Sosada9632e2013-03-04 12:28:06 -080032from crostestutils.lib import mount_helper
33from crostestutils.lib import test_helper
34
35
Chris Sosada9632e2013-03-04 12:28:06 -080036class TestError(Exception):
37 """Raised on any error during testing. It being raised is a test failure."""
38
39
40class DevModeTest(object):
41 """Wrapper for dev mode tests."""
42 def __init__(self, image_path, board, binhost):
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080043 """Initializes DevModeTest.
44
Chris Sosada9632e2013-03-04 12:28:06 -080045 Args:
46 image_path: Filesystem path to the image to test.
47 board: Board of the image under test.
48 binhost: Binhost override. Binhost as defined here is where dev-install
49 or gmerge go to search for binary packages. By default this will
50 be set to the devserver url of the host running this script.
51 If no override i.e. the default is ok, set to None.
52 """
53 self.image_path = image_path
54 self.board = board
55 self.binhost = binhost
Chris Sosada9632e2013-03-04 12:28:06 -080056 self.tmpdir = tempfile.mkdtemp('DevModeTest')
Chris Sosada9632e2013-03-04 12:28:06 -080057 self.working_image_path = None
58 self.devserver = None
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080059 self.vm = None
60 self.device = None
Chris Sosada9632e2013-03-04 12:28:06 -080061
62 def Cleanup(self):
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080063 """Cleans up any state at the end of the test."""
Chris Sosada9632e2013-03-04 12:28:06 -080064 try:
Chris Sosada9632e2013-03-04 12:28:06 -080065 if self.devserver:
66 self.devserver.Stop()
67
68 self.devserver = None
Yu-Ju Hong640d4d02014-02-20 15:52:11 -080069 if self.device:
70 self.device.Cleanup()
71
72 self.device = None
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080073 self.vm.Stop()
74 self.vm = None
75 osutils.RmDir(self.tmpdir, ignore_missing=True)
Chris Sosada9632e2013-03-04 12:28:06 -080076 self.tmpdir = None
77 except Exception:
78 logging.warning('Received error during cleanup', exc_info=True)
79
Chris Sosab8c2af52013-07-03 10:45:39 -070080 def _WipeDevInstall(self):
81 """Wipes the devinstall state."""
Chris Sosada9632e2013-03-04 12:28:06 -080082 r_mount_point = os.path.join(self.tmpdir, 'm')
83 s_mount_point = os.path.join(self.tmpdir, 's')
Chris Sosab8c2af52013-07-03 10:45:39 -070084 dev_image_path = os.path.join(s_mount_point, 'dev_image')
Chris Sosada9632e2013-03-04 12:28:06 -080085 mount_helper.MountImage(self.working_image_path,
86 r_mount_point, s_mount_point, read_only=False,
87 safe=True)
Chris Sosab8c2af52013-07-03 10:45:39 -070088 try:
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080089 osutils.RmDir(dev_image_path, sudo=True)
Chris Sosab8c2af52013-07-03 10:45:39 -070090 finally:
91 mount_helper.UnmountImage(r_mount_point, s_mount_point)
Chris Sosada9632e2013-03-04 12:28:06 -080092
Chris Sosada9632e2013-03-04 12:28:06 -080093 def PrepareTest(self):
94 """Pre-test modification to the image and env to setup test."""
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080095 logging.info('Setting up the image %s for vm testing.', self.image_path)
96 vm_path = vm.CreateVMImage(image=self.image_path, board=self.board,
Yu-Ju Hongb933bfc2014-02-19 11:15:34 -080097 updatable=False)
Chris Sosada9632e2013-03-04 12:28:06 -080098
99 logging.info('Making copy of the vm image %s to manipulate.', vm_path)
David James45b55dd2013-04-24 09:16:40 -0700100 self.working_image_path = os.path.join(self.tmpdir,
101 os.path.basename(vm_path))
Chris Sosada9632e2013-03-04 12:28:06 -0800102 shutil.copyfile(vm_path, self.working_image_path)
103 logging.debug('Copy of vm image stored at %s.', self.working_image_path)
104
Chris Sosab8c2af52013-07-03 10:45:39 -0700105 logging.info('Wiping /usr/local/bin from the image.')
106 self._WipeDevInstall()
Chris Sosada9632e2013-03-04 12:28:06 -0800107
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -0800108 self.vm = vm.VMInstance(self.working_image_path, tempdir=self.tmpdir)
109 logging.info('Starting the vm on port %d.', self.vm.port)
110 self.vm.Start()
111
112 self.device = remote_access.ChromiumOSDevice(
Yu-Ju Hong640d4d02014-02-20 15:52:11 -0800113 remote_access.LOCALHOST, port=self.vm.port, base_dir=self.tmpdir)
Chris Sosa068c1e92013-03-17 22:54:20 -0700114
Chris Sosada9632e2013-03-04 12:28:06 -0800115 if not self.binhost:
116 logging.info('Starting the devserver.')
Chris Sosaa404a382013-08-22 11:28:38 -0700117 self.devserver = dev_server_wrapper.DevServerWrapper()
118 self.devserver.Start()
Yu-Ju Hong0dbc2b92014-07-14 17:19:17 -0700119 self.binhost = self.devserver.GetURL(
Chris Sosac9447962013-03-12 10:12:29 -0700120 sub_dir='static/pkgroot/%s/packages' % self.board)
Chris Sosada9632e2013-03-04 12:28:06 -0800121
122 logging.info('Using binhost %s', self.binhost)
123
124 def TestDevInstall(self):
125 """Tests that we can run dev-install and have python work afterwards."""
126 try:
127 logging.info('Running dev install in the vm.')
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -0800128 self.device.RunCommand(
Chris Sosada9632e2013-03-04 12:28:06 -0800129 ['bash', '-l', '-c',
130 '"/usr/bin/dev_install --yes --binhost %s"' % self.binhost])
131
132 logging.info('Verifying that python works on the image.')
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -0800133 self.device.RunCommand(['sudo', '-u', 'chronos', '--', 'python', '-c',
134 '"print \'hello world\'"'])
Yu-Ju Hong5ed02452014-01-30 09:05:00 -0800135 except (cros_build_lib.RunCommandError,
136 remote_access.SSHConnectionError) as e:
Chris Sosada9632e2013-03-04 12:28:06 -0800137 self.devserver.PrintLog()
138 logging.error('dev-install test failed. See devserver log above for more '
139 'details.')
140 raise TestError('dev-install test failed with: %s' % str(e))
141
Chris Sosada9632e2013-03-04 12:28:06 -0800142 def TestGmerge(self):
143 """Evaluates whether the test passed or failed."""
Chris Sosa928085e2013-03-08 17:25:30 -0800144 logging.info('Testing that gmerge works on the image after dev install.')
Chris Sosada9632e2013-03-04 12:28:06 -0800145 try:
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -0800146 self.device.RunCommand(
Chris Sosac9447962013-03-12 10:12:29 -0700147 ['gmerge', 'gmerge', '--accept_stable', '--usepkg',
Yu-Ju Hong0dbc2b92014-07-14 17:19:17 -0700148 '--devserver_url', self.devserver.GetURL(),
Chris Sosac9447962013-03-12 10:12:29 -0700149 '--board', self.board])
Yu-Ju Hong5ed02452014-01-30 09:05:00 -0800150 except (cros_build_lib.RunCommandError,
151 remote_access.SSHConnectionError) as e:
Chris Sosada9632e2013-03-04 12:28:06 -0800152 logging.error('gmerge test failed. See log for details')
153 raise TestError('gmerge test failed with: %s' % str(e))
154
155
156def main():
157 usage = ('%s <board> <path_to_[test|vm]_image>. '
158 'See --help for more options' % os.path.basename(sys.argv[0]))
159 parser = optparse.OptionParser(usage)
160 parser.add_option('--binhost', metavar='URL',
161 help='binhost override. By default, starts up a devserver '
162 'and uses it as the binhost.')
163 parser.add_option('-v', '--verbose', default=False, action='store_true',
164 help='Print out added debugging information')
165
166 (options, args) = parser.parse_args()
167
168 if len(args) != 2:
169 parser.print_usage()
170 parser.error('Need board and path to test image.')
171
172 board = args[0]
173 image_path = os.path.realpath(args[1])
174
175 test_helper.SetupCommonLoggingFormat(verbose=options.verbose)
176
177 test = DevModeTest(image_path, board, options.binhost)
178 try:
179 test.PrepareTest()
180 test.TestDevInstall()
Chris Sosa928085e2013-03-08 17:25:30 -0800181 test.TestGmerge()
Chris Sosada9632e2013-03-04 12:28:06 -0800182 logging.info('All tests passed.')
183 finally:
184 test.Cleanup()
185
186
187if __name__ == '__main__':
188 main()