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.realpath(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 420 # rerun-related arguments 421 configuration.rerun_all_issues = args.rerun_all_issues 422 423 if args.lldb_platform_name: 424 configuration.lldb_platform_name = args.lldb_platform_name 425 if args.lldb_platform_url: 426 configuration.lldb_platform_url = args.lldb_platform_url 427 if args.lldb_platform_working_dir: 428 configuration.lldb_platform_working_dir = args.lldb_platform_working_dir 429 if args.test_build_dir: 430 configuration.test_build_dir = args.test_build_dir 431 if args.lldb_module_cache_dir: 432 configuration.lldb_module_cache_dir = args.lldb_module_cache_dir 433 else: 434 configuration.lldb_module_cache_dir = os.path.join( 435 configuration.test_build_dir, 'module-cache-lldb') 436 if args.clang_module_cache_dir: 437 configuration.clang_module_cache_dir = args.clang_module_cache_dir 438 else: 439 configuration.clang_module_cache_dir = os.path.join( 440 configuration.test_build_dir, 'module-cache-clang') 441 442 if args.lldb_libs_dir: 443 configuration.lldb_libs_dir = args.lldb_libs_dir 444 445 if args.enabled_plugins: 446 configuration.enabled_plugins = args.enabled_plugins 447 448 # Gather all the dirs passed on the command line. 449 if len(args.args) > 0: 450 configuration.testdirs = [os.path.realpath(os.path.abspath(x)) for x in args.args] 451 452 lldbtest_config.codesign_identity = args.codesign_identity 453 454def setupSysPath(): 455 """ 456 Add LLDB.framework/Resources/Python to the search paths for modules. 457 As a side effect, we also discover the 'lldb' executable and export it here. 458 """ 459 460 # Get the directory containing the current script. 461 if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ: 462 scriptPath = os.environ["DOTEST_SCRIPT_DIR"] 463 else: 464 scriptPath = os.path.dirname(os.path.realpath(__file__)) 465 if not scriptPath.endswith('test'): 466 print("This script expects to reside in lldb's test directory.") 467 sys.exit(-1) 468 469 os.environ["LLDB_TEST"] = scriptPath 470 os.environ["LLDB_TEST_SRC"] = lldbsuite.lldb_test_root 471 472 # Set up the root build directory. 473 if not configuration.test_build_dir: 474 raise Exception("test_build_dir is not set") 475 configuration.test_build_dir = os.path.abspath(configuration.test_build_dir) 476 477 # Set up the LLDB_SRC environment variable, so that the tests can locate 478 # the LLDB source code. 479 os.environ["LLDB_SRC"] = lldbsuite.lldb_root 480 481 pluginPath = os.path.join(scriptPath, 'plugins') 482 toolsLLDBVSCode = os.path.join(scriptPath, 'tools', 'lldb-vscode') 483 toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server') 484 485 # Insert script dir, plugin dir and lldb-server dir to the sys.path. 486 sys.path.insert(0, pluginPath) 487 # Adding test/tools/lldb-vscode to the path makes it easy to 488 # "import lldb_vscode_testcase" from the VSCode tests 489 sys.path.insert(0, toolsLLDBVSCode) 490 # Adding test/tools/lldb-server to the path makes it easy 491 sys.path.insert(0, toolsLLDBServerPath) 492 # to "import lldbgdbserverutils" from the lldb-server tests 493 494 # This is the root of the lldb git/svn checkout 495 # When this changes over to a package instead of a standalone script, this 496 # will be `lldbsuite.lldb_root` 497 lldbRootDirectory = lldbsuite.lldb_root 498 499 # Some of the tests can invoke the 'lldb' command directly. 500 # We'll try to locate the appropriate executable right here. 501 502 # The lldb executable can be set from the command line 503 # if it's not set, we try to find it now 504 # first, we try the environment 505 if not lldbtest_config.lldbExec: 506 # First, you can define an environment variable LLDB_EXEC specifying the 507 # full pathname of the lldb executable. 508 if "LLDB_EXEC" in os.environ: 509 lldbtest_config.lldbExec = os.environ["LLDB_EXEC"] 510 511 if not lldbtest_config.lldbExec: 512 # Last, check the path 513 lldbtest_config.lldbExec = which('lldb') 514 515 if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec): 516 print( 517 "'{}' is not a path to a valid executable".format( 518 lldbtest_config.lldbExec)) 519 lldbtest_config.lldbExec = None 520 521 if not lldbtest_config.lldbExec: 522 print("The 'lldb' executable cannot be located. Some of the tests may not be run as a result.") 523 sys.exit(-1) 524 525 # confusingly, this is the "bin" directory 526 lldbLibDir = os.path.dirname(lldbtest_config.lldbExec) 527 os.environ["LLDB_LIB_DIR"] = lldbLibDir 528 lldbImpLibDir = configuration.lldb_libs_dir 529 os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir 530 print("LLDB library dir:", os.environ["LLDB_LIB_DIR"]) 531 print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"]) 532 os.system('%s -v' % lldbtest_config.lldbExec) 533 534 lldbDir = os.path.dirname(lldbtest_config.lldbExec) 535 536 lldbVSCodeExec = os.path.join(lldbDir, "lldb-vscode") 537 if is_exe(lldbVSCodeExec): 538 os.environ["LLDBVSCODE_EXEC"] = lldbVSCodeExec 539 else: 540 if not configuration.shouldSkipBecauseOfCategories(["lldb-vscode"]): 541 print( 542 "The 'lldb-vscode' executable cannot be located. The lldb-vscode tests can not be run as a result.") 543 configuration.skip_categories.append("lldb-vscode") 544 545 lldbPythonDir = None # The directory that contains 'lldb/__init__.py' 546 if not configuration.lldb_framework_path and os.path.exists(os.path.join(lldbLibDir, "LLDB.framework")): 547 configuration.lldb_framework_path = os.path.join(lldbLibDir, "LLDB.framework") 548 if configuration.lldb_framework_path: 549 lldbtest_config.lldb_framework_path = configuration.lldb_framework_path 550 candidatePath = os.path.join( 551 configuration.lldb_framework_path, 'Resources', 'Python') 552 if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')): 553 lldbPythonDir = candidatePath 554 if not lldbPythonDir: 555 print( 556 'Resources/Python/lldb/__init__.py was not found in ' + 557 configuration.lldb_framework_path) 558 sys.exit(-1) 559 else: 560 # If our lldb supports the -P option, use it to find the python path: 561 init_in_python_dir = os.path.join('lldb', '__init__.py') 562 563 lldb_dash_p_result = subprocess.check_output( 564 [lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True) 565 566 if lldb_dash_p_result and not lldb_dash_p_result.startswith( 567 ("<", "lldb: invalid option:")) and not lldb_dash_p_result.startswith("Traceback"): 568 lines = lldb_dash_p_result.splitlines() 569 570 # Workaround for readline vs libedit issue on FreeBSD. If stdout 571 # is not a terminal Python executes 572 # rl_variable_bind ("enable-meta-key", "off"); 573 # This produces a warning with FreeBSD's libedit because the 574 # enable-meta-key variable is unknown. Not an issue on Apple 575 # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__ 576 # around the call. See http://bugs.python.org/issue19884 for more 577 # information. For now we just discard the warning output. 578 if len(lines) >= 1 and lines[0].startswith( 579 "bind: Invalid command"): 580 lines.pop(0) 581 582 # Taking the last line because lldb outputs 583 # 'Cannot read termcap database;\nusing dumb terminal settings.\n' 584 # before the path 585 if len(lines) >= 1 and os.path.isfile( 586 os.path.join(lines[-1], init_in_python_dir)): 587 lldbPythonDir = lines[-1] 588 if "freebsd" in sys.platform or "linux" in sys.platform: 589 os.environ['LLDB_LIB_DIR'] = os.path.join( 590 lldbPythonDir, '..', '..') 591 592 if not lldbPythonDir: 593 print( 594 "Unable to load lldb extension module. Possible reasons for this include:") 595 print(" 1) LLDB was built with LLDB_ENABLE_PYTHON=0") 596 print( 597 " 2) PYTHONPATH and PYTHONHOME are not set correctly. PYTHONHOME should refer to") 598 print( 599 " the version of Python that LLDB built and linked against, and PYTHONPATH") 600 print( 601 " should contain the Lib directory for the same python distro, as well as the") 602 print(" location of LLDB\'s site-packages folder.") 603 print( 604 " 3) A different version of Python than that which was built against is exported in") 605 print(" the system\'s PATH environment variable, causing conflicts.") 606 print( 607 " 4) The executable '%s' could not be found. Please check " % 608 lldbtest_config.lldbExec) 609 print(" that it exists and is executable.") 610 611 if lldbPythonDir: 612 lldbPythonDir = os.path.normpath(lldbPythonDir) 613 # Some of the code that uses this path assumes it hasn't resolved the Versions... link. 614 # If the path we've constructed looks like that, then we'll strip out 615 # the Versions/A part. 616 (before, frameWithVersion, after) = lldbPythonDir.rpartition( 617 "LLDB.framework/Versions/A") 618 if frameWithVersion != "": 619 lldbPythonDir = before + "LLDB.framework" + after 620 621 lldbPythonDir = os.path.abspath(lldbPythonDir) 622 623 # If tests need to find LLDB_FRAMEWORK, now they can do it 624 os.environ["LLDB_FRAMEWORK"] = os.path.dirname( 625 os.path.dirname(lldbPythonDir)) 626 627 # This is to locate the lldb.py module. Insert it right after 628 # sys.path[0]. 629 sys.path[1:1] = [lldbPythonDir] 630 631 632def visit_file(dir, name): 633 # Try to match the regexp pattern, if specified. 634 if configuration.regexp: 635 if not re.search(configuration.regexp, name): 636 # We didn't match the regex, we're done. 637 return 638 639 if configuration.skip_tests: 640 for file_regexp in configuration.skip_tests: 641 if re.search(file_regexp, name): 642 return 643 644 # We found a match for our test. Add it to the suite. 645 646 # Update the sys.path first. 647 if not sys.path.count(dir): 648 sys.path.insert(0, dir) 649 base = os.path.splitext(name)[0] 650 651 # Thoroughly check the filterspec against the base module and admit 652 # the (base, filterspec) combination only when it makes sense. 653 654 def check(obj, parts): 655 for part in parts: 656 try: 657 parent, obj = obj, getattr(obj, part) 658 except AttributeError: 659 # The filterspec has failed. 660 return False 661 return True 662 663 module = __import__(base) 664 665 def iter_filters(): 666 for filterspec in configuration.filters: 667 parts = filterspec.split('.') 668 if check(module, parts): 669 yield filterspec 670 elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]): 671 yield '.'.join(parts[1:]) 672 else: 673 for key,value in module.__dict__.items(): 674 if check(value, parts): 675 yield key + '.' + filterspec 676 677 filtered = False 678 for filterspec in iter_filters(): 679 filtered = True 680 print("adding filter spec %s to module %s" % (filterspec, repr(module))) 681 tests = unittest2.defaultTestLoader.loadTestsFromName(filterspec, module) 682 configuration.suite.addTests(tests) 683 684 # Forgo this module if the (base, filterspec) combo is invalid 685 if configuration.filters and not filtered: 686 return 687 688 if not filtered: 689 # Add the entire file's worth of tests since we're not filtered. 690 # Also the fail-over case when the filterspec branch 691 # (base, filterspec) combo doesn't make sense. 692 configuration.suite.addTests( 693 unittest2.defaultTestLoader.loadTestsFromName(base)) 694 695 696def visit(prefix, dir, names): 697 """Visitor function for os.path.walk(path, visit, arg).""" 698 699 dir_components = set(dir.split(os.sep)) 700 excluded_components = set(['.svn', '.git']) 701 if dir_components.intersection(excluded_components): 702 return 703 704 # Gather all the Python test file names that follow the Test*.py pattern. 705 python_test_files = [ 706 name 707 for name in names 708 if name.endswith('.py') and name.startswith(prefix)] 709 710 # Visit all the python test files. 711 for name in python_test_files: 712 # Ensure we error out if we have multiple tests with the same 713 # base name. 714 # Future improvement: find all the places where we work with base 715 # names and convert to full paths. We have directory structure 716 # to disambiguate these, so we shouldn't need this constraint. 717 if name in configuration.all_tests: 718 raise Exception("Found multiple tests with the name %s" % name) 719 configuration.all_tests.add(name) 720 721 # Run the relevant tests in the python file. 722 visit_file(dir, name) 723 724 725# ======================================== # 726# # 727# Execution of the test driver starts here # 728# # 729# ======================================== # 730 731 732def checkDsymForUUIDIsNotOn(): 733 cmd = ["defaults", "read", "com.apple.DebugSymbols"] 734 process = subprocess.Popen( 735 cmd, 736 stdout=subprocess.PIPE, 737 stderr=subprocess.STDOUT) 738 cmd_output = process.stdout.read() 739 output_str = cmd_output.decode("utf-8") 740 if "DBGFileMappedPaths = " in output_str: 741 print("%s =>" % ' '.join(cmd)) 742 print(output_str) 743 print( 744 "Disable automatic lookup and caching of dSYMs before running the test suite!") 745 print("Exiting...") 746 sys.exit(0) 747 748 749def exitTestSuite(exitCode=None): 750 # lldb.py does SBDebugger.Initialize(). 751 # Call SBDebugger.Terminate() on exit. 752 import lldb 753 lldb.SBDebugger.Terminate() 754 if exitCode: 755 sys.exit(exitCode) 756 757 758def getVersionForSDK(sdk): 759 sdk = str.lower(sdk) 760 full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk) 761 basename = os.path.basename(full_path) 762 basename = os.path.splitext(basename)[0] 763 basename = str.lower(basename) 764 ver = basename.replace(sdk, '') 765 return ver 766 767 768def setDefaultTripleForPlatform(): 769 if configuration.lldb_platform_name == 'ios-simulator': 770 triple_str = 'x86_64-apple-ios%s' % ( 771 getVersionForSDK('iphonesimulator')) 772 os.environ['TRIPLE'] = triple_str 773 return {'TRIPLE': triple_str} 774 return {} 775 776 777def checkCompiler(): 778 # Add some intervention here to sanity check that the compiler requested is sane. 779 # If found not to be an executable program, we abort. 780 c = configuration.compiler 781 if which(c): 782 return 783 784 if not sys.platform.startswith("darwin"): 785 raise Exception(c + " is not a valid compiler") 786 787 pipe = subprocess.Popen( 788 ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 789 cmd_output = pipe.stdout.read() 790 if not cmd_output or "not found" in cmd_output: 791 raise Exception(c + " is not a valid compiler") 792 793 configuration.compiler = cmd_output.split('\n')[0] 794 print("'xcrun -find %s' returning %s" % (c, configuration.compiler)) 795 796def canRunLibcxxTests(): 797 from lldbsuite.test import lldbplatformutil 798 799 platform = lldbplatformutil.getPlatform() 800 801 if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin(): 802 return True, "libc++ always present" 803 804 if platform == "linux": 805 if os.path.isdir("/usr/include/c++/v1"): 806 return True, "Headers found, let's hope they work" 807 with tempfile.NamedTemporaryFile() as f: 808 cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"] 809 p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 810 _, stderr = p.communicate("#include <algorithm>\nint main() {}") 811 if not p.returncode: 812 return True, "Compiling with -stdlib=libc++ works" 813 return False, "Compiling with -stdlib=libc++ fails with the error: %s" % stderr 814 815 return False, "Don't know how to build with libc++ on %s" % platform 816 817def checkLibcxxSupport(): 818 result, reason = canRunLibcxxTests() 819 if result: 820 return # libc++ supported 821 if "libc++" in configuration.categories_list: 822 return # libc++ category explicitly requested, let it run. 823 print("Libc++ tests will not be run because: " + reason) 824 configuration.skip_categories.append("libc++") 825 826def canRunLibstdcxxTests(): 827 from lldbsuite.test import lldbplatformutil 828 829 platform = lldbplatformutil.getPlatform() 830 if lldbplatformutil.target_is_android(): 831 platform = "android" 832 if platform == "linux": 833 return True, "libstdcxx always present" 834 return False, "Don't know how to build with libstdcxx on %s" % platform 835 836def checkLibstdcxxSupport(): 837 result, reason = canRunLibstdcxxTests() 838 if result: 839 return # libstdcxx supported 840 if "libstdcxx" in configuration.categories_list: 841 return # libstdcxx category explicitly requested, let it run. 842 print("libstdcxx tests will not be run because: " + reason) 843 configuration.skip_categories.append("libstdcxx") 844 845def canRunWatchpointTests(): 846 from lldbsuite.test import lldbplatformutil 847 848 platform = lldbplatformutil.getPlatform() 849 if platform == "netbsd": 850 if os.geteuid() == 0: 851 return True, "root can always write dbregs" 852 try: 853 output = subprocess.check_output(["/sbin/sysctl", "-n", 854 "security.models.extensions.user_set_dbregs"]).decode().strip() 855 if output == "1": 856 return True, "security.models.extensions.user_set_dbregs enabled" 857 except subprocess.CalledProcessError: 858 pass 859 return False, "security.models.extensions.user_set_dbregs disabled" 860 return True, "watchpoint support available" 861 862def checkWatchpointSupport(): 863 result, reason = canRunWatchpointTests() 864 if result: 865 return # watchpoints supported 866 if "watchpoint" in configuration.categories_list: 867 return # watchpoint category explicitly requested, let it run. 868 print("watchpoint tests will not be run because: " + reason) 869 configuration.skip_categories.append("watchpoint") 870 871def checkDebugInfoSupport(): 872 import lldb 873 874 platform = lldb.selected_platform.GetTriple().split('-')[2] 875 compiler = configuration.compiler 876 skipped = [] 877 for cat in test_categories.debug_info_categories: 878 if cat in configuration.categories_list: 879 continue # Category explicitly requested, let it run. 880 if test_categories.is_supported_on_platform(cat, platform, compiler): 881 continue 882 configuration.skip_categories.append(cat) 883 skipped.append(cat) 884 if skipped: 885 print("Skipping following debug info categories:", skipped) 886 887def run_suite(): 888 # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults 889 # does not exist before proceeding to running the test suite. 890 if sys.platform.startswith("darwin"): 891 checkDsymForUUIDIsNotOn() 892 893 # Start the actions by first parsing the options while setting up the test 894 # directories, followed by setting up the search paths for lldb utilities; 895 # then, we walk the directory trees and collect the tests into our test suite. 896 # 897 parseOptionsAndInitTestdirs() 898 899 setupSysPath() 900 901 import lldbconfig 902 if configuration.capture_path or configuration.replay_path: 903 lldbconfig.INITIALIZE = False 904 import lldb 905 906 if configuration.capture_path: 907 lldb.SBReproducer.Capture(configuration.capture_path) 908 lldb.SBReproducer.SetAutoGenerate(True) 909 elif configuration.replay_path: 910 lldb.SBReproducer.PassiveReplay(configuration.replay_path) 911 912 if not lldbconfig.INITIALIZE: 913 lldb.SBDebugger.Initialize() 914 915 # Use host platform by default. 916 lldb.selected_platform = lldb.SBPlatform.GetHostPlatform() 917 918 # Now we can also import lldbutil 919 from lldbsuite.test import lldbutil 920 921 if configuration.lldb_platform_name: 922 print("Setting up remote platform '%s'" % 923 (configuration.lldb_platform_name)) 924 lldb.remote_platform = lldb.SBPlatform( 925 configuration.lldb_platform_name) 926 if not lldb.remote_platform.IsValid(): 927 print( 928 "error: unable to create the LLDB platform named '%s'." % 929 (configuration.lldb_platform_name)) 930 exitTestSuite(1) 931 if configuration.lldb_platform_url: 932 # We must connect to a remote platform if a LLDB platform URL was 933 # specified 934 print( 935 "Connecting to remote platform '%s' at '%s'..." % 936 (configuration.lldb_platform_name, configuration.lldb_platform_url)) 937 platform_connect_options = lldb.SBPlatformConnectOptions( 938 configuration.lldb_platform_url) 939 err = lldb.remote_platform.ConnectRemote(platform_connect_options) 940 if err.Success(): 941 print("Connected.") 942 else: 943 print("error: failed to connect to remote platform using URL '%s': %s" % ( 944 configuration.lldb_platform_url, err)) 945 exitTestSuite(1) 946 else: 947 configuration.lldb_platform_url = None 948 949 platform_changes = setDefaultTripleForPlatform() 950 first = True 951 for key in platform_changes: 952 if first: 953 print("Environment variables setup for platform support:") 954 first = False 955 print("%s = %s" % (key, platform_changes[key])) 956 957 if configuration.lldb_platform_working_dir: 958 print("Setting remote platform working directory to '%s'..." % 959 (configuration.lldb_platform_working_dir)) 960 error = lldb.remote_platform.MakeDirectory( 961 configuration.lldb_platform_working_dir, 448) # 448 = 0o700 962 if error.Fail(): 963 raise Exception("making remote directory '%s': %s" % ( 964 configuration.lldb_platform_working_dir, error)) 965 966 if not lldb.remote_platform.SetWorkingDirectory( 967 configuration.lldb_platform_working_dir): 968 raise Exception("failed to set working directory '%s'" % configuration.lldb_platform_working_dir) 969 lldb.selected_platform = lldb.remote_platform 970 else: 971 lldb.remote_platform = None 972 configuration.lldb_platform_working_dir = None 973 configuration.lldb_platform_url = None 974 975 # Set up the working directory. 976 # Note that it's not dotest's job to clean this directory. 977 lldbutil.mkdir_p(configuration.test_build_dir) 978 979 target_platform = lldb.selected_platform.GetTriple().split('-')[2] 980 981 checkLibcxxSupport() 982 checkLibstdcxxSupport() 983 checkWatchpointSupport() 984 checkDebugInfoSupport() 985 986 # Don't do debugserver tests on anything except OS X. 987 configuration.dont_do_debugserver_test = ( 988 "linux" in target_platform or 989 "freebsd" in target_platform or 990 "netbsd" in target_platform or 991 "windows" in target_platform) 992 993 # Don't do lldb-server (llgs) tests on anything except Linux and Windows. 994 configuration.dont_do_llgs_test = not ( 995 "linux" in target_platform or 996 "netbsd" in target_platform or 997 "windows" in target_platform) 998 999 for testdir in configuration.testdirs: 1000 for (dirpath, dirnames, filenames) in os.walk(testdir): 1001 visit('Test', dirpath, filenames) 1002 1003 # 1004 # Now that we have loaded all the test cases, run the whole test suite. 1005 # 1006 1007 # Install the control-c handler. 1008 unittest2.signals.installHandler() 1009 1010 lldbutil.mkdir_p(configuration.sdir_name) 1011 os.environ["LLDB_SESSION_DIRNAME"] = configuration.sdir_name 1012 1013 sys.stderr.write( 1014 "\nSession logs for test failures/errors/unexpected successes" 1015 " will go into directory '%s'\n" % 1016 configuration.sdir_name) 1017 1018 # 1019 # Invoke the default TextTestRunner to run the test suite 1020 # 1021 checkCompiler() 1022 1023 if configuration.verbose: 1024 print("compiler=%s" % configuration.compiler) 1025 1026 # Iterating over all possible architecture and compiler combinations. 1027 configString = "arch=%s compiler=%s" % (configuration.arch, 1028 configuration.compiler) 1029 1030 # Output the configuration. 1031 if configuration.verbose: 1032 sys.stderr.write("\nConfiguration: " + configString + "\n") 1033 1034 # First, write out the number of collected test cases. 1035 if configuration.verbose: 1036 sys.stderr.write(configuration.separator + "\n") 1037 sys.stderr.write( 1038 "Collected %d test%s\n\n" % 1039 (configuration.suite.countTestCases(), 1040 configuration.suite.countTestCases() != 1 and "s" or "")) 1041 1042 # Invoke the test runner. 1043 if configuration.count == 1: 1044 result = unittest2.TextTestRunner( 1045 stream=sys.stderr, 1046 verbosity=configuration.verbose, 1047 resultclass=test_result.LLDBTestResult).run( 1048 configuration.suite) 1049 else: 1050 # We are invoking the same test suite more than once. In this case, 1051 # mark __ignore_singleton__ flag as True so the signleton pattern is 1052 # not enforced. 1053 test_result.LLDBTestResult.__ignore_singleton__ = True 1054 for i in range(configuration.count): 1055 1056 result = unittest2.TextTestRunner( 1057 stream=sys.stderr, 1058 verbosity=configuration.verbose, 1059 resultclass=test_result.LLDBTestResult).run( 1060 configuration.suite) 1061 1062 configuration.failed = not result.wasSuccessful() 1063 1064 if configuration.sdir_has_content and configuration.verbose: 1065 sys.stderr.write( 1066 "Session logs for test failures/errors/unexpected successes" 1067 " can be found in directory '%s'\n" % 1068 configuration.sdir_name) 1069 1070 if configuration.use_categories and len( 1071 configuration.failures_per_category) > 0: 1072 sys.stderr.write("Failures per category:\n") 1073 for category in configuration.failures_per_category: 1074 sys.stderr.write( 1075 "%s - %d\n" % 1076 (category, configuration.failures_per_category[category])) 1077 1078 # Exiting. 1079 exitTestSuite(configuration.failed) 1080 1081if __name__ == "__main__": 1082 print( 1083 __file__ + 1084 " is for use as a module only. It should not be run as a standalone script.") 1085 sys.exit(-1) 1086