From 061da546b983eb767bad15e67af1174fb0bcf31c Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 3 Aug 2020 14:33:06 +0000 Subject: Import LLVM 10.0.0 release including clang, lld and lldb. ok hackroom tested by plenty --- .../lldb/packages/Python/lldbsuite/test/dotest.py | 1154 ++++++++++++++++++++ 1 file changed, 1154 insertions(+) create mode 100644 gnu/llvm/lldb/packages/Python/lldbsuite/test/dotest.py (limited to 'gnu/llvm/lldb/packages/Python/lldbsuite/test/dotest.py') diff --git a/gnu/llvm/lldb/packages/Python/lldbsuite/test/dotest.py b/gnu/llvm/lldb/packages/Python/lldbsuite/test/dotest.py new file mode 100644 index 00000000000..560e47dc58d --- /dev/null +++ b/gnu/llvm/lldb/packages/Python/lldbsuite/test/dotest.py @@ -0,0 +1,1154 @@ +""" +A simple testing framework for lldb using python's unit testing framework. + +Tests for lldb are written as python scripts which take advantage of the script +bridging provided by LLDB.framework to interact with lldb core. + +A specific naming pattern is followed by the .py script to be recognized as +a module which implements a test scenario, namely, Test*.py. + +To specify the directories where "Test*.py" python test scripts are located, +you need to pass in a list of directory names. By default, the current +working directory is searched if nothing is specified on the command line. + +Type: + +./dotest.py -h + +for available options. +""" + +from __future__ import absolute_import +from __future__ import print_function + +# System modules +import atexit +import datetime +import errno +import logging +import os +import platform +import re +import signal +import subprocess +import sys +import tempfile + +# Third-party modules +import six +import unittest2 + +# LLDB Modules +import lldbsuite +from . import configuration +from . import dotest_args +from . import lldbtest_config +from . import test_categories +from lldbsuite.test_event import formatter +from . import test_result +from lldbsuite.test_event.event_builder import EventBuilder +from ..support import seven + +def get_dotest_invocation(): + return ' '.join(sys.argv) + + +def is_exe(fpath): + """Returns true if fpath is an executable.""" + if fpath == None: + return False + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + +def which(program): + """Returns the full path to a program; None otherwise.""" + fpath, _ = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + + +def usage(parser): + parser.print_help() + if configuration.verbose > 0: + print(""" +Examples: + +This is an example of using the -f option to pinpoint to a specific test class +and test method to be run: + +$ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command +---------------------------------------------------------------------- +Collected 1 test + +test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase) +Test 'frame variable this' when stopped on a class constructor. ... ok + +---------------------------------------------------------------------- +Ran 1 test in 1.396s + +OK + +And this is an example of using the -p option to run a single file (the filename +matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'): + +$ ./dotest.py -v -p ObjC +---------------------------------------------------------------------- +Collected 4 tests + +test_break_with_dsym (TestObjCMethods.FoundationTestCase) +Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok +test_break_with_dwarf (TestObjCMethods.FoundationTestCase) +Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok +test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase) +Lookup objective-c data types and evaluate expressions. ... ok +test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase) +Lookup objective-c data types and evaluate expressions. ... ok + +---------------------------------------------------------------------- +Ran 4 tests in 16.661s + +OK + +Running of this script also sets up the LLDB_TEST environment variable so that +individual test cases can locate their supporting files correctly. The script +tries to set up Python's search paths for modules by looking at the build tree +relative to this script. See also the '-i' option in the following example. + +Finally, this is an example of using the lldb.py module distributed/installed by +Xcode4 to run against the tests under the 'forward' directory, and with the '-w' +option to add some delay between two tests. It uses ARCH=x86_64 to specify that +as the architecture and CC=clang to specify the compiler used for the test run: + +$ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward + +Session logs for test failures/errors will go into directory '2010-11-11-13_56_16' +---------------------------------------------------------------------- +Collected 2 tests + +test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase) +Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok +test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase) +Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok + +---------------------------------------------------------------------- +Ran 2 tests in 5.659s + +OK + +The 'Session ...' verbiage is recently introduced (see also the '-s' option) to +notify the directory containing the session logs for test failures or errors. +In case there is any test failure/error, a similar message is appended at the +end of the stderr output for your convenience. + +ENABLING LOGS FROM TESTS + +Option 1: + +Writing logs into different files per test case:: + +$ ./dotest.py --channel "lldb all" + +$ ./dotest.py --channel "lldb all" --channel "gdb-remote packets" + +These log files are written to: + +/-host.log (logs from lldb host process) +/-server.log (logs from debugserver/lldb-server) +/-.log (console logs) + +By default, logs from successful runs are deleted. Use the --log-success flag +to create reference logs for debugging. + +$ ./dotest.py --log-success + +""") + sys.exit(0) + + +def parseExclusion(exclusion_file): + """Parse an exclusion file, of the following format, where + 'skip files', 'skip methods', 'xfail files', and 'xfail methods' + are the possible list heading values: + + skip files + + + + xfail methods + + """ + excl_type = None + + with open(exclusion_file) as f: + for line in f: + line = line.strip() + if not excl_type: + excl_type = line + continue + + if not line: + excl_type = None + elif excl_type == 'skip': + if not configuration.skip_tests: + configuration.skip_tests = [] + configuration.skip_tests.append(line) + elif excl_type == 'xfail': + if not configuration.xfail_tests: + configuration.xfail_tests = [] + configuration.xfail_tests.append(line) + + +def parseOptionsAndInitTestdirs(): + """Initialize the list of directories containing our unittest scripts. + + '-h/--help as the first option prints out usage info and exit the program. + """ + + do_help = False + + platform_system = platform.system() + platform_machine = platform.machine() + + try: + parser = dotest_args.create_parser() + args = parser.parse_args() + except: + print(get_dotest_invocation()) + raise + + if args.unset_env_varnames: + for env_var in args.unset_env_varnames: + if env_var in os.environ: + # From Python Doc: When unsetenv() is supported, deletion of items in os.environ + # is automatically translated into a corresponding call to + # unsetenv(). + del os.environ[env_var] + # os.unsetenv(env_var) + + if args.set_env_vars: + for env_var in args.set_env_vars: + parts = env_var.split('=', 1) + if len(parts) == 1: + os.environ[parts[0]] = "" + else: + os.environ[parts[0]] = parts[1] + + if args.set_inferior_env_vars: + lldbtest_config.inferior_env = ' '.join(args.set_inferior_env_vars) + + # Only print the args if being verbose. + if args.v: + print(get_dotest_invocation()) + + if args.h: + do_help = True + + if args.compiler: + configuration.compiler = os.path.realpath(args.compiler) + if not is_exe(configuration.compiler): + configuration.compiler = which(args.compiler) + if not is_exe(configuration.compiler): + logging.error( + '%s is not a valid compiler executable; aborting...', + args.compiler) + sys.exit(-1) + else: + # Use a compiler appropriate appropriate for the Apple SDK if one was + # specified + if platform_system == 'Darwin' and args.apple_sdk: + configuration.compiler = seven.get_command_output( + 'xcrun -sdk "%s" -find clang 2> /dev/null' % + (args.apple_sdk)) + else: + # 'clang' on ubuntu 14.04 is 3.4 so we try clang-3.5 first + candidateCompilers = ['clang-3.5', 'clang', 'gcc'] + for candidate in candidateCompilers: + if which(candidate): + configuration.compiler = candidate + break + + if args.dsymutil: + os.environ['DSYMUTIL'] = args.dsymutil + elif platform_system == 'Darwin': + os.environ['DSYMUTIL'] = seven.get_command_output( + 'xcrun -find -toolchain default dsymutil') + + if args.filecheck: + # The lldb-dotest script produced by the CMake build passes in a path + # to a working FileCheck binary. So does one specific Xcode project + # target. However, when invoking dotest.py directly, a valid --filecheck + # option needs to be given. + configuration.filecheck = os.path.abspath(args.filecheck) + + if not configuration.get_filecheck_path(): + logging.warning('No valid FileCheck executable; some tests may fail...') + logging.warning('(Double-check the --filecheck argument to dotest.py)') + + if args.channels: + lldbtest_config.channels = args.channels + + if args.log_success: + lldbtest_config.log_success = args.log_success + + if args.out_of_tree_debugserver: + lldbtest_config.out_of_tree_debugserver = args.out_of_tree_debugserver + + # Set SDKROOT if we are using an Apple SDK + if platform_system == 'Darwin' and args.apple_sdk: + os.environ['SDKROOT'] = seven.get_command_output( + 'xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % + (args.apple_sdk)) + + if args.arch: + configuration.arch = args.arch + if configuration.arch.startswith( + 'arm') and platform_system == 'Darwin' and not args.apple_sdk: + os.environ['SDKROOT'] = seven.get_command_output( + 'xcrun --sdk iphoneos.internal --show-sdk-path 2> /dev/null') + if not os.path.exists(os.environ['SDKROOT']): + os.environ['SDKROOT'] = seven.get_command_output( + 'xcrun --sdk iphoneos --show-sdk-path 2> /dev/null') + else: + configuration.arch = platform_machine + + if args.categories_list: + configuration.categories_list = set( + test_categories.validate( + args.categories_list, False)) + configuration.use_categories = True + else: + configuration.categories_list = [] + + if args.skip_categories: + configuration.skip_categories += test_categories.validate( + args.skip_categories, False) + + if args.xfail_categories: + configuration.xfail_categories += test_categories.validate( + args.xfail_categories, False) + + if args.E: + os.environ['CFLAGS_EXTRAS'] = args.E + + if args.dwarf_version: + configuration.dwarf_version = args.dwarf_version + # We cannot modify CFLAGS_EXTRAS because they're used in test cases + # that explicitly require no debug info. + os.environ['CFLAGS'] = '-gdwarf-{}'.format(configuration.dwarf_version) + + if args.settings: + for setting in args.settings: + if not len(setting) == 1 or not setting[0].count('='): + logging.error('"%s" is not a setting in the form "key=value"', + setting[0]) + sys.exit(-1) + configuration.settings.append(setting[0].split('=', 1)) + + if args.d: + sys.stdout.write( + "Suspending the process %d to wait for debugger to attach...\n" % + os.getpid()) + sys.stdout.flush() + os.kill(os.getpid(), signal.SIGSTOP) + + if args.f: + if any([x.startswith('-') for x in args.f]): + usage(parser) + configuration.filters.extend(args.f) + + if args.framework: + configuration.lldb_framework_path = args.framework + + if args.executable: + # lldb executable is passed explicitly + lldbtest_config.lldbExec = os.path.realpath(args.executable) + if not is_exe(lldbtest_config.lldbExec): + lldbtest_config.lldbExec = which(args.executable) + if not is_exe(lldbtest_config.lldbExec): + logging.error( + '%s is not a valid executable to test; aborting...', + args.executable) + sys.exit(-1) + + if args.server: + os.environ['LLDB_DEBUGSERVER_PATH'] = args.server + + if args.excluded: + for excl_file in args.excluded: + parseExclusion(excl_file) + + if args.p: + if args.p.startswith('-'): + usage(parser) + configuration.regexp = args.p + + if args.s: + configuration.sdir_name = args.s + else: + timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S") + configuration.sdir_name = os.path.join(os.getcwd(), timestamp_started) + + configuration.session_file_format = args.session_file_format + + if args.t: + os.environ['LLDB_COMMAND_TRACE'] = 'YES' + + if args.v: + configuration.verbose = 2 + + # argparse makes sure we have a number + if args.sharp: + configuration.count = args.sharp + + if sys.platform.startswith('win32'): + os.environ['LLDB_DISABLE_CRASH_DIALOG'] = str( + args.disable_crash_dialog) + os.environ['LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE'] = str(True) + + if do_help: + usage(parser) + + if args.results_file: + configuration.results_filename = args.results_file + + if args.results_formatter: + configuration.results_formatter_name = args.results_formatter + if args.results_formatter_options: + configuration.results_formatter_options = args.results_formatter_options + + # Default to using the BasicResultsFormatter if no formatter is specified. + if configuration.results_formatter_name is None: + configuration.results_formatter_name = ( + "lldbsuite.test_event.formatter.results_formatter.ResultsFormatter") + + # rerun-related arguments + configuration.rerun_all_issues = args.rerun_all_issues + + if args.lldb_platform_name: + configuration.lldb_platform_name = args.lldb_platform_name + if args.lldb_platform_url: + configuration.lldb_platform_url = args.lldb_platform_url + if args.lldb_platform_working_dir: + configuration.lldb_platform_working_dir = args.lldb_platform_working_dir + if args.test_build_dir: + configuration.test_build_dir = args.test_build_dir + if args.lldb_module_cache_dir: + configuration.lldb_module_cache_dir = args.lldb_module_cache_dir + else: + configuration.lldb_module_cache_dir = os.path.join( + configuration.test_build_dir, 'module-cache-lldb') + if args.clang_module_cache_dir: + configuration.clang_module_cache_dir = args.clang_module_cache_dir + else: + configuration.clang_module_cache_dir = os.path.join( + configuration.test_build_dir, 'module-cache-clang') + + os.environ['CLANG_MODULE_CACHE_DIR'] = configuration.clang_module_cache_dir + + # Gather all the dirs passed on the command line. + if len(args.args) > 0: + configuration.testdirs = [os.path.realpath(os.path.abspath(x)) for x in args.args] + + lldbtest_config.codesign_identity = args.codesign_identity + + +def setupTestResults(): + """Sets up test results-related objects based on arg settings.""" + # Setup the results formatter configuration. + formatter_config = formatter.FormatterConfig() + formatter_config.filename = configuration.results_filename + formatter_config.formatter_name = configuration.results_formatter_name + formatter_config.formatter_options = ( + configuration.results_formatter_options) + + # Create the results formatter. + formatter_spec = formatter.create_results_formatter( + formatter_config) + if formatter_spec is not None and formatter_spec.formatter is not None: + configuration.results_formatter_object = formatter_spec.formatter + + # Send an initialize message to the formatter. + initialize_event = EventBuilder.bare_event("initialize") + initialize_event["worker_count"] = 1 + + formatter_spec.formatter.handle_event(initialize_event) + + # Make sure we clean up the formatter on shutdown. + if formatter_spec.cleanup_func is not None: + atexit.register(formatter_spec.cleanup_func) + + +def setupSysPath(): + """ + Add LLDB.framework/Resources/Python to the search paths for modules. + As a side effect, we also discover the 'lldb' executable and export it here. + """ + + # Get the directory containing the current script. + if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ: + scriptPath = os.environ["DOTEST_SCRIPT_DIR"] + else: + scriptPath = os.path.dirname(os.path.realpath(__file__)) + if not scriptPath.endswith('test'): + print("This script expects to reside in lldb's test directory.") + sys.exit(-1) + + os.environ["LLDB_TEST"] = scriptPath + + # Set up the root build directory. + builddir = configuration.test_build_dir + if not configuration.test_build_dir: + raise Exception("test_build_dir is not set") + os.environ["LLDB_BUILD"] = os.path.abspath(configuration.test_build_dir) + + # Set up the LLDB_SRC environment variable, so that the tests can locate + # the LLDB source code. + os.environ["LLDB_SRC"] = lldbsuite.lldb_root + + pluginPath = os.path.join(scriptPath, 'plugins') + toolsLLDBVSCode = os.path.join(scriptPath, 'tools', 'lldb-vscode') + toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server') + + # Insert script dir, plugin dir and lldb-server dir to the sys.path. + sys.path.insert(0, pluginPath) + # Adding test/tools/lldb-vscode to the path makes it easy to + # "import lldb_vscode_testcase" from the VSCode tests + sys.path.insert(0, toolsLLDBVSCode) + # Adding test/tools/lldb-server to the path makes it easy + sys.path.insert(0, toolsLLDBServerPath) + # to "import lldbgdbserverutils" from the lldb-server tests + + # This is the root of the lldb git/svn checkout + # When this changes over to a package instead of a standalone script, this + # will be `lldbsuite.lldb_root` + lldbRootDirectory = lldbsuite.lldb_root + + # Some of the tests can invoke the 'lldb' command directly. + # We'll try to locate the appropriate executable right here. + + # The lldb executable can be set from the command line + # if it's not set, we try to find it now + # first, we try the environment + if not lldbtest_config.lldbExec: + # First, you can define an environment variable LLDB_EXEC specifying the + # full pathname of the lldb executable. + if "LLDB_EXEC" in os.environ: + lldbtest_config.lldbExec = os.environ["LLDB_EXEC"] + + if not lldbtest_config.lldbExec: + # Last, check the path + lldbtest_config.lldbExec = which('lldb') + + if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec): + print( + "'{}' is not a path to a valid executable".format( + lldbtest_config.lldbExec)) + lldbtest_config.lldbExec = None + + if not lldbtest_config.lldbExec: + print("The 'lldb' executable cannot be located. Some of the tests may not be run as a result.") + sys.exit(-1) + + # confusingly, this is the "bin" directory + lldbLibDir = os.path.dirname(lldbtest_config.lldbExec) + os.environ["LLDB_LIB_DIR"] = lldbLibDir + lldbImpLibDir = os.path.join( + lldbLibDir, + '..', + 'lib') if sys.platform.startswith('win32') else lldbLibDir + os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir + print("LLDB library dir:", os.environ["LLDB_LIB_DIR"]) + print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"]) + os.system('%s -v' % lldbtest_config.lldbExec) + + lldbDir = os.path.dirname(lldbtest_config.lldbExec) + + lldbVSCodeExec = os.path.join(lldbDir, "lldb-vscode") + if is_exe(lldbVSCodeExec): + os.environ["LLDBVSCODE_EXEC"] = lldbVSCodeExec + else: + if not configuration.shouldSkipBecauseOfCategories(["lldb-vscode"]): + print( + "The 'lldb-vscode' executable cannot be located. The lldb-vscode tests can not be run as a result.") + configuration.skip_categories.append("lldb-vscode") + + lldbPythonDir = None # The directory that contains 'lldb/__init__.py' + if not configuration.lldb_framework_path and os.path.exists(os.path.join(lldbLibDir, "LLDB.framework")): + configuration.lldb_framework_path = os.path.join(lldbLibDir, "LLDB.framework") + if configuration.lldb_framework_path: + lldbtest_config.lldb_framework_path = configuration.lldb_framework_path + candidatePath = os.path.join( + configuration.lldb_framework_path, 'Resources', 'Python') + if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')): + lldbPythonDir = candidatePath + if not lldbPythonDir: + print( + 'Resources/Python/lldb/__init__.py was not found in ' + + configuration.lldb_framework_path) + sys.exit(-1) + else: + # If our lldb supports the -P option, use it to find the python path: + init_in_python_dir = os.path.join('lldb', '__init__.py') + + lldb_dash_p_result = subprocess.check_output( + [lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True) + + if lldb_dash_p_result and not lldb_dash_p_result.startswith( + ("<", "lldb: invalid option:")) and not lldb_dash_p_result.startswith("Traceback"): + lines = lldb_dash_p_result.splitlines() + + # Workaround for readline vs libedit issue on FreeBSD. If stdout + # is not a terminal Python executes + # rl_variable_bind ("enable-meta-key", "off"); + # This produces a warning with FreeBSD's libedit because the + # enable-meta-key variable is unknown. Not an issue on Apple + # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__ + # around the call. See http://bugs.python.org/issue19884 for more + # information. For now we just discard the warning output. + if len(lines) >= 1 and lines[0].startswith( + "bind: Invalid command"): + lines.pop(0) + + # Taking the last line because lldb outputs + # 'Cannot read termcap database;\nusing dumb terminal settings.\n' + # before the path + if len(lines) >= 1 and os.path.isfile( + os.path.join(lines[-1], init_in_python_dir)): + lldbPythonDir = lines[-1] + if "freebsd" in sys.platform or "linux" in sys.platform: + os.environ['LLDB_LIB_DIR'] = os.path.join( + lldbPythonDir, '..', '..') + + if not lldbPythonDir: + print( + "Unable to load lldb extension module. Possible reasons for this include:") + print(" 1) LLDB was built with LLDB_ENABLE_PYTHON=0") + print( + " 2) PYTHONPATH and PYTHONHOME are not set correctly. PYTHONHOME should refer to") + print( + " the version of Python that LLDB built and linked against, and PYTHONPATH") + print( + " should contain the Lib directory for the same python distro, as well as the") + print(" location of LLDB\'s site-packages folder.") + print( + " 3) A different version of Python than that which was built against is exported in") + print(" the system\'s PATH environment variable, causing conflicts.") + print( + " 4) The executable '%s' could not be found. Please check " % + lldbtest_config.lldbExec) + print(" that it exists and is executable.") + + if lldbPythonDir: + lldbPythonDir = os.path.normpath(lldbPythonDir) + # Some of the code that uses this path assumes it hasn't resolved the Versions... link. + # If the path we've constructed looks like that, then we'll strip out + # the Versions/A part. + (before, frameWithVersion, after) = lldbPythonDir.rpartition( + "LLDB.framework/Versions/A") + if frameWithVersion != "": + lldbPythonDir = before + "LLDB.framework" + after + + lldbPythonDir = os.path.abspath(lldbPythonDir) + + # If tests need to find LLDB_FRAMEWORK, now they can do it + os.environ["LLDB_FRAMEWORK"] = os.path.dirname( + os.path.dirname(lldbPythonDir)) + + # This is to locate the lldb.py module. Insert it right after + # sys.path[0]. + sys.path[1:1] = [lldbPythonDir] + + +def visit_file(dir, name): + # Try to match the regexp pattern, if specified. + if configuration.regexp: + if not re.search(configuration.regexp, name): + # We didn't match the regex, we're done. + return + + if configuration.skip_tests: + for file_regexp in configuration.skip_tests: + if re.search(file_regexp, name): + return + + # We found a match for our test. Add it to the suite. + + # Update the sys.path first. + if not sys.path.count(dir): + sys.path.insert(0, dir) + base = os.path.splitext(name)[0] + + # Thoroughly check the filterspec against the base module and admit + # the (base, filterspec) combination only when it makes sense. + + def check(obj, parts): + for part in parts: + try: + parent, obj = obj, getattr(obj, part) + except AttributeError: + # The filterspec has failed. + return False + return True + + module = __import__(base) + + def iter_filters(): + for filterspec in configuration.filters: + parts = filterspec.split('.') + if check(module, parts): + yield filterspec + elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]): + yield '.'.join(parts[1:]) + else: + for key,value in module.__dict__.items(): + if check(value, parts): + yield key + '.' + filterspec + + filtered = False + for filterspec in iter_filters(): + filtered = True + print("adding filter spec %s to module %s" % (filterspec, repr(module))) + tests = unittest2.defaultTestLoader.loadTestsFromName(filterspec, module) + configuration.suite.addTests(tests) + + # Forgo this module if the (base, filterspec) combo is invalid + if configuration.filters and not filtered: + return + + if not filtered: + # Add the entire file's worth of tests since we're not filtered. + # Also the fail-over case when the filterspec branch + # (base, filterspec) combo doesn't make sense. + configuration.suite.addTests( + unittest2.defaultTestLoader.loadTestsFromName(base)) + + +def visit(prefix, dir, names): + """Visitor function for os.path.walk(path, visit, arg).""" + + dir_components = set(dir.split(os.sep)) + excluded_components = set(['.svn', '.git']) + if dir_components.intersection(excluded_components): + return + + # Gather all the Python test file names that follow the Test*.py pattern. + python_test_files = [ + name + for name in names + if name.endswith('.py') and name.startswith(prefix)] + + # Visit all the python test files. + for name in python_test_files: + try: + # Ensure we error out if we have multiple tests with the same + # base name. + # Future improvement: find all the places where we work with base + # names and convert to full paths. We have directory structure + # to disambiguate these, so we shouldn't need this constraint. + if name in configuration.all_tests: + raise Exception("Found multiple tests with the name %s" % name) + configuration.all_tests.add(name) + + # Run the relevant tests in the python file. + visit_file(dir, name) + except Exception as ex: + # Convert this exception to a test event error for the file. + test_filename = os.path.abspath(os.path.join(dir, name)) + if configuration.results_formatter_object is not None: + # Grab the backtrace for the exception. + import traceback + backtrace = traceback.format_exc() + + # Generate the test event. + configuration.results_formatter_object.handle_event( + EventBuilder.event_for_job_test_add_error( + test_filename, ex, backtrace)) + raise + + +def setSetting(setting, value): + import lldb + ci = lldb.DBG.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + cmd = 'setting set %s %s'%(setting, value) + print(cmd) + ci.HandleCommand(cmd, res, False) + if not res.Succeeded(): + raise Exception('failed to run "%s"'%cmd) + +# ======================================== # +# # +# Execution of the test driver starts here # +# # +# ======================================== # + + +def checkDsymForUUIDIsNotOn(): + cmd = ["defaults", "read", "com.apple.DebugSymbols"] + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + cmd_output = process.stdout.read() + output_str = cmd_output.decode("utf-8") + if "DBGFileMappedPaths = " in output_str: + print("%s =>" % ' '.join(cmd)) + print(output_str) + print( + "Disable automatic lookup and caching of dSYMs before running the test suite!") + print("Exiting...") + sys.exit(0) + + +def exitTestSuite(exitCode=None): + import lldb + lldb.SBDebugger.Terminate() + if exitCode: + sys.exit(exitCode) + + +def getVersionForSDK(sdk): + sdk = str.lower(sdk) + full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk) + basename = os.path.basename(full_path) + basename = os.path.splitext(basename)[0] + basename = str.lower(basename) + ver = basename.replace(sdk, '') + return ver + + +def setDefaultTripleForPlatform(): + if configuration.lldb_platform_name == 'ios-simulator': + triple_str = 'x86_64-apple-ios%s' % ( + getVersionForSDK('iphonesimulator')) + os.environ['TRIPLE'] = triple_str + return {'TRIPLE': triple_str} + return {} + + +def checkCompiler(): + # Add some intervention here to sanity check that the compiler requested is sane. + # If found not to be an executable program, we abort. + c = configuration.compiler + if which(c): + return + + if not sys.platform.startswith("darwin"): + raise Exception(c + " is not a valid compiler") + + pipe = subprocess.Popen( + ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + cmd_output = pipe.stdout.read() + if not cmd_output or "not found" in cmd_output: + raise Exception(c + " is not a valid compiler") + + configuration.compiler = cmd_output.split('\n')[0] + print("'xcrun -find %s' returning %s" % (c, configuration.compiler)) + +def canRunLibcxxTests(): + from lldbsuite.test import lldbplatformutil + + platform = lldbplatformutil.getPlatform() + + if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin(): + return True, "libc++ always present" + + if platform == "linux": + if os.path.isdir("/usr/include/c++/v1"): + return True, "Headers found, let's hope they work" + with tempfile.NamedTemporaryFile() as f: + cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"] + p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + _, stderr = p.communicate("#include \nint main() {}") + if not p.returncode: + return True, "Compiling with -stdlib=libc++ works" + return False, "Compiling with -stdlib=libc++ fails with the error: %s" % stderr + + return False, "Don't know how to build with libc++ on %s" % platform + +def checkLibcxxSupport(): + result, reason = canRunLibcxxTests() + if result: + return # libc++ supported + if "libc++" in configuration.categories_list: + return # libc++ category explicitly requested, let it run. + print("Libc++ tests will not be run because: " + reason) + configuration.skip_categories.append("libc++") + +def canRunLibstdcxxTests(): + from lldbsuite.test import lldbplatformutil + + platform = lldbplatformutil.getPlatform() + if lldbplatformutil.target_is_android(): + platform = "android" + if platform == "linux": + return True, "libstdcxx always present" + return False, "Don't know how to build with libstdcxx on %s" % platform + +def checkLibstdcxxSupport(): + result, reason = canRunLibstdcxxTests() + if result: + return # libstdcxx supported + if "libstdcxx" in configuration.categories_list: + return # libstdcxx category explicitly requested, let it run. + print("libstdcxx tests will not be run because: " + reason) + configuration.skip_categories.append("libstdcxx") + +def canRunWatchpointTests(): + from lldbsuite.test import lldbplatformutil + + platform = lldbplatformutil.getPlatform() + if platform == "netbsd": + if os.geteuid() == 0: + return True, "root can always write dbregs" + try: + output = subprocess.check_output(["/sbin/sysctl", "-n", + "security.models.extensions.user_set_dbregs"]).decode().strip() + if output == "1": + return True, "security.models.extensions.user_set_dbregs enabled" + except subprocess.CalledProcessError: + pass + return False, "security.models.extensions.user_set_dbregs disabled" + return True, "watchpoint support available" + +def checkWatchpointSupport(): + result, reason = canRunWatchpointTests() + if result: + return # watchpoints supported + if "watchpoint" in configuration.categories_list: + return # watchpoint category explicitly requested, let it run. + print("watchpoint tests will not be run because: " + reason) + configuration.skip_categories.append("watchpoint") + +def checkDebugInfoSupport(): + import lldb + + platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] + compiler = configuration.compiler + skipped = [] + for cat in test_categories.debug_info_categories: + if cat in configuration.categories_list: + continue # Category explicitly requested, let it run. + if test_categories.is_supported_on_platform(cat, platform, compiler): + continue + configuration.skip_categories.append(cat) + skipped.append(cat) + if skipped: + print("Skipping following debug info categories:", skipped) + +def run_suite(): + # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults + # does not exist before proceeding to running the test suite. + if sys.platform.startswith("darwin"): + checkDsymForUUIDIsNotOn() + + # Start the actions by first parsing the options while setting up the test + # directories, followed by setting up the search paths for lldb utilities; + # then, we walk the directory trees and collect the tests into our test suite. + # + parseOptionsAndInitTestdirs() + + # Setup test results (test results formatter and output handling). + setupTestResults() + + setupSysPath() + + + # For the time being, let's bracket the test runner within the + # lldb.SBDebugger.Initialize()/Terminate() pair. + import lldb + + # Now we can also import lldbutil + from lldbsuite.test import lldbutil + + # Create a singleton SBDebugger in the lldb namespace. + lldb.DBG = lldb.SBDebugger.Create() + + if configuration.lldb_platform_name: + print("Setting up remote platform '%s'" % + (configuration.lldb_platform_name)) + lldb.remote_platform = lldb.SBPlatform( + configuration.lldb_platform_name) + if not lldb.remote_platform.IsValid(): + print( + "error: unable to create the LLDB platform named '%s'." % + (configuration.lldb_platform_name)) + exitTestSuite(1) + if configuration.lldb_platform_url: + # We must connect to a remote platform if a LLDB platform URL was + # specified + print( + "Connecting to remote platform '%s' at '%s'..." % + (configuration.lldb_platform_name, configuration.lldb_platform_url)) + platform_connect_options = lldb.SBPlatformConnectOptions( + configuration.lldb_platform_url) + err = lldb.remote_platform.ConnectRemote(platform_connect_options) + if err.Success(): + print("Connected.") + else: + print("error: failed to connect to remote platform using URL '%s': %s" % ( + configuration.lldb_platform_url, err)) + exitTestSuite(1) + else: + configuration.lldb_platform_url = None + + platform_changes = setDefaultTripleForPlatform() + first = True + for key in platform_changes: + if first: + print("Environment variables setup for platform support:") + first = False + print("%s = %s" % (key, platform_changes[key])) + + if configuration.lldb_platform_working_dir: + print("Setting remote platform working directory to '%s'..." % + (configuration.lldb_platform_working_dir)) + error = lldb.remote_platform.MakeDirectory( + configuration.lldb_platform_working_dir, 448) # 448 = 0o700 + if error.Fail(): + raise Exception("making remote directory '%s': %s" % ( + configuration.lldb_platform_working_dir, error)) + + if not lldb.remote_platform.SetWorkingDirectory( + configuration.lldb_platform_working_dir): + raise Exception("failed to set working directory '%s'" % configuration.lldb_platform_working_dir) + lldb.DBG.SetSelectedPlatform(lldb.remote_platform) + else: + lldb.remote_platform = None + configuration.lldb_platform_working_dir = None + configuration.lldb_platform_url = None + + # Set up the working directory. + # Note that it's not dotest's job to clean this directory. + build_dir = configuration.test_build_dir + lldbutil.mkdir_p(build_dir) + + target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] + + checkLibcxxSupport() + checkLibstdcxxSupport() + checkWatchpointSupport() + checkDebugInfoSupport() + + # Don't do debugserver tests on anything except OS X. + configuration.dont_do_debugserver_test = ( + "linux" in target_platform or + "freebsd" in target_platform or + "netbsd" in target_platform or + "windows" in target_platform) + + # Don't do lldb-server (llgs) tests on anything except Linux and Windows. + configuration.dont_do_llgs_test = not ( + "linux" in target_platform or + "netbsd" in target_platform or + "windows" in target_platform) + + # Collect tests from the specified testing directories. If a test + # subdirectory filter is explicitly specified, limit the search to that + # subdirectory. + exclusive_test_subdir = configuration.get_absolute_path_to_exclusive_test_subdir() + if exclusive_test_subdir: + dirs_to_search = [exclusive_test_subdir] + else: + dirs_to_search = configuration.testdirs + for testdir in dirs_to_search: + for (dirpath, dirnames, filenames) in os.walk(testdir): + visit('Test', dirpath, filenames) + + # + # Now that we have loaded all the test cases, run the whole test suite. + # + + # Set any user-overridden settings. + for key, value in configuration.settings: + setSetting(key, value) + + # Install the control-c handler. + unittest2.signals.installHandler() + + lldbutil.mkdir_p(configuration.sdir_name) + os.environ["LLDB_SESSION_DIRNAME"] = configuration.sdir_name + + sys.stderr.write( + "\nSession logs for test failures/errors/unexpected successes" + " will go into directory '%s'\n" % + configuration.sdir_name) + sys.stderr.write("Command invoked: %s\n" % get_dotest_invocation()) + + # + # Invoke the default TextTestRunner to run the test suite + # + checkCompiler() + + if configuration.verbose: + print("compiler=%s" % configuration.compiler) + + # Iterating over all possible architecture and compiler combinations. + os.environ["ARCH"] = configuration.arch + os.environ["CC"] = configuration.compiler + configString = "arch=%s compiler=%s" % (configuration.arch, + configuration.compiler) + + # Output the configuration. + if configuration.verbose: + sys.stderr.write("\nConfiguration: " + configString + "\n") + + # First, write out the number of collected test cases. + if configuration.verbose: + sys.stderr.write(configuration.separator + "\n") + sys.stderr.write( + "Collected %d test%s\n\n" % + (configuration.suite.countTestCases(), + configuration.suite.countTestCases() != 1 and "s" or "")) + + # Invoke the test runner. + if configuration.count == 1: + result = unittest2.TextTestRunner( + stream=sys.stderr, + verbosity=configuration.verbose, + resultclass=test_result.LLDBTestResult).run( + configuration.suite) + else: + # We are invoking the same test suite more than once. In this case, + # mark __ignore_singleton__ flag as True so the signleton pattern is + # not enforced. + test_result.LLDBTestResult.__ignore_singleton__ = True + for i in range(configuration.count): + + result = unittest2.TextTestRunner( + stream=sys.stderr, + verbosity=configuration.verbose, + resultclass=test_result.LLDBTestResult).run( + configuration.suite) + + configuration.failed = not result.wasSuccessful() + + if configuration.sdir_has_content and configuration.verbose: + sys.stderr.write( + "Session logs for test failures/errors/unexpected successes" + " can be found in directory '%s'\n" % + configuration.sdir_name) + + if configuration.use_categories and len( + configuration.failures_per_category) > 0: + sys.stderr.write("Failures per category:\n") + for category in configuration.failures_per_category: + sys.stderr.write( + "%s - %d\n" % + (category, configuration.failures_per_category[category])) + + # Exiting. + exitTestSuite(configuration.failed) + +if __name__ == "__main__": + print( + __file__ + + " is for use as a module only. It should not be run as a standalone script.") + sys.exit(-1) -- cgit v1.2.3-59-g8ed1b