xref: /aosp_15_r20/external/angle/build/chromeos/test_runner.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env vpython3
2*8975f5c5SAndroid Build Coastguard Worker#
3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2018 The Chromium Authors
4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
6*8975f5c5SAndroid Build Coastguard Worker
7*8975f5c5SAndroid Build Coastguard Workerimport argparse
8*8975f5c5SAndroid Build Coastguard Workerimport collections
9*8975f5c5SAndroid Build Coastguard Workerimport json
10*8975f5c5SAndroid Build Coastguard Workerimport logging
11*8975f5c5SAndroid Build Coastguard Workerimport os
12*8975f5c5SAndroid Build Coastguard Workerimport re
13*8975f5c5SAndroid Build Coastguard Workerimport shutil
14*8975f5c5SAndroid Build Coastguard Workerimport signal
15*8975f5c5SAndroid Build Coastguard Workerimport socket
16*8975f5c5SAndroid Build Coastguard Workerimport sys
17*8975f5c5SAndroid Build Coastguard Workerimport tempfile
18*8975f5c5SAndroid Build Coastguard Worker
19*8975f5c5SAndroid Build Coastguard Worker# The following non-std imports are fetched via vpython. See the list at
20*8975f5c5SAndroid Build Coastguard Worker# //.vpython3
21*8975f5c5SAndroid Build Coastguard Workerimport dateutil.parser  # pylint: disable=import-error
22*8975f5c5SAndroid Build Coastguard Workerimport jsonlines  # pylint: disable=import-error
23*8975f5c5SAndroid Build Coastguard Workerimport psutil  # pylint: disable=import-error
24*8975f5c5SAndroid Build Coastguard Worker
25*8975f5c5SAndroid Build Coastguard WorkerCHROMIUM_SRC_PATH = os.path.abspath(
26*8975f5c5SAndroid Build Coastguard Worker    os.path.join(os.path.dirname(__file__), '..', '..'))
27*8975f5c5SAndroid Build Coastguard Worker
28*8975f5c5SAndroid Build Coastguard Worker# Use the android test-runner's gtest results support library for generating
29*8975f5c5SAndroid Build Coastguard Worker# output json ourselves.
30*8975f5c5SAndroid Build Coastguard Workersys.path.insert(0, os.path.join(CHROMIUM_SRC_PATH, 'build', 'android'))
31*8975f5c5SAndroid Build Coastguard Workerfrom pylib.base import base_test_result  # pylint: disable=import-error
32*8975f5c5SAndroid Build Coastguard Workerfrom pylib.results import json_results  # pylint: disable=import-error
33*8975f5c5SAndroid Build Coastguard Worker
34*8975f5c5SAndroid Build Coastguard Workersys.path.insert(0, os.path.join(CHROMIUM_SRC_PATH, 'build', 'util'))
35*8975f5c5SAndroid Build Coastguard Worker# TODO(crbug.com/40259280): Re-enable the 'no-name-in-module' check.
36*8975f5c5SAndroid Build Coastguard Workerfrom lib.results import result_sink  # pylint: disable=import-error,no-name-in-module
37*8975f5c5SAndroid Build Coastguard Worker
38*8975f5c5SAndroid Build Coastguard Workerimport subprocess  # pylint: disable=import-error,wrong-import-order
39*8975f5c5SAndroid Build Coastguard Worker
40*8975f5c5SAndroid Build Coastguard WorkerDEFAULT_CROS_CACHE = os.path.abspath(
41*8975f5c5SAndroid Build Coastguard Worker    os.path.join(CHROMIUM_SRC_PATH, 'build', 'cros_cache'))
42*8975f5c5SAndroid Build Coastguard WorkerCHROMITE_PATH = os.path.abspath(
43*8975f5c5SAndroid Build Coastguard Worker    os.path.join(CHROMIUM_SRC_PATH, 'third_party', 'chromite'))
44*8975f5c5SAndroid Build Coastguard WorkerCROS_RUN_TEST_PATH = os.path.abspath(
45*8975f5c5SAndroid Build Coastguard Worker    os.path.join(CHROMITE_PATH, 'bin', 'cros_run_test'))
46*8975f5c5SAndroid Build Coastguard Worker
47*8975f5c5SAndroid Build Coastguard Worker# This is a special hostname that resolves to a different DUT in the lab
48*8975f5c5SAndroid Build Coastguard Worker# depending on which lab machine you're on.
49*8975f5c5SAndroid Build Coastguard WorkerLAB_DUT_HOSTNAME = 'variable_chromeos_device_hostname'
50*8975f5c5SAndroid Build Coastguard Worker
51*8975f5c5SAndroid Build Coastguard WorkerSYSTEM_LOG_LOCATIONS = [
52*8975f5c5SAndroid Build Coastguard Worker    '/home/chronos/crash/',
53*8975f5c5SAndroid Build Coastguard Worker    '/var/log/chrome/',
54*8975f5c5SAndroid Build Coastguard Worker    '/var/log/messages',
55*8975f5c5SAndroid Build Coastguard Worker    '/var/log/ui/',
56*8975f5c5SAndroid Build Coastguard Worker]
57*8975f5c5SAndroid Build Coastguard Worker
58*8975f5c5SAndroid Build Coastguard WorkerTAST_DEBUG_DOC = 'https://bit.ly/2LgvIXz'
59*8975f5c5SAndroid Build Coastguard Worker
60*8975f5c5SAndroid Build Coastguard Worker
61*8975f5c5SAndroid Build Coastguard Workerclass TestFormatError(Exception):
62*8975f5c5SAndroid Build Coastguard Worker  pass
63*8975f5c5SAndroid Build Coastguard Worker
64*8975f5c5SAndroid Build Coastguard Worker
65*8975f5c5SAndroid Build Coastguard Workerclass RemoteTest:
66*8975f5c5SAndroid Build Coastguard Worker
67*8975f5c5SAndroid Build Coastguard Worker  # This is a basic shell script that can be appended to in order to invoke the
68*8975f5c5SAndroid Build Coastguard Worker  # test on the device.
69*8975f5c5SAndroid Build Coastguard Worker  BASIC_SHELL_SCRIPT = [
70*8975f5c5SAndroid Build Coastguard Worker      '#!/bin/sh',
71*8975f5c5SAndroid Build Coastguard Worker
72*8975f5c5SAndroid Build Coastguard Worker      # /home and /tmp are mounted with "noexec" in the device, but some of our
73*8975f5c5SAndroid Build Coastguard Worker      # tools and tests use those dirs as a workspace (eg: vpython downloads
74*8975f5c5SAndroid Build Coastguard Worker      # python binaries to ~/.vpython-root and /tmp/vpython_bootstrap).
75*8975f5c5SAndroid Build Coastguard Worker      # /usr/local/tmp doesn't have this restriction, so change the location of
76*8975f5c5SAndroid Build Coastguard Worker      # the home and temp dirs for the duration of the test.
77*8975f5c5SAndroid Build Coastguard Worker      'export HOME=/usr/local/tmp',
78*8975f5c5SAndroid Build Coastguard Worker      'export TMPDIR=/usr/local/tmp',
79*8975f5c5SAndroid Build Coastguard Worker  ]
80*8975f5c5SAndroid Build Coastguard Worker
81*8975f5c5SAndroid Build Coastguard Worker  def __init__(self, args, unknown_args):
82*8975f5c5SAndroid Build Coastguard Worker    self._additional_args = unknown_args
83*8975f5c5SAndroid Build Coastguard Worker    self._path_to_outdir = args.path_to_outdir
84*8975f5c5SAndroid Build Coastguard Worker    self._test_launcher_summary_output = args.test_launcher_summary_output
85*8975f5c5SAndroid Build Coastguard Worker    self._logs_dir = args.logs_dir
86*8975f5c5SAndroid Build Coastguard Worker    self._use_vm = args.use_vm
87*8975f5c5SAndroid Build Coastguard Worker    self._rdb_client = result_sink.TryInitClient()
88*8975f5c5SAndroid Build Coastguard Worker
89*8975f5c5SAndroid Build Coastguard Worker    self._retries = 0
90*8975f5c5SAndroid Build Coastguard Worker    self._timeout = None
91*8975f5c5SAndroid Build Coastguard Worker    self._test_launcher_shard_index = args.test_launcher_shard_index
92*8975f5c5SAndroid Build Coastguard Worker    self._test_launcher_total_shards = args.test_launcher_total_shards
93*8975f5c5SAndroid Build Coastguard Worker
94*8975f5c5SAndroid Build Coastguard Worker    # The location on disk of a shell script that can be optionally used to
95*8975f5c5SAndroid Build Coastguard Worker    # invoke the test on the device. If it's not set, we assume self._test_cmd
96*8975f5c5SAndroid Build Coastguard Worker    # contains the test invocation.
97*8975f5c5SAndroid Build Coastguard Worker    self._on_device_script = None
98*8975f5c5SAndroid Build Coastguard Worker
99*8975f5c5SAndroid Build Coastguard Worker    self._test_cmd = [
100*8975f5c5SAndroid Build Coastguard Worker        CROS_RUN_TEST_PATH,
101*8975f5c5SAndroid Build Coastguard Worker        '--board',
102*8975f5c5SAndroid Build Coastguard Worker        args.board,
103*8975f5c5SAndroid Build Coastguard Worker        '--cache-dir',
104*8975f5c5SAndroid Build Coastguard Worker        args.cros_cache,
105*8975f5c5SAndroid Build Coastguard Worker    ]
106*8975f5c5SAndroid Build Coastguard Worker    if args.use_vm:
107*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd += [
108*8975f5c5SAndroid Build Coastguard Worker          '--start',
109*8975f5c5SAndroid Build Coastguard Worker          # Don't persist any filesystem changes after the VM shutsdown.
110*8975f5c5SAndroid Build Coastguard Worker          '--copy-on-write',
111*8975f5c5SAndroid Build Coastguard Worker      ]
112*8975f5c5SAndroid Build Coastguard Worker    else:
113*8975f5c5SAndroid Build Coastguard Worker      if args.fetch_cros_hostname:
114*8975f5c5SAndroid Build Coastguard Worker        self._test_cmd += ['--device', get_cros_hostname()]
115*8975f5c5SAndroid Build Coastguard Worker      else:
116*8975f5c5SAndroid Build Coastguard Worker        self._test_cmd += [
117*8975f5c5SAndroid Build Coastguard Worker            '--device', args.device if args.device else LAB_DUT_HOSTNAME
118*8975f5c5SAndroid Build Coastguard Worker        ]
119*8975f5c5SAndroid Build Coastguard Worker
120*8975f5c5SAndroid Build Coastguard Worker    if args.logs_dir:
121*8975f5c5SAndroid Build Coastguard Worker      for log in SYSTEM_LOG_LOCATIONS:
122*8975f5c5SAndroid Build Coastguard Worker        self._test_cmd += ['--results-src', log]
123*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd += [
124*8975f5c5SAndroid Build Coastguard Worker          '--results-dest-dir',
125*8975f5c5SAndroid Build Coastguard Worker          os.path.join(args.logs_dir, 'system_logs')
126*8975f5c5SAndroid Build Coastguard Worker      ]
127*8975f5c5SAndroid Build Coastguard Worker    if args.flash:
128*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd += ['--flash']
129*8975f5c5SAndroid Build Coastguard Worker      if args.public_image:
130*8975f5c5SAndroid Build Coastguard Worker        self._test_cmd += ['--public-image']
131*8975f5c5SAndroid Build Coastguard Worker
132*8975f5c5SAndroid Build Coastguard Worker    self._test_env = setup_env()
133*8975f5c5SAndroid Build Coastguard Worker
134*8975f5c5SAndroid Build Coastguard Worker  @property
135*8975f5c5SAndroid Build Coastguard Worker  def suite_name(self):
136*8975f5c5SAndroid Build Coastguard Worker    raise NotImplementedError('Child classes need to define suite name.')
137*8975f5c5SAndroid Build Coastguard Worker
138*8975f5c5SAndroid Build Coastguard Worker  @property
139*8975f5c5SAndroid Build Coastguard Worker  def test_cmd(self):
140*8975f5c5SAndroid Build Coastguard Worker    return self._test_cmd
141*8975f5c5SAndroid Build Coastguard Worker
142*8975f5c5SAndroid Build Coastguard Worker  def write_test_script_to_disk(self, script_contents):
143*8975f5c5SAndroid Build Coastguard Worker    # Since we're using an on_device_script to invoke the test, we'll need to
144*8975f5c5SAndroid Build Coastguard Worker    # set cwd.
145*8975f5c5SAndroid Build Coastguard Worker    self._test_cmd += [
146*8975f5c5SAndroid Build Coastguard Worker        '--remote-cmd',
147*8975f5c5SAndroid Build Coastguard Worker        '--cwd',
148*8975f5c5SAndroid Build Coastguard Worker        os.path.relpath(self._path_to_outdir, CHROMIUM_SRC_PATH),
149*8975f5c5SAndroid Build Coastguard Worker    ]
150*8975f5c5SAndroid Build Coastguard Worker    logging.info('Running the following command on the device:')
151*8975f5c5SAndroid Build Coastguard Worker    logging.info('\n%s', '\n'.join(script_contents))
152*8975f5c5SAndroid Build Coastguard Worker    fd, tmp_path = tempfile.mkstemp(suffix='.sh', dir=self._path_to_outdir)
153*8975f5c5SAndroid Build Coastguard Worker    os.fchmod(fd, 0o755)
154*8975f5c5SAndroid Build Coastguard Worker    with os.fdopen(fd, 'w') as f:
155*8975f5c5SAndroid Build Coastguard Worker      f.write('\n'.join(script_contents) + '\n')
156*8975f5c5SAndroid Build Coastguard Worker    return tmp_path
157*8975f5c5SAndroid Build Coastguard Worker
158*8975f5c5SAndroid Build Coastguard Worker  def write_runtime_files_to_disk(self, runtime_files):
159*8975f5c5SAndroid Build Coastguard Worker    logging.info('Writing runtime files to disk.')
160*8975f5c5SAndroid Build Coastguard Worker    fd, tmp_path = tempfile.mkstemp(suffix='.txt', dir=self._path_to_outdir)
161*8975f5c5SAndroid Build Coastguard Worker    os.fchmod(fd, 0o755)
162*8975f5c5SAndroid Build Coastguard Worker    with os.fdopen(fd, 'w') as f:
163*8975f5c5SAndroid Build Coastguard Worker      f.write('\n'.join(runtime_files) + '\n')
164*8975f5c5SAndroid Build Coastguard Worker    return tmp_path
165*8975f5c5SAndroid Build Coastguard Worker
166*8975f5c5SAndroid Build Coastguard Worker  def run_test(self):
167*8975f5c5SAndroid Build Coastguard Worker    # Traps SIGTERM and kills all child processes of cros_run_test when it's
168*8975f5c5SAndroid Build Coastguard Worker    # caught. This will allow us to capture logs from the device if a test hangs
169*8975f5c5SAndroid Build Coastguard Worker    # and gets timeout-killed by swarming. See also:
170*8975f5c5SAndroid Build Coastguard Worker    # https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
171*8975f5c5SAndroid Build Coastguard Worker    test_proc = None
172*8975f5c5SAndroid Build Coastguard Worker
173*8975f5c5SAndroid Build Coastguard Worker    def _kill_child_procs(trapped_signal, _):
174*8975f5c5SAndroid Build Coastguard Worker      logging.warning('Received signal %d. Killing child processes of test.',
175*8975f5c5SAndroid Build Coastguard Worker                      trapped_signal)
176*8975f5c5SAndroid Build Coastguard Worker      if not test_proc or not test_proc.pid:
177*8975f5c5SAndroid Build Coastguard Worker        # This shouldn't happen?
178*8975f5c5SAndroid Build Coastguard Worker        logging.error('Test process not running.')
179*8975f5c5SAndroid Build Coastguard Worker        return
180*8975f5c5SAndroid Build Coastguard Worker      for child in psutil.Process(test_proc.pid).children():
181*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Killing process %s', child)
182*8975f5c5SAndroid Build Coastguard Worker        child.kill()
183*8975f5c5SAndroid Build Coastguard Worker
184*8975f5c5SAndroid Build Coastguard Worker    signal.signal(signal.SIGTERM, _kill_child_procs)
185*8975f5c5SAndroid Build Coastguard Worker
186*8975f5c5SAndroid Build Coastguard Worker    for i in range(self._retries + 1):
187*8975f5c5SAndroid Build Coastguard Worker      logging.info('########################################')
188*8975f5c5SAndroid Build Coastguard Worker      logging.info('Test attempt #%d', i)
189*8975f5c5SAndroid Build Coastguard Worker      logging.info('########################################')
190*8975f5c5SAndroid Build Coastguard Worker      test_proc = subprocess.Popen(
191*8975f5c5SAndroid Build Coastguard Worker          self._test_cmd,
192*8975f5c5SAndroid Build Coastguard Worker          stdout=sys.stdout,
193*8975f5c5SAndroid Build Coastguard Worker          stderr=sys.stderr,
194*8975f5c5SAndroid Build Coastguard Worker          env=self._test_env)
195*8975f5c5SAndroid Build Coastguard Worker      try:
196*8975f5c5SAndroid Build Coastguard Worker        test_proc.wait(timeout=self._timeout)
197*8975f5c5SAndroid Build Coastguard Worker      except subprocess.TimeoutExpired:  # pylint: disable=no-member
198*8975f5c5SAndroid Build Coastguard Worker        logging.error('Test timed out. Sending SIGTERM.')
199*8975f5c5SAndroid Build Coastguard Worker        # SIGTERM the proc and wait 10s for it to close.
200*8975f5c5SAndroid Build Coastguard Worker        test_proc.terminate()
201*8975f5c5SAndroid Build Coastguard Worker        try:
202*8975f5c5SAndroid Build Coastguard Worker          test_proc.wait(timeout=10)
203*8975f5c5SAndroid Build Coastguard Worker        except subprocess.TimeoutExpired:  # pylint: disable=no-member
204*8975f5c5SAndroid Build Coastguard Worker          # If it hasn't closed in 10s, SIGKILL it.
205*8975f5c5SAndroid Build Coastguard Worker          logging.error('Test did not exit in time. Sending SIGKILL.')
206*8975f5c5SAndroid Build Coastguard Worker          test_proc.kill()
207*8975f5c5SAndroid Build Coastguard Worker          test_proc.wait()
208*8975f5c5SAndroid Build Coastguard Worker      logging.info('Test exitted with %d.', test_proc.returncode)
209*8975f5c5SAndroid Build Coastguard Worker      if test_proc.returncode == 0:
210*8975f5c5SAndroid Build Coastguard Worker        break
211*8975f5c5SAndroid Build Coastguard Worker
212*8975f5c5SAndroid Build Coastguard Worker    self.post_run(test_proc.returncode)
213*8975f5c5SAndroid Build Coastguard Worker    # Allow post_run to override test proc return code. (Useful when the host
214*8975f5c5SAndroid Build Coastguard Worker    # side Tast bin returns 0 even for failed tests.)
215*8975f5c5SAndroid Build Coastguard Worker    return test_proc.returncode
216*8975f5c5SAndroid Build Coastguard Worker
217*8975f5c5SAndroid Build Coastguard Worker  def post_run(self, _):
218*8975f5c5SAndroid Build Coastguard Worker    if self._on_device_script:
219*8975f5c5SAndroid Build Coastguard Worker      os.remove(self._on_device_script)
220*8975f5c5SAndroid Build Coastguard Worker
221*8975f5c5SAndroid Build Coastguard Worker  @staticmethod
222*8975f5c5SAndroid Build Coastguard Worker  def get_artifacts(path):
223*8975f5c5SAndroid Build Coastguard Worker    """Crawls a given directory for file artifacts to attach to a test.
224*8975f5c5SAndroid Build Coastguard Worker
225*8975f5c5SAndroid Build Coastguard Worker    Args:
226*8975f5c5SAndroid Build Coastguard Worker      path: Path to a directory to search for artifacts.
227*8975f5c5SAndroid Build Coastguard Worker    Returns:
228*8975f5c5SAndroid Build Coastguard Worker      A dict mapping name of the artifact to its absolute filepath.
229*8975f5c5SAndroid Build Coastguard Worker    """
230*8975f5c5SAndroid Build Coastguard Worker    artifacts = {}
231*8975f5c5SAndroid Build Coastguard Worker    for dirpath, _, filenames in os.walk(path):
232*8975f5c5SAndroid Build Coastguard Worker      for f in filenames:
233*8975f5c5SAndroid Build Coastguard Worker        artifact_path = os.path.join(dirpath, f)
234*8975f5c5SAndroid Build Coastguard Worker        artifact_id = os.path.relpath(artifact_path, path)
235*8975f5c5SAndroid Build Coastguard Worker        # Some artifacts will have non-Latin characters in the filename, eg:
236*8975f5c5SAndroid Build Coastguard Worker        # 'ui_tree_Chinese Pinyin-你好.txt'. ResultDB's API rejects such
237*8975f5c5SAndroid Build Coastguard Worker        # characters as an artifact ID, so force the file name down into ascii.
238*8975f5c5SAndroid Build Coastguard Worker        # For more info, see:
239*8975f5c5SAndroid Build Coastguard Worker        # https://source.chromium.org/chromium/infra/infra/+/main:go/src/go.chromium.org/luci/resultdb/proto/v1/artifact.proto;drc=3bff13b8037ca76ec19f9810033d914af7ec67cb;l=46
240*8975f5c5SAndroid Build Coastguard Worker        artifact_id = artifact_id.encode('ascii', 'replace').decode()
241*8975f5c5SAndroid Build Coastguard Worker        artifact_id = artifact_id.replace('\\', '?')
242*8975f5c5SAndroid Build Coastguard Worker        artifacts[artifact_id] = {
243*8975f5c5SAndroid Build Coastguard Worker            'filePath': artifact_path,
244*8975f5c5SAndroid Build Coastguard Worker        }
245*8975f5c5SAndroid Build Coastguard Worker    return artifacts
246*8975f5c5SAndroid Build Coastguard Worker
247*8975f5c5SAndroid Build Coastguard Worker
248*8975f5c5SAndroid Build Coastguard Workerclass TastTest(RemoteTest):
249*8975f5c5SAndroid Build Coastguard Worker
250*8975f5c5SAndroid Build Coastguard Worker  def __init__(self, args, unknown_args):
251*8975f5c5SAndroid Build Coastguard Worker    super().__init__(args, unknown_args)
252*8975f5c5SAndroid Build Coastguard Worker
253*8975f5c5SAndroid Build Coastguard Worker    self._suite_name = args.suite_name
254*8975f5c5SAndroid Build Coastguard Worker    self._tast_vars = args.tast_vars
255*8975f5c5SAndroid Build Coastguard Worker    self._tast_retries = args.tast_retries
256*8975f5c5SAndroid Build Coastguard Worker    self._tests = args.tests
257*8975f5c5SAndroid Build Coastguard Worker    # The CQ passes in '--gtest_filter' when specifying tests to skip. Store it
258*8975f5c5SAndroid Build Coastguard Worker    # here and parse it later to integrate it into Tast executions.
259*8975f5c5SAndroid Build Coastguard Worker    self._gtest_style_filter = args.gtest_filter
260*8975f5c5SAndroid Build Coastguard Worker    self._attr_expr = args.attr_expr
261*8975f5c5SAndroid Build Coastguard Worker    self._should_strip = args.strip_chrome
262*8975f5c5SAndroid Build Coastguard Worker    self._deploy_chrome = args.deploy_chrome
263*8975f5c5SAndroid Build Coastguard Worker
264*8975f5c5SAndroid Build Coastguard Worker    if not self._logs_dir:
265*8975f5c5SAndroid Build Coastguard Worker      # The host-side Tast bin returns 0 when tests fail, so we need to capture
266*8975f5c5SAndroid Build Coastguard Worker      # and parse its json results to reliably determine if tests fail.
267*8975f5c5SAndroid Build Coastguard Worker      raise TestFormatError(
268*8975f5c5SAndroid Build Coastguard Worker          'When using the host-side Tast bin, "--logs-dir" must be passed in '
269*8975f5c5SAndroid Build Coastguard Worker          'order to parse its results.')
270*8975f5c5SAndroid Build Coastguard Worker
271*8975f5c5SAndroid Build Coastguard Worker    # If the first test filter is negative, it should be safe to assume all of
272*8975f5c5SAndroid Build Coastguard Worker    # them are, so just test the first filter.
273*8975f5c5SAndroid Build Coastguard Worker    if self._gtest_style_filter and self._gtest_style_filter[0] == '-':
274*8975f5c5SAndroid Build Coastguard Worker      raise TestFormatError('Negative test filters not supported for Tast.')
275*8975f5c5SAndroid Build Coastguard Worker
276*8975f5c5SAndroid Build Coastguard Worker  @property
277*8975f5c5SAndroid Build Coastguard Worker  def suite_name(self):
278*8975f5c5SAndroid Build Coastguard Worker    return self._suite_name
279*8975f5c5SAndroid Build Coastguard Worker
280*8975f5c5SAndroid Build Coastguard Worker  def build_test_command(self):
281*8975f5c5SAndroid Build Coastguard Worker    unsupported_args = [
282*8975f5c5SAndroid Build Coastguard Worker        '--test-launcher-retry-limit',
283*8975f5c5SAndroid Build Coastguard Worker        '--test-launcher-batch-limit',
284*8975f5c5SAndroid Build Coastguard Worker        '--gtest_repeat',
285*8975f5c5SAndroid Build Coastguard Worker    ]
286*8975f5c5SAndroid Build Coastguard Worker    for unsupported_arg in unsupported_args:
287*8975f5c5SAndroid Build Coastguard Worker      if any(arg.startswith(unsupported_arg) for arg in self._additional_args):
288*8975f5c5SAndroid Build Coastguard Worker        logging.info(
289*8975f5c5SAndroid Build Coastguard Worker            '%s not supported for Tast tests. The arg will be ignored.',
290*8975f5c5SAndroid Build Coastguard Worker            unsupported_arg)
291*8975f5c5SAndroid Build Coastguard Worker        self._additional_args = [
292*8975f5c5SAndroid Build Coastguard Worker            arg for arg in self._additional_args
293*8975f5c5SAndroid Build Coastguard Worker            if not arg.startswith(unsupported_arg)
294*8975f5c5SAndroid Build Coastguard Worker        ]
295*8975f5c5SAndroid Build Coastguard Worker
296*8975f5c5SAndroid Build Coastguard Worker    self._test_cmd.extend(['--deploy', '--mount'])
297*8975f5c5SAndroid Build Coastguard Worker    self._test_cmd += [
298*8975f5c5SAndroid Build Coastguard Worker        '--build-dir',
299*8975f5c5SAndroid Build Coastguard Worker        os.path.relpath(self._path_to_outdir, CHROMIUM_SRC_PATH)
300*8975f5c5SAndroid Build Coastguard Worker    ] + self._additional_args
301*8975f5c5SAndroid Build Coastguard Worker
302*8975f5c5SAndroid Build Coastguard Worker    # Capture tast's results in the logs dir as well.
303*8975f5c5SAndroid Build Coastguard Worker    if self._logs_dir:
304*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd += [
305*8975f5c5SAndroid Build Coastguard Worker          '--results-dir',
306*8975f5c5SAndroid Build Coastguard Worker          self._logs_dir,
307*8975f5c5SAndroid Build Coastguard Worker      ]
308*8975f5c5SAndroid Build Coastguard Worker    self._test_cmd += [
309*8975f5c5SAndroid Build Coastguard Worker        '--tast-total-shards=%d' % self._test_launcher_total_shards,
310*8975f5c5SAndroid Build Coastguard Worker        '--tast-shard-index=%d' % self._test_launcher_shard_index,
311*8975f5c5SAndroid Build Coastguard Worker    ]
312*8975f5c5SAndroid Build Coastguard Worker    # If we're using a test filter, replace the contents of the Tast
313*8975f5c5SAndroid Build Coastguard Worker    # conditional with a long list of "name:test" expressions, one for each
314*8975f5c5SAndroid Build Coastguard Worker    # test in the filter.
315*8975f5c5SAndroid Build Coastguard Worker    if self._gtest_style_filter:
316*8975f5c5SAndroid Build Coastguard Worker      if self._attr_expr or self._tests:
317*8975f5c5SAndroid Build Coastguard Worker        logging.warning(
318*8975f5c5SAndroid Build Coastguard Worker            'Presence of --gtest_filter will cause the specified Tast expr'
319*8975f5c5SAndroid Build Coastguard Worker            ' or test list to be ignored.')
320*8975f5c5SAndroid Build Coastguard Worker      names = []
321*8975f5c5SAndroid Build Coastguard Worker      for test in self._gtest_style_filter.split(':'):
322*8975f5c5SAndroid Build Coastguard Worker        names.append('"name:%s"' % test)
323*8975f5c5SAndroid Build Coastguard Worker      self._attr_expr = '(' + ' || '.join(names) + ')'
324*8975f5c5SAndroid Build Coastguard Worker
325*8975f5c5SAndroid Build Coastguard Worker    if self._attr_expr:
326*8975f5c5SAndroid Build Coastguard Worker      # Don't use shlex.quote() here. Something funky happens with the arg
327*8975f5c5SAndroid Build Coastguard Worker      # as it gets passed down from cros_run_test to tast. (Tast picks up the
328*8975f5c5SAndroid Build Coastguard Worker      # escaping single quotes and complains that the attribute expression
329*8975f5c5SAndroid Build Coastguard Worker      # "must be within parentheses".)
330*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd.append('--tast=%s' % self._attr_expr)
331*8975f5c5SAndroid Build Coastguard Worker    else:
332*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd.append('--tast')
333*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd.extend(self._tests)
334*8975f5c5SAndroid Build Coastguard Worker
335*8975f5c5SAndroid Build Coastguard Worker    for v in self._tast_vars or []:
336*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd.extend(['--tast-var', v])
337*8975f5c5SAndroid Build Coastguard Worker
338*8975f5c5SAndroid Build Coastguard Worker    if self._tast_retries:
339*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd.append('--tast-retries=%d' % self._tast_retries)
340*8975f5c5SAndroid Build Coastguard Worker
341*8975f5c5SAndroid Build Coastguard Worker    # Mounting ash-chrome gives it enough disk space to not need stripping,
342*8975f5c5SAndroid Build Coastguard Worker    # but only for one not instrumented with code coverage.
343*8975f5c5SAndroid Build Coastguard Worker    if not self._should_strip:
344*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd.append('--nostrip')
345*8975f5c5SAndroid Build Coastguard Worker
346*8975f5c5SAndroid Build Coastguard Worker  def post_run(self, return_code):
347*8975f5c5SAndroid Build Coastguard Worker    tast_results_path = os.path.join(self._logs_dir, 'streamed_results.jsonl')
348*8975f5c5SAndroid Build Coastguard Worker    if not os.path.exists(tast_results_path):
349*8975f5c5SAndroid Build Coastguard Worker      logging.error(
350*8975f5c5SAndroid Build Coastguard Worker          'Tast results not found at %s. Falling back to generic result '
351*8975f5c5SAndroid Build Coastguard Worker          'reporting.', tast_results_path)
352*8975f5c5SAndroid Build Coastguard Worker      return super().post_run(return_code)
353*8975f5c5SAndroid Build Coastguard Worker
354*8975f5c5SAndroid Build Coastguard Worker    # See the link below for the format of the results:
355*8975f5c5SAndroid Build Coastguard Worker    # https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/chromiumos/cmd/tast/run#TestResult
356*8975f5c5SAndroid Build Coastguard Worker    with jsonlines.open(tast_results_path) as reader:
357*8975f5c5SAndroid Build Coastguard Worker      tast_results = collections.deque(reader)
358*8975f5c5SAndroid Build Coastguard Worker
359*8975f5c5SAndroid Build Coastguard Worker    suite_results = base_test_result.TestRunResults()
360*8975f5c5SAndroid Build Coastguard Worker    for test in tast_results:
361*8975f5c5SAndroid Build Coastguard Worker      errors = test['errors']
362*8975f5c5SAndroid Build Coastguard Worker      start, end = test['start'], test['end']
363*8975f5c5SAndroid Build Coastguard Worker      # Use dateutil to parse the timestamps since datetime can't handle
364*8975f5c5SAndroid Build Coastguard Worker      # nanosecond precision.
365*8975f5c5SAndroid Build Coastguard Worker      duration = dateutil.parser.parse(end) - dateutil.parser.parse(start)
366*8975f5c5SAndroid Build Coastguard Worker      # If the duration is negative, Tast has likely reported an incorrect
367*8975f5c5SAndroid Build Coastguard Worker      # duration. See https://issuetracker.google.com/issues/187973541. Round
368*8975f5c5SAndroid Build Coastguard Worker      # up to 0 in that case to avoid confusing RDB.
369*8975f5c5SAndroid Build Coastguard Worker      duration_ms = max(duration.total_seconds() * 1000, 0)
370*8975f5c5SAndroid Build Coastguard Worker      if bool(test['skipReason']):
371*8975f5c5SAndroid Build Coastguard Worker        result = base_test_result.ResultType.SKIP
372*8975f5c5SAndroid Build Coastguard Worker      elif errors:
373*8975f5c5SAndroid Build Coastguard Worker        result = base_test_result.ResultType.FAIL
374*8975f5c5SAndroid Build Coastguard Worker      else:
375*8975f5c5SAndroid Build Coastguard Worker        result = base_test_result.ResultType.PASS
376*8975f5c5SAndroid Build Coastguard Worker      primary_error_message = None
377*8975f5c5SAndroid Build Coastguard Worker      error_log = ''
378*8975f5c5SAndroid Build Coastguard Worker      if errors:
379*8975f5c5SAndroid Build Coastguard Worker        # See the link below for the format of these errors:
380*8975f5c5SAndroid Build Coastguard Worker        # https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/tast/src/chromiumos/tast/cmd/tast/internal/run/resultsjson/resultsjson.go
381*8975f5c5SAndroid Build Coastguard Worker        primary_error_message = errors[0]['reason']
382*8975f5c5SAndroid Build Coastguard Worker        for err in errors:
383*8975f5c5SAndroid Build Coastguard Worker          error_log += err['stack'] + '\n'
384*8975f5c5SAndroid Build Coastguard Worker      debug_link = ("If you're unsure why this test failed, consult the steps "
385*8975f5c5SAndroid Build Coastguard Worker                    'outlined <a href="%s">here</a>.' % TAST_DEBUG_DOC)
386*8975f5c5SAndroid Build Coastguard Worker      base_result = base_test_result.BaseTestResult(
387*8975f5c5SAndroid Build Coastguard Worker          test['name'], result, duration=duration_ms, log=error_log)
388*8975f5c5SAndroid Build Coastguard Worker      suite_results.AddResult(base_result)
389*8975f5c5SAndroid Build Coastguard Worker      self._maybe_handle_perf_results(test['name'])
390*8975f5c5SAndroid Build Coastguard Worker
391*8975f5c5SAndroid Build Coastguard Worker      if self._rdb_client:
392*8975f5c5SAndroid Build Coastguard Worker        # Walk the contents of the test's "outDir" and atttach any file found
393*8975f5c5SAndroid Build Coastguard Worker        # inside as an RDB 'artifact'. (This could include system logs, screen
394*8975f5c5SAndroid Build Coastguard Worker        # shots, etc.)
395*8975f5c5SAndroid Build Coastguard Worker        artifacts = self.get_artifacts(test['outDir'])
396*8975f5c5SAndroid Build Coastguard Worker        html_artifact = debug_link
397*8975f5c5SAndroid Build Coastguard Worker        if result == base_test_result.ResultType.SKIP:
398*8975f5c5SAndroid Build Coastguard Worker          html_artifact = 'Test was skipped because: ' + test['skipReason']
399*8975f5c5SAndroid Build Coastguard Worker        self._rdb_client.Post(
400*8975f5c5SAndroid Build Coastguard Worker            test['name'],
401*8975f5c5SAndroid Build Coastguard Worker            result,
402*8975f5c5SAndroid Build Coastguard Worker            duration_ms,
403*8975f5c5SAndroid Build Coastguard Worker            error_log,
404*8975f5c5SAndroid Build Coastguard Worker            None,
405*8975f5c5SAndroid Build Coastguard Worker            artifacts=artifacts,
406*8975f5c5SAndroid Build Coastguard Worker            failure_reason=primary_error_message,
407*8975f5c5SAndroid Build Coastguard Worker            html_artifact=html_artifact)
408*8975f5c5SAndroid Build Coastguard Worker
409*8975f5c5SAndroid Build Coastguard Worker    if self._rdb_client and self._logs_dir:
410*8975f5c5SAndroid Build Coastguard Worker      # Attach artifacts from the device that don't apply to a single test.
411*8975f5c5SAndroid Build Coastguard Worker      artifacts = self.get_artifacts(
412*8975f5c5SAndroid Build Coastguard Worker          os.path.join(self._logs_dir, 'system_logs'))
413*8975f5c5SAndroid Build Coastguard Worker      artifacts.update(
414*8975f5c5SAndroid Build Coastguard Worker          self.get_artifacts(os.path.join(self._logs_dir, 'crashes')))
415*8975f5c5SAndroid Build Coastguard Worker      self._rdb_client.ReportInvocationLevelArtifacts(artifacts)
416*8975f5c5SAndroid Build Coastguard Worker
417*8975f5c5SAndroid Build Coastguard Worker    if self._test_launcher_summary_output:
418*8975f5c5SAndroid Build Coastguard Worker      with open(self._test_launcher_summary_output, 'w') as f:
419*8975f5c5SAndroid Build Coastguard Worker        json.dump(json_results.GenerateResultsDict([suite_results]), f)
420*8975f5c5SAndroid Build Coastguard Worker
421*8975f5c5SAndroid Build Coastguard Worker    if not suite_results.DidRunPass():
422*8975f5c5SAndroid Build Coastguard Worker      return 1
423*8975f5c5SAndroid Build Coastguard Worker    if return_code:
424*8975f5c5SAndroid Build Coastguard Worker      logging.warning(
425*8975f5c5SAndroid Build Coastguard Worker          'No failed tests found, but exit code of %d was returned from '
426*8975f5c5SAndroid Build Coastguard Worker          'cros_run_test.', return_code)
427*8975f5c5SAndroid Build Coastguard Worker      return return_code
428*8975f5c5SAndroid Build Coastguard Worker    return 0
429*8975f5c5SAndroid Build Coastguard Worker
430*8975f5c5SAndroid Build Coastguard Worker  def _maybe_handle_perf_results(self, test_name):
431*8975f5c5SAndroid Build Coastguard Worker    """Prepares any perf results from |test_name| for process_perf_results.
432*8975f5c5SAndroid Build Coastguard Worker
433*8975f5c5SAndroid Build Coastguard Worker    - process_perf_results looks for top level directories containing a
434*8975f5c5SAndroid Build Coastguard Worker      perf_results.json file and a test_results.json file. The directory names
435*8975f5c5SAndroid Build Coastguard Worker      are used as the benchmark names.
436*8975f5c5SAndroid Build Coastguard Worker    - If a perf_results.json or results-chart.json file exists in the
437*8975f5c5SAndroid Build Coastguard Worker      |test_name| results directory, a top level directory is created and the
438*8975f5c5SAndroid Build Coastguard Worker      perf results file is copied to perf_results.json.
439*8975f5c5SAndroid Build Coastguard Worker    - A trivial test_results.json file is also created to indicate that the test
440*8975f5c5SAndroid Build Coastguard Worker      succeeded (this function would not be called otherwise).
441*8975f5c5SAndroid Build Coastguard Worker    - When process_perf_results is run, it will find the expected files in the
442*8975f5c5SAndroid Build Coastguard Worker      named directory and upload the benchmark results.
443*8975f5c5SAndroid Build Coastguard Worker    """
444*8975f5c5SAndroid Build Coastguard Worker
445*8975f5c5SAndroid Build Coastguard Worker    perf_results = os.path.join(self._logs_dir, 'tests', test_name,
446*8975f5c5SAndroid Build Coastguard Worker                                'perf_results.json')
447*8975f5c5SAndroid Build Coastguard Worker    # TODO(stevenjb): Remove check for crosbolt results-chart.json file.
448*8975f5c5SAndroid Build Coastguard Worker    if not os.path.exists(perf_results):
449*8975f5c5SAndroid Build Coastguard Worker      perf_results = os.path.join(self._logs_dir, 'tests', test_name,
450*8975f5c5SAndroid Build Coastguard Worker                                  'results-chart.json')
451*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(perf_results):
452*8975f5c5SAndroid Build Coastguard Worker      benchmark_dir = os.path.join(self._logs_dir, test_name)
453*8975f5c5SAndroid Build Coastguard Worker      if not os.path.isdir(benchmark_dir):
454*8975f5c5SAndroid Build Coastguard Worker        os.makedirs(benchmark_dir)
455*8975f5c5SAndroid Build Coastguard Worker      shutil.copyfile(perf_results,
456*8975f5c5SAndroid Build Coastguard Worker                      os.path.join(benchmark_dir, 'perf_results.json'))
457*8975f5c5SAndroid Build Coastguard Worker      # process_perf_results.py expects a test_results.json file.
458*8975f5c5SAndroid Build Coastguard Worker      test_results = {'valid': True, 'failures': []}
459*8975f5c5SAndroid Build Coastguard Worker      with open(os.path.join(benchmark_dir, 'test_results.json'), 'w') as out:
460*8975f5c5SAndroid Build Coastguard Worker        json.dump(test_results, out)
461*8975f5c5SAndroid Build Coastguard Worker
462*8975f5c5SAndroid Build Coastguard Worker
463*8975f5c5SAndroid Build Coastguard Workerclass GTestTest(RemoteTest):
464*8975f5c5SAndroid Build Coastguard Worker
465*8975f5c5SAndroid Build Coastguard Worker  # The following list corresponds to paths that should not be copied over to
466*8975f5c5SAndroid Build Coastguard Worker  # the device during tests. In other words, these files are only ever used on
467*8975f5c5SAndroid Build Coastguard Worker  # the host.
468*8975f5c5SAndroid Build Coastguard Worker  _FILE_IGNORELIST = [
469*8975f5c5SAndroid Build Coastguard Worker      re.compile(r'.*build/android.*'),
470*8975f5c5SAndroid Build Coastguard Worker      re.compile(r'.*build/chromeos.*'),
471*8975f5c5SAndroid Build Coastguard Worker      re.compile(r'.*build/cros_cache.*'),
472*8975f5c5SAndroid Build Coastguard Worker      # The following matches anything under //testing/ that isn't under
473*8975f5c5SAndroid Build Coastguard Worker      # //testing/buildbot/filters/.
474*8975f5c5SAndroid Build Coastguard Worker      re.compile(r'.*testing/(?!buildbot/filters).*'),
475*8975f5c5SAndroid Build Coastguard Worker      re.compile(r'.*third_party/chromite.*'),
476*8975f5c5SAndroid Build Coastguard Worker  ]
477*8975f5c5SAndroid Build Coastguard Worker
478*8975f5c5SAndroid Build Coastguard Worker  def __init__(self, args, unknown_args):
479*8975f5c5SAndroid Build Coastguard Worker    super().__init__(args, unknown_args)
480*8975f5c5SAndroid Build Coastguard Worker
481*8975f5c5SAndroid Build Coastguard Worker    self._test_cmd = ['vpython3'] + self._test_cmd
482*8975f5c5SAndroid Build Coastguard Worker    if not args.clean:
483*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd += ['--no-clean']
484*8975f5c5SAndroid Build Coastguard Worker
485*8975f5c5SAndroid Build Coastguard Worker    self._test_exe = args.test_exe
486*8975f5c5SAndroid Build Coastguard Worker    self._runtime_deps_path = args.runtime_deps_path
487*8975f5c5SAndroid Build Coastguard Worker    self._vpython_dir = args.vpython_dir
488*8975f5c5SAndroid Build Coastguard Worker
489*8975f5c5SAndroid Build Coastguard Worker    self._on_device_script = None
490*8975f5c5SAndroid Build Coastguard Worker    self._env_vars = args.env_var
491*8975f5c5SAndroid Build Coastguard Worker    self._stop_ui = args.stop_ui
492*8975f5c5SAndroid Build Coastguard Worker    self._as_root = args.as_root
493*8975f5c5SAndroid Build Coastguard Worker    self._trace_dir = args.trace_dir
494*8975f5c5SAndroid Build Coastguard Worker    self._run_test_sudo_helper = args.run_test_sudo_helper
495*8975f5c5SAndroid Build Coastguard Worker    self._set_selinux_label = args.set_selinux_label
496*8975f5c5SAndroid Build Coastguard Worker    self._use_deployed_dbus_configs = args.use_deployed_dbus_configs
497*8975f5c5SAndroid Build Coastguard Worker
498*8975f5c5SAndroid Build Coastguard Worker  @property
499*8975f5c5SAndroid Build Coastguard Worker  def suite_name(self):
500*8975f5c5SAndroid Build Coastguard Worker    return self._test_exe
501*8975f5c5SAndroid Build Coastguard Worker
502*8975f5c5SAndroid Build Coastguard Worker  def build_test_command(self):
503*8975f5c5SAndroid Build Coastguard Worker    # To keep things easy for us, ensure both types of output locations are
504*8975f5c5SAndroid Build Coastguard Worker    # the same.
505*8975f5c5SAndroid Build Coastguard Worker    if self._test_launcher_summary_output and self._logs_dir:
506*8975f5c5SAndroid Build Coastguard Worker      json_out_dir = os.path.dirname(self._test_launcher_summary_output) or '.'
507*8975f5c5SAndroid Build Coastguard Worker      if os.path.abspath(json_out_dir) != os.path.abspath(self._logs_dir):
508*8975f5c5SAndroid Build Coastguard Worker        raise TestFormatError(
509*8975f5c5SAndroid Build Coastguard Worker            '--test-launcher-summary-output and --logs-dir must point to '
510*8975f5c5SAndroid Build Coastguard Worker            'the same directory.')
511*8975f5c5SAndroid Build Coastguard Worker
512*8975f5c5SAndroid Build Coastguard Worker    if self._test_launcher_summary_output:
513*8975f5c5SAndroid Build Coastguard Worker      result_dir, result_file = os.path.split(
514*8975f5c5SAndroid Build Coastguard Worker          self._test_launcher_summary_output)
515*8975f5c5SAndroid Build Coastguard Worker      # If args.test_launcher_summary_output is a file in cwd, result_dir will
516*8975f5c5SAndroid Build Coastguard Worker      # be an empty string, so replace it with '.' when this is the case so
517*8975f5c5SAndroid Build Coastguard Worker      # cros_run_test can correctly handle it.
518*8975f5c5SAndroid Build Coastguard Worker      if not result_dir:
519*8975f5c5SAndroid Build Coastguard Worker        result_dir = '.'
520*8975f5c5SAndroid Build Coastguard Worker      device_result_file = '/tmp/%s' % result_file
521*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd += [
522*8975f5c5SAndroid Build Coastguard Worker          '--results-src',
523*8975f5c5SAndroid Build Coastguard Worker          device_result_file,
524*8975f5c5SAndroid Build Coastguard Worker          '--results-dest-dir',
525*8975f5c5SAndroid Build Coastguard Worker          result_dir,
526*8975f5c5SAndroid Build Coastguard Worker      ]
527*8975f5c5SAndroid Build Coastguard Worker
528*8975f5c5SAndroid Build Coastguard Worker    if self._trace_dir and self._logs_dir:
529*8975f5c5SAndroid Build Coastguard Worker      trace_path = os.path.dirname(self._trace_dir) or '.'
530*8975f5c5SAndroid Build Coastguard Worker      if os.path.abspath(trace_path) != os.path.abspath(self._logs_dir):
531*8975f5c5SAndroid Build Coastguard Worker        raise TestFormatError(
532*8975f5c5SAndroid Build Coastguard Worker            '--trace-dir and --logs-dir must point to the same directory.')
533*8975f5c5SAndroid Build Coastguard Worker
534*8975f5c5SAndroid Build Coastguard Worker    if self._trace_dir:
535*8975f5c5SAndroid Build Coastguard Worker      trace_path, trace_dirname = os.path.split(self._trace_dir)
536*8975f5c5SAndroid Build Coastguard Worker      device_trace_dir = '/tmp/%s' % trace_dirname
537*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd += [
538*8975f5c5SAndroid Build Coastguard Worker          '--results-src',
539*8975f5c5SAndroid Build Coastguard Worker          device_trace_dir,
540*8975f5c5SAndroid Build Coastguard Worker          '--results-dest-dir',
541*8975f5c5SAndroid Build Coastguard Worker          trace_path,
542*8975f5c5SAndroid Build Coastguard Worker      ]
543*8975f5c5SAndroid Build Coastguard Worker
544*8975f5c5SAndroid Build Coastguard Worker    # Build the shell script that will be used on the device to invoke the test.
545*8975f5c5SAndroid Build Coastguard Worker    # Stored here as a list of lines.
546*8975f5c5SAndroid Build Coastguard Worker    device_test_script_contents = self.BASIC_SHELL_SCRIPT[:]
547*8975f5c5SAndroid Build Coastguard Worker    for var_name, var_val in self._env_vars:
548*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents += ['export %s=%s' % (var_name, var_val)]
549*8975f5c5SAndroid Build Coastguard Worker
550*8975f5c5SAndroid Build Coastguard Worker    if self._vpython_dir:
551*8975f5c5SAndroid Build Coastguard Worker      vpython_path = os.path.join(self._path_to_outdir, self._vpython_dir,
552*8975f5c5SAndroid Build Coastguard Worker                                  'vpython3')
553*8975f5c5SAndroid Build Coastguard Worker      cpython_path = os.path.join(self._path_to_outdir, self._vpython_dir,
554*8975f5c5SAndroid Build Coastguard Worker                                  'bin', 'python3')
555*8975f5c5SAndroid Build Coastguard Worker      if not os.path.exists(vpython_path) or not os.path.exists(cpython_path):
556*8975f5c5SAndroid Build Coastguard Worker        raise TestFormatError(
557*8975f5c5SAndroid Build Coastguard Worker            '--vpython-dir must point to a dir with both '
558*8975f5c5SAndroid Build Coastguard Worker            'infra/3pp/tools/cpython3 and infra/tools/luci/vpython3 '
559*8975f5c5SAndroid Build Coastguard Worker            'installed.')
560*8975f5c5SAndroid Build Coastguard Worker      vpython_spec_path = os.path.relpath(
561*8975f5c5SAndroid Build Coastguard Worker          os.path.join(CHROMIUM_SRC_PATH, '.vpython3'), self._path_to_outdir)
562*8975f5c5SAndroid Build Coastguard Worker      # Initialize the vpython cache. This can take 10-20s, and some tests
563*8975f5c5SAndroid Build Coastguard Worker      # can't afford to wait that long on the first invocation.
564*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents.extend([
565*8975f5c5SAndroid Build Coastguard Worker          'export PATH=$PWD/%s:$PWD/%s/bin/:$PATH' %
566*8975f5c5SAndroid Build Coastguard Worker          (self._vpython_dir, self._vpython_dir),
567*8975f5c5SAndroid Build Coastguard Worker          'vpython3 -vpython-spec %s -vpython-tool install' %
568*8975f5c5SAndroid Build Coastguard Worker          (vpython_spec_path),
569*8975f5c5SAndroid Build Coastguard Worker      ])
570*8975f5c5SAndroid Build Coastguard Worker
571*8975f5c5SAndroid Build Coastguard Worker    test_invocation = ('LD_LIBRARY_PATH=./ ./%s --test-launcher-shard-index=%d '
572*8975f5c5SAndroid Build Coastguard Worker                       '--test-launcher-total-shards=%d' %
573*8975f5c5SAndroid Build Coastguard Worker                       (self._test_exe, self._test_launcher_shard_index,
574*8975f5c5SAndroid Build Coastguard Worker                        self._test_launcher_total_shards))
575*8975f5c5SAndroid Build Coastguard Worker    if self._test_launcher_summary_output:
576*8975f5c5SAndroid Build Coastguard Worker      test_invocation += ' --test-launcher-summary-output=%s' % (
577*8975f5c5SAndroid Build Coastguard Worker          device_result_file)
578*8975f5c5SAndroid Build Coastguard Worker
579*8975f5c5SAndroid Build Coastguard Worker    if self._trace_dir:
580*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents.extend([
581*8975f5c5SAndroid Build Coastguard Worker          'rm -rf %s' % device_trace_dir,
582*8975f5c5SAndroid Build Coastguard Worker          'sudo -E -u chronos -- /bin/bash -c "mkdir -p %s"' % device_trace_dir,
583*8975f5c5SAndroid Build Coastguard Worker      ])
584*8975f5c5SAndroid Build Coastguard Worker      test_invocation += ' --trace-dir=%s' % device_trace_dir
585*8975f5c5SAndroid Build Coastguard Worker
586*8975f5c5SAndroid Build Coastguard Worker    if self._run_test_sudo_helper:
587*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents.extend([
588*8975f5c5SAndroid Build Coastguard Worker          'TEST_SUDO_HELPER_PATH=$(mktemp)',
589*8975f5c5SAndroid Build Coastguard Worker          './test_sudo_helper.py --socket-path=${TEST_SUDO_HELPER_PATH} &',
590*8975f5c5SAndroid Build Coastguard Worker          'TEST_SUDO_HELPER_PID=$!'
591*8975f5c5SAndroid Build Coastguard Worker      ])
592*8975f5c5SAndroid Build Coastguard Worker      test_invocation += (
593*8975f5c5SAndroid Build Coastguard Worker          ' --test-sudo-helper-socket-path=${TEST_SUDO_HELPER_PATH}')
594*8975f5c5SAndroid Build Coastguard Worker
595*8975f5c5SAndroid Build Coastguard Worker    # Append the selinux labels. The 'setfiles' command takes a file with each
596*8975f5c5SAndroid Build Coastguard Worker    # line consisting of "<file-regex> <file-type> <new-label>", where '--' is
597*8975f5c5SAndroid Build Coastguard Worker    # the type of a regular file.
598*8975f5c5SAndroid Build Coastguard Worker    if self._set_selinux_label:
599*8975f5c5SAndroid Build Coastguard Worker      for label_pair in self._set_selinux_label:
600*8975f5c5SAndroid Build Coastguard Worker        filename, label = label_pair.split('=', 1)
601*8975f5c5SAndroid Build Coastguard Worker        specfile = filename + '.specfile'
602*8975f5c5SAndroid Build Coastguard Worker        device_test_script_contents.extend([
603*8975f5c5SAndroid Build Coastguard Worker            'echo %s -- %s > %s' % (filename, label, specfile),
604*8975f5c5SAndroid Build Coastguard Worker            'setfiles -F %s %s' % (specfile, filename),
605*8975f5c5SAndroid Build Coastguard Worker        ])
606*8975f5c5SAndroid Build Coastguard Worker
607*8975f5c5SAndroid Build Coastguard Worker    # Mount the deploy dbus config dir on top of chrome's dbus dir. Send SIGHUP
608*8975f5c5SAndroid Build Coastguard Worker    # to dbus daemon to reload config from the newly mounted dir.
609*8975f5c5SAndroid Build Coastguard Worker    if self._use_deployed_dbus_configs:
610*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents.extend([
611*8975f5c5SAndroid Build Coastguard Worker          'mount --bind ./dbus /opt/google/chrome/dbus',
612*8975f5c5SAndroid Build Coastguard Worker          'kill -s HUP $(pgrep dbus)',
613*8975f5c5SAndroid Build Coastguard Worker      ])
614*8975f5c5SAndroid Build Coastguard Worker
615*8975f5c5SAndroid Build Coastguard Worker    if self._additional_args:
616*8975f5c5SAndroid Build Coastguard Worker      test_invocation += ' %s' % ' '.join(self._additional_args)
617*8975f5c5SAndroid Build Coastguard Worker
618*8975f5c5SAndroid Build Coastguard Worker    if self._stop_ui:
619*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents += [
620*8975f5c5SAndroid Build Coastguard Worker          'stop ui',
621*8975f5c5SAndroid Build Coastguard Worker      ]
622*8975f5c5SAndroid Build Coastguard Worker      # Send a user activity ping to powerd to ensure the display is on.
623*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents += [
624*8975f5c5SAndroid Build Coastguard Worker          'dbus-send --system --type=method_call'
625*8975f5c5SAndroid Build Coastguard Worker          ' --dest=org.chromium.PowerManager /org/chromium/PowerManager'
626*8975f5c5SAndroid Build Coastguard Worker          ' org.chromium.PowerManager.HandleUserActivity int32:0'
627*8975f5c5SAndroid Build Coastguard Worker      ]
628*8975f5c5SAndroid Build Coastguard Worker      # The UI service on the device owns the chronos user session, so shutting
629*8975f5c5SAndroid Build Coastguard Worker      # it down as chronos kills the entire execution of the test. So we'll have
630*8975f5c5SAndroid Build Coastguard Worker      # to run as root up until the test invocation.
631*8975f5c5SAndroid Build Coastguard Worker      test_invocation = (
632*8975f5c5SAndroid Build Coastguard Worker          'sudo -E -u chronos -- /bin/bash -c "%s"' % test_invocation)
633*8975f5c5SAndroid Build Coastguard Worker      # And we'll need to chown everything since cros_run_test's "--as-chronos"
634*8975f5c5SAndroid Build Coastguard Worker      # option normally does that for us.
635*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents.append('chown -R chronos: ../..')
636*8975f5c5SAndroid Build Coastguard Worker    elif not self._as_root:
637*8975f5c5SAndroid Build Coastguard Worker      self._test_cmd += [
638*8975f5c5SAndroid Build Coastguard Worker          # Some tests fail as root, so run as the less privileged user
639*8975f5c5SAndroid Build Coastguard Worker          # 'chronos'.
640*8975f5c5SAndroid Build Coastguard Worker          '--as-chronos',
641*8975f5c5SAndroid Build Coastguard Worker      ]
642*8975f5c5SAndroid Build Coastguard Worker
643*8975f5c5SAndroid Build Coastguard Worker    device_test_script_contents.append(test_invocation)
644*8975f5c5SAndroid Build Coastguard Worker    device_test_script_contents.append('TEST_RETURN_CODE=$?')
645*8975f5c5SAndroid Build Coastguard Worker
646*8975f5c5SAndroid Build Coastguard Worker    # (Re)start ui after all tests are done. This is for developer convenienve.
647*8975f5c5SAndroid Build Coastguard Worker    # Without this, the device would remain in a black screen which looks like
648*8975f5c5SAndroid Build Coastguard Worker    # powered off.
649*8975f5c5SAndroid Build Coastguard Worker    if self._stop_ui:
650*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents += [
651*8975f5c5SAndroid Build Coastguard Worker          'start ui',
652*8975f5c5SAndroid Build Coastguard Worker      ]
653*8975f5c5SAndroid Build Coastguard Worker
654*8975f5c5SAndroid Build Coastguard Worker    # Stop the crosier helper.
655*8975f5c5SAndroid Build Coastguard Worker    if self._run_test_sudo_helper:
656*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents.extend([
657*8975f5c5SAndroid Build Coastguard Worker          'pkill -P $TEST_SUDO_HELPER_PID',
658*8975f5c5SAndroid Build Coastguard Worker          'kill $TEST_SUDO_HELPER_PID',
659*8975f5c5SAndroid Build Coastguard Worker          'unlink ${TEST_SUDO_HELPER_PATH}',
660*8975f5c5SAndroid Build Coastguard Worker      ])
661*8975f5c5SAndroid Build Coastguard Worker
662*8975f5c5SAndroid Build Coastguard Worker    # Undo the dbus config mount and reload dbus config.
663*8975f5c5SAndroid Build Coastguard Worker    if self._use_deployed_dbus_configs:
664*8975f5c5SAndroid Build Coastguard Worker      device_test_script_contents.extend([
665*8975f5c5SAndroid Build Coastguard Worker          'umount /opt/google/chrome/dbus',
666*8975f5c5SAndroid Build Coastguard Worker          'kill -s HUP $(pgrep dbus)',
667*8975f5c5SAndroid Build Coastguard Worker      ])
668*8975f5c5SAndroid Build Coastguard Worker
669*8975f5c5SAndroid Build Coastguard Worker    # This command should always be the last bash commandline so infra can
670*8975f5c5SAndroid Build Coastguard Worker    # correctly get the error code from test invocations.
671*8975f5c5SAndroid Build Coastguard Worker    device_test_script_contents.append('exit $TEST_RETURN_CODE')
672*8975f5c5SAndroid Build Coastguard Worker
673*8975f5c5SAndroid Build Coastguard Worker    self._on_device_script = self.write_test_script_to_disk(
674*8975f5c5SAndroid Build Coastguard Worker        device_test_script_contents)
675*8975f5c5SAndroid Build Coastguard Worker
676*8975f5c5SAndroid Build Coastguard Worker    runtime_files = [os.path.relpath(self._on_device_script)]
677*8975f5c5SAndroid Build Coastguard Worker    runtime_files += self._read_runtime_files()
678*8975f5c5SAndroid Build Coastguard Worker    if self._vpython_dir:
679*8975f5c5SAndroid Build Coastguard Worker      # --vpython-dir is relative to the out dir, but --files-from expects paths
680*8975f5c5SAndroid Build Coastguard Worker      # relative to src dir, so fix the path up a bit.
681*8975f5c5SAndroid Build Coastguard Worker      runtime_files.append(
682*8975f5c5SAndroid Build Coastguard Worker          os.path.relpath(
683*8975f5c5SAndroid Build Coastguard Worker              os.path.abspath(
684*8975f5c5SAndroid Build Coastguard Worker                  os.path.join(self._path_to_outdir, self._vpython_dir)),
685*8975f5c5SAndroid Build Coastguard Worker              CHROMIUM_SRC_PATH))
686*8975f5c5SAndroid Build Coastguard Worker
687*8975f5c5SAndroid Build Coastguard Worker    self._test_cmd.extend(
688*8975f5c5SAndroid Build Coastguard Worker        ['--files-from',
689*8975f5c5SAndroid Build Coastguard Worker         self.write_runtime_files_to_disk(runtime_files)])
690*8975f5c5SAndroid Build Coastguard Worker
691*8975f5c5SAndroid Build Coastguard Worker    self._test_cmd += [
692*8975f5c5SAndroid Build Coastguard Worker        '--',
693*8975f5c5SAndroid Build Coastguard Worker        './' + os.path.relpath(self._on_device_script, self._path_to_outdir)
694*8975f5c5SAndroid Build Coastguard Worker    ]
695*8975f5c5SAndroid Build Coastguard Worker
696*8975f5c5SAndroid Build Coastguard Worker  def _read_runtime_files(self):
697*8975f5c5SAndroid Build Coastguard Worker    if not self._runtime_deps_path:
698*8975f5c5SAndroid Build Coastguard Worker      return []
699*8975f5c5SAndroid Build Coastguard Worker
700*8975f5c5SAndroid Build Coastguard Worker    abs_runtime_deps_path = os.path.abspath(
701*8975f5c5SAndroid Build Coastguard Worker        os.path.join(self._path_to_outdir, self._runtime_deps_path))
702*8975f5c5SAndroid Build Coastguard Worker    with open(abs_runtime_deps_path) as runtime_deps_file:
703*8975f5c5SAndroid Build Coastguard Worker      files = [l.strip() for l in runtime_deps_file if l]
704*8975f5c5SAndroid Build Coastguard Worker    rel_file_paths = []
705*8975f5c5SAndroid Build Coastguard Worker    for f in files:
706*8975f5c5SAndroid Build Coastguard Worker      rel_file_path = os.path.relpath(
707*8975f5c5SAndroid Build Coastguard Worker          os.path.abspath(os.path.join(self._path_to_outdir, f)))
708*8975f5c5SAndroid Build Coastguard Worker      if not any(regex.match(rel_file_path) for regex in self._FILE_IGNORELIST):
709*8975f5c5SAndroid Build Coastguard Worker        rel_file_paths.append(rel_file_path)
710*8975f5c5SAndroid Build Coastguard Worker    return rel_file_paths
711*8975f5c5SAndroid Build Coastguard Worker
712*8975f5c5SAndroid Build Coastguard Worker  def post_run(self, _):
713*8975f5c5SAndroid Build Coastguard Worker    if self._on_device_script:
714*8975f5c5SAndroid Build Coastguard Worker      os.remove(self._on_device_script)
715*8975f5c5SAndroid Build Coastguard Worker
716*8975f5c5SAndroid Build Coastguard Worker    if self._test_launcher_summary_output and self._rdb_client:
717*8975f5c5SAndroid Build Coastguard Worker      logging.error('Native ResultDB integration is not supported for GTests. '
718*8975f5c5SAndroid Build Coastguard Worker                    'Upload results via result_adapter instead. '
719*8975f5c5SAndroid Build Coastguard Worker                    'See crbug.com/1330441.')
720*8975f5c5SAndroid Build Coastguard Worker
721*8975f5c5SAndroid Build Coastguard Worker
722*8975f5c5SAndroid Build Coastguard Workerdef device_test(args, unknown_args):
723*8975f5c5SAndroid Build Coastguard Worker  # cros_run_test has trouble with relative paths that go up directories,
724*8975f5c5SAndroid Build Coastguard Worker  # so cd to src/, which should be the root of all data deps.
725*8975f5c5SAndroid Build Coastguard Worker  os.chdir(CHROMIUM_SRC_PATH)
726*8975f5c5SAndroid Build Coastguard Worker
727*8975f5c5SAndroid Build Coastguard Worker  # TODO: Remove the above when depot_tool's pylint is updated to include the
728*8975f5c5SAndroid Build Coastguard Worker  # fix to https://github.com/PyCQA/pylint/issues/710.
729*8975f5c5SAndroid Build Coastguard Worker  if args.test_type == 'tast':
730*8975f5c5SAndroid Build Coastguard Worker    test = TastTest(args, unknown_args)
731*8975f5c5SAndroid Build Coastguard Worker  else:
732*8975f5c5SAndroid Build Coastguard Worker    test = GTestTest(args, unknown_args)
733*8975f5c5SAndroid Build Coastguard Worker
734*8975f5c5SAndroid Build Coastguard Worker  test.build_test_command()
735*8975f5c5SAndroid Build Coastguard Worker  logging.info('Running the following command on the device:')
736*8975f5c5SAndroid Build Coastguard Worker  logging.info(' '.join(test.test_cmd))
737*8975f5c5SAndroid Build Coastguard Worker
738*8975f5c5SAndroid Build Coastguard Worker  return test.run_test()
739*8975f5c5SAndroid Build Coastguard Worker
740*8975f5c5SAndroid Build Coastguard Worker
741*8975f5c5SAndroid Build Coastguard Workerdef host_cmd(args, cmd_args):
742*8975f5c5SAndroid Build Coastguard Worker  if not cmd_args:
743*8975f5c5SAndroid Build Coastguard Worker    raise TestFormatError('Must specify command to run on the host.')
744*8975f5c5SAndroid Build Coastguard Worker  if args.deploy_chrome and not args.path_to_outdir:
745*8975f5c5SAndroid Build Coastguard Worker    raise TestFormatError(
746*8975f5c5SAndroid Build Coastguard Worker        '--path-to-outdir must be specified if --deploy-chrome is passed.')
747*8975f5c5SAndroid Build Coastguard Worker
748*8975f5c5SAndroid Build Coastguard Worker  cros_run_test_cmd = [
749*8975f5c5SAndroid Build Coastguard Worker      CROS_RUN_TEST_PATH,
750*8975f5c5SAndroid Build Coastguard Worker      '--board',
751*8975f5c5SAndroid Build Coastguard Worker      args.board,
752*8975f5c5SAndroid Build Coastguard Worker      '--cache-dir',
753*8975f5c5SAndroid Build Coastguard Worker      os.path.join(CHROMIUM_SRC_PATH, args.cros_cache),
754*8975f5c5SAndroid Build Coastguard Worker  ]
755*8975f5c5SAndroid Build Coastguard Worker  if args.use_vm:
756*8975f5c5SAndroid Build Coastguard Worker    cros_run_test_cmd += [
757*8975f5c5SAndroid Build Coastguard Worker        '--start',
758*8975f5c5SAndroid Build Coastguard Worker        # Don't persist any filesystem changes after the VM shutsdown.
759*8975f5c5SAndroid Build Coastguard Worker        '--copy-on-write',
760*8975f5c5SAndroid Build Coastguard Worker    ]
761*8975f5c5SAndroid Build Coastguard Worker  else:
762*8975f5c5SAndroid Build Coastguard Worker    if args.fetch_cros_hostname:
763*8975f5c5SAndroid Build Coastguard Worker      cros_run_test_cmd += ['--device', get_cros_hostname()]
764*8975f5c5SAndroid Build Coastguard Worker    else:
765*8975f5c5SAndroid Build Coastguard Worker      cros_run_test_cmd += [
766*8975f5c5SAndroid Build Coastguard Worker          '--device', args.device if args.device else LAB_DUT_HOSTNAME
767*8975f5c5SAndroid Build Coastguard Worker      ]
768*8975f5c5SAndroid Build Coastguard Worker  if args.verbose:
769*8975f5c5SAndroid Build Coastguard Worker    cros_run_test_cmd.append('--debug')
770*8975f5c5SAndroid Build Coastguard Worker  if args.flash:
771*8975f5c5SAndroid Build Coastguard Worker    cros_run_test_cmd.append('--flash')
772*8975f5c5SAndroid Build Coastguard Worker    if args.public_image:
773*8975f5c5SAndroid Build Coastguard Worker      cros_run_test_cmd += ['--public-image']
774*8975f5c5SAndroid Build Coastguard Worker
775*8975f5c5SAndroid Build Coastguard Worker  if args.logs_dir:
776*8975f5c5SAndroid Build Coastguard Worker    for log in SYSTEM_LOG_LOCATIONS:
777*8975f5c5SAndroid Build Coastguard Worker      cros_run_test_cmd += ['--results-src', log]
778*8975f5c5SAndroid Build Coastguard Worker    cros_run_test_cmd += [
779*8975f5c5SAndroid Build Coastguard Worker        '--results-dest-dir',
780*8975f5c5SAndroid Build Coastguard Worker        os.path.join(args.logs_dir, 'system_logs')
781*8975f5c5SAndroid Build Coastguard Worker    ]
782*8975f5c5SAndroid Build Coastguard Worker
783*8975f5c5SAndroid Build Coastguard Worker  test_env = setup_env()
784*8975f5c5SAndroid Build Coastguard Worker  if args.deploy_chrome:
785*8975f5c5SAndroid Build Coastguard Worker    # Mounting ash-chrome gives it enough disk space to not need stripping
786*8975f5c5SAndroid Build Coastguard Worker    # most of the time.
787*8975f5c5SAndroid Build Coastguard Worker    cros_run_test_cmd.extend(['--deploy', '--mount'])
788*8975f5c5SAndroid Build Coastguard Worker
789*8975f5c5SAndroid Build Coastguard Worker    if not args.strip_chrome:
790*8975f5c5SAndroid Build Coastguard Worker      cros_run_test_cmd.append('--nostrip')
791*8975f5c5SAndroid Build Coastguard Worker
792*8975f5c5SAndroid Build Coastguard Worker    cros_run_test_cmd += [
793*8975f5c5SAndroid Build Coastguard Worker        '--build-dir',
794*8975f5c5SAndroid Build Coastguard Worker        os.path.join(CHROMIUM_SRC_PATH, args.path_to_outdir)
795*8975f5c5SAndroid Build Coastguard Worker    ]
796*8975f5c5SAndroid Build Coastguard Worker
797*8975f5c5SAndroid Build Coastguard Worker  cros_run_test_cmd += [
798*8975f5c5SAndroid Build Coastguard Worker      '--host-cmd',
799*8975f5c5SAndroid Build Coastguard Worker      '--',
800*8975f5c5SAndroid Build Coastguard Worker  ] + cmd_args
801*8975f5c5SAndroid Build Coastguard Worker
802*8975f5c5SAndroid Build Coastguard Worker  logging.info('Running the following command:')
803*8975f5c5SAndroid Build Coastguard Worker  logging.info(' '.join(cros_run_test_cmd))
804*8975f5c5SAndroid Build Coastguard Worker
805*8975f5c5SAndroid Build Coastguard Worker  return subprocess.call(
806*8975f5c5SAndroid Build Coastguard Worker      cros_run_test_cmd, stdout=sys.stdout, stderr=sys.stderr, env=test_env)
807*8975f5c5SAndroid Build Coastguard Worker
808*8975f5c5SAndroid Build Coastguard Worker
809*8975f5c5SAndroid Build Coastguard Workerdef get_cros_hostname_from_bot_id(bot_id):
810*8975f5c5SAndroid Build Coastguard Worker  """Parse hostname from a chromeos-swarming bot id."""
811*8975f5c5SAndroid Build Coastguard Worker  for prefix in ['cros-', 'crossk-']:
812*8975f5c5SAndroid Build Coastguard Worker    if bot_id.startswith(prefix):
813*8975f5c5SAndroid Build Coastguard Worker      return bot_id[len(prefix):]
814*8975f5c5SAndroid Build Coastguard Worker  return bot_id
815*8975f5c5SAndroid Build Coastguard Worker
816*8975f5c5SAndroid Build Coastguard Worker
817*8975f5c5SAndroid Build Coastguard Workerdef get_cros_hostname():
818*8975f5c5SAndroid Build Coastguard Worker  """Fetch bot_id from env var and parse hostname."""
819*8975f5c5SAndroid Build Coastguard Worker
820*8975f5c5SAndroid Build Coastguard Worker  # In chromeos-swarming, we can extract hostname from bot ID, since
821*8975f5c5SAndroid Build Coastguard Worker  # bot ID is formatted as "{prefix}{hostname}".
822*8975f5c5SAndroid Build Coastguard Worker  bot_id = os.environ.get('SWARMING_BOT_ID')
823*8975f5c5SAndroid Build Coastguard Worker  if bot_id:
824*8975f5c5SAndroid Build Coastguard Worker    return get_cros_hostname_from_bot_id(bot_id)
825*8975f5c5SAndroid Build Coastguard Worker
826*8975f5c5SAndroid Build Coastguard Worker  logging.warning(
827*8975f5c5SAndroid Build Coastguard Worker      'Attempted to read from SWARMING_BOT_ID env var and it was'
828*8975f5c5SAndroid Build Coastguard Worker      ' not defined. Will set %s as device instead.', LAB_DUT_HOSTNAME)
829*8975f5c5SAndroid Build Coastguard Worker  return LAB_DUT_HOSTNAME
830*8975f5c5SAndroid Build Coastguard Worker
831*8975f5c5SAndroid Build Coastguard Worker
832*8975f5c5SAndroid Build Coastguard Workerdef setup_env():
833*8975f5c5SAndroid Build Coastguard Worker  """Returns a copy of the current env with some needed vars added."""
834*8975f5c5SAndroid Build Coastguard Worker  env = os.environ.copy()
835*8975f5c5SAndroid Build Coastguard Worker  # Some chromite scripts expect chromite/bin to be on PATH.
836*8975f5c5SAndroid Build Coastguard Worker  env['PATH'] = env['PATH'] + ':' + os.path.join(CHROMITE_PATH, 'bin')
837*8975f5c5SAndroid Build Coastguard Worker  # deploy_chrome needs a set of GN args used to build chrome to determine if
838*8975f5c5SAndroid Build Coastguard Worker  # certain libraries need to be pushed to the device. It looks for the args via
839*8975f5c5SAndroid Build Coastguard Worker  # an env var. To trigger the default deploying behavior, give it a dummy set
840*8975f5c5SAndroid Build Coastguard Worker  # of args.
841*8975f5c5SAndroid Build Coastguard Worker  # TODO(crbug.com/40567963): Make the GN-dependent deps controllable via cmd
842*8975f5c5SAndroid Build Coastguard Worker  # line args.
843*8975f5c5SAndroid Build Coastguard Worker  if not env.get('GN_ARGS'):
844*8975f5c5SAndroid Build Coastguard Worker    env['GN_ARGS'] = 'enable_nacl = true'
845*8975f5c5SAndroid Build Coastguard Worker  if not env.get('USE'):
846*8975f5c5SAndroid Build Coastguard Worker    env['USE'] = 'highdpi'
847*8975f5c5SAndroid Build Coastguard Worker  return env
848*8975f5c5SAndroid Build Coastguard Worker
849*8975f5c5SAndroid Build Coastguard Worker
850*8975f5c5SAndroid Build Coastguard Workerdef add_common_args(*parsers):
851*8975f5c5SAndroid Build Coastguard Worker  for parser in parsers:
852*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument('--verbose', '-v', action='store_true')
853*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
854*8975f5c5SAndroid Build Coastguard Worker        '--board', type=str, required=True, help='Type of CrOS device.')
855*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
856*8975f5c5SAndroid Build Coastguard Worker        '--deploy-chrome',
857*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
858*8975f5c5SAndroid Build Coastguard Worker        help='Will deploy a locally built ash-chrome binary to the device '
859*8975f5c5SAndroid Build Coastguard Worker        'before running the host-cmd.')
860*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
861*8975f5c5SAndroid Build Coastguard Worker        '--cros-cache',
862*8975f5c5SAndroid Build Coastguard Worker        type=str,
863*8975f5c5SAndroid Build Coastguard Worker        default=DEFAULT_CROS_CACHE,
864*8975f5c5SAndroid Build Coastguard Worker        help='Path to cros cache.')
865*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
866*8975f5c5SAndroid Build Coastguard Worker        '--path-to-outdir',
867*8975f5c5SAndroid Build Coastguard Worker        type=str,
868*8975f5c5SAndroid Build Coastguard Worker        required=True,
869*8975f5c5SAndroid Build Coastguard Worker        help='Path to output directory, all of whose contents will be '
870*8975f5c5SAndroid Build Coastguard Worker        'deployed to the device.')
871*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
872*8975f5c5SAndroid Build Coastguard Worker        '--runtime-deps-path',
873*8975f5c5SAndroid Build Coastguard Worker        type=str,
874*8975f5c5SAndroid Build Coastguard Worker        help='Runtime data dependency file from GN.')
875*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
876*8975f5c5SAndroid Build Coastguard Worker        '--vpython-dir',
877*8975f5c5SAndroid Build Coastguard Worker        type=str,
878*8975f5c5SAndroid Build Coastguard Worker        help='Location on host of a directory containing a vpython binary to '
879*8975f5c5SAndroid Build Coastguard Worker        'deploy to the device before the test starts. The location of '
880*8975f5c5SAndroid Build Coastguard Worker        'this dir will be added onto PATH in the device. WARNING: The '
881*8975f5c5SAndroid Build Coastguard Worker        'arch of the device might not match the arch of the host, so '
882*8975f5c5SAndroid Build Coastguard Worker        'avoid using "${platform}" when downloading vpython via CIPD.')
883*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
884*8975f5c5SAndroid Build Coastguard Worker        '--logs-dir',
885*8975f5c5SAndroid Build Coastguard Worker        type=str,
886*8975f5c5SAndroid Build Coastguard Worker        dest='logs_dir',
887*8975f5c5SAndroid Build Coastguard Worker        help='Will copy everything under /var/log/ from the device after the '
888*8975f5c5SAndroid Build Coastguard Worker        'test into the specified dir.')
889*8975f5c5SAndroid Build Coastguard Worker    # Shard args are parsed here since we might also specify them via env vars.
890*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
891*8975f5c5SAndroid Build Coastguard Worker        '--test-launcher-shard-index',
892*8975f5c5SAndroid Build Coastguard Worker        type=int,
893*8975f5c5SAndroid Build Coastguard Worker        default=os.environ.get('GTEST_SHARD_INDEX', 0),
894*8975f5c5SAndroid Build Coastguard Worker        help='Index of the external shard to run.')
895*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
896*8975f5c5SAndroid Build Coastguard Worker        '--test-launcher-total-shards',
897*8975f5c5SAndroid Build Coastguard Worker        type=int,
898*8975f5c5SAndroid Build Coastguard Worker        default=os.environ.get('GTEST_TOTAL_SHARDS', 1),
899*8975f5c5SAndroid Build Coastguard Worker        help='Total number of external shards.')
900*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
901*8975f5c5SAndroid Build Coastguard Worker        '--flash',
902*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
903*8975f5c5SAndroid Build Coastguard Worker        help='Will flash the device to the current SDK version before running '
904*8975f5c5SAndroid Build Coastguard Worker        'the test.')
905*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
906*8975f5c5SAndroid Build Coastguard Worker        '--no-flash',
907*8975f5c5SAndroid Build Coastguard Worker        action='store_false',
908*8975f5c5SAndroid Build Coastguard Worker        dest='flash',
909*8975f5c5SAndroid Build Coastguard Worker        help='Will not flash the device before running the test.')
910*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
911*8975f5c5SAndroid Build Coastguard Worker        '--public-image',
912*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
913*8975f5c5SAndroid Build Coastguard Worker        help='Will flash a public "full" image to the device.')
914*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
915*8975f5c5SAndroid Build Coastguard Worker        '--magic-vm-cache',
916*8975f5c5SAndroid Build Coastguard Worker        help='Path to the magic CrOS VM cache dir. See the comment above '
917*8975f5c5SAndroid Build Coastguard Worker             '"magic_cros_vm_cache" in mixins.pyl for more info.')
918*8975f5c5SAndroid Build Coastguard Worker
919*8975f5c5SAndroid Build Coastguard Worker    vm_or_device_group = parser.add_mutually_exclusive_group()
920*8975f5c5SAndroid Build Coastguard Worker    vm_or_device_group.add_argument(
921*8975f5c5SAndroid Build Coastguard Worker        '--use-vm',
922*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
923*8975f5c5SAndroid Build Coastguard Worker        help='Will run the test in the VM instead of a device.')
924*8975f5c5SAndroid Build Coastguard Worker    vm_or_device_group.add_argument(
925*8975f5c5SAndroid Build Coastguard Worker        '--device',
926*8975f5c5SAndroid Build Coastguard Worker        type=str,
927*8975f5c5SAndroid Build Coastguard Worker        help='Hostname (or IP) of device to run the test on. This arg is not '
928*8975f5c5SAndroid Build Coastguard Worker        'required if --use-vm is set.')
929*8975f5c5SAndroid Build Coastguard Worker    vm_or_device_group.add_argument(
930*8975f5c5SAndroid Build Coastguard Worker        '--fetch-cros-hostname',
931*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
932*8975f5c5SAndroid Build Coastguard Worker        help='Will extract device hostname from the SWARMING_BOT_ID env var if '
933*8975f5c5SAndroid Build Coastguard Worker        'running on ChromeOS Swarming.')
934*8975f5c5SAndroid Build Coastguard Worker
935*8975f5c5SAndroid Build Coastguard Workerdef main():
936*8975f5c5SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
937*8975f5c5SAndroid Build Coastguard Worker  subparsers = parser.add_subparsers(dest='test_type')
938*8975f5c5SAndroid Build Coastguard Worker  # Host-side test args.
939*8975f5c5SAndroid Build Coastguard Worker  host_cmd_parser = subparsers.add_parser(
940*8975f5c5SAndroid Build Coastguard Worker      'host-cmd',
941*8975f5c5SAndroid Build Coastguard Worker      help='Runs a host-side test. Pass the host-side command to run after '
942*8975f5c5SAndroid Build Coastguard Worker      '"--". If --use-vm is passed, hostname and port for the device '
943*8975f5c5SAndroid Build Coastguard Worker      'will be 127.0.0.1:9222.')
944*8975f5c5SAndroid Build Coastguard Worker  host_cmd_parser.set_defaults(func=host_cmd)
945*8975f5c5SAndroid Build Coastguard Worker  host_cmd_parser.add_argument(
946*8975f5c5SAndroid Build Coastguard Worker      '--strip-chrome',
947*8975f5c5SAndroid Build Coastguard Worker      action='store_true',
948*8975f5c5SAndroid Build Coastguard Worker      help='Strips symbols from ash-chrome before deploying to the device.')
949*8975f5c5SAndroid Build Coastguard Worker
950*8975f5c5SAndroid Build Coastguard Worker  gtest_parser = subparsers.add_parser(
951*8975f5c5SAndroid Build Coastguard Worker      'gtest', help='Runs a device-side gtest.')
952*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.set_defaults(func=device_test)
953*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
954*8975f5c5SAndroid Build Coastguard Worker      '--test-exe',
955*8975f5c5SAndroid Build Coastguard Worker      type=str,
956*8975f5c5SAndroid Build Coastguard Worker      required=True,
957*8975f5c5SAndroid Build Coastguard Worker      help='Path to test executable to run inside the device.')
958*8975f5c5SAndroid Build Coastguard Worker
959*8975f5c5SAndroid Build Coastguard Worker  # GTest args. Some are passed down to the test binary in the device. Others
960*8975f5c5SAndroid Build Coastguard Worker  # are parsed here since they might need tweaking or special handling.
961*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
962*8975f5c5SAndroid Build Coastguard Worker      '--test-launcher-summary-output',
963*8975f5c5SAndroid Build Coastguard Worker      type=str,
964*8975f5c5SAndroid Build Coastguard Worker      help='When set, will pass the same option down to the test and retrieve '
965*8975f5c5SAndroid Build Coastguard Worker      'its result file at the specified location.')
966*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
967*8975f5c5SAndroid Build Coastguard Worker      '--stop-ui',
968*8975f5c5SAndroid Build Coastguard Worker      action='store_true',
969*8975f5c5SAndroid Build Coastguard Worker      help='Will stop the UI service in the device before running the test. '
970*8975f5c5SAndroid Build Coastguard Worker      'Also start the UI service after all tests are done.')
971*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
972*8975f5c5SAndroid Build Coastguard Worker      '--as-root',
973*8975f5c5SAndroid Build Coastguard Worker      action='store_true',
974*8975f5c5SAndroid Build Coastguard Worker      help='Will run the test as root on the device. Runs as user=chronos '
975*8975f5c5SAndroid Build Coastguard Worker      'otherwise. This is mutually exclusive with "--stop-ui" above due to '
976*8975f5c5SAndroid Build Coastguard Worker      'setup issues.')
977*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
978*8975f5c5SAndroid Build Coastguard Worker      '--trace-dir',
979*8975f5c5SAndroid Build Coastguard Worker      type=str,
980*8975f5c5SAndroid Build Coastguard Worker      help='When set, will pass down to the test to generate the trace and '
981*8975f5c5SAndroid Build Coastguard Worker      'retrieve the trace files to the specified location.')
982*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
983*8975f5c5SAndroid Build Coastguard Worker      '--env-var',
984*8975f5c5SAndroid Build Coastguard Worker      nargs=2,
985*8975f5c5SAndroid Build Coastguard Worker      action='append',
986*8975f5c5SAndroid Build Coastguard Worker      default=[],
987*8975f5c5SAndroid Build Coastguard Worker      help='Env var to set on the device for the duration of the test. '
988*8975f5c5SAndroid Build Coastguard Worker      'Expected format is "--env-var SOME_VAR_NAME some_var_value". Specify '
989*8975f5c5SAndroid Build Coastguard Worker      'multiple times for more than one var.')
990*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
991*8975f5c5SAndroid Build Coastguard Worker      '--run-test-sudo-helper',
992*8975f5c5SAndroid Build Coastguard Worker      action='store_true',
993*8975f5c5SAndroid Build Coastguard Worker      help='When set, will run test_sudo_helper before the test and stop it '
994*8975f5c5SAndroid Build Coastguard Worker      'after test finishes.')
995*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
996*8975f5c5SAndroid Build Coastguard Worker      "--no-clean",
997*8975f5c5SAndroid Build Coastguard Worker      action="store_false",
998*8975f5c5SAndroid Build Coastguard Worker      dest="clean",
999*8975f5c5SAndroid Build Coastguard Worker      default=True,
1000*8975f5c5SAndroid Build Coastguard Worker      help="Do not clean up the deployed files after running the test. "
1001*8975f5c5SAndroid Build Coastguard Worker      "Only supported for --remote-cmd tests")
1002*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
1003*8975f5c5SAndroid Build Coastguard Worker      '--set-selinux-label',
1004*8975f5c5SAndroid Build Coastguard Worker      action='append',
1005*8975f5c5SAndroid Build Coastguard Worker      default=[],
1006*8975f5c5SAndroid Build Coastguard Worker      help='Set the selinux label for a file before running. The format is:\n'
1007*8975f5c5SAndroid Build Coastguard Worker      '  --set-selinux-label=<filename>=<label>\n'
1008*8975f5c5SAndroid Build Coastguard Worker      'So:\n'
1009*8975f5c5SAndroid Build Coastguard Worker      '  --set-selinux-label=my_test=u:r:cros_foo_label:s0\n'
1010*8975f5c5SAndroid Build Coastguard Worker      'You can specify it more than one time to set multiple files tags.')
1011*8975f5c5SAndroid Build Coastguard Worker  gtest_parser.add_argument(
1012*8975f5c5SAndroid Build Coastguard Worker      '--use-deployed-dbus-configs',
1013*8975f5c5SAndroid Build Coastguard Worker      action='store_true',
1014*8975f5c5SAndroid Build Coastguard Worker      help='When set, will bind mount deployed dbus config to chrome dbus dir '
1015*8975f5c5SAndroid Build Coastguard Worker      'and ask dbus daemon to reload config before running tests.')
1016*8975f5c5SAndroid Build Coastguard Worker
1017*8975f5c5SAndroid Build Coastguard Worker  # Tast test args.
1018*8975f5c5SAndroid Build Coastguard Worker  # pylint: disable=line-too-long
1019*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser = subparsers.add_parser(
1020*8975f5c5SAndroid Build Coastguard Worker      'tast',
1021*8975f5c5SAndroid Build Coastguard Worker      help='Runs a device-side set of Tast tests. For more details, see: '
1022*8975f5c5SAndroid Build Coastguard Worker      'https://chromium.googlesource.com/chromiumos/platform/tast/+/main/docs/running_tests.md'
1023*8975f5c5SAndroid Build Coastguard Worker  )
1024*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.set_defaults(func=device_test)
1025*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.add_argument(
1026*8975f5c5SAndroid Build Coastguard Worker      '--suite-name',
1027*8975f5c5SAndroid Build Coastguard Worker      type=str,
1028*8975f5c5SAndroid Build Coastguard Worker      required=True,
1029*8975f5c5SAndroid Build Coastguard Worker      help='Name to apply to the set of Tast tests to run. This has no effect '
1030*8975f5c5SAndroid Build Coastguard Worker      'on what is executed, but is used mainly for test results reporting '
1031*8975f5c5SAndroid Build Coastguard Worker      'and tracking (eg: flakiness dashboard).')
1032*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.add_argument(
1033*8975f5c5SAndroid Build Coastguard Worker      '--test-launcher-summary-output',
1034*8975f5c5SAndroid Build Coastguard Worker      type=str,
1035*8975f5c5SAndroid Build Coastguard Worker      help='Generates a simple GTest-style JSON result file for the test run.')
1036*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.add_argument(
1037*8975f5c5SAndroid Build Coastguard Worker      '--attr-expr',
1038*8975f5c5SAndroid Build Coastguard Worker      type=str,
1039*8975f5c5SAndroid Build Coastguard Worker      help='A boolean expression whose matching tests will run '
1040*8975f5c5SAndroid Build Coastguard Worker      '(eg: ("dep:chrome")).')
1041*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.add_argument(
1042*8975f5c5SAndroid Build Coastguard Worker      '--strip-chrome',
1043*8975f5c5SAndroid Build Coastguard Worker      action='store_true',
1044*8975f5c5SAndroid Build Coastguard Worker      help='Strips symbols from ash-chrome before deploying to the device.')
1045*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.add_argument(
1046*8975f5c5SAndroid Build Coastguard Worker      '--tast-var',
1047*8975f5c5SAndroid Build Coastguard Worker      action='append',
1048*8975f5c5SAndroid Build Coastguard Worker      dest='tast_vars',
1049*8975f5c5SAndroid Build Coastguard Worker      help='Runtime variables for Tast tests, and the format are expected to '
1050*8975f5c5SAndroid Build Coastguard Worker      'be "key=value" pairs.')
1051*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.add_argument(
1052*8975f5c5SAndroid Build Coastguard Worker      '--tast-retries',
1053*8975f5c5SAndroid Build Coastguard Worker      type=int,
1054*8975f5c5SAndroid Build Coastguard Worker      dest='tast_retries',
1055*8975f5c5SAndroid Build Coastguard Worker      help='Number of retries for failed Tast tests on the same DUT.')
1056*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.add_argument(
1057*8975f5c5SAndroid Build Coastguard Worker      '--test',
1058*8975f5c5SAndroid Build Coastguard Worker      '-t',
1059*8975f5c5SAndroid Build Coastguard Worker      action='append',
1060*8975f5c5SAndroid Build Coastguard Worker      dest='tests',
1061*8975f5c5SAndroid Build Coastguard Worker      help='A Tast test to run in the device (eg: "login.Chrome").')
1062*8975f5c5SAndroid Build Coastguard Worker  tast_test_parser.add_argument(
1063*8975f5c5SAndroid Build Coastguard Worker      '--gtest_filter',
1064*8975f5c5SAndroid Build Coastguard Worker      type=str,
1065*8975f5c5SAndroid Build Coastguard Worker      help="Similar to GTest's arg of the same name, this will filter out the "
1066*8975f5c5SAndroid Build Coastguard Worker      "specified tests from the Tast run. However, due to the nature of Tast's "
1067*8975f5c5SAndroid Build Coastguard Worker      'cmd-line API, this will overwrite the value(s) of "--test" above.')
1068*8975f5c5SAndroid Build Coastguard Worker
1069*8975f5c5SAndroid Build Coastguard Worker  add_common_args(gtest_parser, tast_test_parser, host_cmd_parser)
1070*8975f5c5SAndroid Build Coastguard Worker  args, unknown_args = parser.parse_known_args()
1071*8975f5c5SAndroid Build Coastguard Worker
1072*8975f5c5SAndroid Build Coastguard Worker  if args.test_type == 'gtest' and args.stop_ui and args.as_root:
1073*8975f5c5SAndroid Build Coastguard Worker    parser.error('Unable to run gtests with both --stop-ui and --as-root')
1074*8975f5c5SAndroid Build Coastguard Worker
1075*8975f5c5SAndroid Build Coastguard Worker  # Re-add N-1 -v/--verbose flags to the args we'll pass to whatever we are
1076*8975f5c5SAndroid Build Coastguard Worker  # running. The assumption is that only one verbosity incrase would be meant
1077*8975f5c5SAndroid Build Coastguard Worker  # for this script since it's a boolean value instead of increasing verbosity
1078*8975f5c5SAndroid Build Coastguard Worker  # with more instances.
1079*8975f5c5SAndroid Build Coastguard Worker  verbose_flags = [a for a in sys.argv if a in ('-v', '--verbose')]
1080*8975f5c5SAndroid Build Coastguard Worker  if verbose_flags:
1081*8975f5c5SAndroid Build Coastguard Worker    unknown_args += verbose_flags[1:]
1082*8975f5c5SAndroid Build Coastguard Worker
1083*8975f5c5SAndroid Build Coastguard Worker  logging.basicConfig(level=logging.DEBUG if args.verbose else logging.WARN)
1084*8975f5c5SAndroid Build Coastguard Worker
1085*8975f5c5SAndroid Build Coastguard Worker  if not args.use_vm and not args.device and not args.fetch_cros_hostname:
1086*8975f5c5SAndroid Build Coastguard Worker    logging.warning(
1087*8975f5c5SAndroid Build Coastguard Worker        'The test runner is now assuming running in the lab environment, if '
1088*8975f5c5SAndroid Build Coastguard Worker        'this is unintentional, please re-invoke the test runner with the '
1089*8975f5c5SAndroid Build Coastguard Worker        '"--use-vm" arg if using a VM, otherwise use the "--device=<DUT>" arg '
1090*8975f5c5SAndroid Build Coastguard Worker        'to specify a DUT.')
1091*8975f5c5SAndroid Build Coastguard Worker
1092*8975f5c5SAndroid Build Coastguard Worker    # If we're not running on a VM, but haven't specified a hostname, assume
1093*8975f5c5SAndroid Build Coastguard Worker    # we're on a lab bot and are trying to run a test on a lab DUT. See if the
1094*8975f5c5SAndroid Build Coastguard Worker    # magic lab DUT hostname resolves to anything. (It will in the lab and will
1095*8975f5c5SAndroid Build Coastguard Worker    # not on dev machines.)
1096*8975f5c5SAndroid Build Coastguard Worker    try:
1097*8975f5c5SAndroid Build Coastguard Worker      socket.getaddrinfo(LAB_DUT_HOSTNAME, None)
1098*8975f5c5SAndroid Build Coastguard Worker    except socket.gaierror:
1099*8975f5c5SAndroid Build Coastguard Worker      logging.error('The default lab DUT hostname of %s is unreachable.',
1100*8975f5c5SAndroid Build Coastguard Worker                    LAB_DUT_HOSTNAME)
1101*8975f5c5SAndroid Build Coastguard Worker      return 1
1102*8975f5c5SAndroid Build Coastguard Worker
1103*8975f5c5SAndroid Build Coastguard Worker  if args.flash and args.public_image:
1104*8975f5c5SAndroid Build Coastguard Worker    # The flashing tools depend on being unauthenticated with GS when flashing
1105*8975f5c5SAndroid Build Coastguard Worker    # public images, so make sure the env var GS uses to locate its creds is
1106*8975f5c5SAndroid Build Coastguard Worker    # unset in that case.
1107*8975f5c5SAndroid Build Coastguard Worker    os.environ.pop('BOTO_CONFIG', None)
1108*8975f5c5SAndroid Build Coastguard Worker
1109*8975f5c5SAndroid Build Coastguard Worker  if args.magic_vm_cache:
1110*8975f5c5SAndroid Build Coastguard Worker    full_vm_cache_path = os.path.join(CHROMIUM_SRC_PATH, args.magic_vm_cache)
1111*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(full_vm_cache_path):
1112*8975f5c5SAndroid Build Coastguard Worker      with open(os.path.join(full_vm_cache_path, 'swarming.txt'), 'w') as f:
1113*8975f5c5SAndroid Build Coastguard Worker        f.write('non-empty file to make swarming persist this cache')
1114*8975f5c5SAndroid Build Coastguard Worker
1115*8975f5c5SAndroid Build Coastguard Worker  return args.func(args, unknown_args)
1116*8975f5c5SAndroid Build Coastguard Worker
1117*8975f5c5SAndroid Build Coastguard Worker
1118*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__':
1119*8975f5c5SAndroid Build Coastguard Worker  sys.exit(main())
1120