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