mikehoran | 63d61b4 | 2017-07-28 15:28:50 -0700 | [diff] [blame] | 1 | # Copyright 2017, 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 | |
mikehoran | 106d74e | 2018-02-02 14:09:27 -0800 | [diff] [blame] | 15 | #pylint: disable=too-many-lines |
mikehoran | 63d61b4 | 2017-07-28 15:28:50 -0700 | [diff] [blame] | 16 | """ |
| 17 | Command Line Translator for atest. |
| 18 | """ |
| 19 | |
nelsonli | 34997d5 | 2018-08-17 09:43:28 +0800 | [diff] [blame] | 20 | from __future__ import print_function |
| 21 | |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 22 | import fnmatch |
mikehoran | 43ed32d | 2017-08-18 17:13:36 -0700 | [diff] [blame] | 23 | import json |
mikehoran | be9102f | 2017-08-04 16:04:03 -0700 | [diff] [blame] | 24 | import logging |
mikehoran | 43ed32d | 2017-08-18 17:13:36 -0700 | [diff] [blame] | 25 | import os |
Dan Shi | fa016d1 | 2018-02-02 00:37:19 -0800 | [diff] [blame] | 26 | import sys |
mikehoran | 43ed32d | 2017-08-18 17:13:36 -0700 | [diff] [blame] | 27 | import time |
mikehoran | 63d61b4 | 2017-07-28 15:28:50 -0700 | [diff] [blame] | 28 | |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 29 | import atest_error |
Dan Shi | 0ddd3e4 | 2018-05-30 11:24:30 -0700 | [diff] [blame] | 30 | import atest_utils |
Dan Shi | fa016d1 | 2018-02-02 00:37:19 -0800 | [diff] [blame] | 31 | import constants |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 32 | import test_finder_handler |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 33 | import test_mapping |
Simran Basi | 3f29a94 | 2017-12-21 15:19:31 -0800 | [diff] [blame] | 34 | |
Dan Shi | 8441cba | 2018-01-30 15:52:29 -0800 | [diff] [blame] | 35 | TEST_MAPPING = 'TEST_MAPPING' |
| 36 | |
mikehoran | 38b9879 | 2017-10-18 16:22:55 -0700 | [diff] [blame] | 37 | |
mikehoran | be9102f | 2017-08-04 16:04:03 -0700 | [diff] [blame] | 38 | #pylint: disable=no-self-use |
mikehoran | 63d61b4 | 2017-07-28 15:28:50 -0700 | [diff] [blame] | 39 | class CLITranslator(object): |
| 40 | """ |
| 41 | CLITranslator class contains public method translate() and some private |
| 42 | helper methods. The atest tool can call the translate() method with a list |
| 43 | of strings, each string referencing a test to run. Translate() will |
| 44 | "translate" this list of test strings into a list of build targets and a |
| 45 | list of TradeFederation run commands. |
mikehoran | 3d55363 | 2017-08-03 14:44:41 -0700 | [diff] [blame] | 46 | |
| 47 | Translation steps for a test string reference: |
| 48 | 1. Narrow down the type of reference the test string could be, i.e. |
| 49 | whether it could be referencing a Module, Class, Package, etc. |
| 50 | 2. Try to find the test files assuming the test string is one of these |
| 51 | types of reference. |
| 52 | 3. If test files found, generate Build Targets and the Run Command. |
mikehoran | 63d61b4 | 2017-07-28 15:28:50 -0700 | [diff] [blame] | 53 | """ |
| 54 | |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 55 | def __init__(self, module_info=None): |
Simran Basi | 05384b8 | 2018-01-03 16:19:30 -0800 | [diff] [blame] | 56 | """CLITranslator constructor |
| 57 | |
| 58 | Args: |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 59 | module_info: ModuleInfo class that has cached module-info.json. |
Simran Basi | 05384b8 | 2018-01-03 16:19:30 -0800 | [diff] [blame] | 60 | """ |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 61 | self.mod_info = module_info |
mikehoran | 43ed32d | 2017-08-18 17:13:36 -0700 | [diff] [blame] | 62 | |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 63 | def _get_test_infos(self, tests, test_mapping_test_details=None): |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 64 | """Return set of TestInfos based on passed in tests. |
mikehoran | 43ed32d | 2017-08-18 17:13:36 -0700 | [diff] [blame] | 65 | |
Simran Basi | 05384b8 | 2018-01-03 16:19:30 -0800 | [diff] [blame] | 66 | Args: |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 67 | tests: List of strings representing test references. |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 68 | test_mapping_test_details: List of TestDetail for tests configured |
| 69 | in TEST_MAPPING files. |
mikehoran | 3622d9a | 2017-09-20 15:43:54 -0700 | [diff] [blame] | 70 | |
| 71 | Returns: |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 72 | Set of TestInfos based on the passed in tests. |
mikehoran | 3622d9a | 2017-09-20 15:43:54 -0700 | [diff] [blame] | 73 | """ |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 74 | test_infos = set() |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 75 | if not test_mapping_test_details: |
| 76 | test_mapping_test_details = [None] * len(tests) |
| 77 | for test, tm_test_detail in zip(tests, test_mapping_test_details): |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 78 | test_found = False |
nelsonli | c4a7145 | 2018-09-13 14:10:30 +0800 | [diff] [blame] | 79 | find_test_err_msg = None |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 80 | for finder in test_finder_handler.get_find_methods_for_test( |
| 81 | self.mod_info, test): |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 82 | # For tests in TEST_MAPPING, find method is only related to |
| 83 | # test name, so the details can be set after test_info object |
| 84 | # is created. |
nelsonli | c4a7145 | 2018-09-13 14:10:30 +0800 | [diff] [blame] | 85 | try: |
| 86 | test_info = finder.find_method(finder.test_finder_instance, |
| 87 | test) |
| 88 | except atest_error.TestDiscoveryException as e: |
| 89 | find_test_err_msg = e |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 90 | if test_info: |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 91 | if tm_test_detail: |
| 92 | test_info.data[constants.TI_MODULE_ARG] = ( |
| 93 | tm_test_detail.options) |
Dan Shi | 6daad63 | 2018-08-30 10:58:03 -0700 | [diff] [blame] | 94 | test_info.from_test_mapping = True |
Dan Shi | 08c7b72 | 2018-11-29 10:25:59 -0800 | [diff] [blame] | 95 | test_info.host = tm_test_detail.host |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 96 | test_infos.add(test_info) |
| 97 | test_found = True |
nelsonli | 34997d5 | 2018-08-17 09:43:28 +0800 | [diff] [blame] | 98 | finder_info = finder.finder_info |
nelsonli | c4a7145 | 2018-09-13 14:10:30 +0800 | [diff] [blame] | 99 | print("Found '%s' as %s" % ( |
| 100 | atest_utils.colorize(test, constants.GREEN), |
| 101 | finder_info)) |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 102 | break |
| 103 | if not test_found: |
nelsonli | c4a7145 | 2018-09-13 14:10:30 +0800 | [diff] [blame] | 104 | print('No test found for: %s' % |
| 105 | atest_utils.colorize(test, constants.RED)) |
| 106 | if find_test_err_msg: |
| 107 | print('%s\n' % (atest_utils.colorize( |
| 108 | find_test_err_msg, constants.MAGENTA))) |
| 109 | else: |
| 110 | print('(This can happen after a repo sync or if the test' |
| 111 | ' is new. Running: with "%s" may resolve the issue.)' |
| 112 | '\n' % (atest_utils.colorize( |
| 113 | constants.REBUILD_MODULE_INFO_FLAG, |
| 114 | constants.RED))) |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 115 | return test_infos |
mikehoran | 3d55363 | 2017-08-03 14:44:41 -0700 | [diff] [blame] | 116 | |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 117 | def _read_tests_in_test_mapping(self, test_mapping_file): |
| 118 | """Read tests from a TEST_MAPPING file. |
| 119 | |
| 120 | Args: |
| 121 | test_mapping_file: Path to a TEST_MAPPING file. |
| 122 | |
| 123 | Returns: |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 124 | A tuple of (all_tests, imports), where |
| 125 | all_tests is a dictionary of all tests in the TEST_MAPPING file, |
| 126 | grouped by test group. |
| 127 | imports is a list of test_mapping.Import to include other test |
| 128 | mapping files. |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 129 | """ |
| 130 | all_tests = {} |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 131 | imports = [] |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 132 | test_mapping_dict = None |
| 133 | with open(test_mapping_file) as json_file: |
| 134 | test_mapping_dict = json.load(json_file) |
| 135 | for test_group_name, test_list in test_mapping_dict.items(): |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 136 | if test_group_name == constants.TEST_MAPPING_IMPORTS: |
| 137 | for import_detail in test_list: |
| 138 | imports.append( |
| 139 | test_mapping.Import(test_mapping_file, import_detail)) |
| 140 | else: |
| 141 | grouped_tests = all_tests.setdefault(test_group_name, set()) |
Simran Basi | 2cc996e | 2018-10-09 12:19:25 -0700 | [diff] [blame] | 142 | tests = [] |
| 143 | for test in test_list: |
| 144 | test_mod_info = self.mod_info.name_to_module_info.get( |
| 145 | test['name']) |
| 146 | if not test_mod_info: |
| 147 | print('WARNING: %s is not a valid build target and ' |
| 148 | 'may not be discoverable by TreeHugger. If you ' |
| 149 | 'want to specify a class or test-package, ' |
| 150 | 'please set \'name\' to the test module and use ' |
| 151 | '\'options\' to specify the right tests via ' |
| 152 | '\'include-filter\'.\nNote: this can also occur ' |
| 153 | 'if the test module is not built for your ' |
| 154 | 'current lunch target.\n' % |
| 155 | atest_utils.colorize(test['name'], constants.RED)) |
| 156 | elif not any(x in test_mod_info['compatibility_suites'] for |
| 157 | x in constants.TEST_MAPPING_SUITES): |
| 158 | print('WARNING: Please add %s to either suite: %s for ' |
| 159 | 'this TEST_MAPPING file to work with TreeHugger.' % |
| 160 | (atest_utils.colorize(test['name'], |
| 161 | constants.RED), |
| 162 | atest_utils.colorize(constants.TEST_MAPPING_SUITES, |
| 163 | constants.GREEN))) |
| 164 | tests.append(test_mapping.TestDetail(test)) |
| 165 | grouped_tests.update(tests) |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 166 | return all_tests, imports |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 167 | |
| 168 | def _find_files(self, path, file_name=TEST_MAPPING): |
| 169 | """Find all files with given name under the given path. |
| 170 | |
| 171 | Args: |
| 172 | path: A string of path in source. |
| 173 | |
| 174 | Returns: |
| 175 | A list of paths of the files with the matching name under the given |
| 176 | path. |
| 177 | """ |
| 178 | test_mapping_files = [] |
| 179 | for root, _, filenames in os.walk(path): |
| 180 | for filename in fnmatch.filter(filenames, file_name): |
| 181 | test_mapping_files.append(os.path.join(root, filename)) |
| 182 | return test_mapping_files |
| 183 | |
| 184 | def _get_tests_from_test_mapping_files( |
| 185 | self, test_group, test_mapping_files): |
| 186 | """Get tests in the given test mapping files with the match group. |
| 187 | |
| 188 | Args: |
| 189 | test_group: Group of tests to run. Default is set to `presubmit`. |
| 190 | test_mapping_files: A list of path of TEST_MAPPING files. |
| 191 | |
| 192 | Returns: |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 193 | A tuple of (tests, all_tests, imports), where, |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 194 | tests is a set of tests (test_mapping.TestDetail) defined in |
| 195 | TEST_MAPPING file of the given path, and its parent directories, |
| 196 | with matching test_group. |
| 197 | all_tests is a dictionary of all tests in TEST_MAPPING files, |
| 198 | grouped by test group. |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 199 | imports is a list of test_mapping.Import objects that contains the |
| 200 | details of where to import a TEST_MAPPING file. |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 201 | """ |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 202 | all_imports = [] |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 203 | # Read and merge the tests in all TEST_MAPPING files. |
| 204 | merged_all_tests = {} |
| 205 | for test_mapping_file in test_mapping_files: |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 206 | all_tests, imports = self._read_tests_in_test_mapping( |
| 207 | test_mapping_file) |
| 208 | all_imports.extend(imports) |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 209 | for test_group_name, test_list in all_tests.items(): |
| 210 | grouped_tests = merged_all_tests.setdefault( |
| 211 | test_group_name, set()) |
| 212 | grouped_tests.update(test_list) |
| 213 | |
| 214 | tests = set(merged_all_tests.get(test_group, [])) |
| 215 | # Postsubmit tests shall include all presubmit tests as well. |
| 216 | if test_group == constants.TEST_GROUP_POSTSUBMIT: |
| 217 | tests.update(merged_all_tests.get( |
| 218 | constants.TEST_GROUP_PRESUBMIT, set())) |
| 219 | elif test_group == constants.TEST_GROUP_ALL: |
| 220 | for grouped_tests in merged_all_tests.values(): |
| 221 | tests.update(grouped_tests) |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 222 | return tests, merged_all_tests, all_imports |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 223 | |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 224 | # pylint: disable=too-many-arguments |
| 225 | # pylint: disable=too-many-locals |
Dan Shi | 7a8e1ad | 2018-04-12 11:01:07 -0700 | [diff] [blame] | 226 | def _find_tests_by_test_mapping( |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 227 | self, path='', test_group=constants.TEST_GROUP_PRESUBMIT, |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 228 | file_name=TEST_MAPPING, include_subdirs=False, checked_files=None): |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 229 | """Find tests defined in TEST_MAPPING in the given path. |
Dan Shi | d6881bb | 2017-10-12 15:13:25 -0700 | [diff] [blame] | 230 | |
| 231 | Args: |
| 232 | path: A string of path in source. Default is set to '', i.e., CWD. |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 233 | test_group: Group of tests to run. Default is set to `presubmit`. |
Dan Shi | 8441cba | 2018-01-30 15:52:29 -0800 | [diff] [blame] | 234 | file_name: Name of TEST_MAPPING file. Default is set to |
Dan Shi | 0ddd3e4 | 2018-05-30 11:24:30 -0700 | [diff] [blame] | 235 | `TEST_MAPPING`. The argument is added for testing purpose. |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 236 | include_subdirs: True to include tests in TEST_MAPPING files in sub |
| 237 | directories. |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 238 | checked_files: Paths of TEST_MAPPING files that have been checked. |
Dan Shi | d6881bb | 2017-10-12 15:13:25 -0700 | [diff] [blame] | 239 | |
| 240 | Returns: |
Dan Shi | 7a8e1ad | 2018-04-12 11:01:07 -0700 | [diff] [blame] | 241 | A tuple of (tests, all_tests), where, |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 242 | tests is a set of tests (test_mapping.TestDetail) defined in |
| 243 | TEST_MAPPING file of the given path, and its parent directories, |
| 244 | with matching test_group. |
Dan Shi | 7a8e1ad | 2018-04-12 11:01:07 -0700 | [diff] [blame] | 245 | all_tests is a dictionary of all tests in TEST_MAPPING files, |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 246 | grouped by test group. |
Dan Shi | d6881bb | 2017-10-12 15:13:25 -0700 | [diff] [blame] | 247 | """ |
Dan Shi | 7a8e1ad | 2018-04-12 11:01:07 -0700 | [diff] [blame] | 248 | path = os.path.realpath(path) |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 249 | test_mapping_files = set() |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 250 | all_tests = {} |
Dan Shi | 7a8e1ad | 2018-04-12 11:01:07 -0700 | [diff] [blame] | 251 | test_mapping_file = os.path.join(path, file_name) |
Dan Shi | d6881bb | 2017-10-12 15:13:25 -0700 | [diff] [blame] | 252 | if os.path.exists(test_mapping_file): |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 253 | test_mapping_files.add(test_mapping_file) |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 254 | # Include all TEST_MAPPING files in sub-directories if `include_subdirs` |
| 255 | # is set to True. |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 256 | if include_subdirs: |
| 257 | test_mapping_files.update(self._find_files(path, file_name)) |
| 258 | # Include all possible TEST_MAPPING files in parent directories. |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 259 | root_dir = os.environ.get(constants.ANDROID_BUILD_TOP, os.sep) |
| 260 | while path != root_dir and path != os.sep: |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 261 | path = os.path.dirname(path) |
| 262 | test_mapping_file = os.path.join(path, file_name) |
| 263 | if os.path.exists(test_mapping_file): |
| 264 | test_mapping_files.add(test_mapping_file) |
Dan Shi | 0ddd3e4 | 2018-05-30 11:24:30 -0700 | [diff] [blame] | 265 | |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 266 | if checked_files is None: |
| 267 | checked_files = set() |
| 268 | test_mapping_files.difference_update(checked_files) |
| 269 | checked_files.update(test_mapping_files) |
| 270 | if not test_mapping_files: |
| 271 | return test_mapping_files, all_tests |
| 272 | |
| 273 | tests, all_tests, imports = self._get_tests_from_test_mapping_files( |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 274 | test_group, test_mapping_files) |
Dan Shi | d6881bb | 2017-10-12 15:13:25 -0700 | [diff] [blame] | 275 | |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 276 | # Load TEST_MAPPING files from imports recursively. |
| 277 | if imports: |
| 278 | for import_detail in imports: |
| 279 | path = import_detail.get_path() |
| 280 | # (b/110166535 #19) Import path might not exist if a project is |
| 281 | # located in different directory in different branches. |
| 282 | if path is None: |
| 283 | logging.warn( |
| 284 | 'Failed to import TEST_MAPPING at %s', import_detail) |
| 285 | continue |
| 286 | # Search for tests based on the imported search path. |
| 287 | import_tests, import_all_tests = ( |
| 288 | self._find_tests_by_test_mapping( |
| 289 | path, test_group, file_name, include_subdirs, |
| 290 | checked_files)) |
| 291 | # Merge the collections |
| 292 | tests.update(import_tests) |
| 293 | for group, grouped_tests in import_all_tests.items(): |
| 294 | all_tests.setdefault(group, set()).update(grouped_tests) |
| 295 | |
| 296 | return tests, all_tests |
| 297 | |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 298 | def _gather_build_targets(self, test_infos): |
| 299 | targets = set() |
mikehoran | 38b9879 | 2017-10-18 16:22:55 -0700 | [diff] [blame] | 300 | for test_info in test_infos: |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 301 | targets |= test_info.build_targets |
| 302 | return targets |
mikehoran | be9102f | 2017-08-04 16:04:03 -0700 | [diff] [blame] | 303 | |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 304 | def _get_test_mapping_tests(self, args): |
| 305 | """Find the tests in TEST_MAPPING files. |
| 306 | |
| 307 | Args: |
| 308 | args: arg parsed object. |
| 309 | |
| 310 | Returns: |
| 311 | A tuple of (test_names, test_details_list), where |
| 312 | test_names: a list of test name |
| 313 | test_details_list: a list of test_mapping.TestDetail objects for |
| 314 | the tests in TEST_MAPPING files with matching test group. |
| 315 | """ |
| 316 | # Pull out tests from test mapping |
| 317 | src_path = '' |
| 318 | test_group = constants.TEST_GROUP_PRESUBMIT |
| 319 | if args.tests: |
| 320 | if ':' in args.tests[0]: |
| 321 | src_path, test_group = args.tests[0].split(':') |
| 322 | else: |
| 323 | src_path = args.tests[0] |
| 324 | |
| 325 | test_details, all_test_details = self._find_tests_by_test_mapping( |
| 326 | path=src_path, test_group=test_group, |
Dan Shi | 350e747 | 2018-06-19 12:25:32 -0700 | [diff] [blame] | 327 | include_subdirs=args.include_subdirs, checked_files=set()) |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 328 | test_details_list = list(test_details) |
| 329 | if not test_details_list: |
| 330 | logging.warn( |
| 331 | 'No tests of group `%s` found in TEST_MAPPING at %s or its ' |
| 332 | 'parent directories.\nYou might be missing atest arguments,' |
| 333 | ' try `atest --help` for more information', |
| 334 | test_group, os.path.realpath('')) |
| 335 | if all_test_details: |
| 336 | tests = '' |
| 337 | for test_group, test_list in all_test_details.items(): |
| 338 | tests += '%s:\n' % test_group |
| 339 | for test_detail in sorted(test_list): |
| 340 | tests += '\t%s\n' % test_detail |
| 341 | logging.warn( |
| 342 | 'All available tests in TEST_MAPPING files are:\n%s', |
| 343 | tests) |
| 344 | sys.exit(constants.EXIT_CODE_TEST_NOT_FOUND) |
| 345 | |
nelsonli | 34997d5 | 2018-08-17 09:43:28 +0800 | [diff] [blame] | 346 | logging.debug( |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 347 | 'Test details:\n%s', |
| 348 | '\n'.join([str(detail) for detail in test_details_list])) |
| 349 | test_names = [detail.name for detail in test_details_list] |
| 350 | return test_names, test_details_list |
| 351 | |
| 352 | |
Dan Shi | 0ddd3e4 | 2018-05-30 11:24:30 -0700 | [diff] [blame] | 353 | def translate(self, args): |
mikehoran | 63d61b4 | 2017-07-28 15:28:50 -0700 | [diff] [blame] | 354 | """Translate atest command line into build targets and run commands. |
| 355 | |
| 356 | Args: |
Dan Shi | 0ddd3e4 | 2018-05-30 11:24:30 -0700 | [diff] [blame] | 357 | args: arg parsed object. |
mikehoran | 63d61b4 | 2017-07-28 15:28:50 -0700 | [diff] [blame] | 358 | |
| 359 | Returns: |
Kevin Cheng | 7edb0b9 | 2017-12-14 15:00:25 -0800 | [diff] [blame] | 360 | A tuple with set of build_target strings and list of TestInfos. |
mikehoran | 63d61b4 | 2017-07-28 15:28:50 -0700 | [diff] [blame] | 361 | """ |
Dan Shi | 0ddd3e4 | 2018-05-30 11:24:30 -0700 | [diff] [blame] | 362 | tests = args.tests |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 363 | # Test details from TEST_MAPPING files |
| 364 | test_details_list = None |
Dan Shi | 0ddd3e4 | 2018-05-30 11:24:30 -0700 | [diff] [blame] | 365 | if atest_utils.is_test_mapping(args): |
Dan Shi | e4e267f | 2018-06-01 11:31:57 -0700 | [diff] [blame] | 366 | tests, test_details_list = self._get_test_mapping_tests(args) |
mikehoran | 1609f8b | 2018-09-04 12:46:27 -0700 | [diff] [blame] | 367 | atest_utils.colorful_print("\nFinding Tests...", constants.CYAN) |
nelsonli | 34997d5 | 2018-08-17 09:43:28 +0800 | [diff] [blame] | 368 | logging.debug('Finding Tests: %s', tests) |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 369 | start = time.time() |
Dan Shi | cdbda55 | 2018-05-18 23:31:33 -0700 | [diff] [blame] | 370 | test_infos = self._get_test_infos(tests, test_details_list) |
Dan Shi | 0ddd3e4 | 2018-05-30 11:24:30 -0700 | [diff] [blame] | 371 | logging.debug('Found tests in %ss', time.time() - start) |
mikehoran | 8bf6d08 | 2018-02-26 16:22:06 -0800 | [diff] [blame] | 372 | for test_info in test_infos: |
| 373 | logging.debug('%s\n', test_info) |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 374 | build_targets = self._gather_build_targets(test_infos) |
Kevin Cheng | 7edb0b9 | 2017-12-14 15:00:25 -0800 | [diff] [blame] | 375 | return build_targets, test_infos |