xref: /aosp_15_r20/external/angle/build/fuchsia/test/run_test.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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
16import monitors
17
18from common import has_ffx_isolate_dir, is_daemon_running, \
19                   register_common_args, register_device_args, \
20                   register_log_args, resolve_packages
21from compatible_utils import running_unattended
22from ffx_integration import ScopedFfxConfig
23from flash_device import register_update_args, update
24from isolate_daemon import IsolateDaemon
25from log_manager import LogManager, start_system_log
26from publish_package import publish_packages, register_package_args
27from run_blink_test import BlinkTestRunner
28from run_executable_test import create_executable_test_runner, \
29                                register_executable_test_args
30from run_telemetry_test import TelemetryTestRunner
31from run_webpage_test import WebpageTestRunner
32from serve_repo import register_serve_args, serve_repository
33from start_emulator import create_emulator_from_args, register_emulator_args
34from test_connection import test_connection, test_device_connection
35from test_runner import TestRunner
36
37
38def _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
58def 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    parser.add_argument('--extra-path',
74                        action='append',
75                        help='Extra paths to append to the PATH environment')
76
77    # Register arguments
78    register_common_args(parser)
79    register_device_args(parser)
80    register_emulator_args(parser)
81    register_executable_test_args(parser)
82    register_update_args(parser, default_os_check='ignore')
83    register_log_args(parser)
84    register_package_args(parser, allow_temp_repo=True)
85    register_serve_args(parser)
86
87    # Treat unrecognized arguments as test specific arguments.
88    runner_args, test_args = parser.parse_known_args()
89
90    if runner_args.target_id:
91        runner_args.device = True
92
93    with ExitStack() as stack:
94        if runner_args.logs_dir:
95            # TODO(crbug.com/343242386): Find a way to upload metric output when
96            # logs_dir is not defined.
97            stack.push(lambda *_: monitors.dump(
98                os.path.join(runner_args.logs_dir, 'invocations')))
99        if runner_args.extra_path:
100            os.environ['PATH'] += os.pathsep + os.pathsep.join(
101                runner_args.extra_path)
102        if running_unattended():
103            # Only restart the daemon if 1) daemon will be run in a new isolate
104            # dir, or 2) if there isn't a daemon running in the predefined
105            # isolate dir.
106            if not has_ffx_isolate_dir() or not is_daemon_running():
107                stack.enter_context(IsolateDaemon(runner_args.logs_dir))
108
109            if runner_args.everlasting:
110                # Setting the emu.instance_dir to match the named cache, so
111                # we can keep these files across multiple runs.
112                # The configuration attaches to the daemon isolate-dir, so it
113                # needs to go after the IsolateDaemon.
114                # There isn't a point of enabling the feature on devbox, it
115                # won't use isolate-dir and the emu.instance_dir always goes to
116                # the HOME directory.
117                stack.enter_context(
118                    ScopedFfxConfig(
119                        'emu.instance_dir',
120                        os.path.join(os.environ['HOME'],
121                                     '.fuchsia_emulator/')))
122        elif runner_args.logs_dir:
123            # Never restart daemon if not in the unattended mode.
124            logging.warning('You are using a --logs-dir, ensure the ffx '
125                            'daemon is started with the logs.dir config '
126                            'updated. We won\'t restart the daemon randomly'
127                            ' anymore.')
128        log_manager = LogManager(runner_args.logs_dir)
129        stack.enter_context(log_manager)
130
131        if runner_args.device:
132            update(runner_args.system_image_dir, runner_args.os_check,
133                   runner_args.target_id, runner_args.serial_num)
134            # Try to reboot the device if necessary since the ffx may ignore the
135            # device state after the flash. See
136            # https://cs.opensource.google/fuchsia/fuchsia/+/main:src/developer/ffx/lib/fastboot/src/common/fastboot.rs;drc=cfba0bdd4f8857adb6409f8ae9e35af52c0da93e;l=454
137            test_device_connection(runner_args.target_id)
138        else:
139            runner_args.target_id = stack.enter_context(
140                create_emulator_from_args(runner_args))
141            test_connection(runner_args.target_id)
142
143        test_runner = _get_test_runner(runner_args, test_args)
144        package_deps = test_runner.package_deps
145
146        # Start system logging, after all possible restarts of the ffx daemon
147        # so that logging will not be interrupted.
148        start_system_log(log_manager, False, package_deps.values(),
149                         ('--since', 'now'), runner_args.target_id)
150
151        if package_deps:
152            if not runner_args.repo:
153                # Create a directory that serves as a temporary repository.
154                runner_args.repo = stack.enter_context(
155                    tempfile.TemporaryDirectory())
156            publish_packages(package_deps.values(), runner_args.repo,
157                             not runner_args.no_repo_init)
158            stack.enter_context(serve_repository(runner_args))
159            resolve_packages(package_deps.keys(), runner_args.target_id)
160
161        return test_runner.run_test().returncode
162
163
164if __name__ == '__main__':
165    sys.exit(main())
166