1# Copyright 2018, The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" 16Module Finder class. 17""" 18 19# pylint: disable=line-too-long 20 21import logging 22import os 23 24import atest_error 25import atest_utils 26import constants 27 28from test_finders import test_info 29from test_finders import test_finder_base 30from test_finders import test_finder_utils 31from test_runners import atest_tf_test_runner 32from test_runners import robolectric_test_runner 33from test_runners import vts_tf_test_runner 34 35_MODULES_IN = 'MODULES-IN-%s' 36_ANDROID_MK = 'Android.mk' 37 38# These are suites in LOCAL_COMPATIBILITY_SUITE that aren't really suites so 39# we can ignore them. 40_SUITES_TO_IGNORE = frozenset({'general-tests', 'device-tests', 'tests'}) 41 42class ModuleFinder(test_finder_base.TestFinderBase): 43 """Module finder class.""" 44 NAME = 'MODULE' 45 _TEST_RUNNER = atest_tf_test_runner.AtestTradefedTestRunner.NAME 46 _ROBOLECTRIC_RUNNER = robolectric_test_runner.RobolectricTestRunner.NAME 47 _VTS_TEST_RUNNER = vts_tf_test_runner.VtsTradefedTestRunner.NAME 48 49 def __init__(self, module_info=None): 50 super(ModuleFinder, self).__init__() 51 self.root_dir = os.environ.get(constants.ANDROID_BUILD_TOP) 52 self.module_info = module_info 53 54 def _determine_testable_module(self, path): 55 """Determine which module the user is trying to test. 56 57 Returns the module to test. If there are multiple possibilities, will 58 ask the user. Otherwise will return the only module found. 59 60 Args: 61 path: String path of module to look for. 62 63 Returns: 64 A list of the module names. 65 """ 66 testable_modules = [] 67 for mod in self.module_info.get_module_names(path): 68 mod_info = self.module_info.get_module_info(mod) 69 # Robolectric tests always exist in pairs of 2, one module to build 70 # the test and another to run it. For now, we are assuming they are 71 # isolated in their own folders and will return if we find one. 72 if self.module_info.is_robolectric_test(mod): 73 # return a list with one module name if it is robolectric. 74 return [mod] 75 if self.module_info.is_testable_module(mod_info): 76 testable_modules.append(mod_info.get(constants.MODULE_NAME)) 77 return test_finder_utils.extract_test_from_tests(testable_modules) 78 79 def _is_vts_module(self, module_name): 80 """Returns True if the module is a vts10 module, else False.""" 81 mod_info = self.module_info.get_module_info(module_name) 82 suites = [] 83 if mod_info: 84 suites = mod_info.get('compatibility_suites', []) 85 # Pull out all *ts (cts, tvts, etc) suites. 86 suites = [suite for suite in suites if suite not in _SUITES_TO_IGNORE] 87 return len(suites) == 1 and 'vts10' in suites 88 89 def _update_to_vts_test_info(self, test): 90 """Fill in the fields with vts10 specific info. 91 92 We need to update the runner to use the vts10 runner and also find the 93 test specific dependencies. 94 95 Args: 96 test: TestInfo to update with vts10 specific details. 97 98 Return: 99 TestInfo that is ready for the vts10 test runner. 100 """ 101 test.test_runner = self._VTS_TEST_RUNNER 102 config_file = os.path.join(self.root_dir, 103 test.data[constants.TI_REL_CONFIG]) 104 # Need to get out dir (special logic is to account for custom out dirs). 105 # The out dir is used to construct the build targets for the test deps. 106 out_dir = os.environ.get(constants.ANDROID_HOST_OUT) 107 custom_out_dir = os.environ.get(constants.ANDROID_OUT_DIR) 108 # If we're not an absolute custom out dir, get no-absolute out dir path. 109 if custom_out_dir is None or not os.path.isabs(custom_out_dir): 110 out_dir = os.path.relpath(out_dir, self.root_dir) 111 vts_out_dir = os.path.join(out_dir, 'vts10', 'android-vts10', 'testcases') 112 # Parse dependency of default staging plans. 113 xml_paths = test_finder_utils.search_integration_dirs( 114 constants.VTS_STAGING_PLAN, 115 self.module_info.get_paths(constants.VTS_TF_MODULE)) 116 vts_xmls = set() 117 vts_xmls.add(config_file) 118 for xml_path in xml_paths: 119 vts_xmls |= test_finder_utils.get_plans_from_vts_xml(xml_path) 120 for config_file in vts_xmls: 121 # Add in vts10 test build targets. 122 test.build_targets |= test_finder_utils.get_targets_from_vts_xml( 123 config_file, vts_out_dir, self.module_info) 124 test.build_targets.add('vts-test-core') 125 test.build_targets.add(test.test_name) 126 return test 127 128 def _update_to_robolectric_test_info(self, test): 129 """Update the fields for a robolectric test. 130 131 Args: 132 test: TestInfo to be updated with robolectric fields. 133 134 Returns: 135 TestInfo with robolectric fields. 136 """ 137 test.test_runner = self._ROBOLECTRIC_RUNNER 138 test.test_name = self.module_info.get_robolectric_test_name(test.test_name) 139 return test 140 141 def _process_test_info(self, test): 142 """Process the test info and return some fields updated/changed. 143 144 We need to check if the test found is a special module (like vts10) and 145 update the test_info fields (like test_runner) appropriately. 146 147 Args: 148 test: TestInfo that has been filled out by a find method. 149 150 Return: 151 TestInfo that has been modified as needed and return None if 152 this module can't be found in the module_info. 153 """ 154 module_name = test.test_name 155 mod_info = self.module_info.get_module_info(module_name) 156 if not mod_info: 157 return None 158 test.module_class = mod_info['class'] 159 test.install_locations = test_finder_utils.get_install_locations( 160 mod_info['installed']) 161 # Check if this is only a vts10 module. 162 if self._is_vts_module(test.test_name): 163 return self._update_to_vts_test_info(test) 164 if self.module_info.is_robolectric_test(test.test_name): 165 return self._update_to_robolectric_test_info(test) 166 rel_config = test.data[constants.TI_REL_CONFIG] 167 test.build_targets = self._get_build_targets(module_name, rel_config) 168 return test 169 170 def _get_build_targets(self, module_name, rel_config): 171 """Get the test deps. 172 173 Args: 174 module_name: name of the test. 175 rel_config: XML for the given test. 176 177 Returns: 178 Set of build targets. 179 """ 180 targets = set() 181 if not self.module_info.is_auto_gen_test_config(module_name): 182 config_file = os.path.join(self.root_dir, rel_config) 183 targets = test_finder_utils.get_targets_from_xml(config_file, 184 self.module_info) 185 if constants.VTS_CORE_SUITE in self.module_info.get_module_info( 186 module_name).get(constants.MODULE_COMPATIBILITY_SUITES, []): 187 targets.add(constants.VTS_CORE_TF_MODULE) 188 for module_path in self.module_info.get_paths(module_name): 189 mod_dir = module_path.replace('/', '-') 190 targets.add(_MODULES_IN % mod_dir) 191 # (b/156457698) Force add vts_kernel_tests as build target if our test 192 # belong to REQUIRED_KERNEL_TEST_MODULES due to required_module option 193 # not working for sh_test in soong. 194 if module_name in constants.REQUIRED_KERNEL_TEST_MODULES: 195 targets.add('vts_kernel_tests') 196 return targets 197 198 def _get_module_test_config(self, module_name, rel_config=None): 199 """Get the value of test_config in module_info. 200 201 Get the value of 'test_config' in module_info if its 202 auto_test_config is not true. 203 In this case, the test_config is specified by user. 204 If not, return rel_config. 205 206 Args: 207 module_name: A string of the test's module name. 208 rel_config: XML for the given test. 209 210 Returns: 211 A string of test_config path if found, else return rel_config. 212 """ 213 mod_info = self.module_info.get_module_info(module_name) 214 if mod_info: 215 test_config = '' 216 test_config_list = mod_info.get(constants.MODULE_TEST_CONFIG, []) 217 if test_config_list: 218 test_config = test_config_list[0] 219 if not self.module_info.is_auto_gen_test_config(module_name) and test_config != '': 220 return test_config 221 return rel_config 222 223 def _get_test_info_filter(self, path, methods, **kwargs): 224 """Get test info filter. 225 226 Args: 227 path: A string of the test's path. 228 methods: A set of method name strings. 229 rel_module_dir: Optional. A string of the module dir no-absolute to 230 root. 231 class_name: Optional. A string of the class name. 232 is_native_test: Optional. A boolean variable of whether to search 233 for a native test or not. 234 235 Returns: 236 A set of test info filter. 237 """ 238 _, file_name = test_finder_utils.get_dir_path_and_filename(path) 239 ti_filter = frozenset() 240 if kwargs.get('is_native_test', None): 241 ti_filter = frozenset([test_info.TestFilter( 242 test_finder_utils.get_cc_filter( 243 kwargs.get('class_name', '*'), methods), frozenset())]) 244 # Path to java file. 245 elif file_name and constants.JAVA_EXT_RE.match(file_name): 246 full_class_name = test_finder_utils.get_fully_qualified_class_name( 247 path) 248 ti_filter = frozenset( 249 [test_info.TestFilter(full_class_name, methods)]) 250 # Path to cc file. 251 elif file_name and constants.CC_EXT_RE.match(file_name): 252 if not test_finder_utils.has_cc_class(path): 253 raise atest_error.MissingCCTestCaseError( 254 "Can't find CC class in %s" % path) 255 if methods: 256 ti_filter = frozenset( 257 [test_info.TestFilter(test_finder_utils.get_cc_filter( 258 kwargs.get('class_name', '*'), methods), frozenset())]) 259 # Path to non-module dir, treat as package. 260 elif (not file_name 261 and kwargs.get('rel_module_dir', None) != 262 os.path.relpath(path, self.root_dir)): 263 dir_items = [os.path.join(path, f) for f in os.listdir(path)] 264 for dir_item in dir_items: 265 if constants.JAVA_EXT_RE.match(dir_item): 266 package_name = test_finder_utils.get_package_name(dir_item) 267 if package_name: 268 # methods should be empty frozenset for package. 269 if methods: 270 raise atest_error.MethodWithoutClassError( 271 '%s: Method filtering requires class' 272 % str(methods)) 273 ti_filter = frozenset( 274 [test_info.TestFilter(package_name, methods)]) 275 break 276 return ti_filter 277 278 def _get_rel_config(self, test_path): 279 """Get config file's no-absolute path. 280 281 Args: 282 test_path: A string of the test absolute path. 283 284 Returns: 285 A string of config's no-absolute path, else None. 286 """ 287 test_dir = os.path.dirname(test_path) 288 rel_module_dir = test_finder_utils.find_parent_module_dir( 289 self.root_dir, test_dir, self.module_info) 290 if rel_module_dir: 291 return os.path.join(rel_module_dir, constants.MODULE_CONFIG) 292 return None 293 294 def _get_test_infos(self, test_path, rel_config, module_name, test_filter): 295 """Get test_info for test_path. 296 297 Args: 298 test_path: A string of the test path. 299 rel_config: A string of rel path of config. 300 module_name: A string of the module name to use. 301 test_filter: A test info filter. 302 303 Returns: 304 A list of TestInfo namedtuple if found, else None. 305 """ 306 if not rel_config: 307 rel_config = self._get_rel_config(test_path) 308 if not rel_config: 309 return None 310 if module_name: 311 module_names = [module_name] 312 else: 313 module_names = self._determine_testable_module( 314 os.path.dirname(rel_config)) 315 test_infos = [] 316 if module_names: 317 for mname in module_names: 318 # The real test config might be record in module-info. 319 rel_config = self._get_module_test_config(mname, 320 rel_config=rel_config) 321 mod_info = self.module_info.get_module_info(mname) 322 tinfo = self._process_test_info(test_info.TestInfo( 323 test_name=mname, 324 test_runner=self._TEST_RUNNER, 325 build_targets=set(), 326 data={constants.TI_FILTER: test_filter, 327 constants.TI_REL_CONFIG: rel_config}, 328 compatibility_suites=mod_info.get( 329 constants.MODULE_COMPATIBILITY_SUITES, []))) 330 if tinfo: 331 test_infos.append(tinfo) 332 return test_infos 333 334 def find_test_by_module_name(self, module_name): 335 """Find test for the given module name. 336 337 Args: 338 module_name: A string of the test's module name. 339 340 Returns: 341 A list that includes only 1 populated TestInfo namedtuple 342 if found, otherwise None. 343 """ 344 mod_info = self.module_info.get_module_info(module_name) 345 if self.module_info.is_testable_module(mod_info): 346 # path is a list with only 1 element. 347 rel_config = os.path.join(mod_info['path'][0], 348 constants.MODULE_CONFIG) 349 rel_config = self._get_module_test_config(module_name, 350 rel_config=rel_config) 351 tinfo = self._process_test_info(test_info.TestInfo( 352 test_name=module_name, 353 test_runner=self._TEST_RUNNER, 354 build_targets=set(), 355 data={constants.TI_REL_CONFIG: rel_config, 356 constants.TI_FILTER: frozenset()}, 357 compatibility_suites=mod_info.get( 358 constants.MODULE_COMPATIBILITY_SUITES, []))) 359 if tinfo: 360 return [tinfo] 361 return None 362 363 def find_test_by_kernel_class_name(self, module_name, class_name): 364 """Find kernel test for the given class name. 365 366 Args: 367 module_name: A string of the module name to use. 368 class_name: A string of the test's class name. 369 370 Returns: 371 A list of populated TestInfo namedtuple if test found, else None. 372 """ 373 class_name, methods = test_finder_utils.split_methods(class_name) 374 test_config = self._get_module_test_config(module_name) 375 if not test_config: 376 return None 377 test_config_path = os.path.join(self.root_dir, test_config) 378 mod_info = self.module_info.get_module_info(module_name) 379 ti_filter = frozenset( 380 [test_info.TestFilter(class_name, methods)]) 381 if test_finder_utils.is_test_from_kernel_xml(test_config_path, class_name): 382 tinfo = self._process_test_info(test_info.TestInfo( 383 test_name=module_name, 384 test_runner=self._TEST_RUNNER, 385 build_targets=set(), 386 data={constants.TI_REL_CONFIG: test_config, 387 constants.TI_FILTER: ti_filter}, 388 compatibility_suites=mod_info.get( 389 constants.MODULE_COMPATIBILITY_SUITES, []))) 390 if tinfo: 391 return [tinfo] 392 return None 393 394 def find_test_by_class_name(self, class_name, module_name=None, 395 rel_config=None, is_native_test=False): 396 """Find test files given a class name. 397 398 If module_name and rel_config not given it will calculate it determine 399 it by looking up the tree from the class file. 400 401 Args: 402 class_name: A string of the test's class name. 403 module_name: Optional. A string of the module name to use. 404 rel_config: Optional. A string of module dir no-absolute to repo root. 405 is_native_test: A boolean variable of whether to search for a 406 native test or not. 407 408 Returns: 409 A list of populated TestInfo namedtuple if test found, else None. 410 """ 411 class_name, methods = test_finder_utils.split_methods(class_name) 412 search_class_name = class_name 413 # For parameterized gtest, test class will be automerged to 414 # $(class_prefix)/$(base_class) name. Using $(base_class) for searching 415 # matched TEST_P to make sure test class is matched. 416 if '/' in search_class_name: 417 search_class_name = str(search_class_name).split('/')[-1] 418 if rel_config: 419 search_dir = os.path.join(self.root_dir, 420 os.path.dirname(rel_config)) 421 else: 422 search_dir = self.root_dir 423 test_paths = test_finder_utils.find_class_file(search_dir, search_class_name, 424 is_native_test, methods) 425 if not test_paths and rel_config: 426 logging.info('Did not find class (%s) under module path (%s), ' 427 'researching from repo root.', class_name, rel_config) 428 test_paths = test_finder_utils.find_class_file(self.root_dir, 429 search_class_name, 430 is_native_test, 431 methods) 432 if not test_paths: 433 return None 434 tinfos = [] 435 for test_path in test_paths: 436 test_filter = self._get_test_info_filter( 437 test_path, methods, class_name=class_name, 438 is_native_test=is_native_test) 439 tinfo = self._get_test_infos(test_path, rel_config, 440 module_name, test_filter) 441 if tinfo: 442 tinfos.extend(tinfo) 443 return tinfos 444 445 def find_test_by_module_and_class(self, module_class): 446 """Find the test info given a MODULE:CLASS string. 447 448 Args: 449 module_class: A string of form MODULE:CLASS or MODULE:CLASS#METHOD. 450 451 Returns: 452 A list of populated TestInfo namedtuple if found, else None. 453 """ 454 if ':' not in module_class: 455 return None 456 module_name, class_name = module_class.split(':') 457 # module_infos is a list with at most 1 element. 458 module_infos = self.find_test_by_module_name(module_name) 459 module_info = module_infos[0] if module_infos else None 460 if not module_info: 461 return None 462 find_result = None 463 # If the target module is NATIVE_TEST, search CC classes only. 464 if not self.module_info.is_native_test(module_name): 465 # Find by java class. 466 find_result = self.find_test_by_class_name( 467 class_name, module_info.test_name, 468 module_info.data.get(constants.TI_REL_CONFIG)) 469 # kernel target test is also define as NATIVE_TEST in build system. 470 # TODO (b/157210083) Update find_test_by_kernel_class_name method to 471 # support gen_rule use case. 472 if not find_result: 473 find_result = self.find_test_by_kernel_class_name( 474 module_name, class_name) 475 # Find by cc class. 476 if not find_result: 477 find_result = self.find_test_by_cc_class_name( 478 class_name, module_info.test_name, 479 module_info.data.get(constants.TI_REL_CONFIG)) 480 return find_result 481 482 def find_test_by_package_name(self, package, module_name=None, 483 rel_config=None): 484 """Find the test info given a PACKAGE string. 485 486 Args: 487 package: A string of the package name. 488 module_name: Optional. A string of the module name. 489 ref_config: Optional. A string of rel path of config. 490 491 Returns: 492 A list of populated TestInfo namedtuple if found, else None. 493 """ 494 _, methods = test_finder_utils.split_methods(package) 495 if methods: 496 raise atest_error.MethodWithoutClassError('%s: Method filtering ' 497 'requires class' % ( 498 methods)) 499 # Confirm that packages exists and get user input for multiples. 500 if rel_config: 501 search_dir = os.path.join(self.root_dir, 502 os.path.dirname(rel_config)) 503 else: 504 search_dir = self.root_dir 505 package_paths = test_finder_utils.run_find_cmd( 506 test_finder_utils.FIND_REFERENCE_TYPE.PACKAGE, search_dir, package) 507 # Package path will be the full path to the dir represented by package. 508 if not package_paths: 509 return None 510 test_filter = frozenset([test_info.TestFilter(package, frozenset())]) 511 test_infos = [] 512 for package_path in package_paths: 513 tinfo = self._get_test_infos(package_path, rel_config, 514 module_name, test_filter) 515 if tinfo: 516 test_infos.extend(tinfo) 517 return test_infos 518 519 def find_test_by_module_and_package(self, module_package): 520 """Find the test info given a MODULE:PACKAGE string. 521 522 Args: 523 module_package: A string of form MODULE:PACKAGE 524 525 Returns: 526 A list of populated TestInfo namedtuple if found, else None. 527 """ 528 module_name, package = module_package.split(':') 529 # module_infos is a list with at most 1 element. 530 module_infos = self.find_test_by_module_name(module_name) 531 module_info = module_infos[0] if module_infos else None 532 if not module_info: 533 return None 534 return self.find_test_by_package_name( 535 package, module_info.test_name, 536 module_info.data.get(constants.TI_REL_CONFIG)) 537 538 def find_test_by_path(self, path): 539 """Find the first test info matching the given path. 540 541 Strategy: 542 path_to_java_file --> Resolve to CLASS 543 path_to_cc_file --> Resolve to CC CLASS 544 path_to_module_file -> Resolve to MODULE 545 path_to_module_dir -> Resolve to MODULE 546 path_to_dir_with_class_files--> Resolve to PACKAGE 547 path_to_any_other_dir --> Resolve as MODULE 548 549 Args: 550 path: A string of the test's path. 551 552 Returns: 553 A list of populated TestInfo namedtuple if test found, else None 554 """ 555 logging.debug('Finding test by path: %s', path) 556 path, methods = test_finder_utils.split_methods(path) 557 # TODO: See if this can be generalized and shared with methods above 558 # create absolute path from cwd and remove symbolic links 559 path = os.path.realpath(path) 560 if not os.path.exists(path): 561 return None 562 if (methods and 563 not test_finder_utils.has_method_in_file(path, methods)): 564 return None 565 dir_path, _ = test_finder_utils.get_dir_path_and_filename(path) 566 # Module/Class 567 rel_module_dir = test_finder_utils.find_parent_module_dir( 568 self.root_dir, dir_path, self.module_info) 569 if not rel_module_dir: 570 return None 571 rel_config = os.path.join(rel_module_dir, constants.MODULE_CONFIG) 572 test_filter = self._get_test_info_filter(path, methods, 573 rel_module_dir=rel_module_dir) 574 return self._get_test_infos(path, rel_config, None, test_filter) 575 576 def find_test_by_cc_class_name(self, class_name, module_name=None, 577 rel_config=None): 578 """Find test files given a cc class name. 579 580 If module_name and rel_config not given, test will be determined 581 by looking up the tree for files which has input class. 582 583 Args: 584 class_name: A string of the test's class name. 585 module_name: Optional. A string of the module name to use. 586 rel_config: Optional. A string of module dir no-absolute to repo root. 587 588 Returns: 589 A list of populated TestInfo namedtuple if test found, else None. 590 """ 591 # Check if class_name is prepended with file name. If so, trim the 592 # prefix and keep only the class_name. 593 if '.' in class_name: 594 # Assume the class name has a format of file_name.class_name 595 class_name = class_name[class_name.rindex('.')+1:] 596 logging.info('Search with updated class name: %s', class_name) 597 return self.find_test_by_class_name( 598 class_name, module_name, rel_config, is_native_test=True) 599 600 def get_testable_modules_with_ld(self, user_input, ld_range=0): 601 """Calculate the edit distances of the input and testable modules. 602 603 The user input will be calculated across all testable modules and 604 results in integers generated by Levenshtein Distance algorithm. 605 To increase the speed of the calculation, a bound can be applied to 606 this method to prevent from calculating every testable modules. 607 608 Guessing from typos, e.g. atest atest_unitests, implies a tangible range 609 of length that Atest only needs to search within it, and the default of 610 the bound is 2. 611 612 Guessing from keywords however, e.g. atest --search Camera, means that 613 the uncertainty of the module name is way higher, and Atest should walk 614 through all testable modules and return the highest possibilities. 615 616 Args: 617 user_input: A string of the user input. 618 ld_range: An integer that range the searching scope. If the length 619 of user_input is 10, then Atest will calculate modules of 620 which length is between 8 and 12. 0 is equivalent to 621 unlimited. 622 623 Returns: 624 A List of LDs and possible module names. If the user_input is "fax", 625 the output will be like: 626 [[2, "fog"], [2, "Fix"], [4, "duck"], [7, "Duckies"]] 627 628 Which means the most lilely names of "fax" are fog and Fix(LD=2), 629 while Dickies is the most unlikely one(LD=7). 630 """ 631 atest_utils.colorful_print('\nSearching for similar module names using ' 632 'fuzzy search...', constants.CYAN) 633 testable_modules = sorted(self.module_info.get_testable_modules(), 634 key=len) 635 lower_bound = len(user_input) - ld_range 636 upper_bound = len(user_input) + ld_range 637 testable_modules_with_ld = [] 638 for module_name in testable_modules: 639 # Dispose those too short or too lengthy. 640 if ld_range != 0: 641 if len(module_name) < lower_bound: 642 continue 643 if len(module_name) > upper_bound: 644 break 645 testable_modules_with_ld.append( 646 [test_finder_utils.get_levenshtein_distance( 647 user_input, module_name), module_name]) 648 return testable_modules_with_ld 649 650 def get_fuzzy_searching_results(self, user_input): 651 """Give results which have no more than allowance of edit distances. 652 653 Args: 654 user_input: the target module name for fuzzy searching. 655 656 Return: 657 A list of guessed modules. 658 """ 659 modules_with_ld = self.get_testable_modules_with_ld( 660 user_input, ld_range=constants.LD_RANGE) 661 guessed_modules = [] 662 for _distance, _module in modules_with_ld: 663 if _distance <= abs(constants.LD_RANGE): 664 guessed_modules.append(_module) 665 return guessed_modules 666