blob: ded2b0a33ecab5220898a6d49e40ed69f7cadf62 [file] [log] [blame]
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 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.
"""Wrapper for tests that are run on builders."""
from __future__ import print_function
import argparse
import os
import sys
import constants
sys.path.append(constants.SOURCE_ROOT)
sys.path.append(constants.CROS_PLATFORM_ROOT)
from chromite.lib import cros_build_lib
from chromite.lib import cros_logging as logging
from crostestutils.lib import image_extractor
from crostestutils.lib import test_helper
class TestException(Exception):
"""Thrown by RunAUTestHarness if there's a test failure."""
class CTest(object):
"""Main class with methods to generate payloads and test them.
Variables:
base: Base image to test from.
board: the board for the latest image.
archive_dir: Location where images for past versions are archived.
crosutils_root: Location of crosutils.
jobs: Numbers of threads to run in parallel.
no_graphics: boolean: If True, disable graphics during vm test.
remote: ip address for real test harness run.
target: Target image to test.
test_results_root: Root directory to store au_test_harness results.
type: which test harness to run. Possible values: real, vm, gce.
whitelist_chrome_crashes: Whether to treat Chrome crashes as non-fatal.
"""
def __init__(self, opts):
"""Initializes the test object.
Args:
opts: Parsed args for module.
"""
self.base = None
self.board = opts.board
self.archive_dir = opts.archive_dir
self.crosutils_root = os.path.join(constants.SOURCE_ROOT, 'src', 'scripts')
self.no_graphics = opts.no_graphics
self.remote = opts.remote
self.target = opts.target_image
self.test_results_root = opts.test_results_root
self.type = opts.type
self.whitelist_chrome_crashes = opts.whitelist_chrome_crashes
self.jobs = opts.jobs
# An optional ssh private key used for testing.
self.ssh_private_key = opts.ssh_private_key
self.ssh_port = opts.ssh_port
def FindTargetAndBaseImages(self):
"""Initializes the target and base images for CTest."""
if not self.target:
# Grab the latest image we've built.
board_images = os.path.join(constants.SOURCE_ROOT, 'src/build/images',
self.board)
latest = os.path.join(board_images, 'latest')
version_dir = os.readlink(latest)
latest_image_dir = os.path.join(board_images, version_dir)
self.target = os.path.join(
latest_image_dir, image_extractor.ImageExtractor.IMAGE_TO_EXTRACT)
# Grab the latest official build for this board to use as the base image.
if self.archive_dir:
target_version = os.path.realpath(self.target).rsplit('/', 2)[-2]
extractor = image_extractor.ImageExtractor(self.archive_dir)
latest_image_dir = extractor.GetLatestImage(target_version)
if latest_image_dir:
self.base = extractor.UnzipImage(latest_image_dir)
if not self.base:
logging.info('Could not find a latest image to use. '
'Using target instead.')
self.base = self.target
def GenerateUpdatePayloads(self, full):
"""Generates payloads for the test harness.
Args:
full: Build payloads for full test suite.
"""
generator = ('../platform/crostestutils/'
'generate_test_payloads/cros_generate_test_payloads.py')
cmd = [generator]
cmd.append('--target=%s' % self.target)
cmd.append('--base=%s' % self.base)
cmd.append('--board=%s' % self.board)
cmd.append('--jobs=%d' % self.jobs)
if full:
cmd.append('--full_suite')
else:
cmd.append('--basic_suite')
if self.type != 'vm':
cmd.append('--novm')
try:
cros_build_lib.RunCommand(cmd, cwd=self.crosutils_root)
except cros_build_lib.RunCommandError:
logging.error('We failed to generate all the update payloads required '
'for testing. Please see the logs for more info. We print '
'out the log from a failing call to '
'cros_generate_update_payload for error handling.')
sys.exit(1)
def RunAUTestHarness(self, only_verify, quick_update, suite):
"""Runs the auto update test harness.
The auto update test harness encapsulates testing the auto-update mechanism
for the latest image against the latest official image from the channel.
This also tests images with suite:smoke (built-in as part of its
verification process).
Args:
only_verify: Only verify the target image.
quick_update: Do a quick update test.
suite: The suite of tests to run.
Raises:
TestException: If the cros_au_test_harness command returns an error code.
"""
path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
cmd = [os.path.join(path, 'au_test_harness', 'cros_au_test_harness.py'),
'--base_image=%s' % self.base,
'--target_image=%s' % self.target,
'--board=%s' % self.board,
'--type=%s' % self.type,
'--remote=%s' % self.remote,
'--verbose',
'--jobs=%d' % self.jobs,
]
if self.ssh_private_key is not None:
cmd.append('--ssh_private_key=%s' % self.ssh_private_key)
if self.ssh_port is not None:
cmd.append('--ssh_port=%s' % self.ssh_port)
if suite:
cmd.append('--verify_suite_name=%s' % suite)
if only_verify:
cmd.append('--test_prefix=SimpleTestVerify')
elif quick_update:
cmd.append('--test_prefix=SimpleTestUpdateAndVerify')
if self.test_results_root:
cmd.append('--test_results_root=%s' % self.test_results_root)
if self.no_graphics:
cmd.append('--no_graphics')
if self.whitelist_chrome_crashes:
cmd.append('--whitelist_chrome_crashes')
# Give tests 10 minutes to clean up before shut down.
res = cros_build_lib.RunCommand(cmd, cwd=self.crosutils_root,
error_code_ok=True, kill_timeout=10 * 60)
if res.returncode != 0:
raise TestException('%s exited with code %d: %s' % (' '.join(res.cmd),
res.returncode,
res.error))
def main():
test_helper.SetupCommonLoggingFormat()
parser = argparse.ArgumentParser()
parser.add_argument('-b', '--board',
help='board for the image to compare against.')
parser.add_argument('--archive_dir',
help='Directory containing previously archived images.')
parser.add_argument('--cache', default=False, action='store_true',
help='Cache payloads')
parser.add_argument('--jobs', default=test_helper.CalculateDefaultJobs(),
type=int,
help='Number of threads to run in parallel.')
parser.add_argument('--no_graphics', action='store_true', default=False,
help='Disable graphics for the vm test.')
parser.add_argument('--only_verify', action='store_true', default=False,
help='Only run basic verification suite.')
parser.add_argument('--quick_update', action='store_true',
help='Run a quick update test. This will run a subset of '
'test suite after running autoupdate from target '
'image to itself.')
parser.add_argument('--remote', default='0.0.0.0',
help='For real tests, ip address of the target machine.')
parser.add_argument('--target_image', default=None,
help='Target image to test.')
parser.add_argument('--suite', default=None, help='Test suite to run.')
parser.add_argument('--test_results_root', default=None,
help='Root directory to store test results. Should '
'be defined relative to chroot root.')
parser.add_argument('--type', default='vm',
help='type of test to run: [vm, real, gce]. Default: vm.')
parser.add_argument('--verbose', default=False, action='store_true',
help='Print out added debugging information')
parser.add_argument('--whitelist_chrome_crashes', default=False,
dest='whitelist_chrome_crashes', action='store_true',
help='Treat Chrome crashes as non-fatal.')
parser.add_argument('--ssh_private_key', default=None,
help='Path to the private key to use to ssh into the '
'image as the root user')
parser.add_argument('--ssh_port', default=None, type=int,
help='ssh port used to ssh into image. (Should only be'
' used with either --quick_update or --only_verify)')
opts = parser.parse_args()
if not opts.board:
parser.error('Need board for image to compare against.')
if opts.only_verify and opts.quick_update:
parser.error(
'Only one of --only_verify or --quick_update should be specified.')
if opts.ssh_port and not (opts.only_verify or opts.quick_update):
parser.error(
'ssh_port should be specified with either --only_verify or '
'--quick_update')
# force absolute path for these opts, since a chdir occurs deeper in the
# codebase.
for x in ('target_image', 'test_results_root'):
if x == 'target_image' and opts.type == 'gce':
# In this case |target_image| is a Google Storage path.
continue
val = getattr(opts, x)
if val is not None:
setattr(opts, x, os.path.abspath(val))
ctest = CTest(opts)
ctest.FindTargetAndBaseImages()
if not opts.only_verify:
ctest.GenerateUpdatePayloads(not opts.quick_update)
try:
ctest.RunAUTestHarness(opts.only_verify, opts.quick_update,
opts.suite)
except TestException as e:
if opts.verbose:
cros_build_lib.Die(str(e))
sys.exit(1)
if __name__ == '__main__':
main()