run_test.py (6990B)
1 #!/usr/bin/env vpython3 2 # Copyright 2022 The Chromium Authors 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 """Implements commands for running tests E2E on a Fuchsia device.""" 6 7 import argparse 8 import logging 9 import os 10 import sys 11 import tempfile 12 13 from contextlib import ExitStack 14 from typing import List 15 16 import monitors 17 18 from common import has_ffx_isolate_dir, is_daemon_running, \ 19 register_common_args, register_device_args, \ 20 register_log_args, resolve_packages 21 from compatible_utils import running_unattended 22 from ffx_integration import ScopedFfxConfig 23 from flash_device import register_update_args, update 24 from isolate_daemon import IsolateDaemon 25 from log_manager import LogManager, start_system_log 26 from publish_package import publish_packages, register_package_args 27 from run_blink_test import BlinkTestRunner 28 from run_executable_test import create_executable_test_runner, \ 29 register_executable_test_args 30 from run_telemetry_test import TelemetryTestRunner 31 from run_webpage_test import WebpageTestRunner 32 from serve_repo import register_serve_args, serve_repository 33 from start_emulator import create_emulator_from_args, register_emulator_args 34 from test_connection import test_connection, test_device_connection 35 from test_runner import TestRunner 36 37 38 def _get_test_runner(runner_args: argparse.Namespace, 39 test_args: List[str]) -> TestRunner: 40 """Initialize a suitable TestRunner class.""" 41 42 if not runner_args.out_dir: 43 raise ValueError('--out-dir must be specified.') 44 45 if runner_args.test_type == 'blink': 46 return BlinkTestRunner(runner_args.out_dir, test_args, 47 runner_args.target_id) 48 if runner_args.test_type in ['gpu', 'perf']: 49 return TelemetryTestRunner(runner_args.test_type, runner_args.out_dir, 50 test_args, runner_args.target_id) 51 if runner_args.test_type == 'webpage': 52 return WebpageTestRunner(runner_args.out_dir, test_args, 53 runner_args.target_id, runner_args.logs_dir) 54 return create_executable_test_runner(runner_args, test_args) 55 56 57 # pylint: disable=too-many-statements 58 def main(): 59 """E2E method for installing packages and running a test.""" 60 # Always add time stamps to the logs. 61 logging.basicConfig(format='%(levelname)s %(asctime)s %(message)s') 62 63 parser = argparse.ArgumentParser() 64 parser.add_argument( 65 'test_type', 66 help='The type of test to run. Options include \'blink\', \'gpu\', ' 67 'or in the case of executable tests, the test name.') 68 parser.add_argument('--device', 69 '-d', 70 action='store_true', 71 default=False, 72 help='Use an existing device.') 73 74 # Register arguments 75 register_common_args(parser) 76 register_device_args(parser) 77 register_emulator_args(parser) 78 register_executable_test_args(parser) 79 register_update_args(parser, default_os_check='ignore') 80 register_log_args(parser) 81 register_package_args(parser, allow_temp_repo=True) 82 register_serve_args(parser) 83 84 # Treat unrecognized arguments as test specific arguments. 85 runner_args, test_args = parser.parse_known_args() 86 87 if runner_args.target_id: 88 runner_args.device = True 89 90 with ExitStack() as stack: 91 if runner_args.logs_dir: 92 # TODO(crbug.com/343242386): Find a way to upload metric output when 93 # logs_dir is not defined. 94 stack.push(lambda *_: monitors.dump( 95 os.path.join(runner_args.logs_dir, 'invocations'))) 96 if running_unattended(): 97 # Only restart the daemon if 1) daemon will be run in a new isolate 98 # dir, or 2) if there isn't a daemon running in the predefined 99 # isolate dir. 100 if not has_ffx_isolate_dir() or not is_daemon_running(): 101 stack.enter_context(IsolateDaemon(runner_args.logs_dir)) 102 103 if runner_args.everlasting: 104 # Setting the emu.instance_dir to match the named cache, so 105 # we can keep these files across multiple runs. 106 # The configuration attaches to the daemon isolate-dir, so it 107 # needs to go after the IsolateDaemon. 108 # There isn't a point of enabling the feature on devbox, it 109 # won't use isolate-dir and the emu.instance_dir always goes to 110 # the HOME directory. 111 stack.enter_context( 112 ScopedFfxConfig( 113 'emu.instance_dir', 114 os.path.join(os.environ['HOME'], 115 '.fuchsia_emulator/'))) 116 elif runner_args.logs_dir: 117 # Never restart daemon if not in the unattended mode. 118 logging.warning('You are using a --logs-dir, ensure the ffx ' 119 'daemon is started with the logs.dir config ' 120 'updated. We won\'t restart the daemon randomly' 121 ' anymore.') 122 log_manager = LogManager(runner_args.logs_dir) 123 stack.enter_context(log_manager) 124 125 if runner_args.device: 126 update(runner_args.system_image_dir, runner_args.os_check, 127 runner_args.target_id, runner_args.serial_num) 128 # Try to reboot the device if necessary since the ffx may ignore the 129 # device state after the flash. See 130 # https://cs.opensource.google/fuchsia/fuchsia/+/main:src/developer/ffx/lib/fastboot/src/common/fastboot.rs;drc=cfba0bdd4f8857adb6409f8ae9e35af52c0da93e;l=454 131 test_device_connection(runner_args.target_id) 132 else: 133 runner_args.target_id = stack.enter_context( 134 create_emulator_from_args(runner_args)) 135 test_connection(runner_args.target_id) 136 137 test_runner = _get_test_runner(runner_args, test_args) 138 package_deps = test_runner.package_deps 139 140 # Start system logging, after all possible restarts of the ffx daemon 141 # so that logging will not be interrupted. 142 start_system_log(log_manager, False, package_deps.values(), 143 ('--since', 'now'), runner_args.target_id) 144 145 if package_deps: 146 if not runner_args.repo: 147 # Create a directory that serves as a temporary repository. 148 runner_args.repo = stack.enter_context( 149 tempfile.TemporaryDirectory()) 150 publish_packages(package_deps.values(), runner_args.repo, 151 not runner_args.no_repo_init) 152 stack.enter_context(serve_repository(runner_args)) 153 resolve_packages(package_deps.keys(), runner_args.target_id) 154 155 return test_runner.run_test().returncode 156 157 158 if __name__ == '__main__': 159 sys.exit(main())