1*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2018, 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"""Module Info class used to hold cached module-info.json.""" 16*c2e18aaaSAndroid Build Coastguard Worker 17*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=too-many-lines 18*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import annotations 19*c2e18aaaSAndroid Build Coastguard Worker 20*c2e18aaaSAndroid Build Coastguard Workerimport collections 21*c2e18aaaSAndroid Build Coastguard Workerimport json 22*c2e18aaaSAndroid Build Coastguard Workerimport logging 23*c2e18aaaSAndroid Build Coastguard Workerimport os 24*c2e18aaaSAndroid Build Coastguard Workerfrom pathlib import Path 25*c2e18aaaSAndroid Build Coastguard Workerimport pickle 26*c2e18aaaSAndroid Build Coastguard Workerimport re 27*c2e18aaaSAndroid Build Coastguard Workerimport shutil 28*c2e18aaaSAndroid Build Coastguard Workerimport sqlite3 29*c2e18aaaSAndroid Build Coastguard Workerimport sys 30*c2e18aaaSAndroid Build Coastguard Workerimport tempfile 31*c2e18aaaSAndroid Build Coastguard Workerimport time 32*c2e18aaaSAndroid Build Coastguard Workerfrom typing import Any, Callable, Dict, List, Set, Tuple 33*c2e18aaaSAndroid Build Coastguard Worker 34*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_utils 35*c2e18aaaSAndroid Build Coastguard Workerfrom atest import constants 36*c2e18aaaSAndroid Build Coastguard Workerfrom atest.atest_enum import DetectType, ExitCode 37*c2e18aaaSAndroid Build Coastguard Workerfrom atest.metrics import metrics 38*c2e18aaaSAndroid Build Coastguard Worker 39*c2e18aaaSAndroid Build Coastguard Worker 40*c2e18aaaSAndroid Build Coastguard Worker# JSON file generated by build system that lists all buildable targets. 41*c2e18aaaSAndroid Build Coastguard Worker_MODULE_INFO = 'module-info.json' 42*c2e18aaaSAndroid Build Coastguard Worker# JSON file generated by build system that lists dependencies for java. 43*c2e18aaaSAndroid Build Coastguard Worker_JAVA_DEP_INFO = 'module_bp_java_deps.json' 44*c2e18aaaSAndroid Build Coastguard Worker# JSON file generated by build system that lists dependencies for cc. 45*c2e18aaaSAndroid Build Coastguard Worker_CC_DEP_INFO = 'module_bp_cc_deps.json' 46*c2e18aaaSAndroid Build Coastguard Worker# JSON file generated by atest merged the content from module-info, 47*c2e18aaaSAndroid Build Coastguard Worker# module_bp_java_deps.json, and module_bp_cc_deps. 48*c2e18aaaSAndroid Build Coastguard Worker_MERGED_INFO = 'atest_merged_dep.json' 49*c2e18aaaSAndroid Build Coastguard Worker_DB_VERSION = 2 50*c2e18aaaSAndroid Build Coastguard Worker_DB_NAME = f'module-info.{_DB_VERSION}.db' 51*c2e18aaaSAndroid Build Coastguard Worker_NAME_MODULE_TABLE = 'modules' 52*c2e18aaaSAndroid Build Coastguard Worker_PATH_MODULE_TABLE = 'path_modules' 53*c2e18aaaSAndroid Build Coastguard Worker 54*c2e18aaaSAndroid Build Coastguard Worker 55*c2e18aaaSAndroid Build Coastguard WorkerModule = Dict[str, Any] 56*c2e18aaaSAndroid Build Coastguard Worker 57*c2e18aaaSAndroid Build Coastguard Worker 58*c2e18aaaSAndroid Build Coastguard Workerdef load_from_file( 59*c2e18aaaSAndroid Build Coastguard Worker module_file: Path = None, 60*c2e18aaaSAndroid Build Coastguard Worker force_build: bool = False, 61*c2e18aaaSAndroid Build Coastguard Worker) -> ModuleInfo: 62*c2e18aaaSAndroid Build Coastguard Worker """Factory method that initializes ModuleInfo from the build-generated 63*c2e18aaaSAndroid Build Coastguard Worker 64*c2e18aaaSAndroid Build Coastguard Worker JSON file 65*c2e18aaaSAndroid Build Coastguard Worker """ 66*c2e18aaaSAndroid Build Coastguard Worker loader = Loader( 67*c2e18aaaSAndroid Build Coastguard Worker module_file=module_file, 68*c2e18aaaSAndroid Build Coastguard Worker force_build=force_build, 69*c2e18aaaSAndroid Build Coastguard Worker need_merge_fn=lambda: False, 70*c2e18aaaSAndroid Build Coastguard Worker ) 71*c2e18aaaSAndroid Build Coastguard Worker 72*c2e18aaaSAndroid Build Coastguard Worker mi = loader.load() 73*c2e18aaaSAndroid Build Coastguard Worker 74*c2e18aaaSAndroid Build Coastguard Worker return mi 75*c2e18aaaSAndroid Build Coastguard Worker 76*c2e18aaaSAndroid Build Coastguard Worker 77*c2e18aaaSAndroid Build Coastguard Workerdef load_from_dict(name_to_module_info: Dict[str, Any]) -> ModuleInfo: 78*c2e18aaaSAndroid Build Coastguard Worker """Factory method that initializes ModuleInfo from a dictionary.""" 79*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info = get_path_to_module_info(name_to_module_info) 80*c2e18aaaSAndroid Build Coastguard Worker return ModuleInfo( 81*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info=name_to_module_info, 82*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info=path_to_module_info, 83*c2e18aaaSAndroid Build Coastguard Worker get_testable_modules=lambda s: _get_testable_modules( 84*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info, path_to_module_info, s 85*c2e18aaaSAndroid Build Coastguard Worker ), 86*c2e18aaaSAndroid Build Coastguard Worker ) 87*c2e18aaaSAndroid Build Coastguard Worker 88*c2e18aaaSAndroid Build Coastguard Worker 89*c2e18aaaSAndroid Build Coastguard Workerdef create_empty() -> ModuleInfo: 90*c2e18aaaSAndroid Build Coastguard Worker """Factory method that initializes an empty ModuleInfo.""" 91*c2e18aaaSAndroid Build Coastguard Worker return ModuleInfo() 92*c2e18aaaSAndroid Build Coastguard Worker 93*c2e18aaaSAndroid Build Coastguard Worker 94*c2e18aaaSAndroid Build Coastguard Workerdef load( 95*c2e18aaaSAndroid Build Coastguard Worker force_build: bool = False, sqlite_module_cache: bool = False 96*c2e18aaaSAndroid Build Coastguard Worker) -> ModuleInfo: 97*c2e18aaaSAndroid Build Coastguard Worker """Factory method that initializes ModuleInfo from the build-generated 98*c2e18aaaSAndroid Build Coastguard Worker 99*c2e18aaaSAndroid Build Coastguard Worker JSON or Sqlite file. 100*c2e18aaaSAndroid Build Coastguard Worker """ 101*c2e18aaaSAndroid Build Coastguard Worker mod_start = time.time() 102*c2e18aaaSAndroid Build Coastguard Worker loader = Loader( 103*c2e18aaaSAndroid Build Coastguard Worker force_build=force_build, sqlite_module_cache=sqlite_module_cache 104*c2e18aaaSAndroid Build Coastguard Worker ) 105*c2e18aaaSAndroid Build Coastguard Worker mod_stop = time.time() - mod_start 106*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 107*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.MODULE_INFO_INIT_MS, result=int(mod_stop * 1000) 108*c2e18aaaSAndroid Build Coastguard Worker ) 109*c2e18aaaSAndroid Build Coastguard Worker 110*c2e18aaaSAndroid Build Coastguard Worker return loader.load(save_timestamps=True) 111*c2e18aaaSAndroid Build Coastguard Worker 112*c2e18aaaSAndroid Build Coastguard Worker 113*c2e18aaaSAndroid Build Coastguard Workerdef metrics_timer(func): 114*c2e18aaaSAndroid Build Coastguard Worker """Decorator method for sending data to metrics.""" 115*c2e18aaaSAndroid Build Coastguard Worker 116*c2e18aaaSAndroid Build Coastguard Worker def wrapper(*args, **kwargs): 117*c2e18aaaSAndroid Build Coastguard Worker start = time.time() 118*c2e18aaaSAndroid Build Coastguard Worker result = func(*args, **kwargs) 119*c2e18aaaSAndroid Build Coastguard Worker elapsed_time = int(time.time() - start) 120*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 121*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.TESTABLE_MODULES, result=elapsed_time 122*c2e18aaaSAndroid Build Coastguard Worker ) 123*c2e18aaaSAndroid Build Coastguard Worker return result 124*c2e18aaaSAndroid Build Coastguard Worker 125*c2e18aaaSAndroid Build Coastguard Worker return wrapper 126*c2e18aaaSAndroid Build Coastguard Worker 127*c2e18aaaSAndroid Build Coastguard Worker 128*c2e18aaaSAndroid Build Coastguard Workerclass Loader: 129*c2e18aaaSAndroid Build Coastguard Worker """Class that handles load and merge processes.""" 130*c2e18aaaSAndroid Build Coastguard Worker 131*c2e18aaaSAndroid Build Coastguard Worker def __init__( 132*c2e18aaaSAndroid Build Coastguard Worker self, 133*c2e18aaaSAndroid Build Coastguard Worker module_file: Path = None, 134*c2e18aaaSAndroid Build Coastguard Worker force_build: bool = False, 135*c2e18aaaSAndroid Build Coastguard Worker sqlite_module_cache: bool = False, 136*c2e18aaaSAndroid Build Coastguard Worker need_merge_fn: Callable = None, 137*c2e18aaaSAndroid Build Coastguard Worker ): 138*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 139*c2e18aaaSAndroid Build Coastguard Worker 'Creating module info loader object with module_file: %s, force_build:' 140*c2e18aaaSAndroid Build Coastguard Worker ' %s, sqlite_module_cache: %s, need_merge_fn: %s', 141*c2e18aaaSAndroid Build Coastguard Worker module_file, 142*c2e18aaaSAndroid Build Coastguard Worker force_build, 143*c2e18aaaSAndroid Build Coastguard Worker sqlite_module_cache, 144*c2e18aaaSAndroid Build Coastguard Worker need_merge_fn, 145*c2e18aaaSAndroid Build Coastguard Worker ) 146*c2e18aaaSAndroid Build Coastguard Worker self.java_dep_path = atest_utils.get_build_out_dir('soong', _JAVA_DEP_INFO) 147*c2e18aaaSAndroid Build Coastguard Worker self.cc_dep_path = atest_utils.get_build_out_dir('soong', _CC_DEP_INFO) 148*c2e18aaaSAndroid Build Coastguard Worker self.merged_dep_path = atest_utils.get_product_out(_MERGED_INFO) 149*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 150*c2e18aaaSAndroid Build Coastguard Worker 'java_dep_path: %s, cc_dep_path: %s, merged_dep_path: %s', 151*c2e18aaaSAndroid Build Coastguard Worker self.java_dep_path, 152*c2e18aaaSAndroid Build Coastguard Worker self.cc_dep_path, 153*c2e18aaaSAndroid Build Coastguard Worker self.merged_dep_path, 154*c2e18aaaSAndroid Build Coastguard Worker ) 155*c2e18aaaSAndroid Build Coastguard Worker 156*c2e18aaaSAndroid Build Coastguard Worker self.sqlite_module_cache = sqlite_module_cache 157*c2e18aaaSAndroid Build Coastguard Worker logging.debug('sqlite_module_cache: %s', sqlite_module_cache) 158*c2e18aaaSAndroid Build Coastguard Worker if self.sqlite_module_cache: 159*c2e18aaaSAndroid Build Coastguard Worker self.cache_file = atest_utils.get_product_out(_DB_NAME) 160*c2e18aaaSAndroid Build Coastguard Worker self.save_cache_async = self._save_db_async 161*c2e18aaaSAndroid Build Coastguard Worker self.load_from_cache = self._load_from_db 162*c2e18aaaSAndroid Build Coastguard Worker else: 163*c2e18aaaSAndroid Build Coastguard Worker self.cache_file = self.merged_dep_path 164*c2e18aaaSAndroid Build Coastguard Worker self.save_cache_async = self._save_json_async 165*c2e18aaaSAndroid Build Coastguard Worker self.load_from_cache = self._load_from_json 166*c2e18aaaSAndroid Build Coastguard Worker 167*c2e18aaaSAndroid Build Coastguard Worker if need_merge_fn: 168*c2e18aaaSAndroid Build Coastguard Worker self.save_cache_async = lambda _, __: None 169*c2e18aaaSAndroid Build Coastguard Worker 170*c2e18aaaSAndroid Build Coastguard Worker self.update_merge_info = False 171*c2e18aaaSAndroid Build Coastguard Worker self.module_index = atest_utils.get_index_path( 172*c2e18aaaSAndroid Build Coastguard Worker f'suite-modules.{_DB_VERSION}.idx' 173*c2e18aaaSAndroid Build Coastguard Worker ) 174*c2e18aaaSAndroid Build Coastguard Worker self.module_index_proc = None 175*c2e18aaaSAndroid Build Coastguard Worker logging.debug('module_index: %s', self.module_index) 176*c2e18aaaSAndroid Build Coastguard Worker 177*c2e18aaaSAndroid Build Coastguard Worker if module_file: 178*c2e18aaaSAndroid Build Coastguard Worker self.mod_info_file_path = Path(module_file) 179*c2e18aaaSAndroid Build Coastguard Worker self.load_module_info = self._load_module_info_from_file_wo_merging 180*c2e18aaaSAndroid Build Coastguard Worker else: 181*c2e18aaaSAndroid Build Coastguard Worker self.mod_info_file_path = atest_utils.get_product_out(_MODULE_INFO) 182*c2e18aaaSAndroid Build Coastguard Worker if force_build: 183*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Triggering module info build by force build.') 184*c2e18aaaSAndroid Build Coastguard Worker build() 185*c2e18aaaSAndroid Build Coastguard Worker elif not self.mod_info_file_path.is_file(): 186*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 187*c2e18aaaSAndroid Build Coastguard Worker 'Triggering module info build due to module info file path %s not' 188*c2e18aaaSAndroid Build Coastguard Worker ' exist.', 189*c2e18aaaSAndroid Build Coastguard Worker self.mod_info_file_path, 190*c2e18aaaSAndroid Build Coastguard Worker ) 191*c2e18aaaSAndroid Build Coastguard Worker build() 192*c2e18aaaSAndroid Build Coastguard Worker 193*c2e18aaaSAndroid Build Coastguard Worker self.update_merge_info = self.need_merge_module_info() 194*c2e18aaaSAndroid Build Coastguard Worker self.load_module_info = self._load_module_info_file 195*c2e18aaaSAndroid Build Coastguard Worker 196*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 197*c2e18aaaSAndroid Build Coastguard Worker 'Executing load_module_info function %s', self.load_module_info 198*c2e18aaaSAndroid Build Coastguard Worker ) 199*c2e18aaaSAndroid Build Coastguard Worker self.name_to_module_info, self.path_to_module_info = self.load_module_info() 200*c2e18aaaSAndroid Build Coastguard Worker 201*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Completed creating module info loader object') 202*c2e18aaaSAndroid Build Coastguard Worker 203*c2e18aaaSAndroid Build Coastguard Worker def load(self, save_timestamps: bool = False): 204*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Loading ModuleInfo. save_timestamps: %s', save_timestamps) 205*c2e18aaaSAndroid Build Coastguard Worker if save_timestamps: 206*c2e18aaaSAndroid Build Coastguard Worker atest_utils.run_multi_proc(func=atest_utils.save_build_files_timestamp) 207*c2e18aaaSAndroid Build Coastguard Worker 208*c2e18aaaSAndroid Build Coastguard Worker return ModuleInfo( 209*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info=self.name_to_module_info, 210*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info=self.path_to_module_info, 211*c2e18aaaSAndroid Build Coastguard Worker mod_info_file_path=self.mod_info_file_path, 212*c2e18aaaSAndroid Build Coastguard Worker get_testable_modules=self.get_testable_modules, 213*c2e18aaaSAndroid Build Coastguard Worker ) 214*c2e18aaaSAndroid Build Coastguard Worker 215*c2e18aaaSAndroid Build Coastguard Worker def _load_module_info_file(self): 216*c2e18aaaSAndroid Build Coastguard Worker """Load module-info.json file as ModuleInfo and merge related JSON files 217*c2e18aaaSAndroid Build Coastguard Worker 218*c2e18aaaSAndroid Build Coastguard Worker whenever required. 219*c2e18aaaSAndroid Build Coastguard Worker 220*c2e18aaaSAndroid Build Coastguard Worker Returns: 221*c2e18aaaSAndroid Build Coastguard Worker Dict of module name to module info and dict of module path to module 222*c2e18aaaSAndroid Build Coastguard Worker info. 223*c2e18aaaSAndroid Build Coastguard Worker """ 224*c2e18aaaSAndroid Build Coastguard Worker # +--------------+ +----------------------------------+ 225*c2e18aaaSAndroid Build Coastguard Worker # | ModuleInfo() | | ModuleInfo(module_file=foo.json) | 226*c2e18aaaSAndroid Build Coastguard Worker # +-------+------+ +----------------+-----------------+ 227*c2e18aaaSAndroid Build Coastguard Worker # | module_info.build() | load 228*c2e18aaaSAndroid Build Coastguard Worker # v V 229*c2e18aaaSAndroid Build Coastguard Worker # +--------------------------+ +--------------------------+ 230*c2e18aaaSAndroid Build Coastguard Worker # | module-info.json | | foo.json | 231*c2e18aaaSAndroid Build Coastguard Worker # | module_bp_cc_deps.json | | module_bp_cc_deps.json | 232*c2e18aaaSAndroid Build Coastguard Worker # | module_bp_java_deps.json | | module_bp_java_deps.json | 233*c2e18aaaSAndroid Build Coastguard Worker # +--------------------------+ +--------------------------+ 234*c2e18aaaSAndroid Build Coastguard Worker # | | 235*c2e18aaaSAndroid Build Coastguard Worker # | _merge_soong_info() <--------------------+ 236*c2e18aaaSAndroid Build Coastguard Worker # v 237*c2e18aaaSAndroid Build Coastguard Worker # +============================+ 238*c2e18aaaSAndroid Build Coastguard Worker # | $ANDROID_PRODUCT_OUT | 239*c2e18aaaSAndroid Build Coastguard Worker # | /atest_merged_dep.json |--> load as module info. 240*c2e18aaaSAndroid Build Coastguard Worker # +============================+ 241*c2e18aaaSAndroid Build Coastguard Worker if not self.update_merge_info: 242*c2e18aaaSAndroid Build Coastguard Worker return self.load_from_cache() 243*c2e18aaaSAndroid Build Coastguard Worker 244*c2e18aaaSAndroid Build Coastguard Worker name_modules, path_modules = self._load_from_json(merge=True) 245*c2e18aaaSAndroid Build Coastguard Worker self.save_cache_async(name_modules, path_modules) 246*c2e18aaaSAndroid Build Coastguard Worker self._save_testable_modules_async(name_modules, path_modules) 247*c2e18aaaSAndroid Build Coastguard Worker 248*c2e18aaaSAndroid Build Coastguard Worker return name_modules, path_modules 249*c2e18aaaSAndroid Build Coastguard Worker 250*c2e18aaaSAndroid Build Coastguard Worker def _load_module_info_from_file_wo_merging(self): 251*c2e18aaaSAndroid Build Coastguard Worker """Load module-info.json as ModuleInfo without merging.""" 252*c2e18aaaSAndroid Build Coastguard Worker name_modules = atest_utils.load_json_safely(self.mod_info_file_path) 253*c2e18aaaSAndroid Build Coastguard Worker _add_missing_variant_modules(name_modules) 254*c2e18aaaSAndroid Build Coastguard Worker 255*c2e18aaaSAndroid Build Coastguard Worker return name_modules, get_path_to_module_info(name_modules) 256*c2e18aaaSAndroid Build Coastguard Worker 257*c2e18aaaSAndroid Build Coastguard Worker def _save_db_async( 258*c2e18aaaSAndroid Build Coastguard Worker self, 259*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict[str, Any], 260*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict[str, Any], 261*c2e18aaaSAndroid Build Coastguard Worker ): 262*c2e18aaaSAndroid Build Coastguard Worker """Save data to a Sqlite database in parallel.""" 263*c2e18aaaSAndroid Build Coastguard Worker data_map = { 264*c2e18aaaSAndroid Build Coastguard Worker _NAME_MODULE_TABLE: name_to_module_info, 265*c2e18aaaSAndroid Build Coastguard Worker _PATH_MODULE_TABLE: path_to_module_info, 266*c2e18aaaSAndroid Build Coastguard Worker } 267*c2e18aaaSAndroid Build Coastguard Worker _save_data_async( 268*c2e18aaaSAndroid Build Coastguard Worker function=_create_db, 269*c2e18aaaSAndroid Build Coastguard Worker contents=data_map, 270*c2e18aaaSAndroid Build Coastguard Worker target_path=self.cache_file, 271*c2e18aaaSAndroid Build Coastguard Worker ) 272*c2e18aaaSAndroid Build Coastguard Worker 273*c2e18aaaSAndroid Build Coastguard Worker def _load_from_db(self) -> Tuple[Dict[str, Any], Dict[str, Any]]: 274*c2e18aaaSAndroid Build Coastguard Worker """Return a tuple of dicts by from SqliteDict.""" 275*c2e18aaaSAndroid Build Coastguard Worker conn = sqlite3.connect(self.cache_file) 276*c2e18aaaSAndroid Build Coastguard Worker with conn: 277*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info = SqliteDict(conn, _NAME_MODULE_TABLE) 278*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info = SqliteDict(conn, _PATH_MODULE_TABLE) 279*c2e18aaaSAndroid Build Coastguard Worker 280*c2e18aaaSAndroid Build Coastguard Worker return name_to_module_info, path_to_module_info 281*c2e18aaaSAndroid Build Coastguard Worker 282*c2e18aaaSAndroid Build Coastguard Worker def _save_json_async(self, name_to_module_info: Dict[str, Any], _): 283*c2e18aaaSAndroid Build Coastguard Worker """Save data to a JSON format in parallel.""" 284*c2e18aaaSAndroid Build Coastguard Worker _save_data_async( 285*c2e18aaaSAndroid Build Coastguard Worker function=_create_json, 286*c2e18aaaSAndroid Build Coastguard Worker contents=name_to_module_info, 287*c2e18aaaSAndroid Build Coastguard Worker target_path=self.cache_file, 288*c2e18aaaSAndroid Build Coastguard Worker ) 289*c2e18aaaSAndroid Build Coastguard Worker 290*c2e18aaaSAndroid Build Coastguard Worker def _load_from_json(self, merge: bool = False) -> Tuple[Dict, Dict]: 291*c2e18aaaSAndroid Build Coastguard Worker """Load or merge module info from json file. 292*c2e18aaaSAndroid Build Coastguard Worker 293*c2e18aaaSAndroid Build Coastguard Worker Args: 294*c2e18aaaSAndroid Build Coastguard Worker merge: Boolean whether to merge build system infos. 295*c2e18aaaSAndroid Build Coastguard Worker 296*c2e18aaaSAndroid Build Coastguard Worker Returns: 297*c2e18aaaSAndroid Build Coastguard Worker A tuple of (name_to_module_info, path_to_module_info). 298*c2e18aaaSAndroid Build Coastguard Worker """ 299*c2e18aaaSAndroid Build Coastguard Worker start = time.time() 300*c2e18aaaSAndroid Build Coastguard Worker if merge: 301*c2e18aaaSAndroid Build Coastguard Worker name_info = self._merge_build_system_infos( 302*c2e18aaaSAndroid Build Coastguard Worker atest_utils.load_json_safely(self.mod_info_file_path) 303*c2e18aaaSAndroid Build Coastguard Worker ) 304*c2e18aaaSAndroid Build Coastguard Worker duration = time.time() - start 305*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Merging module info took %ss', duration) 306*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 307*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.MODULE_MERGE_MS, result=int(duration * 1000) 308*c2e18aaaSAndroid Build Coastguard Worker ) 309*c2e18aaaSAndroid Build Coastguard Worker 310*c2e18aaaSAndroid Build Coastguard Worker return name_info, get_path_to_module_info(name_info) 311*c2e18aaaSAndroid Build Coastguard Worker 312*c2e18aaaSAndroid Build Coastguard Worker name_info = atest_utils.load_json_safely(self.merged_dep_path) 313*c2e18aaaSAndroid Build Coastguard Worker duration = time.time() - start 314*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Loading module info took %ss', duration) 315*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 316*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.MODULE_LOAD_MS, result=int(duration * 1000) 317*c2e18aaaSAndroid Build Coastguard Worker ) 318*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Loading %s as module-info.', self.merged_dep_path) 319*c2e18aaaSAndroid Build Coastguard Worker 320*c2e18aaaSAndroid Build Coastguard Worker return name_info, get_path_to_module_info(name_info) 321*c2e18aaaSAndroid Build Coastguard Worker 322*c2e18aaaSAndroid Build Coastguard Worker def _save_testable_modules_async( 323*c2e18aaaSAndroid Build Coastguard Worker self, 324*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict[str, Any], 325*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict[str, Any], 326*c2e18aaaSAndroid Build Coastguard Worker ): 327*c2e18aaaSAndroid Build Coastguard Worker """Save testable modules in parallel.""" 328*c2e18aaaSAndroid Build Coastguard Worker return atest_utils.run_multi_proc( 329*c2e18aaaSAndroid Build Coastguard Worker func=_get_testable_modules, 330*c2e18aaaSAndroid Build Coastguard Worker kwargs={ 331*c2e18aaaSAndroid Build Coastguard Worker 'name_to_module_info': name_to_module_info, 332*c2e18aaaSAndroid Build Coastguard Worker 'path_to_module_info': path_to_module_info, 333*c2e18aaaSAndroid Build Coastguard Worker 'index_path': self.module_index, 334*c2e18aaaSAndroid Build Coastguard Worker }, 335*c2e18aaaSAndroid Build Coastguard Worker ) 336*c2e18aaaSAndroid Build Coastguard Worker 337*c2e18aaaSAndroid Build Coastguard Worker def need_merge_module_info(self): 338*c2e18aaaSAndroid Build Coastguard Worker """Check if needed to regenerate the cache file. 339*c2e18aaaSAndroid Build Coastguard Worker 340*c2e18aaaSAndroid Build Coastguard Worker If the cache file is non-existent or testable module index is inexistent 341*c2e18aaaSAndroid Build Coastguard Worker or older than any of the JSON files used to generate it, the cache file 342*c2e18aaaSAndroid Build Coastguard Worker must re-generate. 343*c2e18aaaSAndroid Build Coastguard Worker 344*c2e18aaaSAndroid Build Coastguard Worker Returns: 345*c2e18aaaSAndroid Build Coastguard Worker True when the cache file is older or non-existent, False otherwise. 346*c2e18aaaSAndroid Build Coastguard Worker """ 347*c2e18aaaSAndroid Build Coastguard Worker if not self.cache_file.is_file(): 348*c2e18aaaSAndroid Build Coastguard Worker return True 349*c2e18aaaSAndroid Build Coastguard Worker 350*c2e18aaaSAndroid Build Coastguard Worker if not self.module_index.is_file(): 351*c2e18aaaSAndroid Build Coastguard Worker return True 352*c2e18aaaSAndroid Build Coastguard Worker 353*c2e18aaaSAndroid Build Coastguard Worker # The dependency input files should be generated at this point. 354*c2e18aaaSAndroid Build Coastguard Worker return any( 355*c2e18aaaSAndroid Build Coastguard Worker self.cache_file.stat().st_mtime < f.stat().st_mtime 356*c2e18aaaSAndroid Build Coastguard Worker for f in (self.mod_info_file_path, self.java_dep_path, self.cc_dep_path) 357*c2e18aaaSAndroid Build Coastguard Worker ) 358*c2e18aaaSAndroid Build Coastguard Worker 359*c2e18aaaSAndroid Build Coastguard Worker def _merge_build_system_infos( 360*c2e18aaaSAndroid Build Coastguard Worker self, name_to_module_info, java_bp_info_path=None, cc_bp_info_path=None 361*c2e18aaaSAndroid Build Coastguard Worker ): 362*c2e18aaaSAndroid Build Coastguard Worker """Merge the content of module-info.json and CC/Java dependency files 363*c2e18aaaSAndroid Build Coastguard Worker 364*c2e18aaaSAndroid Build Coastguard Worker to name_to_module_info. 365*c2e18aaaSAndroid Build Coastguard Worker 366*c2e18aaaSAndroid Build Coastguard Worker Args: 367*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict of module name to module info dict. 368*c2e18aaaSAndroid Build Coastguard Worker java_bp_info_path: String of path to java dep file to load up. Used for 369*c2e18aaaSAndroid Build Coastguard Worker testing. 370*c2e18aaaSAndroid Build Coastguard Worker cc_bp_info_path: String of path to cc dep file to load up. Used for 371*c2e18aaaSAndroid Build Coastguard Worker testing. 372*c2e18aaaSAndroid Build Coastguard Worker 373*c2e18aaaSAndroid Build Coastguard Worker Returns: 374*c2e18aaaSAndroid Build Coastguard Worker Dict of updated name_to_module_info. 375*c2e18aaaSAndroid Build Coastguard Worker """ 376*c2e18aaaSAndroid Build Coastguard Worker # Merge _JAVA_DEP_INFO 377*c2e18aaaSAndroid Build Coastguard Worker if not java_bp_info_path: 378*c2e18aaaSAndroid Build Coastguard Worker java_bp_info_path = self.java_dep_path 379*c2e18aaaSAndroid Build Coastguard Worker java_bp_infos = atest_utils.load_json_safely(java_bp_info_path) 380*c2e18aaaSAndroid Build Coastguard Worker if java_bp_infos: 381*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Merging Java build info: %s', java_bp_info_path) 382*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info = merge_soong_info(name_to_module_info, java_bp_infos) 383*c2e18aaaSAndroid Build Coastguard Worker # Merge _CC_DEP_INFO 384*c2e18aaaSAndroid Build Coastguard Worker if not cc_bp_info_path: 385*c2e18aaaSAndroid Build Coastguard Worker cc_bp_info_path = self.cc_dep_path 386*c2e18aaaSAndroid Build Coastguard Worker cc_bp_infos = atest_utils.load_json_safely(cc_bp_info_path) 387*c2e18aaaSAndroid Build Coastguard Worker if cc_bp_infos: 388*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Merging CC build info: %s', cc_bp_info_path) 389*c2e18aaaSAndroid Build Coastguard Worker # CC's dep json format is different with java. 390*c2e18aaaSAndroid Build Coastguard Worker # Below is the example content: 391*c2e18aaaSAndroid Build Coastguard Worker # { 392*c2e18aaaSAndroid Build Coastguard Worker # "clang": "${ANDROID_ROOT}/bin/clang", 393*c2e18aaaSAndroid Build Coastguard Worker # "clang++": "${ANDROID_ROOT}/bin/clang++", 394*c2e18aaaSAndroid Build Coastguard Worker # "modules": { 395*c2e18aaaSAndroid Build Coastguard Worker # "ACameraNdkVendorTest": { 396*c2e18aaaSAndroid Build Coastguard Worker # "path": [ 397*c2e18aaaSAndroid Build Coastguard Worker # "frameworks/av/camera/ndk" 398*c2e18aaaSAndroid Build Coastguard Worker # ], 399*c2e18aaaSAndroid Build Coastguard Worker # "srcs": [ 400*c2e18aaaSAndroid Build Coastguard Worker # "frameworks/tests/AImageVendorTest.cpp", 401*c2e18aaaSAndroid Build Coastguard Worker # "frameworks/tests/ACameraManagerTest.cpp" 402*c2e18aaaSAndroid Build Coastguard Worker # ], 403*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info = merge_soong_info( 404*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info, cc_bp_infos.get('modules', {}) 405*c2e18aaaSAndroid Build Coastguard Worker ) 406*c2e18aaaSAndroid Build Coastguard Worker # If $ANDROID_PRODUCT_OUT was not created in pyfakefs, simply return it 407*c2e18aaaSAndroid Build Coastguard Worker # without dumping atest_merged_dep.json in real. 408*c2e18aaaSAndroid Build Coastguard Worker 409*c2e18aaaSAndroid Build Coastguard Worker # Adds the key into module info as a unique ID. 410*c2e18aaaSAndroid Build Coastguard Worker for key, info in name_to_module_info.items(): 411*c2e18aaaSAndroid Build Coastguard Worker info[constants.MODULE_INFO_ID] = key 412*c2e18aaaSAndroid Build Coastguard Worker 413*c2e18aaaSAndroid Build Coastguard Worker _add_missing_variant_modules(name_to_module_info) 414*c2e18aaaSAndroid Build Coastguard Worker 415*c2e18aaaSAndroid Build Coastguard Worker return name_to_module_info 416*c2e18aaaSAndroid Build Coastguard Worker 417*c2e18aaaSAndroid Build Coastguard Worker @metrics_timer 418*c2e18aaaSAndroid Build Coastguard Worker def get_testable_modules(self, suite=None): 419*c2e18aaaSAndroid Build Coastguard Worker """Return the testable modules of the given suite name. 420*c2e18aaaSAndroid Build Coastguard Worker 421*c2e18aaaSAndroid Build Coastguard Worker Atest does not index testable modules against compatibility_suites. When 422*c2e18aaaSAndroid Build Coastguard Worker suite was given, or the index file was interrupted, always run 423*c2e18aaaSAndroid Build Coastguard Worker _get_testable_modules() and re-index. 424*c2e18aaaSAndroid Build Coastguard Worker 425*c2e18aaaSAndroid Build Coastguard Worker Args: 426*c2e18aaaSAndroid Build Coastguard Worker suite: A string of suite name. 427*c2e18aaaSAndroid Build Coastguard Worker 428*c2e18aaaSAndroid Build Coastguard Worker Returns: 429*c2e18aaaSAndroid Build Coastguard Worker If suite is not given, return all the testable modules in module 430*c2e18aaaSAndroid Build Coastguard Worker info, otherwise return only modules that belong to the suite. 431*c2e18aaaSAndroid Build Coastguard Worker """ 432*c2e18aaaSAndroid Build Coastguard Worker modules = set() 433*c2e18aaaSAndroid Build Coastguard Worker 434*c2e18aaaSAndroid Build Coastguard Worker if self.module_index.is_file(): 435*c2e18aaaSAndroid Build Coastguard Worker modules = self.get_testable_modules_from_index(suite) 436*c2e18aaaSAndroid Build Coastguard Worker # If the modules.idx does not exist or invalid for any reason, generate 437*c2e18aaaSAndroid Build Coastguard Worker # a new one arbitrarily. 438*c2e18aaaSAndroid Build Coastguard Worker if not modules: 439*c2e18aaaSAndroid Build Coastguard Worker modules = self.get_testable_module_from_memory(suite) 440*c2e18aaaSAndroid Build Coastguard Worker 441*c2e18aaaSAndroid Build Coastguard Worker return modules 442*c2e18aaaSAndroid Build Coastguard Worker 443*c2e18aaaSAndroid Build Coastguard Worker def get_testable_modules_from_index(self, suite: str = None) -> Set[str]: 444*c2e18aaaSAndroid Build Coastguard Worker """Return the testable modules of the given suite name.""" 445*c2e18aaaSAndroid Build Coastguard Worker suite_to_modules = {} 446*c2e18aaaSAndroid Build Coastguard Worker with open(self.module_index, 'rb') as cache: 447*c2e18aaaSAndroid Build Coastguard Worker try: 448*c2e18aaaSAndroid Build Coastguard Worker suite_to_modules = pickle.load(cache, encoding='utf-8') 449*c2e18aaaSAndroid Build Coastguard Worker except UnicodeDecodeError: 450*c2e18aaaSAndroid Build Coastguard Worker suite_to_modules = pickle.load(cache) 451*c2e18aaaSAndroid Build Coastguard Worker # when module indexing was interrupted. 452*c2e18aaaSAndroid Build Coastguard Worker except EOFError: 453*c2e18aaaSAndroid Build Coastguard Worker pass 454*c2e18aaaSAndroid Build Coastguard Worker 455*c2e18aaaSAndroid Build Coastguard Worker return _filter_modules_by_suite(suite_to_modules, suite) 456*c2e18aaaSAndroid Build Coastguard Worker 457*c2e18aaaSAndroid Build Coastguard Worker def get_testable_module_from_memory(self, suite: str = None) -> Set[str]: 458*c2e18aaaSAndroid Build Coastguard Worker """Return the testable modules of the given suite name.""" 459*c2e18aaaSAndroid Build Coastguard Worker return _get_testable_modules( 460*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info=self.name_to_module_info, 461*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info=self.path_to_module_info, 462*c2e18aaaSAndroid Build Coastguard Worker index_path=self.module_index, 463*c2e18aaaSAndroid Build Coastguard Worker suite=suite, 464*c2e18aaaSAndroid Build Coastguard Worker ) 465*c2e18aaaSAndroid Build Coastguard Worker 466*c2e18aaaSAndroid Build Coastguard Worker 467*c2e18aaaSAndroid Build Coastguard Workerclass ModuleInfo: 468*c2e18aaaSAndroid Build Coastguard Worker """Class that offers fast/easy lookup for Module related details.""" 469*c2e18aaaSAndroid Build Coastguard Worker 470*c2e18aaaSAndroid Build Coastguard Worker def __init__( 471*c2e18aaaSAndroid Build Coastguard Worker self, 472*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict[str, Any] = None, 473*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict[str, Any] = None, 474*c2e18aaaSAndroid Build Coastguard Worker mod_info_file_path: Path = None, 475*c2e18aaaSAndroid Build Coastguard Worker get_testable_modules: Callable = None, 476*c2e18aaaSAndroid Build Coastguard Worker ): 477*c2e18aaaSAndroid Build Coastguard Worker """Initialize the ModuleInfo object. 478*c2e18aaaSAndroid Build Coastguard Worker 479*c2e18aaaSAndroid Build Coastguard Worker Load up the module-info.json file and initialize the helper vars. 480*c2e18aaaSAndroid Build Coastguard Worker Note that module-info.json does not contain all module dependencies, 481*c2e18aaaSAndroid Build Coastguard Worker therefore, Atest needs to accumulate dependencies defined in bp files. 482*c2e18aaaSAndroid Build Coastguard Worker 483*c2e18aaaSAndroid Build Coastguard Worker Args: 484*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict of name to module info. 485*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict of path to module info. 486*c2e18aaaSAndroid Build Coastguard Worker mod_info_file_path: Path of module-info.json. 487*c2e18aaaSAndroid Build Coastguard Worker get_testable_modules: Function to get all testable modules. 488*c2e18aaaSAndroid Build Coastguard Worker """ 489*c2e18aaaSAndroid Build Coastguard Worker # +----------------------+ +----------------------------+ 490*c2e18aaaSAndroid Build Coastguard Worker # | $ANDROID_PRODUCT_OUT | |$ANDROID_BUILD_TOP/out/soong| 491*c2e18aaaSAndroid Build Coastguard Worker # | /module-info.json | | /module_bp_java_deps.json | 492*c2e18aaaSAndroid Build Coastguard Worker # +-----------+----------+ +-------------+--------------+ 493*c2e18aaaSAndroid Build Coastguard Worker # | _merge_soong_info() | 494*c2e18aaaSAndroid Build Coastguard Worker # +------------------------------+ 495*c2e18aaaSAndroid Build Coastguard Worker # | 496*c2e18aaaSAndroid Build Coastguard Worker # v 497*c2e18aaaSAndroid Build Coastguard Worker # +----------------------------+ +----------------------------+ 498*c2e18aaaSAndroid Build Coastguard Worker # |tempfile.NamedTemporaryFile | |$ANDROID_BUILD_TOP/out/soong| 499*c2e18aaaSAndroid Build Coastguard Worker # +-------------+--------------+ | /module_bp_cc_deps.json | 500*c2e18aaaSAndroid Build Coastguard Worker # | +-------------+--------------+ 501*c2e18aaaSAndroid Build Coastguard Worker # | _merge_soong_info() | 502*c2e18aaaSAndroid Build Coastguard Worker # +-------------------------------+ 503*c2e18aaaSAndroid Build Coastguard Worker # | 504*c2e18aaaSAndroid Build Coastguard Worker # +-------| 505*c2e18aaaSAndroid Build Coastguard Worker # v 506*c2e18aaaSAndroid Build Coastguard Worker # +============================+ 507*c2e18aaaSAndroid Build Coastguard Worker # | $ANDROID_PRODUCT_OUT | 508*c2e18aaaSAndroid Build Coastguard Worker # | /atest_merged_dep.json |--> load as module info. 509*c2e18aaaSAndroid Build Coastguard Worker # +============================+ 510*c2e18aaaSAndroid Build Coastguard Worker self.root_dir = os.environ.get(constants.ANDROID_BUILD_TOP) 511*c2e18aaaSAndroid Build Coastguard Worker 512*c2e18aaaSAndroid Build Coastguard Worker self.name_to_module_info = name_to_module_info or {} 513*c2e18aaaSAndroid Build Coastguard Worker self.path_to_module_info = path_to_module_info or {} 514*c2e18aaaSAndroid Build Coastguard Worker self.mod_info_file_path = mod_info_file_path 515*c2e18aaaSAndroid Build Coastguard Worker self._get_testable_modules = get_testable_modules 516*c2e18aaaSAndroid Build Coastguard Worker 517*c2e18aaaSAndroid Build Coastguard Worker def is_module(self, name): 518*c2e18aaaSAndroid Build Coastguard Worker """Return True if name is a module, False otherwise.""" 519*c2e18aaaSAndroid Build Coastguard Worker info = self.get_module_info(name) 520*c2e18aaaSAndroid Build Coastguard Worker # From aosp/2293302 it started merging all modules' dependency in bp 521*c2e18aaaSAndroid Build Coastguard Worker # even the module is not be exposed to make, and those modules could not 522*c2e18aaaSAndroid Build Coastguard Worker # be treated as a build target using m. Only treat input name as module 523*c2e18aaaSAndroid Build Coastguard Worker # if it also has the module_name attribute which means it could be a 524*c2e18aaaSAndroid Build Coastguard Worker # build target for m. 525*c2e18aaaSAndroid Build Coastguard Worker if info and info.get(constants.MODULE_NAME): 526*c2e18aaaSAndroid Build Coastguard Worker return True 527*c2e18aaaSAndroid Build Coastguard Worker return False 528*c2e18aaaSAndroid Build Coastguard Worker 529*c2e18aaaSAndroid Build Coastguard Worker def get_paths(self, name) -> list[str]: 530*c2e18aaaSAndroid Build Coastguard Worker """Return paths of supplied module name, Empty list if non-existent.""" 531*c2e18aaaSAndroid Build Coastguard Worker info = self.get_module_info(name) 532*c2e18aaaSAndroid Build Coastguard Worker if info: 533*c2e18aaaSAndroid Build Coastguard Worker return info.get(constants.MODULE_PATH, []) 534*c2e18aaaSAndroid Build Coastguard Worker return [] 535*c2e18aaaSAndroid Build Coastguard Worker 536*c2e18aaaSAndroid Build Coastguard Worker def get_module_names(self, rel_module_path): 537*c2e18aaaSAndroid Build Coastguard Worker """Get the modules that all have module_path. 538*c2e18aaaSAndroid Build Coastguard Worker 539*c2e18aaaSAndroid Build Coastguard Worker Args: 540*c2e18aaaSAndroid Build Coastguard Worker rel_module_path: path of module in module-info.json 541*c2e18aaaSAndroid Build Coastguard Worker 542*c2e18aaaSAndroid Build Coastguard Worker Returns: 543*c2e18aaaSAndroid Build Coastguard Worker List of module names. 544*c2e18aaaSAndroid Build Coastguard Worker """ 545*c2e18aaaSAndroid Build Coastguard Worker return _get_module_names(self.path_to_module_info, rel_module_path) 546*c2e18aaaSAndroid Build Coastguard Worker 547*c2e18aaaSAndroid Build Coastguard Worker def get_module_info(self, mod_name): 548*c2e18aaaSAndroid Build Coastguard Worker """Return dict of info for given module name, None if non-existence.""" 549*c2e18aaaSAndroid Build Coastguard Worker return self.name_to_module_info.get(mod_name) 550*c2e18aaaSAndroid Build Coastguard Worker 551*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 552*c2e18aaaSAndroid Build Coastguard Worker def is_suite_in_compatibility_suites(suite, mod_info): 553*c2e18aaaSAndroid Build Coastguard Worker """Check if suite exists in the compatibility_suites of module-info. 554*c2e18aaaSAndroid Build Coastguard Worker 555*c2e18aaaSAndroid Build Coastguard Worker Args: 556*c2e18aaaSAndroid Build Coastguard Worker suite: A string of suite name. 557*c2e18aaaSAndroid Build Coastguard Worker mod_info: Dict of module info to check. 558*c2e18aaaSAndroid Build Coastguard Worker 559*c2e18aaaSAndroid Build Coastguard Worker Returns: 560*c2e18aaaSAndroid Build Coastguard Worker True if it exists in mod_info, False otherwise. 561*c2e18aaaSAndroid Build Coastguard Worker """ 562*c2e18aaaSAndroid Build Coastguard Worker if not isinstance(mod_info, dict): 563*c2e18aaaSAndroid Build Coastguard Worker return False 564*c2e18aaaSAndroid Build Coastguard Worker return suite in mod_info.get(constants.MODULE_COMPATIBILITY_SUITES, []) 565*c2e18aaaSAndroid Build Coastguard Worker 566*c2e18aaaSAndroid Build Coastguard Worker def get_testable_modules(self, suite=None): 567*c2e18aaaSAndroid Build Coastguard Worker return self._get_testable_modules(suite) 568*c2e18aaaSAndroid Build Coastguard Worker 569*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 570*c2e18aaaSAndroid Build Coastguard Worker def is_tradefed_testable_module(info: Dict[str, Any]) -> bool: 571*c2e18aaaSAndroid Build Coastguard Worker """Check whether the module is a Tradefed executable test.""" 572*c2e18aaaSAndroid Build Coastguard Worker if not info: 573*c2e18aaaSAndroid Build Coastguard Worker return False 574*c2e18aaaSAndroid Build Coastguard Worker if not info.get(constants.MODULE_INSTALLED, []): 575*c2e18aaaSAndroid Build Coastguard Worker return False 576*c2e18aaaSAndroid Build Coastguard Worker return ModuleInfo.has_test_config(info) 577*c2e18aaaSAndroid Build Coastguard Worker 578*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 579*c2e18aaaSAndroid Build Coastguard Worker def is_mobly_module(info: Dict[str, Any]) -> bool: 580*c2e18aaaSAndroid Build Coastguard Worker """Check whether the module is a Mobly test. 581*c2e18aaaSAndroid Build Coastguard Worker 582*c2e18aaaSAndroid Build Coastguard Worker Note: Only python_test_host modules marked with a test_options tag of 583*c2e18aaaSAndroid Build Coastguard Worker "mobly" is considered a Mobly module. 584*c2e18aaaSAndroid Build Coastguard Worker 585*c2e18aaaSAndroid Build Coastguard Worker Args: 586*c2e18aaaSAndroid Build Coastguard Worker info: Dict of module info to check. 587*c2e18aaaSAndroid Build Coastguard Worker 588*c2e18aaaSAndroid Build Coastguard Worker Returns: 589*c2e18aaaSAndroid Build Coastguard Worker True if this is a Mobly test module, False otherwise. 590*c2e18aaaSAndroid Build Coastguard Worker """ 591*c2e18aaaSAndroid Build Coastguard Worker return constants.MOBLY_TEST_OPTIONS_TAG in info.get( 592*c2e18aaaSAndroid Build Coastguard Worker constants.MODULE_TEST_OPTIONS_TAGS, [] 593*c2e18aaaSAndroid Build Coastguard Worker ) 594*c2e18aaaSAndroid Build Coastguard Worker 595*c2e18aaaSAndroid Build Coastguard Worker def is_testable_module(self, info: Dict[str, Any]) -> bool: 596*c2e18aaaSAndroid Build Coastguard Worker """Check if module is something we can test. 597*c2e18aaaSAndroid Build Coastguard Worker 598*c2e18aaaSAndroid Build Coastguard Worker A module is testable if: 599*c2e18aaaSAndroid Build Coastguard Worker - it's a tradefed testable module, or 600*c2e18aaaSAndroid Build Coastguard Worker - it's a Mobly module, or 601*c2e18aaaSAndroid Build Coastguard Worker - it's a robolectric module (or shares path with one). 602*c2e18aaaSAndroid Build Coastguard Worker 603*c2e18aaaSAndroid Build Coastguard Worker Args: 604*c2e18aaaSAndroid Build Coastguard Worker info: Dict of module info to check. 605*c2e18aaaSAndroid Build Coastguard Worker 606*c2e18aaaSAndroid Build Coastguard Worker Returns: 607*c2e18aaaSAndroid Build Coastguard Worker True if we can test this module, False otherwise. 608*c2e18aaaSAndroid Build Coastguard Worker """ 609*c2e18aaaSAndroid Build Coastguard Worker return _is_testable_module( 610*c2e18aaaSAndroid Build Coastguard Worker self.name_to_module_info, self.path_to_module_info, info 611*c2e18aaaSAndroid Build Coastguard Worker ) 612*c2e18aaaSAndroid Build Coastguard Worker 613*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 614*c2e18aaaSAndroid Build Coastguard Worker def has_test_config(info: Dict[str, Any]) -> bool: 615*c2e18aaaSAndroid Build Coastguard Worker """Validate if this module has a test config. 616*c2e18aaaSAndroid Build Coastguard Worker 617*c2e18aaaSAndroid Build Coastguard Worker A module can have a test config in the following manner: 618*c2e18aaaSAndroid Build Coastguard Worker - test_config be set in module-info.json. 619*c2e18aaaSAndroid Build Coastguard Worker - Auto-generated config via the auto_test_config key 620*c2e18aaaSAndroid Build Coastguard Worker in module-info.json. 621*c2e18aaaSAndroid Build Coastguard Worker 622*c2e18aaaSAndroid Build Coastguard Worker Args: 623*c2e18aaaSAndroid Build Coastguard Worker info: Dict of module info to check. 624*c2e18aaaSAndroid Build Coastguard Worker 625*c2e18aaaSAndroid Build Coastguard Worker Returns: 626*c2e18aaaSAndroid Build Coastguard Worker True if this module has a test config, False otherwise. 627*c2e18aaaSAndroid Build Coastguard Worker """ 628*c2e18aaaSAndroid Build Coastguard Worker return bool( 629*c2e18aaaSAndroid Build Coastguard Worker info.get(constants.MODULE_TEST_CONFIG, []) 630*c2e18aaaSAndroid Build Coastguard Worker or info.get('auto_test_config', []) 631*c2e18aaaSAndroid Build Coastguard Worker ) 632*c2e18aaaSAndroid Build Coastguard Worker 633*c2e18aaaSAndroid Build Coastguard Worker def is_legacy_robolectric_test(self, info: Dict[str, Any]) -> bool: 634*c2e18aaaSAndroid Build Coastguard Worker """Return whether the module_name is a legacy Robolectric test""" 635*c2e18aaaSAndroid Build Coastguard Worker return _is_legacy_robolectric_test( 636*c2e18aaaSAndroid Build Coastguard Worker self.name_to_module_info, self.path_to_module_info, info 637*c2e18aaaSAndroid Build Coastguard Worker ) 638*c2e18aaaSAndroid Build Coastguard Worker 639*c2e18aaaSAndroid Build Coastguard Worker def get_robolectric_test_name(self, info: Dict[str, Any]) -> str: 640*c2e18aaaSAndroid Build Coastguard Worker """Returns runnable robolectric module name. 641*c2e18aaaSAndroid Build Coastguard Worker 642*c2e18aaaSAndroid Build Coastguard Worker This method is for legacy robolectric tests and returns one of associated 643*c2e18aaaSAndroid Build Coastguard Worker modules. The pattern is determined by the amount of shards: 644*c2e18aaaSAndroid Build Coastguard Worker 645*c2e18aaaSAndroid Build Coastguard Worker 10 shards: 646*c2e18aaaSAndroid Build Coastguard Worker FooTests -> RunFooTests0, RunFooTests1 ... RunFooTests9 647*c2e18aaaSAndroid Build Coastguard Worker No shard: 648*c2e18aaaSAndroid Build Coastguard Worker FooTests -> RunFooTests 649*c2e18aaaSAndroid Build Coastguard Worker 650*c2e18aaaSAndroid Build Coastguard Worker Arg: 651*c2e18aaaSAndroid Build Coastguard Worker info: Dict of module info to check. 652*c2e18aaaSAndroid Build Coastguard Worker 653*c2e18aaaSAndroid Build Coastguard Worker Returns: 654*c2e18aaaSAndroid Build Coastguard Worker String of the first-matched associated module that belongs to the 655*c2e18aaaSAndroid Build Coastguard Worker actual robolectric module, None if nothing has been found. 656*c2e18aaaSAndroid Build Coastguard Worker """ 657*c2e18aaaSAndroid Build Coastguard Worker return _get_robolectric_test_name( 658*c2e18aaaSAndroid Build Coastguard Worker self.name_to_module_info, self.path_to_module_info, info 659*c2e18aaaSAndroid Build Coastguard Worker ) 660*c2e18aaaSAndroid Build Coastguard Worker 661*c2e18aaaSAndroid Build Coastguard Worker def is_robolectric_test(self, module_name): 662*c2e18aaaSAndroid Build Coastguard Worker """Check if the given module is a robolectric test. 663*c2e18aaaSAndroid Build Coastguard Worker 664*c2e18aaaSAndroid Build Coastguard Worker Args: 665*c2e18aaaSAndroid Build Coastguard Worker module_name: String of module to check. 666*c2e18aaaSAndroid Build Coastguard Worker 667*c2e18aaaSAndroid Build Coastguard Worker Returns: 668*c2e18aaaSAndroid Build Coastguard Worker Boolean whether it's a robotest or not. 669*c2e18aaaSAndroid Build Coastguard Worker """ 670*c2e18aaaSAndroid Build Coastguard Worker if self.get_robolectric_type(module_name): 671*c2e18aaaSAndroid Build Coastguard Worker return True 672*c2e18aaaSAndroid Build Coastguard Worker return False 673*c2e18aaaSAndroid Build Coastguard Worker 674*c2e18aaaSAndroid Build Coastguard Worker def get_robolectric_type(self, module_name: str) -> int: 675*c2e18aaaSAndroid Build Coastguard Worker """Check if the given module is a robolectric test and return type of it. 676*c2e18aaaSAndroid Build Coastguard Worker 677*c2e18aaaSAndroid Build Coastguard Worker Robolectric declaration is converting from Android.mk to Android.bp, and 678*c2e18aaaSAndroid Build Coastguard Worker in the interim Atest needs to support testing both types of tests. 679*c2e18aaaSAndroid Build Coastguard Worker 680*c2e18aaaSAndroid Build Coastguard Worker The modern robolectric tests defined by 'android_robolectric_test' in an 681*c2e18aaaSAndroid Build Coastguard Worker Android.bp file can can be run in Tradefed Test Runner: 682*c2e18aaaSAndroid Build Coastguard Worker 683*c2e18aaaSAndroid Build Coastguard Worker SettingsRoboTests -> Tradefed Test Runner 684*c2e18aaaSAndroid Build Coastguard Worker 685*c2e18aaaSAndroid Build Coastguard Worker Legacy tests defined in an Android.mk can only run with the 'make' way. 686*c2e18aaaSAndroid Build Coastguard Worker 687*c2e18aaaSAndroid Build Coastguard Worker SettingsRoboTests -> make RunSettingsRoboTests0 688*c2e18aaaSAndroid Build Coastguard Worker 689*c2e18aaaSAndroid Build Coastguard Worker To determine whether the test is a modern/legacy robolectric test: 690*c2e18aaaSAndroid Build Coastguard Worker 1. If the 'robolectric-test` in the compatibility_suites, it's a 691*c2e18aaaSAndroid Build Coastguard Worker modern one, otherwise it's a legacy test. This is accurate since 692*c2e18aaaSAndroid Build Coastguard Worker aosp/2308586 already set the test suite of `robolectric-test` 693*c2e18aaaSAndroid Build Coastguard Worker for all `modern` Robolectric tests in Soong. 694*c2e18aaaSAndroid Build Coastguard Worker 2. Traverse all modules share the module path. If one of the 695*c2e18aaaSAndroid Build Coastguard Worker modules has a ROBOLECTRIC class, it's a legacy robolectric test. 696*c2e18aaaSAndroid Build Coastguard Worker 697*c2e18aaaSAndroid Build Coastguard Worker Args: 698*c2e18aaaSAndroid Build Coastguard Worker module_name: String of module to check. 699*c2e18aaaSAndroid Build Coastguard Worker 700*c2e18aaaSAndroid Build Coastguard Worker Returns: 701*c2e18aaaSAndroid Build Coastguard Worker 0: not a robolectric test. 702*c2e18aaaSAndroid Build Coastguard Worker 1: a modern robolectric test(defined in Android.bp) 703*c2e18aaaSAndroid Build Coastguard Worker 2: a legacy robolectric test(defined in Android.mk) 704*c2e18aaaSAndroid Build Coastguard Worker """ 705*c2e18aaaSAndroid Build Coastguard Worker info = self.get_module_info(module_name) 706*c2e18aaaSAndroid Build Coastguard Worker if not info: 707*c2e18aaaSAndroid Build Coastguard Worker return 0 708*c2e18aaaSAndroid Build Coastguard Worker # Some Modern mode Robolectric test has related module which compliant 709*c2e18aaaSAndroid Build Coastguard Worker # with the Legacy Robolectric test. In this case, the Modern mode 710*c2e18aaaSAndroid Build Coastguard Worker # Robolectric tests should be prior to the Legacy mode. 711*c2e18aaaSAndroid Build Coastguard Worker if self.is_modern_robolectric_test(info): 712*c2e18aaaSAndroid Build Coastguard Worker return constants.ROBOTYPE_MODERN 713*c2e18aaaSAndroid Build Coastguard Worker if self.is_legacy_robolectric_test(info): 714*c2e18aaaSAndroid Build Coastguard Worker return constants.ROBOTYPE_LEGACY 715*c2e18aaaSAndroid Build Coastguard Worker return 0 716*c2e18aaaSAndroid Build Coastguard Worker 717*c2e18aaaSAndroid Build Coastguard Worker def get_instrumentation_target_apps(self, module_name: str) -> Dict: 718*c2e18aaaSAndroid Build Coastguard Worker """Return target APKs of an instrumentation test. 719*c2e18aaaSAndroid Build Coastguard Worker 720*c2e18aaaSAndroid Build Coastguard Worker Returns: 721*c2e18aaaSAndroid Build Coastguard Worker A dict of target module and target APK(s). e.g. 722*c2e18aaaSAndroid Build Coastguard Worker {"FooService": {"/path/to/the/FooService.apk"}} 723*c2e18aaaSAndroid Build Coastguard Worker """ 724*c2e18aaaSAndroid Build Coastguard Worker # 1. Determine the actual manifest filename from an Android.bp(if any) 725*c2e18aaaSAndroid Build Coastguard Worker manifest = self.get_filepath_from_module(module_name, 'AndroidManifest.xml') 726*c2e18aaaSAndroid Build Coastguard Worker bpfile = self.get_filepath_from_module(module_name, 'Android.bp') 727*c2e18aaaSAndroid Build Coastguard Worker if bpfile.is_file(): 728*c2e18aaaSAndroid Build Coastguard Worker bp_info = atest_utils.get_bp_content(bpfile, 'android_test') 729*c2e18aaaSAndroid Build Coastguard Worker if not bp_info or not bp_info.get(module_name): 730*c2e18aaaSAndroid Build Coastguard Worker return {} 731*c2e18aaaSAndroid Build Coastguard Worker manifest = self.get_filepath_from_module( 732*c2e18aaaSAndroid Build Coastguard Worker module_name, bp_info.get(module_name).get('manifest') 733*c2e18aaaSAndroid Build Coastguard Worker ) 734*c2e18aaaSAndroid Build Coastguard Worker xml_info = atest_utils.get_manifest_info(manifest) 735*c2e18aaaSAndroid Build Coastguard Worker # 2. Translate package name to a module name. 736*c2e18aaaSAndroid Build Coastguard Worker package = xml_info.get('package') 737*c2e18aaaSAndroid Build Coastguard Worker target_package = xml_info.get('target_package') 738*c2e18aaaSAndroid Build Coastguard Worker # Ensure it's an instrumentation test(excluding self-instrmented) 739*c2e18aaaSAndroid Build Coastguard Worker if target_package and package != target_package: 740*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Found %s an instrumentation test.', module_name) 741*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 742*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.FOUND_INSTRUMENTATION_TEST, result=1 743*c2e18aaaSAndroid Build Coastguard Worker ) 744*c2e18aaaSAndroid Build Coastguard Worker target_module = self.get_target_module_by_pkg( 745*c2e18aaaSAndroid Build Coastguard Worker package=target_package, search_from=manifest.parent 746*c2e18aaaSAndroid Build Coastguard Worker ) 747*c2e18aaaSAndroid Build Coastguard Worker if target_module: 748*c2e18aaaSAndroid Build Coastguard Worker return self.get_artifact_map(target_module) 749*c2e18aaaSAndroid Build Coastguard Worker return {} 750*c2e18aaaSAndroid Build Coastguard Worker 751*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=anomalous-backslash-in-string 752*c2e18aaaSAndroid Build Coastguard Worker def get_target_module_by_pkg(self, package: str, search_from: Path) -> str: 753*c2e18aaaSAndroid Build Coastguard Worker """Translate package name to the target module name. 754*c2e18aaaSAndroid Build Coastguard Worker 755*c2e18aaaSAndroid Build Coastguard Worker This method is dedicated to determine the target module by translating 756*c2e18aaaSAndroid Build Coastguard Worker a package name. 757*c2e18aaaSAndroid Build Coastguard Worker 758*c2e18aaaSAndroid Build Coastguard Worker Phase 1: Find out possible manifest files among parent directories. 759*c2e18aaaSAndroid Build Coastguard Worker Phase 2. Look for the defined package fits the given name, and ensure 760*c2e18aaaSAndroid Build Coastguard Worker it is not a persistent app. 761*c2e18aaaSAndroid Build Coastguard Worker Phase 3: Translate the manifest path to possible modules. A valid module 762*c2e18aaaSAndroid Build Coastguard Worker must fulfill: 763*c2e18aaaSAndroid Build Coastguard Worker 1. The 'class' type must be ['APPS']. 764*c2e18aaaSAndroid Build Coastguard Worker 2. It is not a Robolectric test. 765*c2e18aaaSAndroid Build Coastguard Worker 766*c2e18aaaSAndroid Build Coastguard Worker Returns: 767*c2e18aaaSAndroid Build Coastguard Worker A string of module name. 768*c2e18aaaSAndroid Build Coastguard Worker """ 769*c2e18aaaSAndroid Build Coastguard Worker xmls = [] 770*c2e18aaaSAndroid Build Coastguard Worker for pth in search_from.parents: 771*c2e18aaaSAndroid Build Coastguard Worker if pth == Path(self.root_dir): 772*c2e18aaaSAndroid Build Coastguard Worker break 773*c2e18aaaSAndroid Build Coastguard Worker for name in os.listdir(pth): 774*c2e18aaaSAndroid Build Coastguard Worker if pth.joinpath(name).is_file(): 775*c2e18aaaSAndroid Build Coastguard Worker match = re.match('.*AndroidManifest.*\.xml$', name) 776*c2e18aaaSAndroid Build Coastguard Worker if match: 777*c2e18aaaSAndroid Build Coastguard Worker xmls.append(os.path.join(pth, name)) 778*c2e18aaaSAndroid Build Coastguard Worker possible_modules = [] 779*c2e18aaaSAndroid Build Coastguard Worker for xml in xmls: 780*c2e18aaaSAndroid Build Coastguard Worker rel_dir = str(Path(xml).relative_to(self.root_dir).parent) 781*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Looking for package "%s" in %s...', package, xml) 782*c2e18aaaSAndroid Build Coastguard Worker xml_info = atest_utils.get_manifest_info(xml) 783*c2e18aaaSAndroid Build Coastguard Worker if xml_info.get('package') == package: 784*c2e18aaaSAndroid Build Coastguard Worker if xml_info.get('persistent'): 785*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s is a persistent app.', package) 786*c2e18aaaSAndroid Build Coastguard Worker continue 787*c2e18aaaSAndroid Build Coastguard Worker for _m in self.path_to_module_info.get(rel_dir): 788*c2e18aaaSAndroid Build Coastguard Worker possible_modules.append(_m) 789*c2e18aaaSAndroid Build Coastguard Worker if possible_modules: 790*c2e18aaaSAndroid Build Coastguard Worker for mod in possible_modules: 791*c2e18aaaSAndroid Build Coastguard Worker name = mod.get('module_name') 792*c2e18aaaSAndroid Build Coastguard Worker if mod.get('class') == ['APPS'] and not self.is_robolectric_test(name): 793*c2e18aaaSAndroid Build Coastguard Worker return name 794*c2e18aaaSAndroid Build Coastguard Worker return '' 795*c2e18aaaSAndroid Build Coastguard Worker 796*c2e18aaaSAndroid Build Coastguard Worker def get_artifact_map(self, module_name: str) -> Dict: 797*c2e18aaaSAndroid Build Coastguard Worker """Get the installed APK path of the given module.""" 798*c2e18aaaSAndroid Build Coastguard Worker target_mod_info = self.get_module_info(module_name) 799*c2e18aaaSAndroid Build Coastguard Worker artifact_map = {} 800*c2e18aaaSAndroid Build Coastguard Worker if target_mod_info: 801*c2e18aaaSAndroid Build Coastguard Worker apks = set() 802*c2e18aaaSAndroid Build Coastguard Worker artifacts = target_mod_info.get('installed') 803*c2e18aaaSAndroid Build Coastguard Worker for artifact in artifacts: 804*c2e18aaaSAndroid Build Coastguard Worker if Path(artifact).suffix == '.apk': 805*c2e18aaaSAndroid Build Coastguard Worker apks.add(os.path.join(self.root_dir, artifact)) 806*c2e18aaaSAndroid Build Coastguard Worker artifact_map.update({module_name: apks}) 807*c2e18aaaSAndroid Build Coastguard Worker return artifact_map 808*c2e18aaaSAndroid Build Coastguard Worker 809*c2e18aaaSAndroid Build Coastguard Worker def is_auto_gen_test_config(self, module_name): 810*c2e18aaaSAndroid Build Coastguard Worker """Check if the test config file will be generated automatically. 811*c2e18aaaSAndroid Build Coastguard Worker 812*c2e18aaaSAndroid Build Coastguard Worker Args: 813*c2e18aaaSAndroid Build Coastguard Worker module_name: A string of the module name. 814*c2e18aaaSAndroid Build Coastguard Worker 815*c2e18aaaSAndroid Build Coastguard Worker Returns: 816*c2e18aaaSAndroid Build Coastguard Worker True if the test config file will be generated automatically. 817*c2e18aaaSAndroid Build Coastguard Worker """ 818*c2e18aaaSAndroid Build Coastguard Worker if self.is_module(module_name): 819*c2e18aaaSAndroid Build Coastguard Worker mod_info = self.get_module_info(module_name) 820*c2e18aaaSAndroid Build Coastguard Worker auto_test_config = mod_info.get('auto_test_config', []) 821*c2e18aaaSAndroid Build Coastguard Worker return auto_test_config and auto_test_config[0] 822*c2e18aaaSAndroid Build Coastguard Worker return False 823*c2e18aaaSAndroid Build Coastguard Worker 824*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 825*c2e18aaaSAndroid Build Coastguard Worker def is_legacy_robolectric_class(info: Dict[str, Any]) -> bool: 826*c2e18aaaSAndroid Build Coastguard Worker """Check if the class is `ROBOLECTRIC` 827*c2e18aaaSAndroid Build Coastguard Worker 828*c2e18aaaSAndroid Build Coastguard Worker This method is for legacy robolectric tests that the associated modules 829*c2e18aaaSAndroid Build Coastguard Worker contain: 830*c2e18aaaSAndroid Build Coastguard Worker 'class': ['ROBOLECTRIC'] 831*c2e18aaaSAndroid Build Coastguard Worker 832*c2e18aaaSAndroid Build Coastguard Worker Args: 833*c2e18aaaSAndroid Build Coastguard Worker info: ModuleInfo to check. 834*c2e18aaaSAndroid Build Coastguard Worker 835*c2e18aaaSAndroid Build Coastguard Worker Returns: 836*c2e18aaaSAndroid Build Coastguard Worker True if the attribute class in mod_info is ROBOLECTRIC, False 837*c2e18aaaSAndroid Build Coastguard Worker otherwise. 838*c2e18aaaSAndroid Build Coastguard Worker """ 839*c2e18aaaSAndroid Build Coastguard Worker if info: 840*c2e18aaaSAndroid Build Coastguard Worker module_classes = info.get(constants.MODULE_CLASS, []) 841*c2e18aaaSAndroid Build Coastguard Worker return ( 842*c2e18aaaSAndroid Build Coastguard Worker module_classes 843*c2e18aaaSAndroid Build Coastguard Worker and module_classes[0] == constants.MODULE_CLASS_ROBOLECTRIC 844*c2e18aaaSAndroid Build Coastguard Worker ) 845*c2e18aaaSAndroid Build Coastguard Worker return False 846*c2e18aaaSAndroid Build Coastguard Worker 847*c2e18aaaSAndroid Build Coastguard Worker def is_native_test(self, module_name): 848*c2e18aaaSAndroid Build Coastguard Worker """Check if the input module is a native test. 849*c2e18aaaSAndroid Build Coastguard Worker 850*c2e18aaaSAndroid Build Coastguard Worker Args: 851*c2e18aaaSAndroid Build Coastguard Worker module_name: A string of the module name. 852*c2e18aaaSAndroid Build Coastguard Worker 853*c2e18aaaSAndroid Build Coastguard Worker Returns: 854*c2e18aaaSAndroid Build Coastguard Worker True if the test is a native test, False otherwise. 855*c2e18aaaSAndroid Build Coastguard Worker """ 856*c2e18aaaSAndroid Build Coastguard Worker mod_info = self.get_module_info(module_name) 857*c2e18aaaSAndroid Build Coastguard Worker return constants.MODULE_CLASS_NATIVE_TESTS in mod_info.get( 858*c2e18aaaSAndroid Build Coastguard Worker constants.MODULE_CLASS, [] 859*c2e18aaaSAndroid Build Coastguard Worker ) 860*c2e18aaaSAndroid Build Coastguard Worker 861*c2e18aaaSAndroid Build Coastguard Worker def has_mainline_modules( 862*c2e18aaaSAndroid Build Coastguard Worker self, module_name: str, mainline_binaries: List[str] 863*c2e18aaaSAndroid Build Coastguard Worker ) -> bool: 864*c2e18aaaSAndroid Build Coastguard Worker """Check if the mainline modules are in module-info. 865*c2e18aaaSAndroid Build Coastguard Worker 866*c2e18aaaSAndroid Build Coastguard Worker Args: 867*c2e18aaaSAndroid Build Coastguard Worker module_name: A string of the module name. 868*c2e18aaaSAndroid Build Coastguard Worker mainline_binaries: A list of mainline module binaries. 869*c2e18aaaSAndroid Build Coastguard Worker 870*c2e18aaaSAndroid Build Coastguard Worker Returns: 871*c2e18aaaSAndroid Build Coastguard Worker True if mainline_binaries is in module-info, False otherwise. 872*c2e18aaaSAndroid Build Coastguard Worker """ 873*c2e18aaaSAndroid Build Coastguard Worker mod_info = self.get_module_info(module_name) 874*c2e18aaaSAndroid Build Coastguard Worker # Check 'test_mainline_modules' attribute of the module-info.json. 875*c2e18aaaSAndroid Build Coastguard Worker mm_in_mf = mod_info.get(constants.MODULE_MAINLINE_MODULES, []) 876*c2e18aaaSAndroid Build Coastguard Worker ml_modules_set = set(mainline_binaries) 877*c2e18aaaSAndroid Build Coastguard Worker if mm_in_mf: 878*c2e18aaaSAndroid Build Coastguard Worker return contains_same_mainline_modules(ml_modules_set, set(mm_in_mf)) 879*c2e18aaaSAndroid Build Coastguard Worker for test_config in mod_info.get(constants.MODULE_TEST_CONFIG, []): 880*c2e18aaaSAndroid Build Coastguard Worker # Check the value of 'mainline-param' in the test config. 881*c2e18aaaSAndroid Build Coastguard Worker if not self.is_auto_gen_test_config(module_name): 882*c2e18aaaSAndroid Build Coastguard Worker return contains_same_mainline_modules( 883*c2e18aaaSAndroid Build Coastguard Worker ml_modules_set, 884*c2e18aaaSAndroid Build Coastguard Worker atest_utils.get_mainline_param( 885*c2e18aaaSAndroid Build Coastguard Worker os.path.join(self.root_dir, test_config) 886*c2e18aaaSAndroid Build Coastguard Worker ), 887*c2e18aaaSAndroid Build Coastguard Worker ) 888*c2e18aaaSAndroid Build Coastguard Worker # Unable to verify mainline modules in an auto-gen test config. 889*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 890*c2e18aaaSAndroid Build Coastguard Worker '%s is associated with an auto-generated test config.', module_name 891*c2e18aaaSAndroid Build Coastguard Worker ) 892*c2e18aaaSAndroid Build Coastguard Worker return True 893*c2e18aaaSAndroid Build Coastguard Worker return False 894*c2e18aaaSAndroid Build Coastguard Worker 895*c2e18aaaSAndroid Build Coastguard Worker def get_filepath_from_module(self, module_name: str, filename: str) -> Path: 896*c2e18aaaSAndroid Build Coastguard Worker """Return absolute path of the given module and filename.""" 897*c2e18aaaSAndroid Build Coastguard Worker mod_path = self.get_paths(module_name) 898*c2e18aaaSAndroid Build Coastguard Worker if mod_path: 899*c2e18aaaSAndroid Build Coastguard Worker return Path(self.root_dir).joinpath(mod_path[0], filename) 900*c2e18aaaSAndroid Build Coastguard Worker return Path() 901*c2e18aaaSAndroid Build Coastguard Worker 902*c2e18aaaSAndroid Build Coastguard Worker def get_module_dependency(self, module_name, depend_on=None): 903*c2e18aaaSAndroid Build Coastguard Worker """Get the dependency sets for input module. 904*c2e18aaaSAndroid Build Coastguard Worker 905*c2e18aaaSAndroid Build Coastguard Worker Recursively find all the dependencies of the input module. 906*c2e18aaaSAndroid Build Coastguard Worker 907*c2e18aaaSAndroid Build Coastguard Worker Args: 908*c2e18aaaSAndroid Build Coastguard Worker module_name: String of module to check. 909*c2e18aaaSAndroid Build Coastguard Worker depend_on: The list of parent dependencies. 910*c2e18aaaSAndroid Build Coastguard Worker 911*c2e18aaaSAndroid Build Coastguard Worker Returns: 912*c2e18aaaSAndroid Build Coastguard Worker Set of dependency modules. 913*c2e18aaaSAndroid Build Coastguard Worker """ 914*c2e18aaaSAndroid Build Coastguard Worker if not depend_on: 915*c2e18aaaSAndroid Build Coastguard Worker depend_on = set() 916*c2e18aaaSAndroid Build Coastguard Worker deps = set() 917*c2e18aaaSAndroid Build Coastguard Worker mod_info = self.get_module_info(module_name) 918*c2e18aaaSAndroid Build Coastguard Worker if not mod_info: 919*c2e18aaaSAndroid Build Coastguard Worker return deps 920*c2e18aaaSAndroid Build Coastguard Worker mod_deps = set(mod_info.get(constants.MODULE_DEPENDENCIES, [])) 921*c2e18aaaSAndroid Build Coastguard Worker # Remove item in deps if it already in depend_on: 922*c2e18aaaSAndroid Build Coastguard Worker mod_deps = mod_deps - depend_on 923*c2e18aaaSAndroid Build Coastguard Worker deps = deps.union(mod_deps) 924*c2e18aaaSAndroid Build Coastguard Worker for mod_dep in mod_deps: 925*c2e18aaaSAndroid Build Coastguard Worker deps = deps.union( 926*c2e18aaaSAndroid Build Coastguard Worker set( 927*c2e18aaaSAndroid Build Coastguard Worker self.get_module_dependency( 928*c2e18aaaSAndroid Build Coastguard Worker mod_dep, depend_on=depend_on.union(deps) 929*c2e18aaaSAndroid Build Coastguard Worker ) 930*c2e18aaaSAndroid Build Coastguard Worker ) 931*c2e18aaaSAndroid Build Coastguard Worker ) 932*c2e18aaaSAndroid Build Coastguard Worker return deps 933*c2e18aaaSAndroid Build Coastguard Worker 934*c2e18aaaSAndroid Build Coastguard Worker def get_install_module_dependency(self, module_name, depend_on=None): 935*c2e18aaaSAndroid Build Coastguard Worker """Get the dependency set for the given modules with installed path. 936*c2e18aaaSAndroid Build Coastguard Worker 937*c2e18aaaSAndroid Build Coastguard Worker Args: 938*c2e18aaaSAndroid Build Coastguard Worker module_name: String of module to check. 939*c2e18aaaSAndroid Build Coastguard Worker depend_on: The list of parent dependencies. 940*c2e18aaaSAndroid Build Coastguard Worker 941*c2e18aaaSAndroid Build Coastguard Worker Returns: 942*c2e18aaaSAndroid Build Coastguard Worker Set of dependency modules which has installed path. 943*c2e18aaaSAndroid Build Coastguard Worker """ 944*c2e18aaaSAndroid Build Coastguard Worker install_deps = set() 945*c2e18aaaSAndroid Build Coastguard Worker deps = self.get_module_dependency(module_name, depend_on) 946*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s depends on: %s', module_name, deps) 947*c2e18aaaSAndroid Build Coastguard Worker for module in deps: 948*c2e18aaaSAndroid Build Coastguard Worker mod_info = self.get_module_info(module) 949*c2e18aaaSAndroid Build Coastguard Worker if mod_info and mod_info.get(constants.MODULE_INSTALLED, []): 950*c2e18aaaSAndroid Build Coastguard Worker install_deps.add(module) 951*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 952*c2e18aaaSAndroid Build Coastguard Worker 'modules %s required by %s were not installed', 953*c2e18aaaSAndroid Build Coastguard Worker install_deps, 954*c2e18aaaSAndroid Build Coastguard Worker module_name, 955*c2e18aaaSAndroid Build Coastguard Worker ) 956*c2e18aaaSAndroid Build Coastguard Worker return install_deps 957*c2e18aaaSAndroid Build Coastguard Worker 958*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 959*c2e18aaaSAndroid Build Coastguard Worker def is_unit_test(mod_info): 960*c2e18aaaSAndroid Build Coastguard Worker """Return True if input module is unit test, False otherwise. 961*c2e18aaaSAndroid Build Coastguard Worker 962*c2e18aaaSAndroid Build Coastguard Worker Args: 963*c2e18aaaSAndroid Build Coastguard Worker mod_info: ModuleInfo to check. 964*c2e18aaaSAndroid Build Coastguard Worker 965*c2e18aaaSAndroid Build Coastguard Worker Returns: 966*c2e18aaaSAndroid Build Coastguard Worker True if input module is unit test, False otherwise. 967*c2e18aaaSAndroid Build Coastguard Worker """ 968*c2e18aaaSAndroid Build Coastguard Worker return mod_info.get(constants.MODULE_IS_UNIT_TEST, '') == 'true' 969*c2e18aaaSAndroid Build Coastguard Worker 970*c2e18aaaSAndroid Build Coastguard Worker def is_host_unit_test(self, info: Dict[str, Any]) -> bool: 971*c2e18aaaSAndroid Build Coastguard Worker """Return True if input module is host unit test, False otherwise. 972*c2e18aaaSAndroid Build Coastguard Worker 973*c2e18aaaSAndroid Build Coastguard Worker Args: 974*c2e18aaaSAndroid Build Coastguard Worker info: ModuleInfo to check. 975*c2e18aaaSAndroid Build Coastguard Worker 976*c2e18aaaSAndroid Build Coastguard Worker Returns: 977*c2e18aaaSAndroid Build Coastguard Worker True if input module is host unit test, False otherwise. 978*c2e18aaaSAndroid Build Coastguard Worker """ 979*c2e18aaaSAndroid Build Coastguard Worker return self.is_tradefed_testable_module( 980*c2e18aaaSAndroid Build Coastguard Worker info 981*c2e18aaaSAndroid Build Coastguard Worker ) and self.is_suite_in_compatibility_suites('host-unit-tests', info) 982*c2e18aaaSAndroid Build Coastguard Worker 983*c2e18aaaSAndroid Build Coastguard Worker def is_modern_robolectric_test(self, info: Dict[str, Any]) -> bool: 984*c2e18aaaSAndroid Build Coastguard Worker """Return whether 'robolectric-tests' is in 'compatibility_suites'.""" 985*c2e18aaaSAndroid Build Coastguard Worker return self.is_tradefed_testable_module( 986*c2e18aaaSAndroid Build Coastguard Worker info 987*c2e18aaaSAndroid Build Coastguard Worker ) and self.is_robolectric_test_suite(info) 988*c2e18aaaSAndroid Build Coastguard Worker 989*c2e18aaaSAndroid Build Coastguard Worker def is_robolectric_test_suite(self, mod_info) -> bool: 990*c2e18aaaSAndroid Build Coastguard Worker """Return True if 'robolectric-tests' in the compatibility_suites. 991*c2e18aaaSAndroid Build Coastguard Worker 992*c2e18aaaSAndroid Build Coastguard Worker Args: 993*c2e18aaaSAndroid Build Coastguard Worker mod_info: ModuleInfo to check. 994*c2e18aaaSAndroid Build Coastguard Worker 995*c2e18aaaSAndroid Build Coastguard Worker Returns: 996*c2e18aaaSAndroid Build Coastguard Worker True if the 'robolectric-tests' is in the compatibility_suites, 997*c2e18aaaSAndroid Build Coastguard Worker False otherwise. 998*c2e18aaaSAndroid Build Coastguard Worker """ 999*c2e18aaaSAndroid Build Coastguard Worker return self.is_suite_in_compatibility_suites('robolectric-tests', mod_info) 1000*c2e18aaaSAndroid Build Coastguard Worker 1001*c2e18aaaSAndroid Build Coastguard Worker def is_ravenwood_test(self, info: Dict[str, Any]) -> bool: 1002*c2e18aaaSAndroid Build Coastguard Worker """Return whether 'ravenwood-tests' is in 'compatibility_suites'.""" 1003*c2e18aaaSAndroid Build Coastguard Worker return self.is_tradefed_testable_module( 1004*c2e18aaaSAndroid Build Coastguard Worker info 1005*c2e18aaaSAndroid Build Coastguard Worker ) and self.is_ravenwood_test_suite(info) 1006*c2e18aaaSAndroid Build Coastguard Worker 1007*c2e18aaaSAndroid Build Coastguard Worker def is_ravenwood_test_suite(self, mod_info) -> bool: 1008*c2e18aaaSAndroid Build Coastguard Worker """Return True if 'ravenwood-tests' in the compatibility_suites. 1009*c2e18aaaSAndroid Build Coastguard Worker 1010*c2e18aaaSAndroid Build Coastguard Worker Args: 1011*c2e18aaaSAndroid Build Coastguard Worker mod_info: ModuleInfo to check. 1012*c2e18aaaSAndroid Build Coastguard Worker 1013*c2e18aaaSAndroid Build Coastguard Worker Returns: 1014*c2e18aaaSAndroid Build Coastguard Worker True if the 'ravenwood-tests' is in the compatibility_suites, 1015*c2e18aaaSAndroid Build Coastguard Worker False otherwise. 1016*c2e18aaaSAndroid Build Coastguard Worker """ 1017*c2e18aaaSAndroid Build Coastguard Worker return self.is_suite_in_compatibility_suites('ravenwood-tests', mod_info) 1018*c2e18aaaSAndroid Build Coastguard Worker 1019*c2e18aaaSAndroid Build Coastguard Worker def is_device_driven_test(self, mod_info): 1020*c2e18aaaSAndroid Build Coastguard Worker """Return True if input module is device driven test, False otherwise. 1021*c2e18aaaSAndroid Build Coastguard Worker 1022*c2e18aaaSAndroid Build Coastguard Worker Args: 1023*c2e18aaaSAndroid Build Coastguard Worker mod_info: ModuleInfo to check. 1024*c2e18aaaSAndroid Build Coastguard Worker 1025*c2e18aaaSAndroid Build Coastguard Worker Returns: 1026*c2e18aaaSAndroid Build Coastguard Worker True if input module is device driven test, False otherwise. 1027*c2e18aaaSAndroid Build Coastguard Worker """ 1028*c2e18aaaSAndroid Build Coastguard Worker if self.is_robolectric_test_suite(mod_info): 1029*c2e18aaaSAndroid Build Coastguard Worker return False 1030*c2e18aaaSAndroid Build Coastguard Worker if self.is_ravenwood_test_suite(mod_info): 1031*c2e18aaaSAndroid Build Coastguard Worker return False 1032*c2e18aaaSAndroid Build Coastguard Worker 1033*c2e18aaaSAndroid Build Coastguard Worker return self.is_tradefed_testable_module( 1034*c2e18aaaSAndroid Build Coastguard Worker mod_info 1035*c2e18aaaSAndroid Build Coastguard Worker ) and 'DEVICE' in mod_info.get(constants.MODULE_SUPPORTED_VARIANTS, []) 1036*c2e18aaaSAndroid Build Coastguard Worker 1037*c2e18aaaSAndroid Build Coastguard Worker def is_host_driven_test(self, mod_info): 1038*c2e18aaaSAndroid Build Coastguard Worker """Return True if input module is host driven test, False otherwise. 1039*c2e18aaaSAndroid Build Coastguard Worker 1040*c2e18aaaSAndroid Build Coastguard Worker Args: 1041*c2e18aaaSAndroid Build Coastguard Worker mod_info: ModuleInfo to check. 1042*c2e18aaaSAndroid Build Coastguard Worker 1043*c2e18aaaSAndroid Build Coastguard Worker Returns: 1044*c2e18aaaSAndroid Build Coastguard Worker True if input module is host driven test, False otherwise. 1045*c2e18aaaSAndroid Build Coastguard Worker """ 1046*c2e18aaaSAndroid Build Coastguard Worker return self.is_tradefed_testable_module( 1047*c2e18aaaSAndroid Build Coastguard Worker mod_info 1048*c2e18aaaSAndroid Build Coastguard Worker ) and 'HOST' in mod_info.get(constants.MODULE_SUPPORTED_VARIANTS, []) 1049*c2e18aaaSAndroid Build Coastguard Worker 1050*c2e18aaaSAndroid Build Coastguard Worker def _any_module(self, _: Module) -> bool: 1051*c2e18aaaSAndroid Build Coastguard Worker return True 1052*c2e18aaaSAndroid Build Coastguard Worker 1053*c2e18aaaSAndroid Build Coastguard Worker def get_all_tests(self): 1054*c2e18aaaSAndroid Build Coastguard Worker """Get a list of all the module names which are tests.""" 1055*c2e18aaaSAndroid Build Coastguard Worker return self._get_all_modules(type_predicate=self.is_testable_module) 1056*c2e18aaaSAndroid Build Coastguard Worker 1057*c2e18aaaSAndroid Build Coastguard Worker def get_all_unit_tests(self): 1058*c2e18aaaSAndroid Build Coastguard Worker """Get a list of all the module names which are unit tests.""" 1059*c2e18aaaSAndroid Build Coastguard Worker return self._get_all_modules(type_predicate=ModuleInfo.is_unit_test) 1060*c2e18aaaSAndroid Build Coastguard Worker 1061*c2e18aaaSAndroid Build Coastguard Worker def get_all_host_unit_tests(self): 1062*c2e18aaaSAndroid Build Coastguard Worker """Get a list of all the module names which are host unit tests.""" 1063*c2e18aaaSAndroid Build Coastguard Worker return self._get_all_modules(type_predicate=self.is_host_unit_test) 1064*c2e18aaaSAndroid Build Coastguard Worker 1065*c2e18aaaSAndroid Build Coastguard Worker def get_all_device_driven_tests(self): 1066*c2e18aaaSAndroid Build Coastguard Worker """Get a list of all the module names which are device driven tests.""" 1067*c2e18aaaSAndroid Build Coastguard Worker return self._get_all_modules(type_predicate=self.is_device_driven_test) 1068*c2e18aaaSAndroid Build Coastguard Worker 1069*c2e18aaaSAndroid Build Coastguard Worker def _get_all_modules(self, type_predicate=None): 1070*c2e18aaaSAndroid Build Coastguard Worker """Get a list of all the module names that passed the predicate.""" 1071*c2e18aaaSAndroid Build Coastguard Worker modules = [] 1072*c2e18aaaSAndroid Build Coastguard Worker type_predicate = type_predicate or self._any_module 1073*c2e18aaaSAndroid Build Coastguard Worker for mod_name, mod_info in self.name_to_module_info.items(): 1074*c2e18aaaSAndroid Build Coastguard Worker if mod_info.get(constants.MODULE_NAME, '') == mod_name: 1075*c2e18aaaSAndroid Build Coastguard Worker if type_predicate(mod_info): 1076*c2e18aaaSAndroid Build Coastguard Worker modules.append(mod_name) 1077*c2e18aaaSAndroid Build Coastguard Worker return modules 1078*c2e18aaaSAndroid Build Coastguard Worker 1079*c2e18aaaSAndroid Build Coastguard Worker def get_modules_by_path_in_srcs( 1080*c2e18aaaSAndroid Build Coastguard Worker self, path: str, testable_modules_only: bool = False 1081*c2e18aaaSAndroid Build Coastguard Worker ) -> Set[str]: 1082*c2e18aaaSAndroid Build Coastguard Worker """Get the module name that the given path belongs to.(in 'srcs') 1083*c2e18aaaSAndroid Build Coastguard Worker 1084*c2e18aaaSAndroid Build Coastguard Worker Args: 1085*c2e18aaaSAndroid Build Coastguard Worker path: file path which is relative to ANDROID_BUILD_TOP. 1086*c2e18aaaSAndroid Build Coastguard Worker testable_modules_only: boolean flag which determines whether search 1087*c2e18aaaSAndroid Build Coastguard Worker testable modules only or not. 1088*c2e18aaaSAndroid Build Coastguard Worker 1089*c2e18aaaSAndroid Build Coastguard Worker Returns: 1090*c2e18aaaSAndroid Build Coastguard Worker A set of string for matched module names, empty set if nothing find. 1091*c2e18aaaSAndroid Build Coastguard Worker """ 1092*c2e18aaaSAndroid Build Coastguard Worker modules = set() 1093*c2e18aaaSAndroid Build Coastguard Worker 1094*c2e18aaaSAndroid Build Coastguard Worker for mod_name in ( 1095*c2e18aaaSAndroid Build Coastguard Worker self.get_testable_modules() 1096*c2e18aaaSAndroid Build Coastguard Worker if testable_modules_only 1097*c2e18aaaSAndroid Build Coastguard Worker else self.name_to_module_info.keys() 1098*c2e18aaaSAndroid Build Coastguard Worker ): 1099*c2e18aaaSAndroid Build Coastguard Worker m_info = self.get_module_info(mod_name) 1100*c2e18aaaSAndroid Build Coastguard Worker if m_info: 1101*c2e18aaaSAndroid Build Coastguard Worker for src in m_info.get(constants.MODULE_SRCS, []): 1102*c2e18aaaSAndroid Build Coastguard Worker if src in path: 1103*c2e18aaaSAndroid Build Coastguard Worker modules.add(mod_name) 1104*c2e18aaaSAndroid Build Coastguard Worker 1105*c2e18aaaSAndroid Build Coastguard Worker return modules 1106*c2e18aaaSAndroid Build Coastguard Worker 1107*c2e18aaaSAndroid Build Coastguard Worker def get_modules_by_path( 1108*c2e18aaaSAndroid Build Coastguard Worker self, path: str, testable_modules_only: bool = False 1109*c2e18aaaSAndroid Build Coastguard Worker ) -> set[str]: 1110*c2e18aaaSAndroid Build Coastguard Worker """Get the module names that the give path belongs to. 1111*c2e18aaaSAndroid Build Coastguard Worker 1112*c2e18aaaSAndroid Build Coastguard Worker Args: 1113*c2e18aaaSAndroid Build Coastguard Worker path: dir path for searching among `path` in module information. 1114*c2e18aaaSAndroid Build Coastguard Worker testable_modules_only: boolean flag which determines whether search 1115*c2e18aaaSAndroid Build Coastguard Worker testable modules only or not. 1116*c2e18aaaSAndroid Build Coastguard Worker 1117*c2e18aaaSAndroid Build Coastguard Worker Returns: 1118*c2e18aaaSAndroid Build Coastguard Worker A set of module names. 1119*c2e18aaaSAndroid Build Coastguard Worker """ 1120*c2e18aaaSAndroid Build Coastguard Worker modules = set() 1121*c2e18aaaSAndroid Build Coastguard Worker is_testable_module_fn = ( 1122*c2e18aaaSAndroid Build Coastguard Worker self.is_testable_module if testable_modules_only else lambda _: True 1123*c2e18aaaSAndroid Build Coastguard Worker ) 1124*c2e18aaaSAndroid Build Coastguard Worker 1125*c2e18aaaSAndroid Build Coastguard Worker m_infos = self.path_to_module_info.get(path) 1126*c2e18aaaSAndroid Build Coastguard Worker if m_infos: 1127*c2e18aaaSAndroid Build Coastguard Worker modules = { 1128*c2e18aaaSAndroid Build Coastguard Worker info.get(constants.MODULE_NAME) 1129*c2e18aaaSAndroid Build Coastguard Worker for info in m_infos 1130*c2e18aaaSAndroid Build Coastguard Worker if is_testable_module_fn(info) 1131*c2e18aaaSAndroid Build Coastguard Worker } 1132*c2e18aaaSAndroid Build Coastguard Worker 1133*c2e18aaaSAndroid Build Coastguard Worker return modules 1134*c2e18aaaSAndroid Build Coastguard Worker 1135*c2e18aaaSAndroid Build Coastguard Worker def get_modules_by_include_deps( 1136*c2e18aaaSAndroid Build Coastguard Worker self, deps: Set[str], testable_module_only: bool = False 1137*c2e18aaaSAndroid Build Coastguard Worker ) -> Set[str]: 1138*c2e18aaaSAndroid Build Coastguard Worker """Get the matched module names for the input dependencies. 1139*c2e18aaaSAndroid Build Coastguard Worker 1140*c2e18aaaSAndroid Build Coastguard Worker Args: 1141*c2e18aaaSAndroid Build Coastguard Worker deps: A set of string for dependencies. 1142*c2e18aaaSAndroid Build Coastguard Worker testable_module_only: Option if only want to get testable module. 1143*c2e18aaaSAndroid Build Coastguard Worker 1144*c2e18aaaSAndroid Build Coastguard Worker Returns: 1145*c2e18aaaSAndroid Build Coastguard Worker A set of matched module names for the input dependencies. 1146*c2e18aaaSAndroid Build Coastguard Worker """ 1147*c2e18aaaSAndroid Build Coastguard Worker modules = set() 1148*c2e18aaaSAndroid Build Coastguard Worker 1149*c2e18aaaSAndroid Build Coastguard Worker for mod_name in ( 1150*c2e18aaaSAndroid Build Coastguard Worker self.get_testable_modules() 1151*c2e18aaaSAndroid Build Coastguard Worker if testable_module_only 1152*c2e18aaaSAndroid Build Coastguard Worker else self.name_to_module_info.keys() 1153*c2e18aaaSAndroid Build Coastguard Worker ): 1154*c2e18aaaSAndroid Build Coastguard Worker mod_info = self.get_module_info(mod_name) 1155*c2e18aaaSAndroid Build Coastguard Worker if mod_info and deps.intersection( 1156*c2e18aaaSAndroid Build Coastguard Worker set(mod_info.get(constants.MODULE_DEPENDENCIES, [])) 1157*c2e18aaaSAndroid Build Coastguard Worker ): 1158*c2e18aaaSAndroid Build Coastguard Worker modules.add(mod_info.get(constants.MODULE_NAME)) 1159*c2e18aaaSAndroid Build Coastguard Worker return modules 1160*c2e18aaaSAndroid Build Coastguard Worker 1161*c2e18aaaSAndroid Build Coastguard Worker def get_installed_paths(self, module_name: str) -> List[Path]: 1162*c2e18aaaSAndroid Build Coastguard Worker """Return installed path from module info.""" 1163*c2e18aaaSAndroid Build Coastguard Worker mod_info = self.get_module_info(module_name) 1164*c2e18aaaSAndroid Build Coastguard Worker if not mod_info: 1165*c2e18aaaSAndroid Build Coastguard Worker return [] 1166*c2e18aaaSAndroid Build Coastguard Worker 1167*c2e18aaaSAndroid Build Coastguard Worker def _to_abs_path(p): 1168*c2e18aaaSAndroid Build Coastguard Worker if os.path.isabs(p): 1169*c2e18aaaSAndroid Build Coastguard Worker return Path(p) 1170*c2e18aaaSAndroid Build Coastguard Worker return Path(os.getenv(constants.ANDROID_BUILD_TOP), p) 1171*c2e18aaaSAndroid Build Coastguard Worker 1172*c2e18aaaSAndroid Build Coastguard Worker return [_to_abs_path(p) for p in mod_info.get('installed', [])] 1173*c2e18aaaSAndroid Build Coastguard Worker 1174*c2e18aaaSAndroid Build Coastguard Worker def get_code_under_test(self, module_name: str) -> List[str]: 1175*c2e18aaaSAndroid Build Coastguard Worker """Return code under test from module info.""" 1176*c2e18aaaSAndroid Build Coastguard Worker mod_info = self.get_module_info(module_name) 1177*c2e18aaaSAndroid Build Coastguard Worker if not mod_info: 1178*c2e18aaaSAndroid Build Coastguard Worker atest_utils.colorful_print( 1179*c2e18aaaSAndroid Build Coastguard Worker '\nmodule %s cannot be found in module info, skip generating' 1180*c2e18aaaSAndroid Build Coastguard Worker ' coverage for it.' % module_name, 1181*c2e18aaaSAndroid Build Coastguard Worker constants.YELLOW, 1182*c2e18aaaSAndroid Build Coastguard Worker ) 1183*c2e18aaaSAndroid Build Coastguard Worker return [] 1184*c2e18aaaSAndroid Build Coastguard Worker 1185*c2e18aaaSAndroid Build Coastguard Worker return mod_info.get('code_under_test', []) 1186*c2e18aaaSAndroid Build Coastguard Worker 1187*c2e18aaaSAndroid Build Coastguard Worker def build_variants(self, info: Dict[str, Any]) -> List[str]: 1188*c2e18aaaSAndroid Build Coastguard Worker return info.get(constants.MODULE_SUPPORTED_VARIANTS, []) 1189*c2e18aaaSAndroid Build Coastguard Worker 1190*c2e18aaaSAndroid Build Coastguard Worker def requires_device(self, info: Dict[str, Any]) -> bool: 1191*c2e18aaaSAndroid Build Coastguard Worker 1192*c2e18aaaSAndroid Build Coastguard Worker if self.is_modern_robolectric_test(info): 1193*c2e18aaaSAndroid Build Coastguard Worker return False 1194*c2e18aaaSAndroid Build Coastguard Worker if self.is_ravenwood_test(info): 1195*c2e18aaaSAndroid Build Coastguard Worker return False 1196*c2e18aaaSAndroid Build Coastguard Worker if self.is_host_unit_test(info) and 'DEVICE' not in self.build_variants( 1197*c2e18aaaSAndroid Build Coastguard Worker info 1198*c2e18aaaSAndroid Build Coastguard Worker ): 1199*c2e18aaaSAndroid Build Coastguard Worker return False 1200*c2e18aaaSAndroid Build Coastguard Worker 1201*c2e18aaaSAndroid Build Coastguard Worker return True 1202*c2e18aaaSAndroid Build Coastguard Worker 1203*c2e18aaaSAndroid Build Coastguard Worker 1204*c2e18aaaSAndroid Build Coastguard Workerdef _create_db(data_map: Dict[str, Dict[str, Any]], db_path: Path): 1205*c2e18aaaSAndroid Build Coastguard Worker """Create a Sqlite DB by writing to tempfile and move it to the right place. 1206*c2e18aaaSAndroid Build Coastguard Worker 1207*c2e18aaaSAndroid Build Coastguard Worker Args: 1208*c2e18aaaSAndroid Build Coastguard Worker data_map: A dict where the key is table name and value is data itself. 1209*c2e18aaaSAndroid Build Coastguard Worker db_path: A Path pointing to the DB file. 1210*c2e18aaaSAndroid Build Coastguard Worker """ 1211*c2e18aaaSAndroid Build Coastguard Worker if db_path.is_file(): 1212*c2e18aaaSAndroid Build Coastguard Worker db_path.unlink() 1213*c2e18aaaSAndroid Build Coastguard Worker 1214*c2e18aaaSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(delete=False) as tmp_db: 1215*c2e18aaaSAndroid Build Coastguard Worker _create_db_in_path(data_map, tmp_db.name) 1216*c2e18aaaSAndroid Build Coastguard Worker shutil.move(tmp_db.name, db_path) 1217*c2e18aaaSAndroid Build Coastguard Worker 1218*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s is created successfully.', db_path) 1219*c2e18aaaSAndroid Build Coastguard Worker 1220*c2e18aaaSAndroid Build Coastguard Worker 1221*c2e18aaaSAndroid Build Coastguard Workerdef _create_db_in_path(data_map: Dict[str, Dict[str, Any]], db_path: Path): 1222*c2e18aaaSAndroid Build Coastguard Worker """Create a Sqlite DB with multiple tables. 1223*c2e18aaaSAndroid Build Coastguard Worker 1224*c2e18aaaSAndroid Build Coastguard Worker Args: 1225*c2e18aaaSAndroid Build Coastguard Worker data_map: A dict where the key is table name and value is data itself. 1226*c2e18aaaSAndroid Build Coastguard Worker db_path: A Path pointing to the DB file. 1227*c2e18aaaSAndroid Build Coastguard Worker """ 1228*c2e18aaaSAndroid Build Coastguard Worker con = sqlite3.connect(db_path) 1229*c2e18aaaSAndroid Build Coastguard Worker with con: 1230*c2e18aaaSAndroid Build Coastguard Worker cur = con.cursor() 1231*c2e18aaaSAndroid Build Coastguard Worker for table, contents in data_map.items(): 1232*c2e18aaaSAndroid Build Coastguard Worker cur.execute(f'CREATE TABLE {table}(key TEXT PRIMARY KEY, value TEXT)') 1233*c2e18aaaSAndroid Build Coastguard Worker 1234*c2e18aaaSAndroid Build Coastguard Worker data = [] 1235*c2e18aaaSAndroid Build Coastguard Worker for k, v in contents.items(): 1236*c2e18aaaSAndroid Build Coastguard Worker data.append({'key': k, 'value': json.dumps(v)}) 1237*c2e18aaaSAndroid Build Coastguard Worker cur.executemany(f'INSERT INTO {table} VALUES(:key, :value)', data) 1238*c2e18aaaSAndroid Build Coastguard Worker 1239*c2e18aaaSAndroid Build Coastguard Worker 1240*c2e18aaaSAndroid Build Coastguard Workerdef _create_json(data_map: Dict[str, Any], json_path: Path): 1241*c2e18aaaSAndroid Build Coastguard Worker """Write content onto a JSON file. 1242*c2e18aaaSAndroid Build Coastguard Worker 1243*c2e18aaaSAndroid Build Coastguard Worker Args: 1244*c2e18aaaSAndroid Build Coastguard Worker data_map: A dict where the key is table name and value is data itself. 1245*c2e18aaaSAndroid Build Coastguard Worker json_path: A Path pointing to the JSON file. 1246*c2e18aaaSAndroid Build Coastguard Worker """ 1247*c2e18aaaSAndroid Build Coastguard Worker if json_path.is_file(): 1248*c2e18aaaSAndroid Build Coastguard Worker json_path.unlink() 1249*c2e18aaaSAndroid Build Coastguard Worker 1250*c2e18aaaSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(delete=False) as temp_json: 1251*c2e18aaaSAndroid Build Coastguard Worker with open(temp_json.name, 'w', encoding='utf-8') as _temp: 1252*c2e18aaaSAndroid Build Coastguard Worker json.dump(data_map, _temp, indent=0) 1253*c2e18aaaSAndroid Build Coastguard Worker shutil.move(temp_json.name, json_path) 1254*c2e18aaaSAndroid Build Coastguard Worker 1255*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s is created successfully.', json_path) 1256*c2e18aaaSAndroid Build Coastguard Worker 1257*c2e18aaaSAndroid Build Coastguard Worker 1258*c2e18aaaSAndroid Build Coastguard Workerdef _save_data_async(function: Callable, contents: Any, target_path: Path): 1259*c2e18aaaSAndroid Build Coastguard Worker """Save contents to a static file in asynchronized manner.""" 1260*c2e18aaaSAndroid Build Coastguard Worker atest_utils.run_multi_proc( 1261*c2e18aaaSAndroid Build Coastguard Worker func=function, 1262*c2e18aaaSAndroid Build Coastguard Worker args=[contents, target_path], 1263*c2e18aaaSAndroid Build Coastguard Worker # We set `daemon` to `False` to make sure that Atest doesn't exit before 1264*c2e18aaaSAndroid Build Coastguard Worker # writing the cache file. 1265*c2e18aaaSAndroid Build Coastguard Worker daemon=False, 1266*c2e18aaaSAndroid Build Coastguard Worker ) 1267*c2e18aaaSAndroid Build Coastguard Worker 1268*c2e18aaaSAndroid Build Coastguard Worker 1269*c2e18aaaSAndroid Build Coastguard Workerdef merge_soong_info(name_to_module_info, mod_bp_infos): 1270*c2e18aaaSAndroid Build Coastguard Worker """Merge the dependency and srcs in mod_bp_infos to name_to_module_info. 1271*c2e18aaaSAndroid Build Coastguard Worker 1272*c2e18aaaSAndroid Build Coastguard Worker Args: 1273*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict of module name to module info dict. 1274*c2e18aaaSAndroid Build Coastguard Worker mod_bp_infos: Dict of module name to bp's module info dict. 1275*c2e18aaaSAndroid Build Coastguard Worker 1276*c2e18aaaSAndroid Build Coastguard Worker Returns: 1277*c2e18aaaSAndroid Build Coastguard Worker Dict of updated name_to_module_info. 1278*c2e18aaaSAndroid Build Coastguard Worker """ 1279*c2e18aaaSAndroid Build Coastguard Worker merge_items = [ 1280*c2e18aaaSAndroid Build Coastguard Worker constants.MODULE_DEPENDENCIES, 1281*c2e18aaaSAndroid Build Coastguard Worker constants.MODULE_SRCS, 1282*c2e18aaaSAndroid Build Coastguard Worker constants.MODULE_LIBS, 1283*c2e18aaaSAndroid Build Coastguard Worker constants.MODULE_STATIC_LIBS, 1284*c2e18aaaSAndroid Build Coastguard Worker constants.MODULE_STATIC_DEPS, 1285*c2e18aaaSAndroid Build Coastguard Worker constants.MODULE_PATH, 1286*c2e18aaaSAndroid Build Coastguard Worker ] 1287*c2e18aaaSAndroid Build Coastguard Worker for module_name, dep_info in mod_bp_infos.items(): 1288*c2e18aaaSAndroid Build Coastguard Worker mod_info = name_to_module_info.setdefault(module_name, {}) 1289*c2e18aaaSAndroid Build Coastguard Worker for merge_item in merge_items: 1290*c2e18aaaSAndroid Build Coastguard Worker dep_info_values = dep_info.get(merge_item, []) 1291*c2e18aaaSAndroid Build Coastguard Worker mod_info_values = mod_info.get(merge_item, []) 1292*c2e18aaaSAndroid Build Coastguard Worker mod_info_values.extend(dep_info_values) 1293*c2e18aaaSAndroid Build Coastguard Worker mod_info_values.sort() 1294*c2e18aaaSAndroid Build Coastguard Worker # deduplicate values just in case. 1295*c2e18aaaSAndroid Build Coastguard Worker mod_info_values = list(dict.fromkeys(mod_info_values)) 1296*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info[module_name][merge_item] = mod_info_values 1297*c2e18aaaSAndroid Build Coastguard Worker return name_to_module_info 1298*c2e18aaaSAndroid Build Coastguard Worker 1299*c2e18aaaSAndroid Build Coastguard Worker 1300*c2e18aaaSAndroid Build Coastguard Workerdef _add_missing_variant_modules(name_to_module_info: Dict[str, Module]): 1301*c2e18aaaSAndroid Build Coastguard Worker missing_modules = {} 1302*c2e18aaaSAndroid Build Coastguard Worker 1303*c2e18aaaSAndroid Build Coastguard Worker # Android's build system automatically adds a suffix for some build module 1304*c2e18aaaSAndroid Build Coastguard Worker # variants. For example, a module-info entry for a module originally named 1305*c2e18aaaSAndroid Build Coastguard Worker # 'HelloWorldTest' might appear as 'HelloWorldTest_32' and which Atest would 1306*c2e18aaaSAndroid Build Coastguard Worker # not be able to find. We add such entries if not already present so they 1307*c2e18aaaSAndroid Build Coastguard Worker # can be looked up using their declared module name. 1308*c2e18aaaSAndroid Build Coastguard Worker for mod_name, mod_info in name_to_module_info.items(): 1309*c2e18aaaSAndroid Build Coastguard Worker declared_module_name = mod_info.get(constants.MODULE_NAME, mod_name) 1310*c2e18aaaSAndroid Build Coastguard Worker if declared_module_name in name_to_module_info: 1311*c2e18aaaSAndroid Build Coastguard Worker continue 1312*c2e18aaaSAndroid Build Coastguard Worker missing_modules.setdefault(declared_module_name, mod_info) 1313*c2e18aaaSAndroid Build Coastguard Worker 1314*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info.update(missing_modules) 1315*c2e18aaaSAndroid Build Coastguard Worker 1316*c2e18aaaSAndroid Build Coastguard Worker 1317*c2e18aaaSAndroid Build Coastguard Workerdef contains_same_mainline_modules( 1318*c2e18aaaSAndroid Build Coastguard Worker mainline_modules: Set[str], module_lists: Set[str] 1319*c2e18aaaSAndroid Build Coastguard Worker): 1320*c2e18aaaSAndroid Build Coastguard Worker """Check if mainline modules listed on command line is 1321*c2e18aaaSAndroid Build Coastguard Worker 1322*c2e18aaaSAndroid Build Coastguard Worker the same set as config. 1323*c2e18aaaSAndroid Build Coastguard Worker 1324*c2e18aaaSAndroid Build Coastguard Worker Args: 1325*c2e18aaaSAndroid Build Coastguard Worker mainline_modules: A list of mainline modules from triggered test. 1326*c2e18aaaSAndroid Build Coastguard Worker module_lists: A list of concatenate mainline module string from test 1327*c2e18aaaSAndroid Build Coastguard Worker configs. 1328*c2e18aaaSAndroid Build Coastguard Worker 1329*c2e18aaaSAndroid Build Coastguard Worker Returns 1330*c2e18aaaSAndroid Build Coastguard Worker True if the set mainline modules from triggered test is in the test 1331*c2e18aaaSAndroid Build Coastguard Worker configs. 1332*c2e18aaaSAndroid Build Coastguard Worker """ 1333*c2e18aaaSAndroid Build Coastguard Worker for module_string in module_lists: 1334*c2e18aaaSAndroid Build Coastguard Worker if mainline_modules == set(module_string.split('+')): 1335*c2e18aaaSAndroid Build Coastguard Worker return True 1336*c2e18aaaSAndroid Build Coastguard Worker return False 1337*c2e18aaaSAndroid Build Coastguard Worker 1338*c2e18aaaSAndroid Build Coastguard Worker 1339*c2e18aaaSAndroid Build Coastguard Workerdef get_path_to_module_info(name_to_module_info): 1340*c2e18aaaSAndroid Build Coastguard Worker """Return the path_to_module_info dict. 1341*c2e18aaaSAndroid Build Coastguard Worker 1342*c2e18aaaSAndroid Build Coastguard Worker Args: 1343*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict of module name to module info dict. 1344*c2e18aaaSAndroid Build Coastguard Worker 1345*c2e18aaaSAndroid Build Coastguard Worker Returns: 1346*c2e18aaaSAndroid Build Coastguard Worker Dict of module path to module info dict. 1347*c2e18aaaSAndroid Build Coastguard Worker """ 1348*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info = {} 1349*c2e18aaaSAndroid Build Coastguard Worker for mod_name, mod_info in name_to_module_info.items(): 1350*c2e18aaaSAndroid Build Coastguard Worker # Cross-compiled and multi-arch modules actually all belong to 1351*c2e18aaaSAndroid Build Coastguard Worker # a single target so filter out these extra modules. 1352*c2e18aaaSAndroid Build Coastguard Worker if mod_name != mod_info.get(constants.MODULE_NAME, ''): 1353*c2e18aaaSAndroid Build Coastguard Worker continue 1354*c2e18aaaSAndroid Build Coastguard Worker for path in mod_info.get(constants.MODULE_PATH, []): 1355*c2e18aaaSAndroid Build Coastguard Worker mod_info[constants.MODULE_NAME] = mod_name 1356*c2e18aaaSAndroid Build Coastguard Worker # There could be multiple modules in a path. 1357*c2e18aaaSAndroid Build Coastguard Worker if path in path_to_module_info: 1358*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info[path].append(mod_info) 1359*c2e18aaaSAndroid Build Coastguard Worker else: 1360*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info[path] = [mod_info] 1361*c2e18aaaSAndroid Build Coastguard Worker return path_to_module_info 1362*c2e18aaaSAndroid Build Coastguard Worker 1363*c2e18aaaSAndroid Build Coastguard Worker 1364*c2e18aaaSAndroid Build Coastguard Workerdef _get_module_names(path_to_module_info, rel_module_path): 1365*c2e18aaaSAndroid Build Coastguard Worker """Get the modules that all have module_path. 1366*c2e18aaaSAndroid Build Coastguard Worker 1367*c2e18aaaSAndroid Build Coastguard Worker Args: 1368*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict of path to module info. 1369*c2e18aaaSAndroid Build Coastguard Worker rel_module_path: path of module in module-info.json. 1370*c2e18aaaSAndroid Build Coastguard Worker 1371*c2e18aaaSAndroid Build Coastguard Worker Returns: 1372*c2e18aaaSAndroid Build Coastguard Worker List of module names. 1373*c2e18aaaSAndroid Build Coastguard Worker """ 1374*c2e18aaaSAndroid Build Coastguard Worker return [ 1375*c2e18aaaSAndroid Build Coastguard Worker m.get(constants.MODULE_NAME) 1376*c2e18aaaSAndroid Build Coastguard Worker for m in path_to_module_info.get(rel_module_path, []) 1377*c2e18aaaSAndroid Build Coastguard Worker ] 1378*c2e18aaaSAndroid Build Coastguard Worker 1379*c2e18aaaSAndroid Build Coastguard Worker 1380*c2e18aaaSAndroid Build Coastguard Workerdef _get_robolectric_test_name( 1381*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict[str, Dict], 1382*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict[str, Dict], 1383*c2e18aaaSAndroid Build Coastguard Worker info: Dict[str, Any], 1384*c2e18aaaSAndroid Build Coastguard Worker) -> str: 1385*c2e18aaaSAndroid Build Coastguard Worker """Returns runnable robolectric module name. 1386*c2e18aaaSAndroid Build Coastguard Worker 1387*c2e18aaaSAndroid Build Coastguard Worker This method is for legacy robolectric tests and returns one of associated 1388*c2e18aaaSAndroid Build Coastguard Worker modules. The pattern is determined by the amount of shards: 1389*c2e18aaaSAndroid Build Coastguard Worker 1390*c2e18aaaSAndroid Build Coastguard Worker 10 shards: 1391*c2e18aaaSAndroid Build Coastguard Worker FooTests -> RunFooTests0, RunFooTests1 ... RunFooTests9 1392*c2e18aaaSAndroid Build Coastguard Worker No shard: 1393*c2e18aaaSAndroid Build Coastguard Worker FooTests -> RunFooTests 1394*c2e18aaaSAndroid Build Coastguard Worker 1395*c2e18aaaSAndroid Build Coastguard Worker Arg: 1396*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict of name to module info. 1397*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict of path to module info. 1398*c2e18aaaSAndroid Build Coastguard Worker info: Dict of module info to check. 1399*c2e18aaaSAndroid Build Coastguard Worker 1400*c2e18aaaSAndroid Build Coastguard Worker Returns: 1401*c2e18aaaSAndroid Build Coastguard Worker String of the first-matched associated module that belongs to the 1402*c2e18aaaSAndroid Build Coastguard Worker actual robolectric module, None if nothing has been found. 1403*c2e18aaaSAndroid Build Coastguard Worker """ 1404*c2e18aaaSAndroid Build Coastguard Worker if not info: 1405*c2e18aaaSAndroid Build Coastguard Worker return '' 1406*c2e18aaaSAndroid Build Coastguard Worker module_paths = info.get(constants.MODULE_PATH, []) 1407*c2e18aaaSAndroid Build Coastguard Worker if not module_paths: 1408*c2e18aaaSAndroid Build Coastguard Worker return '' 1409*c2e18aaaSAndroid Build Coastguard Worker filtered_module_names = [ 1410*c2e18aaaSAndroid Build Coastguard Worker name 1411*c2e18aaaSAndroid Build Coastguard Worker for name in _get_module_names(path_to_module_info, module_paths[0]) 1412*c2e18aaaSAndroid Build Coastguard Worker if name.startswith('Run') 1413*c2e18aaaSAndroid Build Coastguard Worker ] 1414*c2e18aaaSAndroid Build Coastguard Worker return next( 1415*c2e18aaaSAndroid Build Coastguard Worker ( 1416*c2e18aaaSAndroid Build Coastguard Worker name 1417*c2e18aaaSAndroid Build Coastguard Worker for name in filtered_module_names 1418*c2e18aaaSAndroid Build Coastguard Worker if ModuleInfo.is_legacy_robolectric_class( 1419*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info.get(name) 1420*c2e18aaaSAndroid Build Coastguard Worker ) 1421*c2e18aaaSAndroid Build Coastguard Worker ), 1422*c2e18aaaSAndroid Build Coastguard Worker '', 1423*c2e18aaaSAndroid Build Coastguard Worker ) 1424*c2e18aaaSAndroid Build Coastguard Worker 1425*c2e18aaaSAndroid Build Coastguard Worker 1426*c2e18aaaSAndroid Build Coastguard Workerdef _is_legacy_robolectric_test( 1427*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict[str, Dict], 1428*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict[str, Dict], 1429*c2e18aaaSAndroid Build Coastguard Worker info: Dict[str, Any], 1430*c2e18aaaSAndroid Build Coastguard Worker) -> bool: 1431*c2e18aaaSAndroid Build Coastguard Worker """Return whether the module_name is a legacy Robolectric test""" 1432*c2e18aaaSAndroid Build Coastguard Worker if ModuleInfo.is_tradefed_testable_module(info): 1433*c2e18aaaSAndroid Build Coastguard Worker return False 1434*c2e18aaaSAndroid Build Coastguard Worker return bool( 1435*c2e18aaaSAndroid Build Coastguard Worker _get_robolectric_test_name(name_to_module_info, path_to_module_info, info) 1436*c2e18aaaSAndroid Build Coastguard Worker ) 1437*c2e18aaaSAndroid Build Coastguard Worker 1438*c2e18aaaSAndroid Build Coastguard Worker 1439*c2e18aaaSAndroid Build Coastguard Workerdef get_module_info_target() -> str: 1440*c2e18aaaSAndroid Build Coastguard Worker """Get module info target name for soong_ui.bash""" 1441*c2e18aaaSAndroid Build Coastguard Worker build_top = atest_utils.get_build_top() 1442*c2e18aaaSAndroid Build Coastguard Worker module_info_path = atest_utils.get_product_out(_MODULE_INFO) 1443*c2e18aaaSAndroid Build Coastguard Worker if module_info_path.is_relative_to(build_top): 1444*c2e18aaaSAndroid Build Coastguard Worker return str(module_info_path.relative_to(build_top)) 1445*c2e18aaaSAndroid Build Coastguard Worker 1446*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Found customized OUT_DIR!') 1447*c2e18aaaSAndroid Build Coastguard Worker return str(module_info_path) 1448*c2e18aaaSAndroid Build Coastguard Worker 1449*c2e18aaaSAndroid Build Coastguard Worker 1450*c2e18aaaSAndroid Build Coastguard Workerdef build(): 1451*c2e18aaaSAndroid Build Coastguard Worker """Build module-info.json""" 1452*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 1453*c2e18aaaSAndroid Build Coastguard Worker 'Generating %s - this is required for initial runs or forced rebuilds.', 1454*c2e18aaaSAndroid Build Coastguard Worker _MODULE_INFO, 1455*c2e18aaaSAndroid Build Coastguard Worker ) 1456*c2e18aaaSAndroid Build Coastguard Worker build_start = time.time() 1457*c2e18aaaSAndroid Build Coastguard Worker if not atest_utils.build([get_module_info_target()]): 1458*c2e18aaaSAndroid Build Coastguard Worker sys.exit(ExitCode.BUILD_FAILURE) 1459*c2e18aaaSAndroid Build Coastguard Worker 1460*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 1461*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.ONLY_BUILD_MODULE_INFO, 1462*c2e18aaaSAndroid Build Coastguard Worker result=int(time.time() - build_start), 1463*c2e18aaaSAndroid Build Coastguard Worker ) 1464*c2e18aaaSAndroid Build Coastguard Worker 1465*c2e18aaaSAndroid Build Coastguard Worker 1466*c2e18aaaSAndroid Build Coastguard Workerdef _is_testable_module( 1467*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict[str, Dict], 1468*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict[str, Dict], 1469*c2e18aaaSAndroid Build Coastguard Worker info: Dict[str, Any], 1470*c2e18aaaSAndroid Build Coastguard Worker) -> bool: 1471*c2e18aaaSAndroid Build Coastguard Worker """Check if module is something we can test. 1472*c2e18aaaSAndroid Build Coastguard Worker 1473*c2e18aaaSAndroid Build Coastguard Worker A module is testable if: 1474*c2e18aaaSAndroid Build Coastguard Worker - it's a tradefed testable module, or 1475*c2e18aaaSAndroid Build Coastguard Worker - it's a Mobly module, or 1476*c2e18aaaSAndroid Build Coastguard Worker - it's a robolectric module (or shares path with one). 1477*c2e18aaaSAndroid Build Coastguard Worker 1478*c2e18aaaSAndroid Build Coastguard Worker Args: 1479*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict of name to module info. 1480*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict of path to module info. 1481*c2e18aaaSAndroid Build Coastguard Worker info: Dict of module info to check. 1482*c2e18aaaSAndroid Build Coastguard Worker 1483*c2e18aaaSAndroid Build Coastguard Worker Returns: 1484*c2e18aaaSAndroid Build Coastguard Worker True if we can test this module, False otherwise. 1485*c2e18aaaSAndroid Build Coastguard Worker """ 1486*c2e18aaaSAndroid Build Coastguard Worker if not info or not info.get(constants.MODULE_NAME): 1487*c2e18aaaSAndroid Build Coastguard Worker return False 1488*c2e18aaaSAndroid Build Coastguard Worker if ModuleInfo.is_tradefed_testable_module(info): 1489*c2e18aaaSAndroid Build Coastguard Worker return True 1490*c2e18aaaSAndroid Build Coastguard Worker if ModuleInfo.is_mobly_module(info): 1491*c2e18aaaSAndroid Build Coastguard Worker return True 1492*c2e18aaaSAndroid Build Coastguard Worker if _is_legacy_robolectric_test( 1493*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info, path_to_module_info, info 1494*c2e18aaaSAndroid Build Coastguard Worker ): 1495*c2e18aaaSAndroid Build Coastguard Worker return True 1496*c2e18aaaSAndroid Build Coastguard Worker return False 1497*c2e18aaaSAndroid Build Coastguard Worker 1498*c2e18aaaSAndroid Build Coastguard Worker 1499*c2e18aaaSAndroid Build Coastguard Workerdef _get_testable_modules( 1500*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict[str, Dict], 1501*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict[str, Dict], 1502*c2e18aaaSAndroid Build Coastguard Worker suite: str = None, 1503*c2e18aaaSAndroid Build Coastguard Worker index_path: Path = None, 1504*c2e18aaaSAndroid Build Coastguard Worker): 1505*c2e18aaaSAndroid Build Coastguard Worker """Return testable modules of the given suite name.""" 1506*c2e18aaaSAndroid Build Coastguard Worker suite_to_modules = _get_suite_to_modules( 1507*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info, path_to_module_info, index_path 1508*c2e18aaaSAndroid Build Coastguard Worker ) 1509*c2e18aaaSAndroid Build Coastguard Worker 1510*c2e18aaaSAndroid Build Coastguard Worker return _filter_modules_by_suite(suite_to_modules, suite) 1511*c2e18aaaSAndroid Build Coastguard Worker 1512*c2e18aaaSAndroid Build Coastguard Worker 1513*c2e18aaaSAndroid Build Coastguard Workerdef _get_suite_to_modules( 1514*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict[str, Dict], 1515*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict[str, Dict], 1516*c2e18aaaSAndroid Build Coastguard Worker index_path: Path = None, 1517*c2e18aaaSAndroid Build Coastguard Worker) -> Dict[str, Set[str]]: 1518*c2e18aaaSAndroid Build Coastguard Worker """Map suite and its modules. 1519*c2e18aaaSAndroid Build Coastguard Worker 1520*c2e18aaaSAndroid Build Coastguard Worker Args: 1521*c2e18aaaSAndroid Build Coastguard Worker name_to_module_info: Dict of name to module info. 1522*c2e18aaaSAndroid Build Coastguard Worker path_to_module_info: Dict of path to module info. 1523*c2e18aaaSAndroid Build Coastguard Worker index_path: Path of the stored content. 1524*c2e18aaaSAndroid Build Coastguard Worker 1525*c2e18aaaSAndroid Build Coastguard Worker Returns: 1526*c2e18aaaSAndroid Build Coastguard Worker Dict of suite and testable modules mapping. 1527*c2e18aaaSAndroid Build Coastguard Worker """ 1528*c2e18aaaSAndroid Build Coastguard Worker suite_to_modules = {} 1529*c2e18aaaSAndroid Build Coastguard Worker 1530*c2e18aaaSAndroid Build Coastguard Worker for _, info in name_to_module_info.items(): 1531*c2e18aaaSAndroid Build Coastguard Worker if _is_testable_module(name_to_module_info, path_to_module_info, info): 1532*c2e18aaaSAndroid Build Coastguard Worker testable_module = info.get(constants.MODULE_NAME) 1533*c2e18aaaSAndroid Build Coastguard Worker suites = ( 1534*c2e18aaaSAndroid Build Coastguard Worker info.get('compatibility_suites') 1535*c2e18aaaSAndroid Build Coastguard Worker if info.get('compatibility_suites') 1536*c2e18aaaSAndroid Build Coastguard Worker else ['null-suite'] 1537*c2e18aaaSAndroid Build Coastguard Worker ) 1538*c2e18aaaSAndroid Build Coastguard Worker 1539*c2e18aaaSAndroid Build Coastguard Worker for suite in suites: 1540*c2e18aaaSAndroid Build Coastguard Worker suite_to_modules.setdefault(suite, set()).add(testable_module) 1541*c2e18aaaSAndroid Build Coastguard Worker 1542*c2e18aaaSAndroid Build Coastguard Worker if index_path: 1543*c2e18aaaSAndroid Build Coastguard Worker _index_testable_modules(suite_to_modules, index_path) 1544*c2e18aaaSAndroid Build Coastguard Worker 1545*c2e18aaaSAndroid Build Coastguard Worker return suite_to_modules 1546*c2e18aaaSAndroid Build Coastguard Worker 1547*c2e18aaaSAndroid Build Coastguard Worker 1548*c2e18aaaSAndroid Build Coastguard Workerdef _filter_modules_by_suite( 1549*c2e18aaaSAndroid Build Coastguard Worker suite_to_modules: Dict[str, Set[str]], 1550*c2e18aaaSAndroid Build Coastguard Worker suite: str = None, 1551*c2e18aaaSAndroid Build Coastguard Worker) -> Set[str]: 1552*c2e18aaaSAndroid Build Coastguard Worker """Return modules of the given suite name.""" 1553*c2e18aaaSAndroid Build Coastguard Worker if suite: 1554*c2e18aaaSAndroid Build Coastguard Worker return suite_to_modules.get(suite) 1555*c2e18aaaSAndroid Build Coastguard Worker 1556*c2e18aaaSAndroid Build Coastguard Worker return {mod for mod_set in suite_to_modules.values() for mod in mod_set} 1557*c2e18aaaSAndroid Build Coastguard Worker 1558*c2e18aaaSAndroid Build Coastguard Worker 1559*c2e18aaaSAndroid Build Coastguard Workerdef _index_testable_modules(contents: Any, index_path: Path): 1560*c2e18aaaSAndroid Build Coastguard Worker """Dump testable modules. 1561*c2e18aaaSAndroid Build Coastguard Worker 1562*c2e18aaaSAndroid Build Coastguard Worker Args: 1563*c2e18aaaSAndroid Build Coastguard Worker content: An object that will be written to the index file. 1564*c2e18aaaSAndroid Build Coastguard Worker index_path: Path to the saved index file. 1565*c2e18aaaSAndroid Build Coastguard Worker """ 1566*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 1567*c2e18aaaSAndroid Build Coastguard Worker r'Indexing testable modules... ' 1568*c2e18aaaSAndroid Build Coastguard Worker r'(This is required whenever module-info.json ' 1569*c2e18aaaSAndroid Build Coastguard Worker r'was rebuilt.)' 1570*c2e18aaaSAndroid Build Coastguard Worker ) 1571*c2e18aaaSAndroid Build Coastguard Worker index_path.parent.mkdir(parents=True, exist_ok=True) 1572*c2e18aaaSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(delete=False) as cache: 1573*c2e18aaaSAndroid Build Coastguard Worker try: 1574*c2e18aaaSAndroid Build Coastguard Worker pickle.dump(contents, cache, protocol=2) 1575*c2e18aaaSAndroid Build Coastguard Worker shutil.move(cache.name, index_path) 1576*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s is created successfully.', index_path) 1577*c2e18aaaSAndroid Build Coastguard Worker except IOError: 1578*c2e18aaaSAndroid Build Coastguard Worker atest_utils.print_and_log_error('Failed in dumping %s', cache) 1579*c2e18aaaSAndroid Build Coastguard Worker os.remove(cache.name) 1580*c2e18aaaSAndroid Build Coastguard Worker 1581*c2e18aaaSAndroid Build Coastguard Worker 1582*c2e18aaaSAndroid Build Coastguard Workerclass SqliteDict(collections.abc.Mapping): 1583*c2e18aaaSAndroid Build Coastguard Worker """A class that loads a Sqlite DB as a dictionary-like object. 1584*c2e18aaaSAndroid Build Coastguard Worker 1585*c2e18aaaSAndroid Build Coastguard Worker Args: 1586*c2e18aaaSAndroid Build Coastguard Worker conn: A connection to the Sqlite database. 1587*c2e18aaaSAndroid Build Coastguard Worker table_name: A string the table name. 1588*c2e18aaaSAndroid Build Coastguard Worker """ 1589*c2e18aaaSAndroid Build Coastguard Worker 1590*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, conn: sqlite3.Connection, table_name: str): 1591*c2e18aaaSAndroid Build Coastguard Worker """Initialize the SqliteDict instance.""" 1592*c2e18aaaSAndroid Build Coastguard Worker self.conn = conn 1593*c2e18aaaSAndroid Build Coastguard Worker self.table = table_name 1594*c2e18aaaSAndroid Build Coastguard Worker 1595*c2e18aaaSAndroid Build Coastguard Worker def __iter__(self) -> str: 1596*c2e18aaaSAndroid Build Coastguard Worker """Iterate over the keys in the SqliteDict.""" 1597*c2e18aaaSAndroid Build Coastguard Worker for key in self._load_key_rows(): 1598*c2e18aaaSAndroid Build Coastguard Worker yield key[0] 1599*c2e18aaaSAndroid Build Coastguard Worker 1600*c2e18aaaSAndroid Build Coastguard Worker def _load_key_rows(self) -> Set[str]: 1601*c2e18aaaSAndroid Build Coastguard Worker """Load the key rows from the database table.""" 1602*c2e18aaaSAndroid Build Coastguard Worker results = self.conn.execute(f'SELECT key FROM {self.table}').fetchall() 1603*c2e18aaaSAndroid Build Coastguard Worker return set(results) 1604*c2e18aaaSAndroid Build Coastguard Worker 1605*c2e18aaaSAndroid Build Coastguard Worker def __len__(self) -> int: 1606*c2e18aaaSAndroid Build Coastguard Worker """Get the size of key-value pairs in the SqliteDict.""" 1607*c2e18aaaSAndroid Build Coastguard Worker return len(self._load_key_rows()) 1608*c2e18aaaSAndroid Build Coastguard Worker 1609*c2e18aaaSAndroid Build Coastguard Worker def __getitem__(self, key) -> Dict[str, Any]: 1610*c2e18aaaSAndroid Build Coastguard Worker """Get the value associated with the specified key.""" 1611*c2e18aaaSAndroid Build Coastguard Worker result = self.conn.execute( 1612*c2e18aaaSAndroid Build Coastguard Worker f'SELECT value FROM {self.table} WHERE key = ?', (key,) 1613*c2e18aaaSAndroid Build Coastguard Worker ).fetchone() 1614*c2e18aaaSAndroid Build Coastguard Worker if result: 1615*c2e18aaaSAndroid Build Coastguard Worker return json.loads(result[0]) 1616*c2e18aaaSAndroid Build Coastguard Worker raise KeyError(f'Bad key: {key}') 1617*c2e18aaaSAndroid Build Coastguard Worker 1618*c2e18aaaSAndroid Build Coastguard Worker def items(self) -> Tuple[str, Dict[str, Any]]: 1619*c2e18aaaSAndroid Build Coastguard Worker """Iterate over the key-value pairs in the SqliteDict.""" 1620*c2e18aaaSAndroid Build Coastguard Worker for key in self: 1621*c2e18aaaSAndroid Build Coastguard Worker value = self[key] 1622*c2e18aaaSAndroid Build Coastguard Worker yield key, value 1623