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