xref: /aosp_15_r20/external/autotest/site_utils/test_that.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li#!/usr/bin/python3
2*9c5db199SXin Li# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Lifrom __future__ import absolute_import
7*9c5db199SXin Lifrom __future__ import division
8*9c5db199SXin Lifrom __future__ import print_function
9*9c5db199SXin Li
10*9c5db199SXin Liimport argparse
11*9c5db199SXin Liimport json
12*9c5db199SXin Liimport os
13*9c5db199SXin Liimport signal
14*9c5db199SXin Liimport subprocess
15*9c5db199SXin Liimport sys
16*9c5db199SXin Li
17*9c5db199SXin Liimport logging
18*9c5db199SXin Li# Turn the logging level to INFO before importing other autotest
19*9c5db199SXin Li# code, to avoid having failed import logging messages confuse the
20*9c5db199SXin Li# test_that user.
21*9c5db199SXin Lilogging.basicConfig(level=logging.INFO)
22*9c5db199SXin Li
23*9c5db199SXin Liimport common
24*9c5db199SXin Lifrom autotest_lib.client.common_lib import error, logging_manager
25*9c5db199SXin Lifrom autotest_lib.server import server_logging_config
26*9c5db199SXin Lifrom autotest_lib.server.cros.dynamic_suite import constants
27*9c5db199SXin Lifrom autotest_lib.server.hosts import factory
28*9c5db199SXin Lifrom autotest_lib.site_utils import test_runner_utils
29*9c5db199SXin Li
30*9c5db199SXin Li
31*9c5db199SXin Li_QUICKMERGE_SCRIPTNAME = '/mnt/host/source/chromite/bin/autotest_quickmerge'
32*9c5db199SXin Li
33*9c5db199SXin Li
34*9c5db199SXin Lidef _get_info_from_host(remote, board=None, model=None, ssh_options=''):
35*9c5db199SXin Li    """Get the info of the remote host if needed.
36*9c5db199SXin Li
37*9c5db199SXin Li    @param remote: string representing the IP of the remote host.
38*9c5db199SXin Li    @param board: board arg from CLI.
39*9c5db199SXin Li    @param model: model arg from CLI.
40*9c5db199SXin Li
41*9c5db199SXin Li    @return: board, model string representing the board, model
42*9c5db199SXin Li        of the remote host.
43*9c5db199SXin Li    """
44*9c5db199SXin Li
45*9c5db199SXin Li    if board and model:
46*9c5db199SXin Li        return board, model
47*9c5db199SXin Li
48*9c5db199SXin Li    host = factory.create_host(remote, ssh_options=ssh_options)
49*9c5db199SXin Li
50*9c5db199SXin Li    if not board:
51*9c5db199SXin Li        logging.info(
52*9c5db199SXin Li                'Board unspecified, attempting to determine board from host.')
53*9c5db199SXin Li        try:
54*9c5db199SXin Li            board = host.get_board().replace(constants.BOARD_PREFIX, '')
55*9c5db199SXin Li        except error.AutoservRunError:
56*9c5db199SXin Li            raise test_runner_utils.TestThatRunError(
57*9c5db199SXin Li                    'Cannot determine board, please specify a --board option.')
58*9c5db199SXin Li        logging.info('Detected host board: %s', board)
59*9c5db199SXin Li
60*9c5db199SXin Li    if not model:
61*9c5db199SXin Li        logging.info(
62*9c5db199SXin Li                'Model unspecified, attempting to determine model from host.')
63*9c5db199SXin Li        try:
64*9c5db199SXin Li            model = host.get_platform()
65*9c5db199SXin Li        except error.AutoservRunError:
66*9c5db199SXin Li            raise test_runner_utils.TestThatRunError(
67*9c5db199SXin Li                    'Cannot determine model, please specify a --model option.')
68*9c5db199SXin Li        logging.info('Detected host model: %s', model)
69*9c5db199SXin Li
70*9c5db199SXin Li    return board, model
71*9c5db199SXin Li
72*9c5db199SXin Li
73*9c5db199SXin Lidef validate_arguments(arguments):
74*9c5db199SXin Li    """
75*9c5db199SXin Li    Validates parsed arguments.
76*9c5db199SXin Li
77*9c5db199SXin Li    @param arguments: arguments object, as parsed by ParseArguments
78*9c5db199SXin Li    @raises: ValueError if arguments were invalid.
79*9c5db199SXin Li    """
80*9c5db199SXin Li    if arguments.remote == ':lab:':
81*9c5db199SXin Li        if arguments.args:
82*9c5db199SXin Li            raise ValueError('--args flag not supported when running against '
83*9c5db199SXin Li                             ':lab:')
84*9c5db199SXin Li        if arguments.pretend:
85*9c5db199SXin Li            raise ValueError('--pretend flag not supported when running '
86*9c5db199SXin Li                             'against :lab:')
87*9c5db199SXin Li        if arguments.ssh_verbosity:
88*9c5db199SXin Li            raise ValueError('--ssh_verbosity flag not supported when running '
89*9c5db199SXin Li                             'against :lab:')
90*9c5db199SXin Li        if not arguments.board or arguments.build == test_runner_utils.NO_BUILD:
91*9c5db199SXin Li            raise ValueError('--board and --build are both required when '
92*9c5db199SXin Li                             'running against :lab:')
93*9c5db199SXin Li    else:
94*9c5db199SXin Li        if arguments.web:
95*9c5db199SXin Li            raise ValueError('--web flag not supported when running locally')
96*9c5db199SXin Li
97*9c5db199SXin Li    try:
98*9c5db199SXin Li        json.loads(arguments.host_attributes)
99*9c5db199SXin Li    except TypeError:
100*9c5db199SXin Li        raise ValueError("--host_attributes must be quoted dict, got: %s" %
101*9c5db199SXin Li                         arguments.host_attributes)
102*9c5db199SXin Li
103*9c5db199SXin Li
104*9c5db199SXin Lidef parse_arguments(argv):
105*9c5db199SXin Li    """
106*9c5db199SXin Li    Parse command line arguments
107*9c5db199SXin Li
108*9c5db199SXin Li    @param argv: argument list to parse
109*9c5db199SXin Li    @returns:    parsed arguments
110*9c5db199SXin Li    @raises SystemExit if arguments are malformed, or required arguments
111*9c5db199SXin Li            are not present.
112*9c5db199SXin Li    """
113*9c5db199SXin Li    return _parse_arguments_internal(argv)[0]
114*9c5db199SXin Li
115*9c5db199SXin Li
116*9c5db199SXin Lidef _parse_arguments_internal(argv):
117*9c5db199SXin Li    """
118*9c5db199SXin Li    Parse command line arguments
119*9c5db199SXin Li
120*9c5db199SXin Li    @param argv: argument list to parse
121*9c5db199SXin Li    @returns:    tuple of parsed arguments and argv suitable for remote runs
122*9c5db199SXin Li    @raises SystemExit if arguments are malformed, or required arguments
123*9c5db199SXin Li            are not present.
124*9c5db199SXin Li    """
125*9c5db199SXin Li    local_parser, remote_argv = parse_local_arguments(argv)
126*9c5db199SXin Li
127*9c5db199SXin Li    parser = argparse.ArgumentParser(description='Run remote tests.',
128*9c5db199SXin Li                                     parents=[local_parser])
129*9c5db199SXin Li
130*9c5db199SXin Li    parser.add_argument('remote', metavar='REMOTE',
131*9c5db199SXin Li                        help='hostname[:port] for remote device. Specify '
132*9c5db199SXin Li                             ':lab: to run in test lab. When tests are run in '
133*9c5db199SXin Li                             'the lab, test_that will use the client autotest '
134*9c5db199SXin Li                             'package for the build specified with --build, '
135*9c5db199SXin Li                             'and the lab server code rather than local '
136*9c5db199SXin Li                             'changes.')
137*9c5db199SXin Li    test_runner_utils.add_common_args(parser)
138*9c5db199SXin Li    parser.add_argument('-b', '--board', metavar='BOARD',
139*9c5db199SXin Li                        action='store',
140*9c5db199SXin Li                        help='Board for which the test will run. '
141*9c5db199SXin Li                             'Default: %(default)s')
142*9c5db199SXin Li    parser.add_argument('-m',
143*9c5db199SXin Li                        '--model',
144*9c5db199SXin Li                        metavar='MODEL',
145*9c5db199SXin Li                        help='Specific model the test will run against. '
146*9c5db199SXin Li                        'Matches the model:FAKE_MODEL label for the host.')
147*9c5db199SXin Li    parser.add_argument('-i', '--build', metavar='BUILD',
148*9c5db199SXin Li                        default=test_runner_utils.NO_BUILD,
149*9c5db199SXin Li                        help='Build to test. Device will be reimaged if '
150*9c5db199SXin Li                             'necessary. Omit flag to skip reimage and test '
151*9c5db199SXin Li                             'against already installed DUT image. Examples: '
152*9c5db199SXin Li                             'link-paladin/R34-5222.0.0-rc2, '
153*9c5db199SXin Li                             'lumpy-release/R34-5205.0.0')
154*9c5db199SXin Li    parser.add_argument('-p', '--pool', metavar='POOL', default='suites',
155*9c5db199SXin Li                        help='Pool to use when running tests in the lab. '
156*9c5db199SXin Li                             'Default is "suites"')
157*9c5db199SXin Li    parser.add_argument('--autotest_dir', metavar='AUTOTEST_DIR',
158*9c5db199SXin Li                        help='Use AUTOTEST_DIR instead of normal board sysroot '
159*9c5db199SXin Li                             'copy of autotest, and skip the quickmerge step.')
160*9c5db199SXin Li    parser.add_argument('--no-quickmerge', action='store_true', default=False,
161*9c5db199SXin Li                        dest='no_quickmerge',
162*9c5db199SXin Li                        help='Skip the quickmerge step and use the sysroot '
163*9c5db199SXin Li                             'as it currently is. May result in un-merged '
164*9c5db199SXin Li                             'source tree changes not being reflected in the '
165*9c5db199SXin Li                             'run. If using --autotest_dir, this flag is '
166*9c5db199SXin Li                             'automatically applied.')
167*9c5db199SXin Li    parser.add_argument('--allow-chrome-crashes',
168*9c5db199SXin Li                        action='store_true',
169*9c5db199SXin Li                        default=False,
170*9c5db199SXin Li                        dest='allow_chrome_crashes',
171*9c5db199SXin Li                        help='Ignore chrome crashes when producing test '
172*9c5db199SXin Li                        'report. This flag gets passed along to the '
173*9c5db199SXin Li                        'report generation tool.')
174*9c5db199SXin Li    parser.add_argument('--ssh_private_key', action='store',
175*9c5db199SXin Li                        default=test_runner_utils.TEST_KEY_PATH,
176*9c5db199SXin Li                        help='Path to the private ssh key.')
177*9c5db199SXin Li    parser.add_argument(
178*9c5db199SXin Li            '--companion_hosts',
179*9c5db199SXin Li            action='store',
180*9c5db199SXin Li            default=None,
181*9c5db199SXin Li            help='Companion duts for the test, quoted space seperated strings')
182*9c5db199SXin Li    parser.add_argument('--dut_servers',
183*9c5db199SXin Li                        action='store',
184*9c5db199SXin Li                        default=None,
185*9c5db199SXin Li                        help='DUT servers for the test.')
186*9c5db199SXin Li    parser.add_argument('--minus',
187*9c5db199SXin Li                        dest='minus',
188*9c5db199SXin Li                        nargs='*',
189*9c5db199SXin Li                        help='List of tests to not use.',
190*9c5db199SXin Li                        default=[''])
191*9c5db199SXin Li    parser.add_argument('--py_version',
192*9c5db199SXin Li                        dest='py_version',
193*9c5db199SXin Li                        help='Python version to use, passed '
194*9c5db199SXin Li                        'to Autotest modules, defaults to 2.',
195*9c5db199SXin Li                        default=None)
196*9c5db199SXin Li    parser.add_argument('--CFT',
197*9c5db199SXin Li                        action='store_true',
198*9c5db199SXin Li                        default=False,
199*9c5db199SXin Li                        dest='CFT',
200*9c5db199SXin Li                        help="If running in, or mocking, the CFT env.")
201*9c5db199SXin Li    parser.add_argument('--host_attributes',
202*9c5db199SXin Li                        action='store',
203*9c5db199SXin Li                        default='{}',
204*9c5db199SXin Li                        help='host_attributes')
205*9c5db199SXin Li    parser.add_argument('--host_labels',
206*9c5db199SXin Li                        action='store',
207*9c5db199SXin Li                        default="",
208*9c5db199SXin Li                        help='host_labels, quoted space seperated strings')
209*9c5db199SXin Li    parser.add_argument('--label',
210*9c5db199SXin Li                        action='store',
211*9c5db199SXin Li                        default="",
212*9c5db199SXin Li                        help='label for test name')
213*9c5db199SXin Li    return parser.parse_args(argv), remote_argv
214*9c5db199SXin Li
215*9c5db199SXin Li
216*9c5db199SXin Lidef parse_local_arguments(argv):
217*9c5db199SXin Li    """
218*9c5db199SXin Li    Strips out arguments that are not to be passed through to runs.
219*9c5db199SXin Li
220*9c5db199SXin Li    Add any arguments that should not be passed to remote test_that runs here.
221*9c5db199SXin Li
222*9c5db199SXin Li    @param argv: argument list to parse.
223*9c5db199SXin Li    @returns: tuple of local argument parser and remaining argv.
224*9c5db199SXin Li    """
225*9c5db199SXin Li    parser = argparse.ArgumentParser(add_help=False)
226*9c5db199SXin Li    parser.add_argument('-w', '--web', dest='web', default=None,
227*9c5db199SXin Li                        help='Address of a webserver to receive test requests.')
228*9c5db199SXin Li    parser.add_argument('-x', '--max_runtime_mins', type=int,
229*9c5db199SXin Li                        dest='max_runtime_mins', default=20,
230*9c5db199SXin Li                        help='Default time allowed for the tests to complete.')
231*9c5db199SXin Li    parser.add_argument('--no-retries', '--no-retry',
232*9c5db199SXin Li                        dest='retry', action='store_false', default=True,
233*9c5db199SXin Li                        help='For local runs only, ignore any retries '
234*9c5db199SXin Li                             'specified in the control files.')
235*9c5db199SXin Li    _, remaining_argv = parser.parse_known_args(argv)
236*9c5db199SXin Li    return parser, remaining_argv
237*9c5db199SXin Li
238*9c5db199SXin Li
239*9c5db199SXin Lidef perform_bootstrap_into_autotest_root(arguments, autotest_path, argv):
240*9c5db199SXin Li    """
241*9c5db199SXin Li    Perfoms a bootstrap to run test_that from the |autotest_path|.
242*9c5db199SXin Li
243*9c5db199SXin Li    This function is to be called from test_that's main() script, when
244*9c5db199SXin Li    test_that is executed from the source tree location. It runs
245*9c5db199SXin Li    autotest_quickmerge to update the sysroot unless arguments.no_quickmerge
246*9c5db199SXin Li    is set. It then executes and waits on the version of test_that.py
247*9c5db199SXin Li    in |autotest_path|.
248*9c5db199SXin Li
249*9c5db199SXin Li    @param arguments: A parsed arguments object, as returned from
250*9c5db199SXin Li                      test_that.parse_arguments(...).
251*9c5db199SXin Li    @param autotest_path: Full absolute path to the autotest root directory.
252*9c5db199SXin Li    @param argv: The arguments list, as passed to main(...)
253*9c5db199SXin Li
254*9c5db199SXin Li    @returns: The return code of the test_that script that was executed in
255*9c5db199SXin Li              |autotest_path|.
256*9c5db199SXin Li    """
257*9c5db199SXin Li    logging_manager.configure_logging(
258*9c5db199SXin Li            server_logging_config.ServerLoggingConfig(),
259*9c5db199SXin Li            use_console=True,
260*9c5db199SXin Li            verbose=arguments.debug)
261*9c5db199SXin Li    if arguments.no_quickmerge:
262*9c5db199SXin Li        logging.info('Skipping quickmerge step.')
263*9c5db199SXin Li    else:
264*9c5db199SXin Li        logging.info('Running autotest_quickmerge step.')
265*9c5db199SXin Li        command = [_QUICKMERGE_SCRIPTNAME, '--board='+arguments.board]
266*9c5db199SXin Li        s = subprocess.Popen(command,
267*9c5db199SXin Li                             stdout=subprocess.PIPE,
268*9c5db199SXin Li                             stderr=subprocess.STDOUT)
269*9c5db199SXin Li        for message in iter(s.stdout.readline, b''):
270*9c5db199SXin Li            logging.info('quickmerge| %s', message.strip())
271*9c5db199SXin Li        return_code = s.wait()
272*9c5db199SXin Li        if return_code:
273*9c5db199SXin Li            raise test_runner_utils.TestThatRunError(
274*9c5db199SXin Li                    'autotest_quickmerge failed with error code %s.' %
275*9c5db199SXin Li                    return_code)
276*9c5db199SXin Li
277*9c5db199SXin Li    logging.info('Re-running test_that script in %s copy of autotest.',
278*9c5db199SXin Li                 autotest_path)
279*9c5db199SXin Li    script_command = os.path.join(autotest_path, 'site_utils',
280*9c5db199SXin Li                                  'test_that.py')
281*9c5db199SXin Li    if not os.path.exists(script_command):
282*9c5db199SXin Li        raise test_runner_utils.TestThatRunError(
283*9c5db199SXin Li            'Unable to bootstrap to autotest root, %s not found.' %
284*9c5db199SXin Li            script_command)
285*9c5db199SXin Li    proc = None
286*9c5db199SXin Li    def resend_sig(signum, stack_frame):
287*9c5db199SXin Li        #pylint: disable-msg=C0111
288*9c5db199SXin Li        if proc:
289*9c5db199SXin Li            proc.send_signal(signum)
290*9c5db199SXin Li    signal.signal(signal.SIGINT, resend_sig)
291*9c5db199SXin Li    signal.signal(signal.SIGTERM, resend_sig)
292*9c5db199SXin Li
293*9c5db199SXin Li    proc = subprocess.Popen([script_command] + argv)
294*9c5db199SXin Li
295*9c5db199SXin Li    return proc.wait()
296*9c5db199SXin Li
297*9c5db199SXin Li
298*9c5db199SXin Lidef _main_for_local_run(argv, arguments):
299*9c5db199SXin Li    """
300*9c5db199SXin Li    Effective entry point for local test_that runs.
301*9c5db199SXin Li
302*9c5db199SXin Li    @param argv: Script command line arguments.
303*9c5db199SXin Li    @param arguments: Parsed command line arguments.
304*9c5db199SXin Li    """
305*9c5db199SXin Li    results_directory = test_runner_utils.create_results_directory(
306*9c5db199SXin Li            arguments.results_dir, arguments.board)
307*9c5db199SXin Li    test_runner_utils.add_ssh_identity(results_directory,
308*9c5db199SXin Li                                       arguments.ssh_private_key)
309*9c5db199SXin Li    arguments.results_dir = results_directory
310*9c5db199SXin Li
311*9c5db199SXin Li    # If the board and/or model is not specified through --board and/or
312*9c5db199SXin Li    # --model, and is not set in the default_board file, determine the board by
313*9c5db199SXin Li    # ssh-ing into the host. Also prepend it to argv so we can re-use it when we
314*9c5db199SXin Li    # run test_that from the sysroot.
315*9c5db199SXin Li    arguments.board, arguments.model = _get_info_from_host(
316*9c5db199SXin Li            arguments.remote,
317*9c5db199SXin Li            arguments.board,
318*9c5db199SXin Li            arguments.model,
319*9c5db199SXin Li            ssh_options=arguments.ssh_options)
320*9c5db199SXin Li    argv = ['--board=%s' % (arguments.board, )] + argv
321*9c5db199SXin Li    argv = ['--model=%s' % (arguments.model, )] + argv
322*9c5db199SXin Li
323*9c5db199SXin Li    if arguments.autotest_dir:
324*9c5db199SXin Li        autotest_path = arguments.autotest_dir
325*9c5db199SXin Li        arguments.no_quickmerge = True
326*9c5db199SXin Li    else:
327*9c5db199SXin Li        sysroot_path = os.path.join('/build', arguments.board, '')
328*9c5db199SXin Li
329*9c5db199SXin Li        if not os.path.exists(sysroot_path):
330*9c5db199SXin Li            print(('%s does not exist. Have you run '
331*9c5db199SXin Li                   'setup_board?' % sysroot_path), file=sys.stderr)
332*9c5db199SXin Li            return 1
333*9c5db199SXin Li
334*9c5db199SXin Li        path_ending = 'usr/local/build/autotest'
335*9c5db199SXin Li        autotest_path = os.path.join(sysroot_path, path_ending)
336*9c5db199SXin Li
337*9c5db199SXin Li    site_utils_path = os.path.join(autotest_path, 'site_utils')
338*9c5db199SXin Li
339*9c5db199SXin Li    if not os.path.exists(autotest_path):
340*9c5db199SXin Li        print(('%s does not exist. Have you run '
341*9c5db199SXin Li               'build_packages? Or if you are using '
342*9c5db199SXin Li               '--autotest_dir, make sure it points to '
343*9c5db199SXin Li               'a valid autotest directory.' % autotest_path), file=sys.stderr)
344*9c5db199SXin Li        return 1
345*9c5db199SXin Li
346*9c5db199SXin Li    realpath = os.path.realpath(__file__)
347*9c5db199SXin Li    site_utils_path = os.path.realpath(site_utils_path)
348*9c5db199SXin Li
349*9c5db199SXin Li    # If we are not running the sysroot version of script, perform
350*9c5db199SXin Li    # a quickmerge if necessary and then re-execute
351*9c5db199SXin Li    # the sysroot version of script with the same arguments.
352*9c5db199SXin Li    if os.path.dirname(realpath) != site_utils_path:
353*9c5db199SXin Li        return perform_bootstrap_into_autotest_root(
354*9c5db199SXin Li                arguments, autotest_path, argv)
355*9c5db199SXin Li    else:
356*9c5db199SXin Li        return test_runner_utils.perform_run_from_autotest_root(
357*9c5db199SXin Li                autotest_path,
358*9c5db199SXin Li                argv,
359*9c5db199SXin Li                arguments.tests,
360*9c5db199SXin Li                arguments.remote,
361*9c5db199SXin Li                build=arguments.build,
362*9c5db199SXin Li                board=arguments.board,
363*9c5db199SXin Li                model=arguments.model,
364*9c5db199SXin Li                args=arguments.args,
365*9c5db199SXin Li                ignore_deps=not arguments.enforce_deps,
366*9c5db199SXin Li                results_directory=results_directory,
367*9c5db199SXin Li                ssh_verbosity=arguments.ssh_verbosity,
368*9c5db199SXin Li                ssh_options=arguments.ssh_options,
369*9c5db199SXin Li                iterations=arguments.iterations,
370*9c5db199SXin Li                fast_mode=arguments.fast_mode,
371*9c5db199SXin Li                debug=arguments.debug,
372*9c5db199SXin Li                allow_chrome_crashes=arguments.allow_chrome_crashes,
373*9c5db199SXin Li                pretend=arguments.pretend,
374*9c5db199SXin Li                job_retry=arguments.retry,
375*9c5db199SXin Li                companion_hosts=arguments.companion_hosts,
376*9c5db199SXin Li                minus=arguments.minus,
377*9c5db199SXin Li                dut_servers=arguments.dut_servers,
378*9c5db199SXin Li                is_cft=arguments.CFT,
379*9c5db199SXin Li                host_attributes=json.loads(arguments.host_attributes),
380*9c5db199SXin Li                host_labels=arguments.host_labels,
381*9c5db199SXin Li                label=arguments.label)
382*9c5db199SXin Li
383*9c5db199SXin Li
384*9c5db199SXin Lidef _main_for_lab_run(argv, arguments):
385*9c5db199SXin Li    """
386*9c5db199SXin Li    Effective entry point for lab test_that runs.
387*9c5db199SXin Li
388*9c5db199SXin Li    @param argv: Script command line arguments.
389*9c5db199SXin Li    @param arguments: Parsed command line arguments.
390*9c5db199SXin Li    """
391*9c5db199SXin Li    autotest_path = os.path.realpath(os.path.join(
392*9c5db199SXin Li            os.path.dirname(os.path.realpath(__file__)),
393*9c5db199SXin Li            '..',
394*9c5db199SXin Li    ))
395*9c5db199SXin Li    command = [os.path.join(autotest_path, 'site_utils',
396*9c5db199SXin Li                            'run_suite.py'),
397*9c5db199SXin Li               '--board=%s' % (arguments.board,),
398*9c5db199SXin Li               '--build=%s' % (arguments.build,),
399*9c5db199SXin Li               '--model=%s' % (arguments.model,),
400*9c5db199SXin Li               '--suite_name=%s' % 'test_that_wrapper',
401*9c5db199SXin Li               '--pool=%s' % (arguments.pool,),
402*9c5db199SXin Li               '--max_runtime_mins=%s' % str(arguments.max_runtime_mins),
403*9c5db199SXin Li               '--suite_args=%s'
404*9c5db199SXin Li               % repr({'tests': _suite_arg_tests(argv)})]
405*9c5db199SXin Li    if arguments.web:
406*9c5db199SXin Li        command.extend(['--web=%s' % (arguments.web,)])
407*9c5db199SXin Li    logging.info('About to start lab suite with command %s.', command)
408*9c5db199SXin Li    return subprocess.call(command)
409*9c5db199SXin Li
410*9c5db199SXin Li
411*9c5db199SXin Lidef _suite_arg_tests(argv):
412*9c5db199SXin Li    """
413*9c5db199SXin Li    Construct a list of tests to pass into suite_args.
414*9c5db199SXin Li
415*9c5db199SXin Li    This is passed in suite_args to run_suite for running a test in the
416*9c5db199SXin Li    lab.
417*9c5db199SXin Li
418*9c5db199SXin Li    @param argv: Remote Script command line arguments.
419*9c5db199SXin Li    """
420*9c5db199SXin Li    arguments = parse_arguments(argv)
421*9c5db199SXin Li    return arguments.tests
422*9c5db199SXin Li
423*9c5db199SXin Li
424*9c5db199SXin Lidef main(argv):
425*9c5db199SXin Li    """
426*9c5db199SXin Li    Entry point for test_that script.
427*9c5db199SXin Li
428*9c5db199SXin Li    @param argv: arguments list
429*9c5db199SXin Li    """
430*9c5db199SXin Li    arguments, remote_argv = _parse_arguments_internal(argv)
431*9c5db199SXin Li    try:
432*9c5db199SXin Li        validate_arguments(arguments)
433*9c5db199SXin Li    except ValueError as err:
434*9c5db199SXin Li        print(('Invalid arguments. %s' % str(err)), file=sys.stderr)
435*9c5db199SXin Li        return 1
436*9c5db199SXin Li
437*9c5db199SXin Li    if arguments.remote == ':lab:':
438*9c5db199SXin Li        return _main_for_lab_run(remote_argv, arguments)
439*9c5db199SXin Li    else:
440*9c5db199SXin Li        return _main_for_local_run(argv, arguments)
441*9c5db199SXin Li
442*9c5db199SXin Li
443*9c5db199SXin Liif __name__ == '__main__':
444*9c5db199SXin Li    sys.exit(main(sys.argv[1:]))
445