• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018, The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""
16SUITE Tradefed test runner class.
17"""
18
19import copy
20import logging
21import os
22
23from typing import List
24
25from atest import atest_utils
26from atest import constants
27
28from atest.atest_enum import ExitCode
29from atest.logstorage import atest_gcp_utils
30from atest.logstorage import logstorage_utils
31from atest.metrics import metrics
32from atest.test_finders import test_info
33from atest.test_runners import atest_tf_test_runner
34
35class SuitePlanTestRunner(atest_tf_test_runner.AtestTradefedTestRunner):
36    """Suite Plan Test Runner class."""
37    NAME = 'SuitePlanTestRunner'
38    EXECUTABLE = '%s-tradefed'
39    _RUN_CMD = ('{exe} run commandAndExit {test} {args}')
40
41    def __init__(self, results_dir, **kwargs):
42        """Init stuff for suite tradefed runner class."""
43        super().__init__(results_dir, **kwargs)
44        self.run_cmd_dict = {'exe': '',
45                             'test': '',
46                             'args': ''}
47
48    def get_test_runner_build_reqs(self, test_infos: List[test_info.TestInfo]):
49        """Return the build requirements.
50
51        Args:
52            test_infos: List of TestInfo.
53
54        Returns:
55            Set of build targets.
56        """
57        build_req = set()
58        build_req |= super().get_test_runner_build_reqs(test_infos)
59        return build_req
60
61    def run_tests(self, test_infos, extra_args, reporter):
62        """Run the list of test_infos.
63        Args:
64            test_infos: List of TestInfo.
65            extra_args: Dict of extra args to add to test run.
66            reporter: An instance of result_report.ResultReporter.
67
68        Returns:
69            Return code of the process for running tests.
70        """
71        reporter.register_unsupported_runner(self.NAME)
72        creds, inv = atest_gcp_utils.do_upload_flow(extra_args)
73
74        run_cmds = self.generate_run_commands(test_infos, extra_args)
75        ret_code = ExitCode.SUCCESS
76        for run_cmd in run_cmds:
77            try:
78                proc = super().run(run_cmd, output_to_stdout=True,
79                               env_vars=self.generate_env_vars(extra_args))
80                ret_code |= self.wait_for_subprocess(proc)
81            finally:
82                if inv:
83                    try:
84                        logging.disable(logging.INFO)
85                        # Always set invocation status to completed due to
86                        # the ATest handle whole process by its own.
87                        inv['schedulerState'] = 'completed'
88                        logstorage_utils.BuildClient(creds).update_invocation(
89                            inv)
90                        reporter.test_result_link = (constants.RESULT_LINK
91                                                    % inv['invocationId'])
92                    finally:
93                        logging.disable(logging.NOTSET)
94        return ret_code
95
96    # pylint: disable=arguments-differ
97    def _parse_extra_args(self, extra_args):
98        """Convert the extra args into something *ts-tf can understand.
99
100        We want to transform the top-level args from atest into specific args
101        that *ts-tradefed supports. The only arg we take as is
102        EXTRA_ARG since that is what the user intentionally wants to pass to
103        the test runner.
104
105        Args:
106            extra_args: Dict of args
107
108        Returns:
109            List of args to append.
110        """
111        args_to_append = []
112        args_not_supported = []
113        for arg in extra_args:
114            if constants.SERIAL == arg:
115                args_to_append.append('--serial')
116                args_to_append.append(extra_args[arg])
117                continue
118            if constants.CUSTOM_ARGS == arg:
119                args_to_append.extend(extra_args[arg])
120                continue
121            if constants.INVOCATION_ID == arg:
122                args_to_append.append('--invocation-data invocation_id=%s'
123                             % extra_args[arg])
124            if constants.WORKUNIT_ID == arg:
125                args_to_append.append('--invocation-data work_unit_id=%s'
126                             % extra_args[arg])
127            if arg in (constants.DRY_RUN,
128                       constants.REQUEST_UPLOAD_RESULT):
129                continue
130            if constants.TF_DEBUG == arg:
131                debug_port = extra_args.get(constants.TF_DEBUG, '')
132                port = (debug_port if debug_port else
133                        constants.DEFAULT_DEBUG_PORT)
134                print('Please attach process to your IDE...(%s)' % port)
135                continue
136            args_not_supported.append(arg)
137        if args_not_supported:
138            logging.info('%s does not support the following args: %s',
139                         self.EXECUTABLE, args_not_supported)
140        return args_to_append
141
142    # pylint: disable=arguments-differ
143    def generate_run_commands(self, test_infos, extra_args):
144        """Generate a list of run commands from TestInfos.
145
146        Args:
147            test_infos: List of TestInfo tests to run.
148            extra_args: Dict of extra args to add to test run.
149
150        Returns:
151            A List of strings that contains the run command
152            which *ts-tradefed supports.
153        """
154        cmds = []
155        args = []
156        args.extend(self._parse_extra_args(extra_args))
157        args.extend(atest_utils.get_result_server_args())
158        for test_info in test_infos:
159            cmd_dict = copy.deepcopy(self.run_cmd_dict)
160            cmd_dict['test'] = test_info.test_name
161            cmd_dict['args'] = ' '.join(args)
162            cmd_dict['exe'] = self.EXECUTABLE % test_info.suite
163            cmds.append(self._RUN_CMD.format(**cmd_dict))
164            if constants.DETECT_TYPE_XTS_SUITE:
165                xts_detect_type = constants.DETECT_TYPE_XTS_SUITE.get(
166                    test_info.suite, '')
167                if xts_detect_type:
168                    metrics.LocalDetectEvent(
169                        detect_type=xts_detect_type,
170                        result=1)
171        return cmds
172
173    def generate_env_vars(self, extra_args):
174        """Convert extra args into env vars."""
175        env_vars = os.environ.copy()
176        debug_port = extra_args.get(constants.TF_DEBUG, '')
177        if debug_port:
178            env_vars['TF_DEBUG'] = 'true'
179            env_vars['TF_DEBUG_PORT'] = str(debug_port)
180        if constants.TF_GLOBAL_CONFIG:
181            env_vars["TF_GLOBAL_CONFIG"] = constants.TF_GLOBAL_CONFIG
182        return env_vars
183