blob: a2c31c176cda9f0c5f64942ed8d9da2b036b7514 [file] [log] [blame]
#!/usr/bin/python
"""
Driver for psq_boot_test. Recieves a command via Swarming, runs boot tests on
the images defined within PSQ_CONFIG_FILE, and returns a JSON representation to
stdout that is parsed via the SWARMING server and sent to ATP.
"""
import unittest
import os
import shutil
import sys
import subprocess
import json
import socket
from datetime import datetime
"""
Variables that are used to set various ENV variables.
This PSQ boot code is adopted from the Buildbot boot_test codebase. As such,
much of the code has a intrinsic assumption that the ENV has been set up
correctly. The variables below cover these cases for this script which is invoked
directly.
"""
ADT_INFRA_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
PATH_DEPENDENCIES = [
os.path.join(ADT_INFRA_PATH),
os.path.join(ADT_INFRA_PATH, 'emu_test'),
os.path.join(ADT_INFRA_PATH, 'emu_test', 'utils')
]
ANDROID_SDK_ROOT = os.path.join(os.path.expanduser('~'), 'Android', 'android-sdk-linux_public')
ANDROID_SDK_PATHS = [
os.path.join(ANDROID_SDK_ROOT, 'tools'),
os.path.join(ANDROID_SDK_ROOT, 'platform-tools'),
os.path.join(ANDROID_SDK_ROOT, 'build_tools', '23.0.2')
]
PSQ_CONFIG_FILE = os.path.join(ADT_INFRA_PATH, 'emu_test', 'config', 'psq_boot_cfg.csv')
def modify_env():
"""
Function to modify the environment as neccessary for this script to run.
Various environment PATHS and VARS must be set to properly run this buildbot
based script. This function is called at first execution and handles this.
"""
for path in PATH_DEPENDENCIES:
sys.path.append(path)
for path in ANDROID_SDK_PATHS:
os.environ['PATH'] += os.pathsep + path
os.environ['ANDROID_SDK_ROOT'] = ANDROID_SDK_ROOT
"""
We must call modify_env() before we finish our imports. The below files are
part of the Buildbot codebase, and have various import dependencies that require
modify_env() to be called before we can successfully import them.
"""
modify_env()
"""
Finish imports now that PYTHONPATH is properly set
"""
import psq_helper
from psq_boot_test import PsqBootTestCase
import emu_test
from emu_test.utils import emu_argparser, emu_unittest
def update_git():
"""
Call git to update the local repository. Note that the executing code will
already be loaded into memory when this occurs, and therefore code updates
will not be RUN until the subsequent PSQ run.
:return: None.
"""
fetch = subprocess.Popen(["git", "fetch"], cwd = ADT_INFRA_PATH)
fetch.communicate()
pull = subprocess.Popen(["git", "pull"], cwd = ADT_INFRA_PATH)
pull.communicate()
def create_test_case():
"""
Create the PsqBootTestCase classes that represent a single unittest. These
tests are created from the information within the PSQ_CONFIG_FILE file.
:return: None.
"""
emu_argparser.emu_args.config_file = PSQ_CONFIG_FILE
emu_test.utils.emu_testcase.create_test_case_from_file("boot", PsqBootTestCase,
PsqBootTestCase.run_boot_test)
def setup_build_environment(ab_buildid, target):
"""
Function to download the emulator zip file from treehuger, and extract the
emulator to the designated location.
:param ab_buildid: Android Build ID number of emulator package.
:param target: SDK target. Usually sdk_linux_tools.
:return: emu_binary. Filepath location of the emulator binary file.
"""
zip_location = psq_helper.downloadArtifact(ab_buildid, target)
emu_binary = psq_helper.unzipArtifacts(zip_location)
return emu_binary
def upload_to_gs():
"""
Uploads the log files generated by this script to GoogleStorage. This
uploaded log file will be included in the report of the run sent back to the
swarming server.
:return: The return code of the script "ADT_INFRA_PATH/emu_test/utils/zip_upload_logs.py
"""
build_source_root = os.path.join(ADT_INFRA_PATH, 'build')
log_util_path = os.path.join(ADT_INFRA_PATH, 'emu_test', 'utils', 'zip_upload_logs.py')
upload_log_args = ['--dir', emu_argparser.emu_args.session_dir,
'--name', '%s.zip' % os.path.basename(emu_argparser.emu_args.session_dir),
'--ip', '', # Unused, but required by script
'--user', '', # Unused, but required by script
'--dst', '', # Unused, but required by script
'--build-dir', build_source_root,
'--skiplog']
zip_and_upload = subprocess.Popen(["/usr/bin/python", log_util_path] +
upload_log_args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = zip_and_upload.communicate()
return zip_and_upload.returncode
def create_json_output(creation_time, test_start_time, test_end_time,
total_tests, failed_tests, session_dir):
"""
Swarming server scans the output of the called script and searches for a JSON
output that is identified via the '#JSON_START#' and '#JSON_END#' flags.
This function creates this json output given the input information.
:param creation_time: Time the PSQ script started running.
:param test_start_time: Time the PSQ script started running PsqBootTestCase's.
:param test_end_time: Time the PSQ script finished running PsqBootTestCase's.
:param total_tests: Total number of PsqBootTestCase's run.
:param failed_tests: Total number of PsqBootTestCase's that failed.
:param session_dir: Directory where logs have been written.
:return: JSON formatted string.
"""
web_base = 'https://pantheon.corp.google.com/storage/browser/emu_psq_logs/'
output = '#JSON_START#'
json_output = {}
if failed_tests > 0:
json_output['status'] = 'FAILED'
else:
json_output['status'] = 'COMPLETED'
json_output['created_on'] = creation_time
json_output['tests_started_on'] = test_start_time
json_output['tests_ended_on'] = test_end_time
json_output['total_test_count'] = total_tests
json_output['failed_test_count'] = failed_tests
json_output['logs_url'] = web_base + '%s' % os.path.basename(session_dir)
output += json.dumps(json_output)
output += '#JSON_END#'
return output
def get_hostname():
"""
Get the hostname of the current machine. If we detect the machine is a dev
machine (".mtv." in name) we 'fake out' the hostname to
androidstudio-swarming-1 to allow testing.
:return: The hostname we will use when reading from the PSQ_CONFIG_FILE.
"""
hostname = socket.gethostname()
if '.mtv.' in hostname:
hostname = 'androidstudio-swarming-1'
return hostname
def set_emu_args(emu_binary):
"""
The main unittest code being used by this PSQ is based upon the existing
Buildbot infrastructure for Emulator Boot Testing. This code has
dependenices upon certain variables within the emu_argparser.emu_args var.
This function overrides/inserts these variables are necc for the PSQ boot test.
:return: None.
"""
emu_argparser.emu_args.builder_name = get_hostname()
emu_argparser.emu_args.emulator_exec = emu_binary
session_dir = os.path.join(os.path.dirname(__file__),
"emu_psq_logs-%s-%s" %
(emu_argparser.emu_args.build_id,
emu_argparser.emu_args.build_target))
emu_argparser.emu_args.session_dir = session_dir
if not os.path.exists(emu_argparser.emu_args.session_dir):
os.makedirs(emu_argparser.emu_args.session_dir)
if __name__ == '__main__':
os.environ["SHELL"] = "/bin/bash"
# Update our local git repo to continually update codebase.
update_git()
emu_argparser.emu_args = emu_argparser.get_parser().parse_args()
emu_argparser.emu_args.creation_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print 'Run Target: %s' % emu_argparser.emu_args.run_target
print 'Branch: %s' % emu_argparser.emu_args.branch
print 'Build Target: %s' % emu_argparser.emu_args.build_target
print 'Build ID: %s' % emu_argparser.emu_args.build_id
emu_dir = setup_build_environment(emu_argparser.emu_args.build_id,
emu_argparser.emu_args.build_target)
emu_binary = os.path.join(emu_dir, "emulator", "emulator")
set_emu_args(emu_binary)
create_test_case()
sys.argv[1:] = emu_argparser.emu_args.unittest_args
test_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
test_suite = unittest.TestLoader().loadTestsFromTestCase(PsqBootTestCase)
test_runner = emu_unittest.EmuTextTestRunner()
test_result = test_runner.run(test_suite)
test_end_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# Upload logs to GCS (gs://emu_psq_logs/)
upload_rc = upload_to_gs()
output = create_json_output(emu_argparser.emu_args.creation_time,
test_start_time, test_end_time,
len(test_result.errors) + len(test_result.passes),
len(test_result.errors),
emu_argparser.emu_args.session_dir)
# Print out the results to stdout. This is what Swarming is looking for.
print output
# Delete the emulator folder artifact
if os.path.exists(emu_dir):
shutil.rmtree(emu_dir)
# Delete the log folder artifact
if os.path.exists(emu_argparser.emu_args.session_dir):
shutil.rmtree(emu_argparser.emu_args.session_dir)
sys.exit(0)