xref: /aosp_15_r20/tools/asuite/atest/module_info.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
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