blob: 2859551aa66a5f968646dc6a10813bd93b0bdb3c [file] [log] [blame] [edit]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Integration test to test the basic functionality of dev-install.
This module contains a test that runs some sanity integration tests against
a VM. First it starts a VM test image and turns it into a base image by wiping
all of the stateful partition. Once done, runs dev_install to restore the
stateful partition.
"""
from __future__ import print_function
import argparse
import os
import shutil
import sys
import tempfile
import constants
sys.path.append(constants.SOURCE_ROOT)
sys.path.append(constants.CROS_PLATFORM_ROOT)
from chromite.lib import constants as chromite_constants
from chromite.lib import cros_build_lib
from chromite.lib import cros_logging as logging
from chromite.lib import dev_server_wrapper
from chromite.lib import image_lib
from chromite.lib import osutils
from chromite.lib import remote_access
from chromite.lib import vm
from crostestutils.lib import test_helper
class TestError(Exception):
"""Raised on any error during testing. It being raised is a test failure."""
class DevModeTest(object):
"""Wrapper for dev mode tests."""
# qemu hardcodes 10.0.2.2 as the host from the guest's point of view
# https://wiki.qemu.org/Documentation/Networking#User_Networking_.28SLIRP.29
# When the host's eth0 IP address is also 10.0.2.*, and the guest tries to
# access it, the requests will be handled by qemu and never be seen by the
# host. Instead, let the guest always connect to 10.0.2.2.
HOST_IP_ADDRESS = '10.0.2.2'
def __init__(self, image_path, board, binhost):
"""Initializes DevModeTest.
Args:
image_path: Filesystem path to the image to test.
board: Board of the image under test.
binhost: Binhost override. Binhost as defined here is where dev-install
go to search for binary packages. By default this will
be set to the devserver url of the host running this script.
If no override i.e. the default is ok, set to None.
"""
self.image_path = image_path
self.board = board
self.binhost = binhost
self.tmpdir = tempfile.mkdtemp('DevModeTest')
self.working_image_path = None
self.devserver = None
self.device = None
self.port = None
def Cleanup(self):
"""Cleans up any state at the end of the test."""
try:
if self.devserver:
self.devserver.Stop()
self.devserver = None
if self.device:
self.device.Cleanup()
self.device = None
if self.port:
cros_build_lib.run(['./cros_vm', '--stop', '--ssh-port=%d' % self.port],
cwd=chromite_constants.CHROMITE_BIN_DIR,
check=False)
self.port = None
osutils.RmDir(self.tmpdir, ignore_missing=True)
self.tmpdir = None
except Exception:
logging.exception('Received error during cleanup')
def _WipeDevInstall(self):
"""Wipes the devinstall state."""
logging.info('Wiping /usr/local/bin from the image.')
with image_lib.LoopbackPartitions(
self.working_image_path, destination=self.tmpdir) as image:
image.Mount(('STATE',), mount_opts=())
dev_image_path = os.path.join(self.tmpdir, 'dir-STATE', 'dev_image')
osutils.RmDir(dev_image_path, sudo=True)
def PrepareTest(self):
"""Pre-test modification to the image and env to setup test."""
logging.info('Setting up the image %s for vm testing.', self.image_path)
vm_path = vm.CreateVMImage(image=self.image_path, board=self.board,
updatable=False)
logging.info('Making copy of the vm image %s to manipulate.', vm_path)
self.working_image_path = os.path.join(self.tmpdir,
os.path.basename(vm_path))
shutil.copyfile(vm_path, self.working_image_path)
logging.debug('Copy of vm image stored at %s.', self.working_image_path)
self._WipeDevInstall()
self.port = remote_access.GetUnusedPort()
logging.info('Starting the vm on port %d.', self.port)
vm_cmd = ['./cros_vm', '--ssh-port=%d' % self.port,
'--image-path=%s' % self.working_image_path, '--start']
cros_build_lib.run(vm_cmd, cwd=chromite_constants.CHROMITE_BIN_DIR)
self.device = remote_access.ChromiumOSDevice(
remote_access.LOCALHOST, port=self.port, base_dir=self.tmpdir)
if not self.device.MountRootfsReadWrite():
raise TestError('Failed to make rootfs writeable')
if not self.binhost:
logging.info('Starting the devserver.')
self.devserver = dev_server_wrapper.DevServerWrapper()
self.devserver.Start()
self.binhost = self.devserver.GetDevServerURL(
ip=self.HOST_IP_ADDRESS, port=self.devserver.port,
sub_dir='static/pkgroot/%s/packages' % self.board)
logging.info('Using binhost %s', self.binhost)
def TestDevInstall(self):
"""Tests that we can run dev-install and have python work afterwards."""
try:
logging.info('Running dev install in the vm.')
self.device.run(
['bash', '-l', '-c',
'"/usr/bin/dev_install --yes --binhost=%s"' % self.binhost])
logging.info('Verifying that all python versions work on the image.')
# Symlinks can be tricky, and running python from one place might work
# while it fails from another. Test all the prefixes (and $PATH).
for prefix in ('/usr/bin', '/usr/local/bin', '/usr/local/usr/bin', ''):
for prog in ('python', 'python2', 'python3'):
self.device.run(['sudo', '-u', 'chronos', '--',
os.path.join(prefix, prog),
'-c', '"print(\'hello world\')"'])
except (cros_build_lib.RunCommandError,
remote_access.SSHConnectionError) as e:
self.devserver.PrintLog()
logging.error('dev-install test failed. See devserver log above for more '
'details.')
raise TestError('dev-install test failed with: %s' % str(e))
def Run(self):
try:
self.PrepareTest()
self.TestDevInstall()
logging.info('All tests passed.')
finally:
self.Cleanup()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--binhost', metavar='URL',
help='binhost override. By default, starts up a devserver'
' and uses it as the binhost.')
parser.add_argument('board', nargs=1, help='board to use.')
parser.add_argument('image_path', nargs=1, help='path to test|vm image.')
parser.add_argument('-v', '--verbose', default=False, action='store_true',
help='Print out added debugging information')
options = parser.parse_args()
test_helper.SetupCommonLoggingFormat(verbose=options.verbose)
DevModeTest(os.path.realpath(options.image_path[0]), options.board[0],
options.binhost).Run()
if __name__ == '__main__':
main()