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 7import argparse 8import logging 9import os 10import sys 11import tempfile 12 13from contextlib import ExitStack 14from typing import List 15 16from common import register_common_args, register_device_args, \ 17 register_log_args, resolve_packages, set_ffx_isolate_dir, \ 18 start_ffx_daemon, stop_ffx_daemon 19from compatible_utils import running_unattended 20from ffx_integration import ScopedFfxConfig, test_connection 21from flash_device import register_update_args, update 22from log_manager import LogManager, start_system_log 23from publish_package import publish_packages, register_package_args 24from run_blink_test import BlinkTestRunner 25from run_executable_test import create_executable_test_runner, \ 26 register_executable_test_args 27from run_telemetry_test import TelemetryTestRunner 28from run_webpage_test import WebpageTestRunner 29from serve_repo import register_serve_args, serve_repository 30from start_emulator import create_emulator_from_args, register_emulator_args 31from test_runner import TestRunner 32from ermine_ctl import ErmineCtl 33 34 35def _get_test_runner(runner_args: argparse.Namespace, 36 test_args: List[str]) -> TestRunner: 37 """Initialize a suitable TestRunner class.""" 38 39 if runner_args.test_type == 'blink': 40 return BlinkTestRunner(runner_args.out_dir, test_args, 41 runner_args.target_id) 42 if runner_args.test_type in ['gpu', 'perf']: 43 return TelemetryTestRunner(runner_args.test_type, runner_args.out_dir, 44 test_args, runner_args.target_id) 45 if runner_args.test_type in ['webpage']: 46 return WebpageTestRunner(runner_args.out_dir, test_args, 47 runner_args.target_id) 48 return create_executable_test_runner(runner_args, test_args) 49 50 51# pylint: disable=too-many-statements 52def main(): 53 """E2E method for installing packages and running a test.""" 54 # Always add time stamps to the logs. 55 logging.basicConfig(format='%(levelname)s %(asctime)s %(message)s') 56 57 parser = argparse.ArgumentParser() 58 parser.add_argument( 59 'test_type', 60 help='The type of test to run. Options include \'blink\', \'gpu\', ' 61 'or in the case of executable tests, the test name.') 62 parser.add_argument('--device', 63 '-d', 64 action='store_true', 65 default=False, 66 help='Use an existing device.') 67 parser.add_argument('--extra-path', 68 action='append', 69 help='Extra paths to append to the PATH environment') 70 71 # Register arguments 72 register_common_args(parser) 73 register_device_args(parser) 74 register_emulator_args(parser) 75 register_executable_test_args(parser) 76 register_update_args(parser, default_os_check='ignore', default_pave=False) 77 register_log_args(parser) 78 register_package_args(parser, allow_temp_repo=True) 79 register_serve_args(parser) 80 81 # Treat unrecognized arguments as test specific arguments. 82 runner_args, test_args = parser.parse_known_args() 83 84 if not runner_args.out_dir: 85 raise ValueError('--out-dir must be specified.') 86 87 if runner_args.target_id: 88 runner_args.device = True 89 90 with ExitStack() as stack: 91 if running_unattended(): 92 # Updating configurations to meet the requirement of isolate. 93 os.environ['FUCHSIA_ANALYTICS_DISABLED'] = '1' 94 stop_ffx_daemon() 95 if runner_args.extra_path: 96 os.environ['PATH'] += os.pathsep + os.pathsep.join( 97 runner_args.extra_path) 98 set_ffx_isolate_dir( 99 stack.enter_context(tempfile.TemporaryDirectory())) 100 # The following configurations are persistent, they are less 101 # problematic if ScopedFfxConfig cannot clear them correctly. 102 stack.enter_context( 103 ScopedFfxConfig('repository.server.listen', '"[::]:0"')) 104 stack.enter_context(ScopedFfxConfig('daemon.autostart', 'false')) 105 stack.enter_context( 106 ScopedFfxConfig('discovery.zedboot.enabled', 'true')) 107 stack.enter_context(ScopedFfxConfig('log.level', 'debug')) 108 log_manager = stack.enter_context(LogManager(runner_args.logs_dir)) 109 start_ffx_daemon() 110 stack.callback(stop_ffx_daemon) 111 else: 112 if runner_args.logs_dir: 113 logging.warning( 114 'You are using a --logs-dir, ensure the ffx ' 115 'daemon is started with the logs.dir config ' 116 'updated. We won\'t restart the daemon randomly' 117 ' anymore.') 118 log_manager = stack.enter_context(LogManager(runner_args.logs_dir)) 119 120 if runner_args.device: 121 update(runner_args.system_image_dir, runner_args.os_check, 122 runner_args.target_id, runner_args.serial_num, 123 runner_args.pave) 124 else: 125 runner_args.target_id = stack.enter_context( 126 create_emulator_from_args(runner_args)) 127 128 test_connection(runner_args.target_id) 129 130 test_runner = _get_test_runner(runner_args, test_args) 131 package_deps = test_runner.package_deps 132 133 if not runner_args.repo: 134 # Create a directory that serves as a temporary repository. 135 runner_args.repo = stack.enter_context( 136 tempfile.TemporaryDirectory()) 137 138 publish_packages(package_deps.values(), runner_args.repo, 139 not runner_args.no_repo_init) 140 141 stack.enter_context(serve_repository(runner_args)) 142 143 # Start system logging, after all possible restarts of the ffx daemon 144 # so that logging will not be interrupted. 145 start_system_log(log_manager, False, package_deps.values(), 146 ('--since', 'now'), runner_args.target_id) 147 148 ermine = ErmineCtl(runner_args.target_id) 149 if ermine.exists: 150 ermine.take_to_shell() 151 152 resolve_packages(package_deps.keys(), runner_args.target_id) 153 return test_runner.run_test().returncode 154 155 156if __name__ == '__main__': 157 sys.exit(main()) 158