1""" 2A simple testing framework for lldb using python's unit testing framework. 3 4Tests for lldb are written as python scripts which take advantage of the script 5bridging provided by LLDB.framework to interact with lldb core. 6 7A specific naming pattern is followed by the .py script to be recognized as 8a module which implements a test scenario, namely, Test*.py. 9 10To specify the directories where "Test*.py" python test scripts are located, 11you need to pass in a list of directory names. By default, the current 12working directory is searched if nothing is specified on the command line. 13 14Type: 15 16./dotest.py -h 17 18for available options. 19""" 20 21from __future__ import absolute_import 22from __future__ import print_function 23 24# System modules 25import atexit 26import datetime 27import errno 28import logging 29import os 30import platform 31import re 32import shutil 33import signal 34import subprocess 35import sys 36import tempfile 37 38# Third-party modules 39import unittest2 40 41# LLDB Modules 42import lldbsuite 43from . import configuration 44from . import dotest_args 45from . import lldbtest_config 46from . import test_categories 47from . import test_result 48from ..support import seven 49 50 51def is_exe(fpath): 52 """Returns true if fpath is an executable.""" 53 if fpath == None: 54 return False 55 if sys.platform == "win32": 56 if not fpath.endswith(".exe"): 57 fpath += ".exe" 58 return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 59 60 61def which(program): 62 """Returns the full path to a program; None otherwise.""" 63 fpath, _ = os.path.split(program) 64 if fpath: 65 if is_exe(program): 66 return program 67 else: 68 for path in os.environ["PATH"].split(os.pathsep): 69 exe_file = os.path.join(path, program) 70 if is_exe(exe_file): 71 return exe_file 72 return None 73 74 75def usage(parser): 76 parser.print_help() 77 if configuration.verbose > 0: 78 print( 79 """ 80Examples: 81 82This is an example of using the -f option to pinpoint to a specific test class 83and test method to be run: 84 85$ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command 86---------------------------------------------------------------------- 87Collected 1 test 88 89test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase) 90Test 'frame variable this' when stopped on a class constructor. ... ok 91 92---------------------------------------------------------------------- 93Ran 1 test in 1.396s 94 95OK 96 97And this is an example of using the -p option to run a single file (the filename 98matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'): 99 100$ ./dotest.py -v -p ObjC 101---------------------------------------------------------------------- 102Collected 4 tests 103 104test_break_with_dsym (TestObjCMethods.FoundationTestCase) 105Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok 106test_break_with_dwarf (TestObjCMethods.FoundationTestCase) 107Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok 108test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase) 109Lookup objective-c data types and evaluate expressions. ... ok 110test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase) 111Lookup objective-c data types and evaluate expressions. ... ok 112 113---------------------------------------------------------------------- 114Ran 4 tests in 16.661s 115 116OK 117 118Running of this script also sets up the LLDB_TEST environment variable so that 119individual test cases can locate their supporting files correctly. The script 120tries to set up Python's search paths for modules by looking at the build tree 121relative to this script. See also the '-i' option in the following example. 122 123Finally, this is an example of using the lldb.py module distributed/installed by 124Xcode4 to run against the tests under the 'forward' directory, and with the '-w' 125option to add some delay between two tests. It uses ARCH=x86_64 to specify that 126as the architecture and CC=clang to specify the compiler used for the test run: 127 128$ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward 129 130Session logs for test failures/errors will go into directory '2010-11-11-13_56_16' 131---------------------------------------------------------------------- 132Collected 2 tests 133 134test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase) 135Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok 136test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase) 137Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok 138 139---------------------------------------------------------------------- 140Ran 2 tests in 5.659s 141 142OK 143 144The 'Session ...' verbiage is recently introduced (see also the '-s' option) to 145notify the directory containing the session logs for test failures or errors. 146In case there is any test failure/error, a similar message is appended at the 147end of the stderr output for your convenience. 148 149ENABLING LOGS FROM TESTS 150 151Option 1: 152 153Writing logs into different files per test case:: 154 155$ ./dotest.py --channel "lldb all" 156 157$ ./dotest.py --channel "lldb all" --channel "gdb-remote packets" 158 159These log files are written to: 160 161<session-dir>/<test-id>-host.log (logs from lldb host process) 162<session-dir>/<test-id>-server.log (logs from debugserver/lldb-server) 163<session-dir>/<test-id>-<test-result>.log (console logs) 164 165By default, logs from successful runs are deleted. Use the --log-success flag 166to create reference logs for debugging. 167 168$ ./dotest.py --log-success 169 170""" 171 ) 172 sys.exit(0) 173 174 175def parseExclusion(exclusion_file): 176 """Parse an exclusion file, of the following format, where 177 'skip files', 'skip methods', 'xfail files', and 'xfail methods' 178 are the possible list heading values: 179 180 skip files 181 <file name> 182 <file name> 183 184 xfail methods 185 <method name> 186 """ 187 excl_type = None 188 189 with open(exclusion_file) as f: 190 for line in f: 191 line = line.strip() 192 if not excl_type: 193 excl_type = line 194 continue 195 196 if not line: 197 excl_type = None 198 elif excl_type == "skip": 199 if not configuration.skip_tests: 200 configuration.skip_tests = [] 201 configuration.skip_tests.append(line) 202 elif excl_type == "xfail": 203 if not configuration.xfail_tests: 204 configuration.xfail_tests = [] 205 configuration.xfail_tests.append(line) 206 207 208def parseOptionsAndInitTestdirs(): 209 """Initialize the list of directories containing our unittest scripts. 210 211 '-h/--help as the first option prints out usage info and exit the program. 212 """ 213 214 do_help = False 215 216 platform_system = platform.system() 217 platform_machine = platform.machine() 218 219 try: 220 parser = dotest_args.create_parser() 221 args = parser.parse_args() 222 except: 223 raise 224 225 if args.unset_env_varnames: 226 for env_var in args.unset_env_varnames: 227 if env_var in os.environ: 228 # From Python Doc: When unsetenv() is supported, deletion of items in os.environ 229 # is automatically translated into a corresponding call to 230 # unsetenv(). 231 del os.environ[env_var] 232 # os.unsetenv(env_var) 233 234 if args.set_env_vars: 235 for env_var in args.set_env_vars: 236 parts = env_var.split("=", 1) 237 if len(parts) == 1: 238 os.environ[parts[0]] = "" 239 else: 240 os.environ[parts[0]] = parts[1] 241 242 if args.set_inferior_env_vars: 243 lldbtest_config.inferior_env = " ".join(args.set_inferior_env_vars) 244 245 if args.h: 246 do_help = True 247 248 if args.compiler: 249 configuration.compiler = os.path.abspath(args.compiler) 250 if not is_exe(configuration.compiler): 251 configuration.compiler = which(args.compiler) 252 if not is_exe(configuration.compiler): 253 logging.error( 254 "%s is not a valid compiler executable; aborting...", args.compiler 255 ) 256 sys.exit(-1) 257 else: 258 # Use a compiler appropriate appropriate for the Apple SDK if one was 259 # specified 260 if platform_system == "Darwin" and args.apple_sdk: 261 configuration.compiler = seven.get_command_output( 262 'xcrun -sdk "%s" -find clang 2> /dev/null' % (args.apple_sdk) 263 ) 264 else: 265 # 'clang' on ubuntu 14.04 is 3.4 so we try clang-3.5 first 266 candidateCompilers = ["clang-3.5", "clang", "gcc"] 267 for candidate in candidateCompilers: 268 if which(candidate): 269 configuration.compiler = candidate 270 break 271 272 if args.dsymutil: 273 configuration.dsymutil = args.dsymutil 274 elif platform_system == "Darwin": 275 configuration.dsymutil = seven.get_command_output( 276 "xcrun -find -toolchain default dsymutil" 277 ) 278 if args.llvm_tools_dir: 279 configuration.filecheck = shutil.which("FileCheck", path=args.llvm_tools_dir) 280 configuration.yaml2obj = shutil.which("yaml2obj", path=args.llvm_tools_dir) 281 282 if not configuration.get_filecheck_path(): 283 logging.warning("No valid FileCheck executable; some tests may fail...") 284 logging.warning("(Double-check the --llvm-tools-dir argument to dotest.py)") 285 286 if args.libcxx_include_dir or args.libcxx_library_dir: 287 if args.lldb_platform_name: 288 logging.warning( 289 "Custom libc++ is not supported for remote runs: ignoring --libcxx arguments" 290 ) 291 elif not (args.libcxx_include_dir and args.libcxx_library_dir): 292 logging.error( 293 "Custom libc++ requires both --libcxx-include-dir and --libcxx-library-dir" 294 ) 295 sys.exit(-1) 296 configuration.libcxx_include_dir = args.libcxx_include_dir 297 configuration.libcxx_include_target_dir = args.libcxx_include_target_dir 298 configuration.libcxx_library_dir = args.libcxx_library_dir 299 300 if args.channels: 301 lldbtest_config.channels = args.channels 302 303 if args.log_success: 304 lldbtest_config.log_success = args.log_success 305 306 if args.out_of_tree_debugserver: 307 lldbtest_config.out_of_tree_debugserver = args.out_of_tree_debugserver 308 309 # Set SDKROOT if we are using an Apple SDK 310 if platform_system == "Darwin" and args.apple_sdk: 311 configuration.sdkroot = seven.get_command_output( 312 'xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % (args.apple_sdk) 313 ) 314 if not configuration.sdkroot: 315 logging.error("No SDK found with the name %s; aborting...", args.apple_sdk) 316 sys.exit(-1) 317 318 if args.arch: 319 configuration.arch = args.arch 320 else: 321 configuration.arch = platform_machine 322 323 if args.categories_list: 324 configuration.categories_list = set( 325 test_categories.validate(args.categories_list, False) 326 ) 327 configuration.use_categories = True 328 else: 329 configuration.categories_list = [] 330 331 if args.skip_categories: 332 configuration.skip_categories += test_categories.validate( 333 args.skip_categories, False 334 ) 335 336 if args.xfail_categories: 337 configuration.xfail_categories += test_categories.validate( 338 args.xfail_categories, False 339 ) 340 341 if args.E: 342 os.environ["CFLAGS_EXTRAS"] = args.E 343 344 if args.dwarf_version: 345 configuration.dwarf_version = args.dwarf_version 346 # We cannot modify CFLAGS_EXTRAS because they're used in test cases 347 # that explicitly require no debug info. 348 os.environ["CFLAGS"] = "-gdwarf-{}".format(configuration.dwarf_version) 349 350 if args.settings: 351 for setting in args.settings: 352 if not len(setting) == 1 or not setting[0].count("="): 353 logging.error( 354 '"%s" is not a setting in the form "key=value"', setting[0] 355 ) 356 sys.exit(-1) 357 setting_list = setting[0].split("=", 1) 358 configuration.settings.append((setting_list[0], setting_list[1])) 359 360 if args.d: 361 sys.stdout.write( 362 "Suspending the process %d to wait for debugger to attach...\n" 363 % os.getpid() 364 ) 365 sys.stdout.flush() 366 os.kill(os.getpid(), signal.SIGSTOP) 367 368 if args.f: 369 if any([x.startswith("-") for x in args.f]): 370 usage(parser) 371 configuration.filters.extend(args.f) 372 373 if args.framework: 374 configuration.lldb_framework_path = args.framework 375 376 if args.executable: 377 # lldb executable is passed explicitly 378 lldbtest_config.lldbExec = os.path.abspath(args.executable) 379 if not is_exe(lldbtest_config.lldbExec): 380 lldbtest_config.lldbExec = which(args.executable) 381 if not is_exe(lldbtest_config.lldbExec): 382 logging.error( 383 "%s is not a valid executable to test; aborting...", args.executable 384 ) 385 sys.exit(-1) 386 387 if args.excluded: 388 for excl_file in args.excluded: 389 parseExclusion(excl_file) 390 391 if args.p: 392 if args.p.startswith("-"): 393 usage(parser) 394 configuration.regexp = args.p 395 396 if args.t: 397 os.environ["LLDB_COMMAND_TRACE"] = "YES" 398 399 if args.v: 400 configuration.verbose = 2 401 402 # argparse makes sure we have a number 403 if args.sharp: 404 configuration.count = args.sharp 405 406 if sys.platform.startswith("win32"): 407 os.environ["LLDB_DISABLE_CRASH_DIALOG"] = str(args.disable_crash_dialog) 408 os.environ["LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"] = str(True) 409 410 if do_help: 411 usage(parser) 412 413 if args.lldb_platform_name: 414 configuration.lldb_platform_name = args.lldb_platform_name 415 if args.lldb_platform_url: 416 configuration.lldb_platform_url = args.lldb_platform_url 417 if args.lldb_platform_working_dir: 418 configuration.lldb_platform_working_dir = args.lldb_platform_working_dir 419 if platform_system == "Darwin" and args.apple_sdk: 420 configuration.apple_sdk = args.apple_sdk 421 if args.test_build_dir: 422 configuration.test_build_dir = args.test_build_dir 423 if args.lldb_module_cache_dir: 424 configuration.lldb_module_cache_dir = args.lldb_module_cache_dir 425 else: 426 configuration.lldb_module_cache_dir = os.path.join( 427 configuration.test_build_dir, "module-cache-lldb" 428 ) 429 if args.clang_module_cache_dir: 430 configuration.clang_module_cache_dir = args.clang_module_cache_dir 431 else: 432 configuration.clang_module_cache_dir = os.path.join( 433 configuration.test_build_dir, "module-cache-clang" 434 ) 435 436 if args.lldb_libs_dir: 437 configuration.lldb_libs_dir = args.lldb_libs_dir 438 439 if args.enabled_plugins: 440 configuration.enabled_plugins = args.enabled_plugins 441 442 # Gather all the dirs passed on the command line. 443 if len(args.args) > 0: 444 configuration.testdirs = [ 445 os.path.realpath(os.path.abspath(x)) for x in args.args 446 ] 447 448 lldbtest_config.codesign_identity = args.codesign_identity 449 450 451def registerFaulthandler(): 452 try: 453 import faulthandler 454 except ImportError: 455 # faulthandler is not available until python3 456 return 457 458 faulthandler.enable() 459 # faulthandler.register is not available on Windows. 460 if getattr(faulthandler, "register", None): 461 faulthandler.register(signal.SIGTERM, chain=True) 462 463 464def setupSysPath(): 465 """ 466 Add LLDB.framework/Resources/Python to the search paths for modules. 467 As a side effect, we also discover the 'lldb' executable and export it here. 468 """ 469 470 # Get the directory containing the current script. 471 if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ: 472 scriptPath = os.environ["DOTEST_SCRIPT_DIR"] 473 else: 474 scriptPath = os.path.dirname(os.path.abspath(__file__)) 475 if not scriptPath.endswith("test"): 476 print("This script expects to reside in lldb's test directory.") 477 sys.exit(-1) 478 479 os.environ["LLDB_TEST"] = scriptPath 480 481 # Set up the root build directory. 482 if not configuration.test_build_dir: 483 raise Exception("test_build_dir is not set") 484 configuration.test_build_dir = os.path.abspath(configuration.test_build_dir) 485 486 # Set up the LLDB_SRC environment variable, so that the tests can locate 487 # the LLDB source code. 488 os.environ["LLDB_SRC"] = lldbsuite.lldb_root 489 490 pluginPath = os.path.join(scriptPath, "plugins") 491 toolsLLDBVSCode = os.path.join(scriptPath, "tools", "lldb-vscode") 492 toolsLLDBServerPath = os.path.join(scriptPath, "tools", "lldb-server") 493 intelpt = os.path.join(scriptPath, "tools", "intelpt") 494 495 # Insert script dir, plugin dir and lldb-server dir to the sys.path. 496 sys.path.insert(0, pluginPath) 497 # Adding test/tools/lldb-vscode to the path makes it easy to 498 # "import lldb_vscode_testcase" from the VSCode tests 499 sys.path.insert(0, toolsLLDBVSCode) 500 # Adding test/tools/lldb-server to the path makes it easy 501 # to "import lldbgdbserverutils" from the lldb-server tests 502 sys.path.insert(0, toolsLLDBServerPath) 503 # Adding test/tools/intelpt to the path makes it easy 504 # to "import intelpt_testcase" from the lldb-server tests 505 sys.path.insert(0, intelpt) 506 507 # This is the root of the lldb git/svn checkout 508 # When this changes over to a package instead of a standalone script, this 509 # will be `lldbsuite.lldb_root` 510 lldbRootDirectory = lldbsuite.lldb_root 511 512 # Some of the tests can invoke the 'lldb' command directly. 513 # We'll try to locate the appropriate executable right here. 514 515 # The lldb executable can be set from the command line 516 # if it's not set, we try to find it now 517 # first, we try the environment 518 if not lldbtest_config.lldbExec: 519 # First, you can define an environment variable LLDB_EXEC specifying the 520 # full pathname of the lldb executable. 521 if "LLDB_EXEC" in os.environ: 522 lldbtest_config.lldbExec = os.environ["LLDB_EXEC"] 523 524 if not lldbtest_config.lldbExec: 525 # Last, check the path 526 lldbtest_config.lldbExec = which("lldb") 527 528 if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec): 529 print( 530 "'{}' is not a path to a valid executable".format(lldbtest_config.lldbExec) 531 ) 532 lldbtest_config.lldbExec = None 533 534 if not lldbtest_config.lldbExec: 535 print( 536 "The 'lldb' executable cannot be located. Some of the tests may not be run as a result." 537 ) 538 sys.exit(-1) 539 540 os.system("%s -v" % lldbtest_config.lldbExec) 541 542 lldbDir = os.path.dirname(lldbtest_config.lldbExec) 543 544 lldbVSCodeExec = os.path.join(lldbDir, "lldb-vscode") 545 if is_exe(lldbVSCodeExec): 546 os.environ["LLDBVSCODE_EXEC"] = lldbVSCodeExec 547 else: 548 if not configuration.shouldSkipBecauseOfCategories(["lldb-vscode"]): 549 print( 550 "The 'lldb-vscode' executable cannot be located. The lldb-vscode tests can not be run as a result." 551 ) 552 configuration.skip_categories.append("lldb-vscode") 553 554 lldbPythonDir = None # The directory that contains 'lldb/__init__.py' 555 556 # If our lldb supports the -P option, use it to find the python path: 557 lldb_dash_p_result = subprocess.check_output( 558 [lldbtest_config.lldbExec, "-P"], universal_newlines=True 559 ) 560 if lldb_dash_p_result: 561 for line in lldb_dash_p_result.splitlines(): 562 if os.path.isdir(line) and os.path.exists( 563 os.path.join(line, "lldb", "__init__.py") 564 ): 565 lldbPythonDir = line 566 break 567 568 if not lldbPythonDir: 569 print( 570 "Unable to load lldb extension module. Possible reasons for this include:" 571 ) 572 print(" 1) LLDB was built with LLDB_ENABLE_PYTHON=0") 573 print( 574 " 2) PYTHONPATH and PYTHONHOME are not set correctly. PYTHONHOME should refer to" 575 ) 576 print( 577 " the version of Python that LLDB built and linked against, and PYTHONPATH" 578 ) 579 print( 580 " should contain the Lib directory for the same python distro, as well as the" 581 ) 582 print(" location of LLDB's site-packages folder.") 583 print( 584 " 3) A different version of Python than that which was built against is exported in" 585 ) 586 print(" the system's PATH environment variable, causing conflicts.") 587 print( 588 " 4) The executable '%s' could not be found. Please check " 589 % lldbtest_config.lldbExec 590 ) 591 print(" that it exists and is executable.") 592 593 if lldbPythonDir: 594 lldbPythonDir = os.path.normpath(lldbPythonDir) 595 # Some of the code that uses this path assumes it hasn't resolved the Versions... link. 596 # If the path we've constructed looks like that, then we'll strip out 597 # the Versions/A part. 598 (before, frameWithVersion, after) = lldbPythonDir.rpartition( 599 "LLDB.framework/Versions/A" 600 ) 601 if frameWithVersion != "": 602 lldbPythonDir = before + "LLDB.framework" + after 603 604 lldbPythonDir = os.path.abspath(lldbPythonDir) 605 606 if "freebsd" in sys.platform or "linux" in sys.platform: 607 os.environ["LLDB_LIB_DIR"] = os.path.join(lldbPythonDir, "..", "..") 608 609 # If tests need to find LLDB_FRAMEWORK, now they can do it 610 os.environ["LLDB_FRAMEWORK"] = os.path.dirname(os.path.dirname(lldbPythonDir)) 611 612 # This is to locate the lldb.py module. Insert it right after 613 # sys.path[0]. 614 sys.path[1:1] = [lldbPythonDir] 615 616 617def visit_file(dir, name): 618 # Try to match the regexp pattern, if specified. 619 if configuration.regexp: 620 if not re.search(configuration.regexp, name): 621 # We didn't match the regex, we're done. 622 return 623 624 if configuration.skip_tests: 625 for file_regexp in configuration.skip_tests: 626 if re.search(file_regexp, name): 627 return 628 629 # We found a match for our test. Add it to the suite. 630 631 # Update the sys.path first. 632 if not sys.path.count(dir): 633 sys.path.insert(0, dir) 634 base = os.path.splitext(name)[0] 635 636 # Thoroughly check the filterspec against the base module and admit 637 # the (base, filterspec) combination only when it makes sense. 638 639 def check(obj, parts): 640 for part in parts: 641 try: 642 parent, obj = obj, getattr(obj, part) 643 except AttributeError: 644 # The filterspec has failed. 645 return False 646 return True 647 648 module = __import__(base) 649 650 def iter_filters(): 651 for filterspec in configuration.filters: 652 parts = filterspec.split(".") 653 if check(module, parts): 654 yield filterspec 655 elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]): 656 yield ".".join(parts[1:]) 657 else: 658 for key, value in module.__dict__.items(): 659 if check(value, parts): 660 yield key + "." + filterspec 661 662 filtered = False 663 for filterspec in iter_filters(): 664 filtered = True 665 print("adding filter spec %s to module %s" % (filterspec, repr(module))) 666 tests = unittest2.defaultTestLoader.loadTestsFromName(filterspec, module) 667 configuration.suite.addTests(tests) 668 669 # Forgo this module if the (base, filterspec) combo is invalid 670 if configuration.filters and not filtered: 671 return 672 673 if not filtered: 674 # Add the entire file's worth of tests since we're not filtered. 675 # Also the fail-over case when the filterspec branch 676 # (base, filterspec) combo doesn't make sense. 677 configuration.suite.addTests( 678 unittest2.defaultTestLoader.loadTestsFromName(base) 679 ) 680 681 682def visit(prefix, dir, names): 683 """Visitor function for os.path.walk(path, visit, arg).""" 684 685 dir_components = set(dir.split(os.sep)) 686 excluded_components = set([".svn", ".git"]) 687 if dir_components.intersection(excluded_components): 688 return 689 690 # Gather all the Python test file names that follow the Test*.py pattern. 691 python_test_files = [ 692 name for name in names if name.endswith(".py") and name.startswith(prefix) 693 ] 694 695 # Visit all the python test files. 696 for name in python_test_files: 697 # Ensure we error out if we have multiple tests with the same 698 # base name. 699 # Future improvement: find all the places where we work with base 700 # names and convert to full paths. We have directory structure 701 # to disambiguate these, so we shouldn't need this constraint. 702 if name in configuration.all_tests: 703 raise Exception("Found multiple tests with the name %s" % name) 704 configuration.all_tests.add(name) 705 706 # Run the relevant tests in the python file. 707 visit_file(dir, name) 708 709 710# ======================================== # 711# # 712# Execution of the test driver starts here # 713# # 714# ======================================== # 715 716 717def checkDsymForUUIDIsNotOn(): 718 cmd = ["defaults", "read", "com.apple.DebugSymbols"] 719 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 720 cmd_output = process.stdout.read() 721 output_str = cmd_output.decode("utf-8") 722 if "DBGFileMappedPaths = " in output_str: 723 print("%s =>" % " ".join(cmd)) 724 print(output_str) 725 print( 726 "Disable automatic lookup and caching of dSYMs before running the test suite!" 727 ) 728 print("Exiting...") 729 sys.exit(0) 730 731 732def exitTestSuite(exitCode=None): 733 # lldb.py does SBDebugger.Initialize(). 734 # Call SBDebugger.Terminate() on exit. 735 import lldb 736 737 lldb.SBDebugger.Terminate() 738 if exitCode: 739 sys.exit(exitCode) 740 741 742def getVersionForSDK(sdk): 743 sdk = str.lower(sdk) 744 full_path = seven.get_command_output("xcrun -sdk %s --show-sdk-path" % sdk) 745 basename = os.path.basename(full_path) 746 basename = os.path.splitext(basename)[0] 747 basename = str.lower(basename) 748 ver = basename.replace(sdk, "") 749 return ver 750 751 752def checkCompiler(): 753 # Add some intervention here to sanity check that the compiler requested is sane. 754 # If found not to be an executable program, we abort. 755 c = configuration.compiler 756 if which(c): 757 return 758 759 if not sys.platform.startswith("darwin"): 760 raise Exception(c + " is not a valid compiler") 761 762 pipe = subprocess.Popen( 763 ["xcrun", "-find", c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT 764 ) 765 cmd_output = pipe.stdout.read() 766 if not cmd_output or "not found" in cmd_output: 767 raise Exception(c + " is not a valid compiler") 768 769 configuration.compiler = cmd_output.split("\n")[0] 770 print("'xcrun -find %s' returning %s" % (c, configuration.compiler)) 771 772 773def canRunLibcxxTests(): 774 from lldbsuite.test import lldbplatformutil 775 776 platform = lldbplatformutil.getPlatform() 777 778 if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin(): 779 return True, "libc++ always present" 780 781 if platform == "linux": 782 with tempfile.NamedTemporaryFile() as f: 783 cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"] 784 p = subprocess.Popen( 785 cmd, 786 stdin=subprocess.PIPE, 787 stdout=subprocess.PIPE, 788 stderr=subprocess.PIPE, 789 universal_newlines=True, 790 ) 791 _, stderr = p.communicate("#include <cassert>\nint main() {}") 792 if not p.returncode: 793 return True, "Compiling with -stdlib=libc++ works" 794 return ( 795 False, 796 "Compiling with -stdlib=libc++ fails with the error: %s" % stderr, 797 ) 798 799 return False, "Don't know how to build with libc++ on %s" % platform 800 801 802def checkLibcxxSupport(): 803 result, reason = canRunLibcxxTests() 804 if result: 805 return # libc++ supported 806 if "libc++" in configuration.categories_list: 807 return # libc++ category explicitly requested, let it run. 808 if configuration.verbose: 809 print("libc++ tests will not be run because: " + reason) 810 configuration.skip_categories.append("libc++") 811 812 813def canRunLibstdcxxTests(): 814 from lldbsuite.test import lldbplatformutil 815 816 platform = lldbplatformutil.getPlatform() 817 if lldbplatformutil.target_is_android(): 818 platform = "android" 819 if platform == "linux": 820 return True, "libstdcxx always present" 821 return False, "Don't know how to build with libstdcxx on %s" % platform 822 823 824def checkLibstdcxxSupport(): 825 result, reason = canRunLibstdcxxTests() 826 if result: 827 return # libstdcxx supported 828 if "libstdcxx" in configuration.categories_list: 829 return # libstdcxx category explicitly requested, let it run. 830 if configuration.verbose: 831 print("libstdcxx tests will not be run because: " + reason) 832 configuration.skip_categories.append("libstdcxx") 833 834 835def canRunWatchpointTests(): 836 from lldbsuite.test import lldbplatformutil 837 838 platform = lldbplatformutil.getPlatform() 839 if platform == "netbsd": 840 if os.geteuid() == 0: 841 return True, "root can always write dbregs" 842 try: 843 output = ( 844 subprocess.check_output( 845 ["/sbin/sysctl", "-n", "security.models.extensions.user_set_dbregs"] 846 ) 847 .decode() 848 .strip() 849 ) 850 if output == "1": 851 return True, "security.models.extensions.user_set_dbregs enabled" 852 except subprocess.CalledProcessError: 853 pass 854 return False, "security.models.extensions.user_set_dbregs disabled" 855 elif platform == "freebsd" and configuration.arch == "aarch64": 856 import lldb 857 858 if lldb.SBPlatform.GetHostPlatform().GetOSMajorVersion() < 13: 859 return False, "Watchpoint support on arm64 requires FreeBSD 13.0" 860 return True, "watchpoint support available" 861 862 863def checkWatchpointSupport(): 864 result, reason = canRunWatchpointTests() 865 if result: 866 return # watchpoints supported 867 if "watchpoint" in configuration.categories_list: 868 return # watchpoint category explicitly requested, let it run. 869 if configuration.verbose: 870 print("watchpoint tests will not be run because: " + reason) 871 configuration.skip_categories.append("watchpoint") 872 873 874def checkObjcSupport(): 875 from lldbsuite.test import lldbplatformutil 876 877 if not lldbplatformutil.platformIsDarwin(): 878 if configuration.verbose: 879 print("objc tests will be skipped because of unsupported platform") 880 configuration.skip_categories.append("objc") 881 882 883def checkDebugInfoSupport(): 884 from lldbsuite.test import lldbplatformutil 885 886 platform = lldbplatformutil.getPlatform() 887 compiler = configuration.compiler 888 for cat in test_categories.debug_info_categories: 889 if cat in configuration.categories_list: 890 continue # Category explicitly requested, let it run. 891 if test_categories.is_supported_on_platform(cat, platform, compiler): 892 continue 893 configuration.skip_categories.append(cat) 894 895 896def checkDebugServerSupport(): 897 from lldbsuite.test import lldbplatformutil 898 import lldb 899 900 skip_msg = "Skipping %s tests, as they are not compatible with remote testing on this platform" 901 if lldbplatformutil.platformIsDarwin(): 902 configuration.skip_categories.append("llgs") 903 if lldb.remote_platform: 904 # <rdar://problem/34539270> 905 configuration.skip_categories.append("debugserver") 906 if configuration.verbose: 907 print(skip_msg % "debugserver") 908 else: 909 configuration.skip_categories.append("debugserver") 910 if lldb.remote_platform and lldbplatformutil.getPlatform() == "windows": 911 configuration.skip_categories.append("llgs") 912 if configuration.verbose: 913 print(skip_msg % "lldb-server") 914 915 916def checkForkVForkSupport(): 917 from lldbsuite.test import lldbplatformutil 918 919 platform = lldbplatformutil.getPlatform() 920 if platform not in ["freebsd", "linux", "netbsd"]: 921 configuration.skip_categories.append("fork") 922 923 924def run_suite(): 925 # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults 926 # does not exist before proceeding to running the test suite. 927 if sys.platform.startswith("darwin"): 928 checkDsymForUUIDIsNotOn() 929 930 # Start the actions by first parsing the options while setting up the test 931 # directories, followed by setting up the search paths for lldb utilities; 932 # then, we walk the directory trees and collect the tests into our test suite. 933 # 934 parseOptionsAndInitTestdirs() 935 936 # Print a stack trace if the test hangs or is passed SIGTERM. 937 registerFaulthandler() 938 939 setupSysPath() 940 941 import lldb 942 943 lldb.SBDebugger.Initialize() 944 lldb.SBDebugger.PrintStackTraceOnError() 945 946 # Use host platform by default. 947 lldb.remote_platform = None 948 lldb.selected_platform = lldb.SBPlatform.GetHostPlatform() 949 950 # Now we can also import lldbutil 951 from lldbsuite.test import lldbutil 952 953 if configuration.lldb_platform_name: 954 print("Setting up remote platform '%s'" % (configuration.lldb_platform_name)) 955 lldb.remote_platform = lldb.SBPlatform(configuration.lldb_platform_name) 956 lldb.selected_platform = lldb.remote_platform 957 if not lldb.remote_platform.IsValid(): 958 print( 959 "error: unable to create the LLDB platform named '%s'." 960 % (configuration.lldb_platform_name) 961 ) 962 exitTestSuite(1) 963 if configuration.lldb_platform_url: 964 # We must connect to a remote platform if a LLDB platform URL was 965 # specified 966 print( 967 "Connecting to remote platform '%s' at '%s'..." 968 % (configuration.lldb_platform_name, configuration.lldb_platform_url) 969 ) 970 platform_connect_options = lldb.SBPlatformConnectOptions( 971 configuration.lldb_platform_url 972 ) 973 err = lldb.remote_platform.ConnectRemote(platform_connect_options) 974 if err.Success(): 975 print("Connected.") 976 else: 977 print( 978 "error: failed to connect to remote platform using URL '%s': %s" 979 % (configuration.lldb_platform_url, err) 980 ) 981 exitTestSuite(1) 982 else: 983 configuration.lldb_platform_url = None 984 985 if configuration.lldb_platform_working_dir: 986 print( 987 "Setting remote platform working directory to '%s'..." 988 % (configuration.lldb_platform_working_dir) 989 ) 990 error = lldb.remote_platform.MakeDirectory( 991 configuration.lldb_platform_working_dir, 448 992 ) # 448 = 0o700 993 if error.Fail(): 994 raise Exception( 995 "making remote directory '%s': %s" 996 % (configuration.lldb_platform_working_dir, error) 997 ) 998 999 if not lldb.remote_platform.SetWorkingDirectory( 1000 configuration.lldb_platform_working_dir 1001 ): 1002 raise Exception( 1003 "failed to set working directory '%s'" 1004 % configuration.lldb_platform_working_dir 1005 ) 1006 lldb.selected_platform = lldb.remote_platform 1007 else: 1008 lldb.remote_platform = None 1009 configuration.lldb_platform_working_dir = None 1010 configuration.lldb_platform_url = None 1011 1012 # Set up the working directory. 1013 # Note that it's not dotest's job to clean this directory. 1014 lldbutil.mkdir_p(configuration.test_build_dir) 1015 1016 checkLibcxxSupport() 1017 checkLibstdcxxSupport() 1018 checkWatchpointSupport() 1019 checkDebugInfoSupport() 1020 checkDebugServerSupport() 1021 checkObjcSupport() 1022 checkForkVForkSupport() 1023 1024 skipped_categories_list = ", ".join(configuration.skip_categories) 1025 print( 1026 "Skipping the following test categories: {}".format( 1027 configuration.skip_categories 1028 ) 1029 ) 1030 1031 for testdir in configuration.testdirs: 1032 for dirpath, dirnames, filenames in os.walk(testdir): 1033 visit("Test", dirpath, filenames) 1034 1035 # 1036 # Now that we have loaded all the test cases, run the whole test suite. 1037 # 1038 1039 # Install the control-c handler. 1040 unittest2.signals.installHandler() 1041 1042 # 1043 # Invoke the default TextTestRunner to run the test suite 1044 # 1045 checkCompiler() 1046 1047 if configuration.verbose: 1048 print("compiler=%s" % configuration.compiler) 1049 1050 # Iterating over all possible architecture and compiler combinations. 1051 configString = "arch=%s compiler=%s" % (configuration.arch, configuration.compiler) 1052 1053 # Output the configuration. 1054 if configuration.verbose: 1055 sys.stderr.write("\nConfiguration: " + configString + "\n") 1056 1057 # First, write out the number of collected test cases. 1058 if configuration.verbose: 1059 sys.stderr.write(configuration.separator + "\n") 1060 sys.stderr.write( 1061 "Collected %d test%s\n\n" 1062 % ( 1063 configuration.suite.countTestCases(), 1064 configuration.suite.countTestCases() != 1 and "s" or "", 1065 ) 1066 ) 1067 1068 if configuration.suite.countTestCases() == 0: 1069 logging.error("did not discover any matching tests") 1070 exitTestSuite(1) 1071 1072 # Invoke the test runner. 1073 if configuration.count == 1: 1074 result = unittest2.TextTestRunner( 1075 stream=sys.stderr, 1076 verbosity=configuration.verbose, 1077 resultclass=test_result.LLDBTestResult, 1078 ).run(configuration.suite) 1079 else: 1080 # We are invoking the same test suite more than once. In this case, 1081 # mark __ignore_singleton__ flag as True so the signleton pattern is 1082 # not enforced. 1083 test_result.LLDBTestResult.__ignore_singleton__ = True 1084 for i in range(configuration.count): 1085 result = unittest2.TextTestRunner( 1086 stream=sys.stderr, 1087 verbosity=configuration.verbose, 1088 resultclass=test_result.LLDBTestResult, 1089 ).run(configuration.suite) 1090 1091 configuration.failed = not result.wasSuccessful() 1092 1093 if configuration.sdir_has_content and configuration.verbose: 1094 sys.stderr.write( 1095 "Session logs for test failures/errors/unexpected successes" 1096 " can be found in the test build directory\n" 1097 ) 1098 1099 if configuration.use_categories and len(configuration.failures_per_category) > 0: 1100 sys.stderr.write("Failures per category:\n") 1101 for category in configuration.failures_per_category: 1102 sys.stderr.write( 1103 "%s - %d\n" % (category, configuration.failures_per_category[category]) 1104 ) 1105 1106 # Exiting. 1107 exitTestSuite(configuration.failed) 1108 1109 1110if __name__ == "__main__": 1111 print( 1112 __file__ 1113 + " is for use as a module only. It should not be run as a standalone script." 1114 ) 1115 sys.exit(-1) 1116