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