1*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2017, The Android Open Source Project 2*c2e18aaaSAndroid Build Coastguard Worker# 3*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*c2e18aaaSAndroid Build Coastguard Worker# 7*c2e18aaaSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*c2e18aaaSAndroid Build Coastguard Worker# 9*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License. 14*c2e18aaaSAndroid Build Coastguard Worker 15*c2e18aaaSAndroid Build Coastguard Worker"""Command Line Translator for atest.""" 16*c2e18aaaSAndroid Build Coastguard Worker 17*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=too-many-lines 18*c2e18aaaSAndroid Build Coastguard Worker 19*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import print_function 20*c2e18aaaSAndroid Build Coastguard Worker 21*c2e18aaaSAndroid Build Coastguard Workerfrom dataclasses import dataclass 22*c2e18aaaSAndroid Build Coastguard Workerimport fnmatch 23*c2e18aaaSAndroid Build Coastguard Workerimport functools 24*c2e18aaaSAndroid Build Coastguard Workerimport json 25*c2e18aaaSAndroid Build Coastguard Workerimport logging 26*c2e18aaaSAndroid Build Coastguard Workerimport os 27*c2e18aaaSAndroid Build Coastguard Workerfrom pathlib import Path 28*c2e18aaaSAndroid Build Coastguard Workerimport re 29*c2e18aaaSAndroid Build Coastguard Workerimport sys 30*c2e18aaaSAndroid Build Coastguard Workerimport threading 31*c2e18aaaSAndroid Build Coastguard Workerimport time 32*c2e18aaaSAndroid Build Coastguard Workerfrom typing import List, Set 33*c2e18aaaSAndroid Build Coastguard Worker 34*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_error 35*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_utils 36*c2e18aaaSAndroid Build Coastguard Workerfrom atest import bazel_mode 37*c2e18aaaSAndroid Build Coastguard Workerfrom atest import constants 38*c2e18aaaSAndroid Build Coastguard Workerfrom atest import rollout_control 39*c2e18aaaSAndroid Build Coastguard Workerfrom atest import test_finder_handler 40*c2e18aaaSAndroid Build Coastguard Workerfrom atest import test_mapping 41*c2e18aaaSAndroid Build Coastguard Workerfrom atest.atest_enum import DetectType, ExitCode 42*c2e18aaaSAndroid Build Coastguard Workerfrom atest.metrics import metrics 43*c2e18aaaSAndroid Build Coastguard Workerfrom atest.metrics import metrics_utils 44*c2e18aaaSAndroid Build Coastguard Workerfrom atest.test_finders import module_finder 45*c2e18aaaSAndroid Build Coastguard Workerfrom atest.test_finders import test_finder_utils 46*c2e18aaaSAndroid Build Coastguard Workerfrom atest.test_finders import test_info 47*c2e18aaaSAndroid Build Coastguard Workerfrom atest.tools import indexing 48*c2e18aaaSAndroid Build Coastguard Worker 49*c2e18aaaSAndroid Build Coastguard WorkerFUZZY_FINDER = 'FUZZY' 50*c2e18aaaSAndroid Build Coastguard WorkerCACHE_FINDER = 'CACHE' 51*c2e18aaaSAndroid Build Coastguard WorkerTESTNAME_CHARS = {'#', ':', '/'} 52*c2e18aaaSAndroid Build Coastguard Worker 53*c2e18aaaSAndroid Build Coastguard WorkerMAINLINE_LOCAL_DOC = 'go/mainline-local-build' 54*c2e18aaaSAndroid Build Coastguard Worker 55*c2e18aaaSAndroid Build Coastguard Worker# Pattern used to identify comments start with '//' or '#' in TEST_MAPPING. 56*c2e18aaaSAndroid Build Coastguard Worker_COMMENTS_RE = re.compile(r'(?m)[\s\t]*(#|//).*|(\".*?\")') 57*c2e18aaaSAndroid Build Coastguard Worker_COMMENTS = frozenset(['//', '#']) 58*c2e18aaaSAndroid Build Coastguard Worker 59*c2e18aaaSAndroid Build Coastguard Worker 60*c2e18aaaSAndroid Build Coastguard Worker@dataclass 61*c2e18aaaSAndroid Build Coastguard Workerclass TestIdentifier: 62*c2e18aaaSAndroid Build Coastguard Worker """Class that stores test and the corresponding mainline modules (if any).""" 63*c2e18aaaSAndroid Build Coastguard Worker 64*c2e18aaaSAndroid Build Coastguard Worker test_name: str 65*c2e18aaaSAndroid Build Coastguard Worker module_names: List[str] 66*c2e18aaaSAndroid Build Coastguard Worker binary_names: List[str] 67*c2e18aaaSAndroid Build Coastguard Worker 68*c2e18aaaSAndroid Build Coastguard Worker 69*c2e18aaaSAndroid Build Coastguard Workerclass CLITranslator: 70*c2e18aaaSAndroid Build Coastguard Worker """CLITranslator class contains public method translate() and some private 71*c2e18aaaSAndroid Build Coastguard Worker 72*c2e18aaaSAndroid Build Coastguard Worker helper methods. The atest tool can call the translate() method with a list 73*c2e18aaaSAndroid Build Coastguard Worker of strings, each string referencing a test to run. Translate() will 74*c2e18aaaSAndroid Build Coastguard Worker "translate" this list of test strings into a list of build targets and a 75*c2e18aaaSAndroid Build Coastguard Worker list of TradeFederation run commands. 76*c2e18aaaSAndroid Build Coastguard Worker 77*c2e18aaaSAndroid Build Coastguard Worker Translation steps for a test string reference: 78*c2e18aaaSAndroid Build Coastguard Worker 1. Narrow down the type of reference the test string could be, i.e. 79*c2e18aaaSAndroid Build Coastguard Worker whether it could be referencing a Module, Class, Package, etc. 80*c2e18aaaSAndroid Build Coastguard Worker 2. Try to find the test files assuming the test string is one of these 81*c2e18aaaSAndroid Build Coastguard Worker types of reference. 82*c2e18aaaSAndroid Build Coastguard Worker 3. If test files found, generate Build Targets and the Run Command. 83*c2e18aaaSAndroid Build Coastguard Worker """ 84*c2e18aaaSAndroid Build Coastguard Worker 85*c2e18aaaSAndroid Build Coastguard Worker def __init__( 86*c2e18aaaSAndroid Build Coastguard Worker self, 87*c2e18aaaSAndroid Build Coastguard Worker mod_info=None, 88*c2e18aaaSAndroid Build Coastguard Worker print_cache_msg=True, 89*c2e18aaaSAndroid Build Coastguard Worker bazel_mode_enabled=False, 90*c2e18aaaSAndroid Build Coastguard Worker host=False, 91*c2e18aaaSAndroid Build Coastguard Worker bazel_mode_features: List[bazel_mode.Features] = None, 92*c2e18aaaSAndroid Build Coastguard Worker indexing_thread: threading.Thread = None, 93*c2e18aaaSAndroid Build Coastguard Worker ): 94*c2e18aaaSAndroid Build Coastguard Worker """CLITranslator constructor 95*c2e18aaaSAndroid Build Coastguard Worker 96*c2e18aaaSAndroid Build Coastguard Worker Args: 97*c2e18aaaSAndroid Build Coastguard Worker mod_info: ModuleInfo class that has cached module-info.json. 98*c2e18aaaSAndroid Build Coastguard Worker print_cache_msg: Boolean whether printing clear cache message or not. 99*c2e18aaaSAndroid Build Coastguard Worker True will print message while False won't print. 100*c2e18aaaSAndroid Build Coastguard Worker bazel_mode_enabled: Boolean of args.bazel_mode. 101*c2e18aaaSAndroid Build Coastguard Worker host: Boolean of args.host. 102*c2e18aaaSAndroid Build Coastguard Worker bazel_mode_features: List of args.bazel_mode_features. 103*c2e18aaaSAndroid Build Coastguard Worker indexing_thread: Thread of indexing. 104*c2e18aaaSAndroid Build Coastguard Worker """ 105*c2e18aaaSAndroid Build Coastguard Worker self.mod_info = mod_info 106*c2e18aaaSAndroid Build Coastguard Worker self.root_dir = os.getenv(constants.ANDROID_BUILD_TOP, os.sep) 107*c2e18aaaSAndroid Build Coastguard Worker self._bazel_mode = ( 108*c2e18aaaSAndroid Build Coastguard Worker bazel_mode_enabled 109*c2e18aaaSAndroid Build Coastguard Worker and not rollout_control.deprecate_bazel_mode.is_enabled() 110*c2e18aaaSAndroid Build Coastguard Worker ) 111*c2e18aaaSAndroid Build Coastguard Worker self._bazel_mode_features = bazel_mode_features or [] 112*c2e18aaaSAndroid Build Coastguard Worker self._host = host 113*c2e18aaaSAndroid Build Coastguard Worker self.enable_file_patterns = False 114*c2e18aaaSAndroid Build Coastguard Worker self.msg = '' 115*c2e18aaaSAndroid Build Coastguard Worker if print_cache_msg: 116*c2e18aaaSAndroid Build Coastguard Worker self.msg = ( 117*c2e18aaaSAndroid Build Coastguard Worker '(Test info has been cached for speeding up the next ' 118*c2e18aaaSAndroid Build Coastguard Worker 'run, if test info needs to be updated, please add -c ' 119*c2e18aaaSAndroid Build Coastguard Worker 'to clean the old cache.)' 120*c2e18aaaSAndroid Build Coastguard Worker ) 121*c2e18aaaSAndroid Build Coastguard Worker self.fuzzy_search = True 122*c2e18aaaSAndroid Build Coastguard Worker self._indexing_thread = indexing_thread 123*c2e18aaaSAndroid Build Coastguard Worker 124*c2e18aaaSAndroid Build Coastguard Worker @functools.cache 125*c2e18aaaSAndroid Build Coastguard Worker def _wait_for_index_if_needed(self) -> None: 126*c2e18aaaSAndroid Build Coastguard Worker """Checks indexing status and wait for it to complete if necessary.""" 127*c2e18aaaSAndroid Build Coastguard Worker if ( 128*c2e18aaaSAndroid Build Coastguard Worker not self._indexing_thread 129*c2e18aaaSAndroid Build Coastguard Worker or not self._indexing_thread.is_alive() 130*c2e18aaaSAndroid Build Coastguard Worker or indexing.Indices().has_all_indices() 131*c2e18aaaSAndroid Build Coastguard Worker ): 132*c2e18aaaSAndroid Build Coastguard Worker return 133*c2e18aaaSAndroid Build Coastguard Worker start_wait_for_indexing = time.time() 134*c2e18aaaSAndroid Build Coastguard Worker print('Waiting for the module indexing to complete.') 135*c2e18aaaSAndroid Build Coastguard Worker self._indexing_thread.join() 136*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 137*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.WAIT_FOR_INDEXING_MS, 138*c2e18aaaSAndroid Build Coastguard Worker result=int(round((time.time() - start_wait_for_indexing) * 1000)), 139*c2e18aaaSAndroid Build Coastguard Worker ) 140*c2e18aaaSAndroid Build Coastguard Worker 141*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=too-many-locals 142*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=too-many-branches 143*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=too-many-statements 144*c2e18aaaSAndroid Build Coastguard Worker def _find_test_infos( 145*c2e18aaaSAndroid Build Coastguard Worker self, test: str, tm_test_detail: test_mapping.TestDetail 146*c2e18aaaSAndroid Build Coastguard Worker ) -> List[test_info.TestInfo]: 147*c2e18aaaSAndroid Build Coastguard Worker """Return set of TestInfos based on a given test. 148*c2e18aaaSAndroid Build Coastguard Worker 149*c2e18aaaSAndroid Build Coastguard Worker Args: 150*c2e18aaaSAndroid Build Coastguard Worker test: A string representing test references. 151*c2e18aaaSAndroid Build Coastguard Worker tm_test_detail: The TestDetail of test configured in TEST_MAPPING files. 152*c2e18aaaSAndroid Build Coastguard Worker 153*c2e18aaaSAndroid Build Coastguard Worker Returns: 154*c2e18aaaSAndroid Build Coastguard Worker List of TestInfos based on the given test. 155*c2e18aaaSAndroid Build Coastguard Worker """ 156*c2e18aaaSAndroid Build Coastguard Worker test_infos = [] 157*c2e18aaaSAndroid Build Coastguard Worker test_find_starts = time.time() 158*c2e18aaaSAndroid Build Coastguard Worker test_found = False 159*c2e18aaaSAndroid Build Coastguard Worker test_finders = [] 160*c2e18aaaSAndroid Build Coastguard Worker test_info_str = '' 161*c2e18aaaSAndroid Build Coastguard Worker find_test_err_msg = None 162*c2e18aaaSAndroid Build Coastguard Worker test_identifier = parse_test_identifier(test) 163*c2e18aaaSAndroid Build Coastguard Worker test_name = test_identifier.test_name 164*c2e18aaaSAndroid Build Coastguard Worker if not self._verified_mainline_modules(test_identifier): 165*c2e18aaaSAndroid Build Coastguard Worker return test_infos 166*c2e18aaaSAndroid Build Coastguard Worker find_methods = test_finder_handler.get_find_methods_for_test( 167*c2e18aaaSAndroid Build Coastguard Worker self.mod_info, test 168*c2e18aaaSAndroid Build Coastguard Worker ) 169*c2e18aaaSAndroid Build Coastguard Worker if self._bazel_mode: 170*c2e18aaaSAndroid Build Coastguard Worker find_methods = [ 171*c2e18aaaSAndroid Build Coastguard Worker bazel_mode.create_new_finder( 172*c2e18aaaSAndroid Build Coastguard Worker self.mod_info, 173*c2e18aaaSAndroid Build Coastguard Worker f, 174*c2e18aaaSAndroid Build Coastguard Worker host=self._host, 175*c2e18aaaSAndroid Build Coastguard Worker enabled_features=self._bazel_mode_features, 176*c2e18aaaSAndroid Build Coastguard Worker ) 177*c2e18aaaSAndroid Build Coastguard Worker for f in find_methods 178*c2e18aaaSAndroid Build Coastguard Worker ] 179*c2e18aaaSAndroid Build Coastguard Worker 180*c2e18aaaSAndroid Build Coastguard Worker for finder in find_methods: 181*c2e18aaaSAndroid Build Coastguard Worker # Ideally whether a find method requires indexing should be defined within the 182*c2e18aaaSAndroid Build Coastguard Worker # finder class itself. However the current finder class design prevent 183*c2e18aaaSAndroid Build Coastguard Worker # us from defining property without a bigger change. Here we use a tuple 184*c2e18aaaSAndroid Build Coastguard Worker # to specify the finders that doesn't require indexing and leave the 185*c2e18aaaSAndroid Build Coastguard Worker # class redesign work for future work. 186*c2e18aaaSAndroid Build Coastguard Worker if finder.finder_info not in ( 187*c2e18aaaSAndroid Build Coastguard Worker 'EXAMPLE', 188*c2e18aaaSAndroid Build Coastguard Worker 'CACHE', 189*c2e18aaaSAndroid Build Coastguard Worker 'MODULE', 190*c2e18aaaSAndroid Build Coastguard Worker 'INTEGRATION', 191*c2e18aaaSAndroid Build Coastguard Worker 'CONFIG', 192*c2e18aaaSAndroid Build Coastguard Worker 'SUITE_PLAN', 193*c2e18aaaSAndroid Build Coastguard Worker ): 194*c2e18aaaSAndroid Build Coastguard Worker self._wait_for_index_if_needed() 195*c2e18aaaSAndroid Build Coastguard Worker 196*c2e18aaaSAndroid Build Coastguard Worker # For tests in TEST_MAPPING, find method is only related to 197*c2e18aaaSAndroid Build Coastguard Worker # test name, so the details can be set after test_info object 198*c2e18aaaSAndroid Build Coastguard Worker # is created. 199*c2e18aaaSAndroid Build Coastguard Worker try: 200*c2e18aaaSAndroid Build Coastguard Worker found_test_infos = finder.find_method( 201*c2e18aaaSAndroid Build Coastguard Worker finder.test_finder_instance, test_name 202*c2e18aaaSAndroid Build Coastguard Worker ) 203*c2e18aaaSAndroid Build Coastguard Worker except atest_error.TestDiscoveryException as e: 204*c2e18aaaSAndroid Build Coastguard Worker find_test_err_msg = e 205*c2e18aaaSAndroid Build Coastguard Worker if found_test_infos: 206*c2e18aaaSAndroid Build Coastguard Worker finder_info = finder.finder_info 207*c2e18aaaSAndroid Build Coastguard Worker for t_info in found_test_infos: 208*c2e18aaaSAndroid Build Coastguard Worker test_deps = set() 209*c2e18aaaSAndroid Build Coastguard Worker if self.mod_info: 210*c2e18aaaSAndroid Build Coastguard Worker test_deps = self.mod_info.get_install_module_dependency( 211*c2e18aaaSAndroid Build Coastguard Worker t_info.test_name 212*c2e18aaaSAndroid Build Coastguard Worker ) 213*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 214*c2e18aaaSAndroid Build Coastguard Worker '(%s) Test dependencies: %s', t_info.test_name, test_deps 215*c2e18aaaSAndroid Build Coastguard Worker ) 216*c2e18aaaSAndroid Build Coastguard Worker if tm_test_detail: 217*c2e18aaaSAndroid Build Coastguard Worker t_info.data[constants.TI_MODULE_ARG] = tm_test_detail.options 218*c2e18aaaSAndroid Build Coastguard Worker t_info.from_test_mapping = True 219*c2e18aaaSAndroid Build Coastguard Worker t_info.host = tm_test_detail.host 220*c2e18aaaSAndroid Build Coastguard Worker if finder_info != CACHE_FINDER: 221*c2e18aaaSAndroid Build Coastguard Worker t_info.test_finder = finder_info 222*c2e18aaaSAndroid Build Coastguard Worker mainline_modules = test_identifier.module_names 223*c2e18aaaSAndroid Build Coastguard Worker if mainline_modules: 224*c2e18aaaSAndroid Build Coastguard Worker t_info.test_name = test 225*c2e18aaaSAndroid Build Coastguard Worker # TODO(b/261607500): Replace usages of raw_test_name 226*c2e18aaaSAndroid Build Coastguard Worker # with test_name once we can ensure that it doesn't 227*c2e18aaaSAndroid Build Coastguard Worker # break any code that expects Mainline modules in the 228*c2e18aaaSAndroid Build Coastguard Worker # string. 229*c2e18aaaSAndroid Build Coastguard Worker t_info.raw_test_name = test_name 230*c2e18aaaSAndroid Build Coastguard Worker # TODO: remove below statement when soong can also 231*c2e18aaaSAndroid Build Coastguard Worker # parse TestConfig and inject mainline modules information 232*c2e18aaaSAndroid Build Coastguard Worker # to module-info. 233*c2e18aaaSAndroid Build Coastguard Worker for mod in mainline_modules: 234*c2e18aaaSAndroid Build Coastguard Worker t_info.add_mainline_module(mod) 235*c2e18aaaSAndroid Build Coastguard Worker 236*c2e18aaaSAndroid Build Coastguard Worker # Only add dependencies to build_targets when they are in 237*c2e18aaaSAndroid Build Coastguard Worker # module info 238*c2e18aaaSAndroid Build Coastguard Worker test_deps_in_mod_info = [ 239*c2e18aaaSAndroid Build Coastguard Worker test_dep 240*c2e18aaaSAndroid Build Coastguard Worker for test_dep in test_deps 241*c2e18aaaSAndroid Build Coastguard Worker if self.mod_info.is_module(test_dep) 242*c2e18aaaSAndroid Build Coastguard Worker ] 243*c2e18aaaSAndroid Build Coastguard Worker for dep in test_deps_in_mod_info: 244*c2e18aaaSAndroid Build Coastguard Worker t_info.add_build_target(dep) 245*c2e18aaaSAndroid Build Coastguard Worker test_infos.append(t_info) 246*c2e18aaaSAndroid Build Coastguard Worker test_found = True 247*c2e18aaaSAndroid Build Coastguard Worker print("Found '%s' as %s" % (atest_utils.mark_green(test), finder_info)) 248*c2e18aaaSAndroid Build Coastguard Worker if finder_info == CACHE_FINDER and test_infos: 249*c2e18aaaSAndroid Build Coastguard Worker test_finders.append(list(test_infos)[0].test_finder) 250*c2e18aaaSAndroid Build Coastguard Worker test_finders.append(finder_info) 251*c2e18aaaSAndroid Build Coastguard Worker test_info_str = ','.join([str(x) for x in found_test_infos]) 252*c2e18aaaSAndroid Build Coastguard Worker break 253*c2e18aaaSAndroid Build Coastguard Worker if not test_found: 254*c2e18aaaSAndroid Build Coastguard Worker print('No test found for: {}'.format(atest_utils.mark_red(test))) 255*c2e18aaaSAndroid Build Coastguard Worker if self.fuzzy_search: 256*c2e18aaaSAndroid Build Coastguard Worker f_results = self._fuzzy_search_and_msg(test, find_test_err_msg) 257*c2e18aaaSAndroid Build Coastguard Worker if f_results: 258*c2e18aaaSAndroid Build Coastguard Worker test_infos.extend(f_results) 259*c2e18aaaSAndroid Build Coastguard Worker test_found = True 260*c2e18aaaSAndroid Build Coastguard Worker test_finders.append(FUZZY_FINDER) 261*c2e18aaaSAndroid Build Coastguard Worker metrics.FindTestFinishEvent( 262*c2e18aaaSAndroid Build Coastguard Worker duration=metrics_utils.convert_duration(time.time() - test_find_starts), 263*c2e18aaaSAndroid Build Coastguard Worker success=test_found, 264*c2e18aaaSAndroid Build Coastguard Worker test_reference=test, 265*c2e18aaaSAndroid Build Coastguard Worker test_finders=test_finders, 266*c2e18aaaSAndroid Build Coastguard Worker test_info=test_info_str, 267*c2e18aaaSAndroid Build Coastguard Worker ) 268*c2e18aaaSAndroid Build Coastguard Worker # Cache test_infos by default except running with TEST_MAPPING which may 269*c2e18aaaSAndroid Build Coastguard Worker # include customized flags and they are likely to mess up other 270*c2e18aaaSAndroid Build Coastguard Worker # non-test_mapping tests. 271*c2e18aaaSAndroid Build Coastguard Worker if test_infos and not tm_test_detail: 272*c2e18aaaSAndroid Build Coastguard Worker atest_utils.update_test_info_cache(test, test_infos) 273*c2e18aaaSAndroid Build Coastguard Worker if self.msg: 274*c2e18aaaSAndroid Build Coastguard Worker print(self.msg) 275*c2e18aaaSAndroid Build Coastguard Worker return test_infos 276*c2e18aaaSAndroid Build Coastguard Worker 277*c2e18aaaSAndroid Build Coastguard Worker def _verified_mainline_modules(self, test_identifier: TestIdentifier) -> bool: 278*c2e18aaaSAndroid Build Coastguard Worker """Verify the test with mainline modules is acceptable. 279*c2e18aaaSAndroid Build Coastguard Worker 280*c2e18aaaSAndroid Build Coastguard Worker The test must be a module and mainline modules are in module-info. 281*c2e18aaaSAndroid Build Coastguard Worker The syntax rule of mainline modules will check in build process. 282*c2e18aaaSAndroid Build Coastguard Worker The rule includes mainline modules are sorted alphabetically, no space, 283*c2e18aaaSAndroid Build Coastguard Worker and no duplication. 284*c2e18aaaSAndroid Build Coastguard Worker 285*c2e18aaaSAndroid Build Coastguard Worker Args: 286*c2e18aaaSAndroid Build Coastguard Worker test_identifier: a TestIdentifier object. 287*c2e18aaaSAndroid Build Coastguard Worker 288*c2e18aaaSAndroid Build Coastguard Worker Returns: 289*c2e18aaaSAndroid Build Coastguard Worker True if this test is acceptable. Otherwise, print the reason and 290*c2e18aaaSAndroid Build Coastguard Worker return False. 291*c2e18aaaSAndroid Build Coastguard Worker """ 292*c2e18aaaSAndroid Build Coastguard Worker mainline_binaries = test_identifier.binary_names 293*c2e18aaaSAndroid Build Coastguard Worker if not mainline_binaries: 294*c2e18aaaSAndroid Build Coastguard Worker return True 295*c2e18aaaSAndroid Build Coastguard Worker 296*c2e18aaaSAndroid Build Coastguard Worker # Exit earlier when any test name or mainline modules are not valid. 297*c2e18aaaSAndroid Build Coastguard Worker if not self._valid_modules(test_identifier): 298*c2e18aaaSAndroid Build Coastguard Worker return False 299*c2e18aaaSAndroid Build Coastguard Worker 300*c2e18aaaSAndroid Build Coastguard Worker # Exit earlier if Atest cannot find relationship between the test and 301*c2e18aaaSAndroid Build Coastguard Worker # the mainline binaries. 302*c2e18aaaSAndroid Build Coastguard Worker return self._declared_mainline_modules(test_identifier) 303*c2e18aaaSAndroid Build Coastguard Worker 304*c2e18aaaSAndroid Build Coastguard Worker def _valid_modules(self, identifier: TestIdentifier) -> bool: 305*c2e18aaaSAndroid Build Coastguard Worker """Determine the test_name and mainline modules are modules.""" 306*c2e18aaaSAndroid Build Coastguard Worker if not self.mod_info.is_module(identifier.test_name): 307*c2e18aaaSAndroid Build Coastguard Worker print( 308*c2e18aaaSAndroid Build Coastguard Worker 'Error: "{}" is not a testable module.'.format( 309*c2e18aaaSAndroid Build Coastguard Worker atest_utils.mark_red(identifier.test_name) 310*c2e18aaaSAndroid Build Coastguard Worker ) 311*c2e18aaaSAndroid Build Coastguard Worker ) 312*c2e18aaaSAndroid Build Coastguard Worker return False 313*c2e18aaaSAndroid Build Coastguard Worker 314*c2e18aaaSAndroid Build Coastguard Worker # Exit earlier if the given mainline modules are unavailable in the 315*c2e18aaaSAndroid Build Coastguard Worker # branch. 316*c2e18aaaSAndroid Build Coastguard Worker unknown_modules = [ 317*c2e18aaaSAndroid Build Coastguard Worker module 318*c2e18aaaSAndroid Build Coastguard Worker for module in identifier.module_names 319*c2e18aaaSAndroid Build Coastguard Worker if not self.mod_info.is_module(module) 320*c2e18aaaSAndroid Build Coastguard Worker ] 321*c2e18aaaSAndroid Build Coastguard Worker if unknown_modules: 322*c2e18aaaSAndroid Build Coastguard Worker print( 323*c2e18aaaSAndroid Build Coastguard Worker 'Error: Cannot find {} in module info!'.format( 324*c2e18aaaSAndroid Build Coastguard Worker atest_utils.mark_red(', '.join(unknown_modules)) 325*c2e18aaaSAndroid Build Coastguard Worker ) 326*c2e18aaaSAndroid Build Coastguard Worker ) 327*c2e18aaaSAndroid Build Coastguard Worker return False 328*c2e18aaaSAndroid Build Coastguard Worker 329*c2e18aaaSAndroid Build Coastguard Worker # Exit earlier if found unsupported `capex` files. 330*c2e18aaaSAndroid Build Coastguard Worker unsupported_binaries = [] 331*c2e18aaaSAndroid Build Coastguard Worker for name in identifier.module_names: 332*c2e18aaaSAndroid Build Coastguard Worker info = self.mod_info.get_module_info(name) 333*c2e18aaaSAndroid Build Coastguard Worker if info.get('installed'): 334*c2e18aaaSAndroid Build Coastguard Worker for bin in info.get('installed'): 335*c2e18aaaSAndroid Build Coastguard Worker if not re.search(atest_utils.MAINLINE_MODULES_EXT_RE, bin): 336*c2e18aaaSAndroid Build Coastguard Worker unsupported_binaries.append(bin) 337*c2e18aaaSAndroid Build Coastguard Worker if unsupported_binaries: 338*c2e18aaaSAndroid Build Coastguard Worker print( 339*c2e18aaaSAndroid Build Coastguard Worker 'The output format {} are not in a supported format; ' 340*c2e18aaaSAndroid Build Coastguard Worker 'did you run mainline local setup script? ' 341*c2e18aaaSAndroid Build Coastguard Worker 'Please refer to {}.'.format( 342*c2e18aaaSAndroid Build Coastguard Worker atest_utils.mark_red(', '.join(unsupported_binaries)), 343*c2e18aaaSAndroid Build Coastguard Worker atest_utils.mark_yellow(MAINLINE_LOCAL_DOC), 344*c2e18aaaSAndroid Build Coastguard Worker ) 345*c2e18aaaSAndroid Build Coastguard Worker ) 346*c2e18aaaSAndroid Build Coastguard Worker return False 347*c2e18aaaSAndroid Build Coastguard Worker 348*c2e18aaaSAndroid Build Coastguard Worker return True 349*c2e18aaaSAndroid Build Coastguard Worker 350*c2e18aaaSAndroid Build Coastguard Worker def _declared_mainline_modules(self, identifier: TestIdentifier) -> bool: 351*c2e18aaaSAndroid Build Coastguard Worker """Determine if all mainline modules were associated to the test.""" 352*c2e18aaaSAndroid Build Coastguard Worker test = identifier.test_name 353*c2e18aaaSAndroid Build Coastguard Worker mainline_binaries = identifier.binary_names 354*c2e18aaaSAndroid Build Coastguard Worker if not self.mod_info.has_mainline_modules(test, mainline_binaries): 355*c2e18aaaSAndroid Build Coastguard Worker print( 356*c2e18aaaSAndroid Build Coastguard Worker 'Error: Mainline modules "{}" were not defined for {} in ' 357*c2e18aaaSAndroid Build Coastguard Worker 'neither build file nor test config.'.format( 358*c2e18aaaSAndroid Build Coastguard Worker atest_utils.mark_red(', '.join(mainline_binaries)), 359*c2e18aaaSAndroid Build Coastguard Worker atest_utils.mark_red(test), 360*c2e18aaaSAndroid Build Coastguard Worker ) 361*c2e18aaaSAndroid Build Coastguard Worker ) 362*c2e18aaaSAndroid Build Coastguard Worker return False 363*c2e18aaaSAndroid Build Coastguard Worker 364*c2e18aaaSAndroid Build Coastguard Worker return True 365*c2e18aaaSAndroid Build Coastguard Worker 366*c2e18aaaSAndroid Build Coastguard Worker def _fuzzy_search_and_msg(self, test, find_test_err_msg): 367*c2e18aaaSAndroid Build Coastguard Worker """Fuzzy search and print message. 368*c2e18aaaSAndroid Build Coastguard Worker 369*c2e18aaaSAndroid Build Coastguard Worker Args: 370*c2e18aaaSAndroid Build Coastguard Worker test: A string representing test references 371*c2e18aaaSAndroid Build Coastguard Worker find_test_err_msg: A string of find test error message. 372*c2e18aaaSAndroid Build Coastguard Worker 373*c2e18aaaSAndroid Build Coastguard Worker Returns: 374*c2e18aaaSAndroid Build Coastguard Worker A list of TestInfos if found, otherwise None. 375*c2e18aaaSAndroid Build Coastguard Worker """ 376*c2e18aaaSAndroid Build Coastguard Worker # Currently we focus on guessing module names. Append names on 377*c2e18aaaSAndroid Build Coastguard Worker # results if more finders support fuzzy searching. 378*c2e18aaaSAndroid Build Coastguard Worker if atest_utils.has_chars(test, TESTNAME_CHARS): 379*c2e18aaaSAndroid Build Coastguard Worker return None 380*c2e18aaaSAndroid Build Coastguard Worker mod_finder = module_finder.ModuleFinder(self.mod_info) 381*c2e18aaaSAndroid Build Coastguard Worker results = mod_finder.get_fuzzy_searching_results(test) 382*c2e18aaaSAndroid Build Coastguard Worker if len(results) == 1 and self._confirm_running(results): 383*c2e18aaaSAndroid Build Coastguard Worker found_test_infos = mod_finder.find_test_by_module_name(results[0]) 384*c2e18aaaSAndroid Build Coastguard Worker # found_test_infos is a list with at most 1 element. 385*c2e18aaaSAndroid Build Coastguard Worker if found_test_infos: 386*c2e18aaaSAndroid Build Coastguard Worker return found_test_infos 387*c2e18aaaSAndroid Build Coastguard Worker elif len(results) > 1: 388*c2e18aaaSAndroid Build Coastguard Worker self._print_fuzzy_searching_results(results) 389*c2e18aaaSAndroid Build Coastguard Worker else: 390*c2e18aaaSAndroid Build Coastguard Worker print('No matching result for {0}.'.format(test)) 391*c2e18aaaSAndroid Build Coastguard Worker if find_test_err_msg: 392*c2e18aaaSAndroid Build Coastguard Worker print(f'{atest_utils.mark_magenta(find_test_err_msg)}\n') 393*c2e18aaaSAndroid Build Coastguard Worker return None 394*c2e18aaaSAndroid Build Coastguard Worker 395*c2e18aaaSAndroid Build Coastguard Worker def _get_test_infos(self, tests, test_mapping_test_details=None): 396*c2e18aaaSAndroid Build Coastguard Worker """Return set of TestInfos based on passed in tests. 397*c2e18aaaSAndroid Build Coastguard Worker 398*c2e18aaaSAndroid Build Coastguard Worker Args: 399*c2e18aaaSAndroid Build Coastguard Worker tests: List of strings representing test references. 400*c2e18aaaSAndroid Build Coastguard Worker test_mapping_test_details: List of TestDetail for tests configured in 401*c2e18aaaSAndroid Build Coastguard Worker TEST_MAPPING files. 402*c2e18aaaSAndroid Build Coastguard Worker 403*c2e18aaaSAndroid Build Coastguard Worker Returns: 404*c2e18aaaSAndroid Build Coastguard Worker Set of TestInfos based on the passed in tests. 405*c2e18aaaSAndroid Build Coastguard Worker """ 406*c2e18aaaSAndroid Build Coastguard Worker test_infos = [] 407*c2e18aaaSAndroid Build Coastguard Worker if not test_mapping_test_details: 408*c2e18aaaSAndroid Build Coastguard Worker test_mapping_test_details = [None] * len(tests) 409*c2e18aaaSAndroid Build Coastguard Worker for test, tm_test_detail in zip(tests, test_mapping_test_details): 410*c2e18aaaSAndroid Build Coastguard Worker found_test_infos = self._find_test_infos(test, tm_test_detail) 411*c2e18aaaSAndroid Build Coastguard Worker test_infos.extend(found_test_infos) 412*c2e18aaaSAndroid Build Coastguard Worker return test_infos 413*c2e18aaaSAndroid Build Coastguard Worker 414*c2e18aaaSAndroid Build Coastguard Worker def _confirm_running(self, results): 415*c2e18aaaSAndroid Build Coastguard Worker """Listen to an answer from raw input. 416*c2e18aaaSAndroid Build Coastguard Worker 417*c2e18aaaSAndroid Build Coastguard Worker Args: 418*c2e18aaaSAndroid Build Coastguard Worker results: A list of results. 419*c2e18aaaSAndroid Build Coastguard Worker 420*c2e18aaaSAndroid Build Coastguard Worker Returns: 421*c2e18aaaSAndroid Build Coastguard Worker True is the answer is affirmative. 422*c2e18aaaSAndroid Build Coastguard Worker """ 423*c2e18aaaSAndroid Build Coastguard Worker return atest_utils.prompt_with_yn_result( 424*c2e18aaaSAndroid Build Coastguard Worker 'Did you mean {0}?'.format(atest_utils.mark_green(results[0])), True 425*c2e18aaaSAndroid Build Coastguard Worker ) 426*c2e18aaaSAndroid Build Coastguard Worker 427*c2e18aaaSAndroid Build Coastguard Worker def _print_fuzzy_searching_results(self, results): 428*c2e18aaaSAndroid Build Coastguard Worker """Print modules when fuzzy searching gives multiple results. 429*c2e18aaaSAndroid Build Coastguard Worker 430*c2e18aaaSAndroid Build Coastguard Worker If the result is lengthy, just print the first 10 items only since we 431*c2e18aaaSAndroid Build Coastguard Worker have already given enough-accurate result. 432*c2e18aaaSAndroid Build Coastguard Worker 433*c2e18aaaSAndroid Build Coastguard Worker Args: 434*c2e18aaaSAndroid Build Coastguard Worker results: A list of guessed testable module names. 435*c2e18aaaSAndroid Build Coastguard Worker """ 436*c2e18aaaSAndroid Build Coastguard Worker atest_utils.colorful_print( 437*c2e18aaaSAndroid Build Coastguard Worker 'Did you mean the following modules?', constants.WHITE 438*c2e18aaaSAndroid Build Coastguard Worker ) 439*c2e18aaaSAndroid Build Coastguard Worker for mod in results[:10]: 440*c2e18aaaSAndroid Build Coastguard Worker atest_utils.colorful_print(mod, constants.GREEN) 441*c2e18aaaSAndroid Build Coastguard Worker 442*c2e18aaaSAndroid Build Coastguard Worker def filter_comments(self, test_mapping_file): 443*c2e18aaaSAndroid Build Coastguard Worker """Remove comments in TEST_MAPPING file to valid format. 444*c2e18aaaSAndroid Build Coastguard Worker 445*c2e18aaaSAndroid Build Coastguard Worker Only '//' and '#' are regarded as comments. 446*c2e18aaaSAndroid Build Coastguard Worker 447*c2e18aaaSAndroid Build Coastguard Worker Args: 448*c2e18aaaSAndroid Build Coastguard Worker test_mapping_file: Path to a TEST_MAPPING file. 449*c2e18aaaSAndroid Build Coastguard Worker 450*c2e18aaaSAndroid Build Coastguard Worker Returns: 451*c2e18aaaSAndroid Build Coastguard Worker Valid json string without comments. 452*c2e18aaaSAndroid Build Coastguard Worker """ 453*c2e18aaaSAndroid Build Coastguard Worker 454*c2e18aaaSAndroid Build Coastguard Worker def _replace(match): 455*c2e18aaaSAndroid Build Coastguard Worker """Replace comments if found matching the defined regular 456*c2e18aaaSAndroid Build Coastguard Worker 457*c2e18aaaSAndroid Build Coastguard Worker expression. 458*c2e18aaaSAndroid Build Coastguard Worker 459*c2e18aaaSAndroid Build Coastguard Worker Args: 460*c2e18aaaSAndroid Build Coastguard Worker match: The matched regex pattern 461*c2e18aaaSAndroid Build Coastguard Worker 462*c2e18aaaSAndroid Build Coastguard Worker Returns: 463*c2e18aaaSAndroid Build Coastguard Worker "" if it matches _COMMENTS, otherwise original string. 464*c2e18aaaSAndroid Build Coastguard Worker """ 465*c2e18aaaSAndroid Build Coastguard Worker line = match.group(0).strip() 466*c2e18aaaSAndroid Build Coastguard Worker return '' if any(map(line.startswith, _COMMENTS)) else line 467*c2e18aaaSAndroid Build Coastguard Worker 468*c2e18aaaSAndroid Build Coastguard Worker with open(test_mapping_file, encoding='utf-8') as json_file: 469*c2e18aaaSAndroid Build Coastguard Worker return re.sub(_COMMENTS_RE, _replace, json_file.read()) 470*c2e18aaaSAndroid Build Coastguard Worker 471*c2e18aaaSAndroid Build Coastguard Worker def _read_tests_in_test_mapping(self, test_mapping_file): 472*c2e18aaaSAndroid Build Coastguard Worker """Read tests from a TEST_MAPPING file. 473*c2e18aaaSAndroid Build Coastguard Worker 474*c2e18aaaSAndroid Build Coastguard Worker Args: 475*c2e18aaaSAndroid Build Coastguard Worker test_mapping_file: Path to a TEST_MAPPING file. 476*c2e18aaaSAndroid Build Coastguard Worker 477*c2e18aaaSAndroid Build Coastguard Worker Returns: 478*c2e18aaaSAndroid Build Coastguard Worker A tuple of (all_tests, imports), where 479*c2e18aaaSAndroid Build Coastguard Worker all_tests is a dictionary of all tests in the TEST_MAPPING file, 480*c2e18aaaSAndroid Build Coastguard Worker grouped by test group. 481*c2e18aaaSAndroid Build Coastguard Worker imports is a list of test_mapping.Import to include other test 482*c2e18aaaSAndroid Build Coastguard Worker mapping files. 483*c2e18aaaSAndroid Build Coastguard Worker """ 484*c2e18aaaSAndroid Build Coastguard Worker all_tests = {} 485*c2e18aaaSAndroid Build Coastguard Worker imports = [] 486*c2e18aaaSAndroid Build Coastguard Worker test_mapping_dict = {} 487*c2e18aaaSAndroid Build Coastguard Worker try: 488*c2e18aaaSAndroid Build Coastguard Worker test_mapping_dict = json.loads(self.filter_comments(test_mapping_file)) 489*c2e18aaaSAndroid Build Coastguard Worker except json.JSONDecodeError as e: 490*c2e18aaaSAndroid Build Coastguard Worker msg = 'Test Mapping file has invalid format: %s.' % e 491*c2e18aaaSAndroid Build Coastguard Worker logging.debug(msg) 492*c2e18aaaSAndroid Build Coastguard Worker atest_utils.colorful_print(msg, constants.RED) 493*c2e18aaaSAndroid Build Coastguard Worker sys.exit(ExitCode.INVALID_TM_FORMAT) 494*c2e18aaaSAndroid Build Coastguard Worker for test_group_name, test_list in test_mapping_dict.items(): 495*c2e18aaaSAndroid Build Coastguard Worker if test_group_name == constants.TEST_MAPPING_IMPORTS: 496*c2e18aaaSAndroid Build Coastguard Worker for import_detail in test_list: 497*c2e18aaaSAndroid Build Coastguard Worker imports.append(test_mapping.Import(test_mapping_file, import_detail)) 498*c2e18aaaSAndroid Build Coastguard Worker else: 499*c2e18aaaSAndroid Build Coastguard Worker grouped_tests = all_tests.setdefault(test_group_name, set()) 500*c2e18aaaSAndroid Build Coastguard Worker tests = [] 501*c2e18aaaSAndroid Build Coastguard Worker for test in test_list: 502*c2e18aaaSAndroid Build Coastguard Worker if ( 503*c2e18aaaSAndroid Build Coastguard Worker self.enable_file_patterns 504*c2e18aaaSAndroid Build Coastguard Worker and not test_mapping.is_match_file_patterns( 505*c2e18aaaSAndroid Build Coastguard Worker test_mapping_file, test 506*c2e18aaaSAndroid Build Coastguard Worker ) 507*c2e18aaaSAndroid Build Coastguard Worker ): 508*c2e18aaaSAndroid Build Coastguard Worker continue 509*c2e18aaaSAndroid Build Coastguard Worker test_name = parse_test_identifier(test['name']).test_name 510*c2e18aaaSAndroid Build Coastguard Worker test_mod_info = self.mod_info.name_to_module_info.get(test_name) 511*c2e18aaaSAndroid Build Coastguard Worker if not test_mod_info: 512*c2e18aaaSAndroid Build Coastguard Worker print( 513*c2e18aaaSAndroid Build Coastguard Worker 'WARNING: %s is not a valid build target and ' 514*c2e18aaaSAndroid Build Coastguard Worker 'may not be discoverable by TreeHugger. If you ' 515*c2e18aaaSAndroid Build Coastguard Worker 'want to specify a class or test-package, ' 516*c2e18aaaSAndroid Build Coastguard Worker "please set 'name' to the test module and use " 517*c2e18aaaSAndroid Build Coastguard Worker "'options' to specify the right tests via " 518*c2e18aaaSAndroid Build Coastguard Worker "'include-filter'.\nNote: this can also occur " 519*c2e18aaaSAndroid Build Coastguard Worker 'if the test module is not built for your ' 520*c2e18aaaSAndroid Build Coastguard Worker 'current lunch target.\n' 521*c2e18aaaSAndroid Build Coastguard Worker % atest_utils.mark_red(test['name']) 522*c2e18aaaSAndroid Build Coastguard Worker ) 523*c2e18aaaSAndroid Build Coastguard Worker elif not any( 524*c2e18aaaSAndroid Build Coastguard Worker x in test_mod_info.get('compatibility_suites', []) 525*c2e18aaaSAndroid Build Coastguard Worker for x in constants.TEST_MAPPING_SUITES 526*c2e18aaaSAndroid Build Coastguard Worker ): 527*c2e18aaaSAndroid Build Coastguard Worker print( 528*c2e18aaaSAndroid Build Coastguard Worker 'WARNING: Please add %s to either suite: %s for ' 529*c2e18aaaSAndroid Build Coastguard Worker 'this TEST_MAPPING file to work with TreeHugger.' 530*c2e18aaaSAndroid Build Coastguard Worker % ( 531*c2e18aaaSAndroid Build Coastguard Worker atest_utils.mark_red(test['name']), 532*c2e18aaaSAndroid Build Coastguard Worker atest_utils.mark_green(constants.TEST_MAPPING_SUITES), 533*c2e18aaaSAndroid Build Coastguard Worker ) 534*c2e18aaaSAndroid Build Coastguard Worker ) 535*c2e18aaaSAndroid Build Coastguard Worker tests.append(test_mapping.TestDetail(test)) 536*c2e18aaaSAndroid Build Coastguard Worker grouped_tests.update(tests) 537*c2e18aaaSAndroid Build Coastguard Worker return all_tests, imports 538*c2e18aaaSAndroid Build Coastguard Worker 539*c2e18aaaSAndroid Build Coastguard Worker def _get_tests_from_test_mapping_files(self, test_groups, test_mapping_files): 540*c2e18aaaSAndroid Build Coastguard Worker """Get tests in the given test mapping files with the match group. 541*c2e18aaaSAndroid Build Coastguard Worker 542*c2e18aaaSAndroid Build Coastguard Worker Args: 543*c2e18aaaSAndroid Build Coastguard Worker test_groups: Groups of tests to run. Default is set to `presubmit` and 544*c2e18aaaSAndroid Build Coastguard Worker `presubmit-large`. 545*c2e18aaaSAndroid Build Coastguard Worker test_mapping_files: A list of path of TEST_MAPPING files. 546*c2e18aaaSAndroid Build Coastguard Worker 547*c2e18aaaSAndroid Build Coastguard Worker Returns: 548*c2e18aaaSAndroid Build Coastguard Worker A tuple of (tests, all_tests, imports), where, 549*c2e18aaaSAndroid Build Coastguard Worker tests is a set of tests (test_mapping.TestDetail) defined in 550*c2e18aaaSAndroid Build Coastguard Worker TEST_MAPPING file of the given path, and its parent directories, 551*c2e18aaaSAndroid Build Coastguard Worker with matching test_group. 552*c2e18aaaSAndroid Build Coastguard Worker all_tests is a dictionary of all tests in TEST_MAPPING files, 553*c2e18aaaSAndroid Build Coastguard Worker grouped by test group. 554*c2e18aaaSAndroid Build Coastguard Worker imports is a list of test_mapping.Import objects that contains the 555*c2e18aaaSAndroid Build Coastguard Worker details of where to import a TEST_MAPPING file. 556*c2e18aaaSAndroid Build Coastguard Worker """ 557*c2e18aaaSAndroid Build Coastguard Worker all_imports = [] 558*c2e18aaaSAndroid Build Coastguard Worker # Read and merge the tests in all TEST_MAPPING files. 559*c2e18aaaSAndroid Build Coastguard Worker merged_all_tests = {} 560*c2e18aaaSAndroid Build Coastguard Worker for test_mapping_file in test_mapping_files: 561*c2e18aaaSAndroid Build Coastguard Worker all_tests, imports = self._read_tests_in_test_mapping(test_mapping_file) 562*c2e18aaaSAndroid Build Coastguard Worker all_imports.extend(imports) 563*c2e18aaaSAndroid Build Coastguard Worker for test_group_name, test_list in all_tests.items(): 564*c2e18aaaSAndroid Build Coastguard Worker grouped_tests = merged_all_tests.setdefault(test_group_name, set()) 565*c2e18aaaSAndroid Build Coastguard Worker grouped_tests.update(test_list) 566*c2e18aaaSAndroid Build Coastguard Worker tests = set() 567*c2e18aaaSAndroid Build Coastguard Worker for test_group in test_groups: 568*c2e18aaaSAndroid Build Coastguard Worker temp_tests = set(merged_all_tests.get(test_group, [])) 569*c2e18aaaSAndroid Build Coastguard Worker tests.update(temp_tests) 570*c2e18aaaSAndroid Build Coastguard Worker if test_group == constants.TEST_GROUP_ALL: 571*c2e18aaaSAndroid Build Coastguard Worker for grouped_tests in merged_all_tests.values(): 572*c2e18aaaSAndroid Build Coastguard Worker tests.update(grouped_tests) 573*c2e18aaaSAndroid Build Coastguard Worker return tests, merged_all_tests, all_imports 574*c2e18aaaSAndroid Build Coastguard Worker 575*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=too-many-arguments 576*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=too-many-locals 577*c2e18aaaSAndroid Build Coastguard Worker def _find_tests_by_test_mapping( 578*c2e18aaaSAndroid Build Coastguard Worker self, 579*c2e18aaaSAndroid Build Coastguard Worker path='', 580*c2e18aaaSAndroid Build Coastguard Worker test_groups=None, 581*c2e18aaaSAndroid Build Coastguard Worker file_name=constants.TEST_MAPPING, 582*c2e18aaaSAndroid Build Coastguard Worker include_subdirs=False, 583*c2e18aaaSAndroid Build Coastguard Worker checked_files=None, 584*c2e18aaaSAndroid Build Coastguard Worker ): 585*c2e18aaaSAndroid Build Coastguard Worker """Find tests defined in TEST_MAPPING in the given path. 586*c2e18aaaSAndroid Build Coastguard Worker 587*c2e18aaaSAndroid Build Coastguard Worker Args: 588*c2e18aaaSAndroid Build Coastguard Worker path: A string of path in source. Default is set to '', i.e., CWD. 589*c2e18aaaSAndroid Build Coastguard Worker test_groups: A List of test groups to run. 590*c2e18aaaSAndroid Build Coastguard Worker file_name: Name of TEST_MAPPING file. Default is set to `TEST_MAPPING`. 591*c2e18aaaSAndroid Build Coastguard Worker The argument is added for testing purpose. 592*c2e18aaaSAndroid Build Coastguard Worker include_subdirs: True to include tests in TEST_MAPPING files in sub 593*c2e18aaaSAndroid Build Coastguard Worker directories. 594*c2e18aaaSAndroid Build Coastguard Worker checked_files: Paths of TEST_MAPPING files that have been checked. 595*c2e18aaaSAndroid Build Coastguard Worker 596*c2e18aaaSAndroid Build Coastguard Worker Returns: 597*c2e18aaaSAndroid Build Coastguard Worker A tuple of (tests, all_tests), where, 598*c2e18aaaSAndroid Build Coastguard Worker tests is a set of tests (test_mapping.TestDetail) defined in 599*c2e18aaaSAndroid Build Coastguard Worker TEST_MAPPING file of the given path, and its parent directories, 600*c2e18aaaSAndroid Build Coastguard Worker with matching test_group. 601*c2e18aaaSAndroid Build Coastguard Worker all_tests is a dictionary of all tests in TEST_MAPPING files, 602*c2e18aaaSAndroid Build Coastguard Worker grouped by test group. 603*c2e18aaaSAndroid Build Coastguard Worker """ 604*c2e18aaaSAndroid Build Coastguard Worker path = os.path.realpath(path) 605*c2e18aaaSAndroid Build Coastguard Worker # Default test_groups is set to [`presubmit`, `presubmit-large`]. 606*c2e18aaaSAndroid Build Coastguard Worker if not test_groups: 607*c2e18aaaSAndroid Build Coastguard Worker test_groups = constants.DEFAULT_TEST_GROUPS 608*c2e18aaaSAndroid Build Coastguard Worker test_mapping_files = set() 609*c2e18aaaSAndroid Build Coastguard Worker all_tests = {} 610*c2e18aaaSAndroid Build Coastguard Worker test_mapping_file = os.path.join(path, file_name) 611*c2e18aaaSAndroid Build Coastguard Worker if os.path.exists(test_mapping_file): 612*c2e18aaaSAndroid Build Coastguard Worker test_mapping_files.add(test_mapping_file) 613*c2e18aaaSAndroid Build Coastguard Worker # Include all TEST_MAPPING files in sub-directories if `include_subdirs` 614*c2e18aaaSAndroid Build Coastguard Worker # is set to True. 615*c2e18aaaSAndroid Build Coastguard Worker if include_subdirs: 616*c2e18aaaSAndroid Build Coastguard Worker test_mapping_files.update(atest_utils.find_files(path, file_name)) 617*c2e18aaaSAndroid Build Coastguard Worker # Include all possible TEST_MAPPING files in parent directories. 618*c2e18aaaSAndroid Build Coastguard Worker while path not in (self.root_dir, os.sep): 619*c2e18aaaSAndroid Build Coastguard Worker path = os.path.dirname(path) 620*c2e18aaaSAndroid Build Coastguard Worker test_mapping_file = os.path.join(path, file_name) 621*c2e18aaaSAndroid Build Coastguard Worker if os.path.exists(test_mapping_file): 622*c2e18aaaSAndroid Build Coastguard Worker test_mapping_files.add(test_mapping_file) 623*c2e18aaaSAndroid Build Coastguard Worker 624*c2e18aaaSAndroid Build Coastguard Worker if checked_files is None: 625*c2e18aaaSAndroid Build Coastguard Worker checked_files = set() 626*c2e18aaaSAndroid Build Coastguard Worker test_mapping_files.difference_update(checked_files) 627*c2e18aaaSAndroid Build Coastguard Worker checked_files.update(test_mapping_files) 628*c2e18aaaSAndroid Build Coastguard Worker if not test_mapping_files: 629*c2e18aaaSAndroid Build Coastguard Worker return test_mapping_files, all_tests 630*c2e18aaaSAndroid Build Coastguard Worker 631*c2e18aaaSAndroid Build Coastguard Worker tests, all_tests, imports = self._get_tests_from_test_mapping_files( 632*c2e18aaaSAndroid Build Coastguard Worker test_groups, test_mapping_files 633*c2e18aaaSAndroid Build Coastguard Worker ) 634*c2e18aaaSAndroid Build Coastguard Worker 635*c2e18aaaSAndroid Build Coastguard Worker # Load TEST_MAPPING files from imports recursively. 636*c2e18aaaSAndroid Build Coastguard Worker if imports: 637*c2e18aaaSAndroid Build Coastguard Worker for import_detail in imports: 638*c2e18aaaSAndroid Build Coastguard Worker path = import_detail.get_path() 639*c2e18aaaSAndroid Build Coastguard Worker # (b/110166535 #19) Import path might not exist if a project is 640*c2e18aaaSAndroid Build Coastguard Worker # located in different directory in different branches. 641*c2e18aaaSAndroid Build Coastguard Worker if path is None: 642*c2e18aaaSAndroid Build Coastguard Worker atest_utils.print_and_log_warning( 643*c2e18aaaSAndroid Build Coastguard Worker 'Failed to import TEST_MAPPING at %s', import_detail 644*c2e18aaaSAndroid Build Coastguard Worker ) 645*c2e18aaaSAndroid Build Coastguard Worker continue 646*c2e18aaaSAndroid Build Coastguard Worker # Search for tests based on the imported search path. 647*c2e18aaaSAndroid Build Coastguard Worker import_tests, import_all_tests = self._find_tests_by_test_mapping( 648*c2e18aaaSAndroid Build Coastguard Worker path, test_groups, file_name, include_subdirs, checked_files 649*c2e18aaaSAndroid Build Coastguard Worker ) 650*c2e18aaaSAndroid Build Coastguard Worker # Merge the collections 651*c2e18aaaSAndroid Build Coastguard Worker tests.update(import_tests) 652*c2e18aaaSAndroid Build Coastguard Worker for group, grouped_tests in import_all_tests.items(): 653*c2e18aaaSAndroid Build Coastguard Worker all_tests.setdefault(group, set()).update(grouped_tests) 654*c2e18aaaSAndroid Build Coastguard Worker 655*c2e18aaaSAndroid Build Coastguard Worker return tests, all_tests 656*c2e18aaaSAndroid Build Coastguard Worker 657*c2e18aaaSAndroid Build Coastguard Worker def _get_test_mapping_tests(self, args, exit_if_no_test_found=True): 658*c2e18aaaSAndroid Build Coastguard Worker """Find the tests in TEST_MAPPING files. 659*c2e18aaaSAndroid Build Coastguard Worker 660*c2e18aaaSAndroid Build Coastguard Worker Args: 661*c2e18aaaSAndroid Build Coastguard Worker args: arg parsed object. exit_if_no_test(s)_found: A flag to exit atest 662*c2e18aaaSAndroid Build Coastguard Worker if no test mapping tests found. 663*c2e18aaaSAndroid Build Coastguard Worker 664*c2e18aaaSAndroid Build Coastguard Worker Returns: 665*c2e18aaaSAndroid Build Coastguard Worker A tuple of (test_names, test_details_list), where 666*c2e18aaaSAndroid Build Coastguard Worker test_names: a list of test name 667*c2e18aaaSAndroid Build Coastguard Worker test_details_list: a list of test_mapping.TestDetail objects for 668*c2e18aaaSAndroid Build Coastguard Worker the tests in TEST_MAPPING files with matching test group. 669*c2e18aaaSAndroid Build Coastguard Worker """ 670*c2e18aaaSAndroid Build Coastguard Worker # Pull out tests from test mapping 671*c2e18aaaSAndroid Build Coastguard Worker src_path = '' 672*c2e18aaaSAndroid Build Coastguard Worker test_groups = constants.DEFAULT_TEST_GROUPS 673*c2e18aaaSAndroid Build Coastguard Worker if args.tests: 674*c2e18aaaSAndroid Build Coastguard Worker if ':' in args.tests[0]: 675*c2e18aaaSAndroid Build Coastguard Worker src_path, test_group = args.tests[0].split(':') 676*c2e18aaaSAndroid Build Coastguard Worker test_groups = [test_group] 677*c2e18aaaSAndroid Build Coastguard Worker else: 678*c2e18aaaSAndroid Build Coastguard Worker src_path = args.tests[0] 679*c2e18aaaSAndroid Build Coastguard Worker 680*c2e18aaaSAndroid Build Coastguard Worker test_details, all_test_details = self._find_tests_by_test_mapping( 681*c2e18aaaSAndroid Build Coastguard Worker path=src_path, 682*c2e18aaaSAndroid Build Coastguard Worker test_groups=test_groups, 683*c2e18aaaSAndroid Build Coastguard Worker include_subdirs=args.include_subdirs, 684*c2e18aaaSAndroid Build Coastguard Worker checked_files=set(), 685*c2e18aaaSAndroid Build Coastguard Worker ) 686*c2e18aaaSAndroid Build Coastguard Worker test_details_list = list(test_details) 687*c2e18aaaSAndroid Build Coastguard Worker if not test_details_list and exit_if_no_test_found: 688*c2e18aaaSAndroid Build Coastguard Worker atest_utils.print_and_log_warning( 689*c2e18aaaSAndroid Build Coastguard Worker 'No tests of group `%s` found in %s or its ' 690*c2e18aaaSAndroid Build Coastguard Worker 'parent directories. (Available groups: %s)\n' 691*c2e18aaaSAndroid Build Coastguard Worker 'You might be missing atest arguments,' 692*c2e18aaaSAndroid Build Coastguard Worker ' try `atest --help` for more information.', 693*c2e18aaaSAndroid Build Coastguard Worker test_groups, 694*c2e18aaaSAndroid Build Coastguard Worker os.path.join(src_path, constants.TEST_MAPPING), 695*c2e18aaaSAndroid Build Coastguard Worker ', '.join(all_test_details.keys()), 696*c2e18aaaSAndroid Build Coastguard Worker ) 697*c2e18aaaSAndroid Build Coastguard Worker if all_test_details: 698*c2e18aaaSAndroid Build Coastguard Worker tests = '' 699*c2e18aaaSAndroid Build Coastguard Worker for test_group, test_list in all_test_details.items(): 700*c2e18aaaSAndroid Build Coastguard Worker tests += '%s:\n' % test_group 701*c2e18aaaSAndroid Build Coastguard Worker for test_detail in sorted(test_list, key=str): 702*c2e18aaaSAndroid Build Coastguard Worker tests += '\t%s\n' % test_detail 703*c2e18aaaSAndroid Build Coastguard Worker atest_utils.print_and_log_warning( 704*c2e18aaaSAndroid Build Coastguard Worker 'All available tests in TEST_MAPPING files are:\n%s', tests 705*c2e18aaaSAndroid Build Coastguard Worker ) 706*c2e18aaaSAndroid Build Coastguard Worker metrics_utils.send_exit_event(ExitCode.TEST_NOT_FOUND) 707*c2e18aaaSAndroid Build Coastguard Worker sys.exit(ExitCode.TEST_NOT_FOUND) 708*c2e18aaaSAndroid Build Coastguard Worker 709*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 710*c2e18aaaSAndroid Build Coastguard Worker 'Test details:\n%s', 711*c2e18aaaSAndroid Build Coastguard Worker '\n'.join([str(detail) for detail in test_details_list]), 712*c2e18aaaSAndroid Build Coastguard Worker ) 713*c2e18aaaSAndroid Build Coastguard Worker test_names = [detail.name for detail in test_details_list] 714*c2e18aaaSAndroid Build Coastguard Worker return test_names, test_details_list 715*c2e18aaaSAndroid Build Coastguard Worker 716*c2e18aaaSAndroid Build Coastguard Worker def _extract_testable_modules_by_wildcard(self, user_input): 717*c2e18aaaSAndroid Build Coastguard Worker """Extract the given string with wildcard symbols to testable 718*c2e18aaaSAndroid Build Coastguard Worker 719*c2e18aaaSAndroid Build Coastguard Worker module names. 720*c2e18aaaSAndroid Build Coastguard Worker 721*c2e18aaaSAndroid Build Coastguard Worker Assume the available testable modules is: 722*c2e18aaaSAndroid Build Coastguard Worker ['Google', 'google', 'G00gle', 'g00gle'] 723*c2e18aaaSAndroid Build Coastguard Worker and the user_input is: 724*c2e18aaaSAndroid Build Coastguard Worker ['*oo*', 'g00gle'] 725*c2e18aaaSAndroid Build Coastguard Worker This method will return: 726*c2e18aaaSAndroid Build Coastguard Worker ['Google', 'google', 'g00gle'] 727*c2e18aaaSAndroid Build Coastguard Worker 728*c2e18aaaSAndroid Build Coastguard Worker Args: 729*c2e18aaaSAndroid Build Coastguard Worker user_input: A list of input. 730*c2e18aaaSAndroid Build Coastguard Worker 731*c2e18aaaSAndroid Build Coastguard Worker Returns: 732*c2e18aaaSAndroid Build Coastguard Worker A list of testable modules. 733*c2e18aaaSAndroid Build Coastguard Worker """ 734*c2e18aaaSAndroid Build Coastguard Worker testable_mods = self.mod_info.get_testable_modules() 735*c2e18aaaSAndroid Build Coastguard Worker extracted_tests = [] 736*c2e18aaaSAndroid Build Coastguard Worker for test in user_input: 737*c2e18aaaSAndroid Build Coastguard Worker if atest_utils.has_wildcard(test): 738*c2e18aaaSAndroid Build Coastguard Worker extracted_tests.extend(fnmatch.filter(testable_mods, test)) 739*c2e18aaaSAndroid Build Coastguard Worker else: 740*c2e18aaaSAndroid Build Coastguard Worker extracted_tests.append(test) 741*c2e18aaaSAndroid Build Coastguard Worker return extracted_tests 742*c2e18aaaSAndroid Build Coastguard Worker 743*c2e18aaaSAndroid Build Coastguard Worker def translate(self, args): 744*c2e18aaaSAndroid Build Coastguard Worker """Translate atest command line into build targets and run commands. 745*c2e18aaaSAndroid Build Coastguard Worker 746*c2e18aaaSAndroid Build Coastguard Worker Args: 747*c2e18aaaSAndroid Build Coastguard Worker args: arg parsed object. 748*c2e18aaaSAndroid Build Coastguard Worker 749*c2e18aaaSAndroid Build Coastguard Worker Returns: 750*c2e18aaaSAndroid Build Coastguard Worker A tuple with set of build_target strings and list of TestInfos. 751*c2e18aaaSAndroid Build Coastguard Worker """ 752*c2e18aaaSAndroid Build Coastguard Worker tests = args.tests 753*c2e18aaaSAndroid Build Coastguard Worker detect_type = DetectType.TEST_WITH_ARGS 754*c2e18aaaSAndroid Build Coastguard Worker # Disable fuzzy searching when running with test mapping related args. 755*c2e18aaaSAndroid Build Coastguard Worker if not args.tests or atest_utils.is_test_mapping(args): 756*c2e18aaaSAndroid Build Coastguard Worker self.fuzzy_search = False 757*c2e18aaaSAndroid Build Coastguard Worker detect_type = DetectType.TEST_NULL_ARGS 758*c2e18aaaSAndroid Build Coastguard Worker start = time.time() 759*c2e18aaaSAndroid Build Coastguard Worker # Not including host unit tests if user specify --test-mapping. 760*c2e18aaaSAndroid Build Coastguard Worker host_unit_tests = [] 761*c2e18aaaSAndroid Build Coastguard Worker if not any((args.tests, args.test_mapping)): 762*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Finding Host Unit Tests...') 763*c2e18aaaSAndroid Build Coastguard Worker host_unit_tests = test_finder_utils.find_host_unit_tests( 764*c2e18aaaSAndroid Build Coastguard Worker self.mod_info, str(Path(os.getcwd()).relative_to(self.root_dir)) 765*c2e18aaaSAndroid Build Coastguard Worker ) 766*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Found host_unit_tests: %s', host_unit_tests) 767*c2e18aaaSAndroid Build Coastguard Worker # Test details from TEST_MAPPING files 768*c2e18aaaSAndroid Build Coastguard Worker test_details_list = None 769*c2e18aaaSAndroid Build Coastguard Worker if atest_utils.is_test_mapping(args): 770*c2e18aaaSAndroid Build Coastguard Worker if args.enable_file_patterns: 771*c2e18aaaSAndroid Build Coastguard Worker self.enable_file_patterns = True 772*c2e18aaaSAndroid Build Coastguard Worker tests, test_details_list = self._get_test_mapping_tests( 773*c2e18aaaSAndroid Build Coastguard Worker args, not bool(host_unit_tests) 774*c2e18aaaSAndroid Build Coastguard Worker ) 775*c2e18aaaSAndroid Build Coastguard Worker atest_utils.colorful_print('\nFinding Tests...', constants.CYAN) 776*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Finding Tests: %s', tests) 777*c2e18aaaSAndroid Build Coastguard Worker # Clear cache if user pass -c option 778*c2e18aaaSAndroid Build Coastguard Worker if args.clear_cache: 779*c2e18aaaSAndroid Build Coastguard Worker atest_utils.clean_test_info_caches(tests + host_unit_tests) 780*c2e18aaaSAndroid Build Coastguard Worker # Process tests which might contain wildcard symbols in advance. 781*c2e18aaaSAndroid Build Coastguard Worker if atest_utils.has_wildcard(tests): 782*c2e18aaaSAndroid Build Coastguard Worker tests = self._extract_testable_modules_by_wildcard(tests) 783*c2e18aaaSAndroid Build Coastguard Worker test_infos = self._get_test_infos(tests, test_details_list) 784*c2e18aaaSAndroid Build Coastguard Worker if host_unit_tests: 785*c2e18aaaSAndroid Build Coastguard Worker host_unit_test_details = [ 786*c2e18aaaSAndroid Build Coastguard Worker test_mapping.TestDetail({'name': test, 'host': True}) 787*c2e18aaaSAndroid Build Coastguard Worker for test in host_unit_tests 788*c2e18aaaSAndroid Build Coastguard Worker ] 789*c2e18aaaSAndroid Build Coastguard Worker host_unit_test_infos = self._get_test_infos( 790*c2e18aaaSAndroid Build Coastguard Worker host_unit_tests, host_unit_test_details 791*c2e18aaaSAndroid Build Coastguard Worker ) 792*c2e18aaaSAndroid Build Coastguard Worker test_infos.extend(host_unit_test_infos) 793*c2e18aaaSAndroid Build Coastguard Worker if atest_utils.has_mixed_type_filters(test_infos): 794*c2e18aaaSAndroid Build Coastguard Worker atest_utils.colorful_print( 795*c2e18aaaSAndroid Build Coastguard Worker 'Mixed type filters found. ' 796*c2e18aaaSAndroid Build Coastguard Worker 'Please separate tests into different runs.', 797*c2e18aaaSAndroid Build Coastguard Worker constants.YELLOW, 798*c2e18aaaSAndroid Build Coastguard Worker ) 799*c2e18aaaSAndroid Build Coastguard Worker sys.exit(ExitCode.MIXED_TYPE_FILTER) 800*c2e18aaaSAndroid Build Coastguard Worker finished_time = time.time() - start 801*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Finding tests finished in %ss', finished_time) 802*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent(detect_type=detect_type, result=int(finished_time)) 803*c2e18aaaSAndroid Build Coastguard Worker for t_info in test_infos: 804*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s\n', t_info) 805*c2e18aaaSAndroid Build Coastguard Worker return test_infos 806*c2e18aaaSAndroid Build Coastguard Worker 807*c2e18aaaSAndroid Build Coastguard Worker 808*c2e18aaaSAndroid Build Coastguard Worker# TODO: (b/265359291) Raise Exception when the brackets are not in pair. 809*c2e18aaaSAndroid Build Coastguard Workerdef parse_test_identifier(test: str) -> TestIdentifier: 810*c2e18aaaSAndroid Build Coastguard Worker """Get mainline module names and binaries information.""" 811*c2e18aaaSAndroid Build Coastguard Worker result = atest_utils.get_test_and_mainline_modules(test) 812*c2e18aaaSAndroid Build Coastguard Worker if not result: 813*c2e18aaaSAndroid Build Coastguard Worker return TestIdentifier(test, [], []) 814*c2e18aaaSAndroid Build Coastguard Worker test_name = result.group('test') 815*c2e18aaaSAndroid Build Coastguard Worker mainline_binaries = result.group('mainline_modules').split('+') 816*c2e18aaaSAndroid Build Coastguard Worker mainline_modules = [Path(m).stem for m in mainline_binaries] 817*c2e18aaaSAndroid Build Coastguard Worker logging.debug('mainline_modules: %s', mainline_modules) 818*c2e18aaaSAndroid Build Coastguard Worker return TestIdentifier(test_name, mainline_modules, mainline_binaries) 819