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