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