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