xref: /llvm-project/lldb/packages/Python/lldbsuite/test/dotest.py (revision aea06684992873f70c5834e2f455f913e5b8d671)
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 is 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.make:
270        configuration.make_path = args.make
271    elif platform_system == "FreeBSD" or platform_system == "NetBSD":
272        configuration.make_path = "gmake"
273    else:
274        configuration.make_path = "make"
275
276    if args.dsymutil:
277        configuration.dsymutil = args.dsymutil
278    elif platform_system == "Darwin":
279        configuration.dsymutil = seven.get_command_output(
280            "xcrun -find -toolchain default dsymutil"
281        )
282    if args.llvm_tools_dir:
283        configuration.llvm_tools_dir = args.llvm_tools_dir
284        configuration.filecheck = shutil.which("FileCheck", path=args.llvm_tools_dir)
285        configuration.yaml2obj = shutil.which("yaml2obj", path=args.llvm_tools_dir)
286
287    if not configuration.get_filecheck_path():
288        logging.warning("No valid FileCheck executable; some tests may fail...")
289        logging.warning("(Double-check the --llvm-tools-dir argument to dotest.py)")
290
291    if args.libcxx_include_dir or args.libcxx_library_dir:
292        if args.lldb_platform_name:
293            logging.warning(
294                "Custom libc++ is not supported for remote runs: ignoring --libcxx arguments"
295            )
296        elif not (args.libcxx_include_dir and args.libcxx_library_dir):
297            logging.error(
298                "Custom libc++ requires both --libcxx-include-dir and --libcxx-library-dir"
299            )
300            sys.exit(-1)
301    configuration.libcxx_include_dir = args.libcxx_include_dir
302    configuration.libcxx_include_target_dir = args.libcxx_include_target_dir
303    configuration.libcxx_library_dir = args.libcxx_library_dir
304
305    if args.channels:
306        lldbtest_config.channels = args.channels
307
308    if args.log_success:
309        lldbtest_config.log_success = args.log_success
310
311    if args.out_of_tree_debugserver:
312        lldbtest_config.out_of_tree_debugserver = args.out_of_tree_debugserver
313
314    # Set SDKROOT if we are using an Apple SDK
315    if args.sysroot is not None:
316        configuration.sdkroot = args.sysroot
317    elif platform_system == "Darwin" and args.apple_sdk:
318        configuration.sdkroot = seven.get_command_output(
319            'xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % (args.apple_sdk)
320        )
321        if not configuration.sdkroot:
322            logging.error("No SDK found with the name %s; aborting...", args.apple_sdk)
323            sys.exit(-1)
324
325    if args.arch:
326        configuration.arch = args.arch
327    else:
328        configuration.arch = platform_machine
329
330    if args.categories_list:
331        configuration.categories_list = set(
332            test_categories.validate(args.categories_list, False)
333        )
334        configuration.use_categories = True
335    else:
336        configuration.categories_list = []
337
338    if args.skip_categories:
339        configuration.skip_categories += test_categories.validate(
340            args.skip_categories, False
341        )
342
343    if args.xfail_categories:
344        configuration.xfail_categories += test_categories.validate(
345            args.xfail_categories, False
346        )
347
348    if args.E:
349        os.environ["CFLAGS_EXTRAS"] = args.E
350
351    if args.dwarf_version:
352        configuration.dwarf_version = args.dwarf_version
353        # We cannot modify CFLAGS_EXTRAS because they're used in test cases
354        # that explicitly require no debug info.
355        os.environ["CFLAGS"] = "-gdwarf-{}".format(configuration.dwarf_version)
356
357    if args.settings:
358        for setting in args.settings:
359            if not len(setting) == 1 or not setting[0].count("="):
360                logging.error(
361                    '"%s" is not a setting in the form "key=value"', setting[0]
362                )
363                sys.exit(-1)
364            setting_list = setting[0].split("=", 1)
365            configuration.settings.append((setting_list[0], setting_list[1]))
366
367    if args.d:
368        sys.stdout.write(
369            "Suspending the process %d to wait for debugger to attach...\n"
370            % os.getpid()
371        )
372        sys.stdout.flush()
373        os.kill(os.getpid(), signal.SIGSTOP)
374
375    if args.f:
376        if any([x.startswith("-") for x in args.f]):
377            usage(parser)
378        configuration.filters.extend(args.f)
379
380    if args.framework:
381        configuration.lldb_framework_path = args.framework
382
383    if args.executable:
384        # lldb executable is passed explicitly
385        lldbtest_config.lldbExec = os.path.abspath(args.executable)
386        if not is_exe(lldbtest_config.lldbExec):
387            lldbtest_config.lldbExec = which(args.executable)
388        if not is_exe(lldbtest_config.lldbExec):
389            logging.error(
390                "%s is not a valid executable to test; aborting...", args.executable
391            )
392            sys.exit(-1)
393
394    if args.excluded:
395        for excl_file in args.excluded:
396            parseExclusion(excl_file)
397
398    if args.p:
399        if args.p.startswith("-"):
400            usage(parser)
401        configuration.regexp = args.p
402
403    if args.t:
404        os.environ["LLDB_COMMAND_TRACE"] = "YES"
405
406    if args.v:
407        configuration.verbose = 2
408
409    # argparse makes sure we have a number
410    if args.sharp:
411        configuration.count = args.sharp
412
413    if sys.platform.startswith("win32"):
414        os.environ["LLDB_DISABLE_CRASH_DIALOG"] = str(args.disable_crash_dialog)
415        os.environ["LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"] = str(True)
416
417    if do_help:
418        usage(parser)
419
420    if args.lldb_platform_name:
421        configuration.lldb_platform_name = args.lldb_platform_name
422    if args.lldb_platform_url:
423        configuration.lldb_platform_url = args.lldb_platform_url
424    if args.lldb_platform_working_dir:
425        configuration.lldb_platform_working_dir = args.lldb_platform_working_dir
426    if platform_system == "Darwin" and args.apple_sdk:
427        configuration.apple_sdk = args.apple_sdk
428    if args.test_build_dir:
429        configuration.test_build_dir = args.test_build_dir
430    if args.lldb_module_cache_dir:
431        configuration.lldb_module_cache_dir = args.lldb_module_cache_dir
432    else:
433        configuration.lldb_module_cache_dir = os.path.join(
434            configuration.test_build_dir, "module-cache-lldb"
435        )
436
437    if args.clang_module_cache_dir:
438        configuration.clang_module_cache_dir = args.clang_module_cache_dir
439    else:
440        configuration.clang_module_cache_dir = os.path.join(
441            configuration.test_build_dir, "module-cache-clang"
442        )
443
444    if args.lldb_libs_dir:
445        configuration.lldb_libs_dir = args.lldb_libs_dir
446    if args.lldb_obj_root:
447        configuration.lldb_obj_root = args.lldb_obj_root
448
449    if args.enabled_plugins:
450        configuration.enabled_plugins = args.enabled_plugins
451
452    # Gather all the dirs passed on the command line.
453    if len(args.args) > 0:
454        configuration.testdirs = [
455            os.path.realpath(os.path.abspath(x)) for x in args.args
456        ]
457
458
459def registerFaulthandler():
460    try:
461        import faulthandler
462    except ImportError:
463        # faulthandler is not available until python3
464        return
465
466    faulthandler.enable()
467    # faulthandler.register is not available on Windows.
468    if getattr(faulthandler, "register", None):
469        faulthandler.register(signal.SIGTERM, chain=True)
470
471
472def setupSysPath():
473    """
474    Add LLDB.framework/Resources/Python to the search paths for modules.
475    As a side effect, we also discover the 'lldb' executable and export it here.
476    """
477
478    # Get the directory containing the current script.
479    if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
480        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
481    else:
482        scriptPath = os.path.dirname(os.path.abspath(__file__))
483    if not scriptPath.endswith("test"):
484        print("This script expects to reside in lldb's test directory.")
485        sys.exit(-1)
486
487    os.environ["LLDB_TEST"] = scriptPath
488
489    # Set up the root build directory.
490    if not configuration.test_build_dir:
491        raise Exception("test_build_dir is not set")
492    configuration.test_build_dir = os.path.abspath(configuration.test_build_dir)
493
494    # Set up the LLDB_SRC environment variable, so that the tests can locate
495    # the LLDB source code.
496    os.environ["LLDB_SRC"] = lldbsuite.lldb_root
497
498    pluginPath = os.path.join(scriptPath, "plugins")
499    toolsLLDBDAP = os.path.join(scriptPath, "tools", "lldb-dap")
500    toolsLLDBServerPath = os.path.join(scriptPath, "tools", "lldb-server")
501    intelpt = os.path.join(scriptPath, "tools", "intelpt")
502
503    # Insert script dir, plugin dir and lldb-server dir to the sys.path.
504    sys.path.insert(0, pluginPath)
505    # Adding test/tools/lldb-dap to the path makes it easy to
506    # "import lldb_dap_testcase" from the DAP tests
507    sys.path.insert(0, toolsLLDBDAP)
508    # Adding test/tools/lldb-server to the path makes it easy
509    # to "import lldbgdbserverutils" from the lldb-server tests
510    sys.path.insert(0, toolsLLDBServerPath)
511    # Adding test/tools/intelpt to the path makes it easy
512    # to "import intelpt_testcase" from the lldb-server tests
513    sys.path.insert(0, intelpt)
514
515    # This is the root of the lldb git/svn checkout
516    # When this changes over to a package instead of a standalone script, this
517    # will be `lldbsuite.lldb_root`
518    lldbRootDirectory = lldbsuite.lldb_root
519
520    # Some of the tests can invoke the 'lldb' command directly.
521    # We'll try to locate the appropriate executable right here.
522
523    # The lldb executable can be set from the command line
524    # if it's not set, we try to find it now
525    # first, we try the environment
526    if not lldbtest_config.lldbExec:
527        # First, you can define an environment variable LLDB_EXEC specifying the
528        # full pathname of the lldb executable.
529        if "LLDB_EXEC" in os.environ:
530            lldbtest_config.lldbExec = os.environ["LLDB_EXEC"]
531
532    if not lldbtest_config.lldbExec:
533        # Last, check the path
534        lldbtest_config.lldbExec = which("lldb")
535
536    if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec):
537        print(
538            "'{}' is not a path to a valid executable".format(lldbtest_config.lldbExec)
539        )
540        lldbtest_config.lldbExec = None
541
542    if not lldbtest_config.lldbExec:
543        print(
544            "The 'lldb' executable cannot be located.  Some of the tests may not be run as a result."
545        )
546        sys.exit(-1)
547
548    os.system("%s -v" % lldbtest_config.lldbExec)
549
550    lldbDir = os.path.dirname(lldbtest_config.lldbExec)
551
552    lldbDAPExec = os.path.join(lldbDir, "lldb-dap")
553    if is_exe(lldbDAPExec):
554        os.environ["LLDBDAP_EXEC"] = lldbDAPExec
555
556    lldbPythonDir = None  # The directory that contains 'lldb/__init__.py'
557
558    # If our lldb supports the -P option, use it to find the python path:
559    lldb_dash_p_result = subprocess.check_output(
560        [lldbtest_config.lldbExec, "-P"], universal_newlines=True
561    )
562    if lldb_dash_p_result:
563        for line in lldb_dash_p_result.splitlines():
564            if os.path.isdir(line) and os.path.exists(
565                os.path.join(line, "lldb", "__init__.py")
566            ):
567                lldbPythonDir = line
568                break
569
570    if not lldbPythonDir:
571        print(
572            "Unable to load lldb extension module.  Possible reasons for this include:"
573        )
574        print("  1) LLDB was built with LLDB_ENABLE_PYTHON=0")
575        print(
576            "  2) PYTHONPATH and PYTHONHOME are not set correctly.  PYTHONHOME should refer to"
577        )
578        print(
579            "     the version of Python that LLDB built and linked against, and PYTHONPATH"
580        )
581        print(
582            "     should contain the Lib directory for the same python distro, as well as the"
583        )
584        print("     location of LLDB's site-packages folder.")
585        print(
586            "  3) A different version of Python than that which was built against is exported in"
587        )
588        print("     the system's PATH environment variable, causing conflicts.")
589        print(
590            "  4) The executable '%s' could not be found.  Please check "
591            % lldbtest_config.lldbExec
592        )
593        print("     that it exists and is executable.")
594
595    if lldbPythonDir:
596        lldbPythonDir = os.path.normpath(lldbPythonDir)
597        # Some of the code that uses this path assumes it hasn't resolved the Versions... link.
598        # If the path we've constructed looks like that, then we'll strip out
599        # the Versions/A part.
600        (before, frameWithVersion, after) = lldbPythonDir.rpartition(
601            "LLDB.framework/Versions/A"
602        )
603        if frameWithVersion != "":
604            lldbPythonDir = before + "LLDB.framework" + after
605
606        lldbPythonDir = os.path.abspath(lldbPythonDir)
607
608        if "freebsd" in sys.platform or "linux" in sys.platform:
609            os.environ["LLDB_LIB_DIR"] = os.path.join(lldbPythonDir, "..", "..")
610
611        # If tests need to find LLDB_FRAMEWORK, now they can do it
612        os.environ["LLDB_FRAMEWORK"] = os.path.dirname(os.path.dirname(lldbPythonDir))
613
614        # This is to locate the lldb.py module.  Insert it right after
615        # sys.path[0].
616        sys.path[1:1] = [lldbPythonDir]
617
618
619def visit_file(dir, name):
620    # Try to match the regexp pattern, if specified.
621    if configuration.regexp:
622        if not re.search(configuration.regexp, name):
623            # We didn't match the regex, we're done.
624            return
625
626    if configuration.skip_tests:
627        for file_regexp in configuration.skip_tests:
628            if re.search(file_regexp, name):
629                return
630
631    # We found a match for our test.  Add it to the suite.
632
633    # Update the sys.path first.
634    if not sys.path.count(dir):
635        sys.path.insert(0, dir)
636    base = os.path.splitext(name)[0]
637
638    # Thoroughly check the filterspec against the base module and admit
639    # the (base, filterspec) combination only when it makes sense.
640
641    def check(obj, parts):
642        for part in parts:
643            try:
644                parent, obj = obj, getattr(obj, part)
645            except AttributeError:
646                # The filterspec has failed.
647                return False
648        return True
649
650    module = __import__(base)
651
652    def iter_filters():
653        for filterspec in configuration.filters:
654            parts = filterspec.split(".")
655            if check(module, parts):
656                yield filterspec
657            elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]):
658                yield ".".join(parts[1:])
659            else:
660                for key, value in module.__dict__.items():
661                    if check(value, parts):
662                        yield key + "." + filterspec
663
664    filtered = False
665    for filterspec in iter_filters():
666        filtered = True
667        print("adding filter spec %s to module %s" % (filterspec, repr(module)))
668        tests = unittest.defaultTestLoader.loadTestsFromName(filterspec, module)
669        configuration.suite.addTests(tests)
670
671    # Forgo this module if the (base, filterspec) combo is invalid
672    if configuration.filters and not filtered:
673        return
674
675    if not filtered:
676        # Add the entire file's worth of tests since we're not filtered.
677        # Also the fail-over case when the filterspec branch
678        # (base, filterspec) combo doesn't make sense.
679        configuration.suite.addTests(unittest.defaultTestLoader.loadTestsFromName(base))
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 checkPexpectSupport():
925    from lldbsuite.test import lldbplatformutil
926
927    platform = lldbplatformutil.getPlatform()
928
929    # llvm.org/pr22274: need a pexpect replacement for windows
930    if platform in ["windows"]:
931        if configuration.verbose:
932            print("pexpect tests will be skipped because of unsupported platform")
933        configuration.skip_categories.append("pexpect")
934
935
936def checkDAPSupport():
937    import lldb
938
939    if "LLDBDAP_EXEC" not in os.environ:
940        msg = (
941            "The 'lldb-dap' executable cannot be located and its tests will not be run."
942        )
943    elif lldb.remote_platform:
944        msg = "lldb-dap tests are not compatible with remote platforms and will not be run."
945    else:
946        msg = None
947
948    if msg:
949        if configuration.verbose:
950            print(msg)
951        configuration.skip_categories.append("lldb-dap")
952
953
954def run_suite():
955    # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
956    # does not exist before proceeding to running the test suite.
957    if sys.platform.startswith("darwin"):
958        checkDsymForUUIDIsNotOn()
959
960    # Start the actions by first parsing the options while setting up the test
961    # directories, followed by setting up the search paths for lldb utilities;
962    # then, we walk the directory trees and collect the tests into our test suite.
963    #
964    parseOptionsAndInitTestdirs()
965
966    # Print a stack trace if the test hangs or is passed SIGTERM.
967    registerFaulthandler()
968
969    setupSysPath()
970
971    import lldb
972
973    lldb.SBDebugger.Initialize()
974    lldb.SBDebugger.PrintStackTraceOnError()
975
976    # Use host platform by default.
977    lldb.remote_platform = None
978    lldb.selected_platform = lldb.SBPlatform.GetHostPlatform()
979
980    # Now we can also import lldbutil
981    from lldbsuite.test import lldbutil
982
983    if configuration.lldb_platform_name:
984        print("Setting up remote platform '%s'" % (configuration.lldb_platform_name))
985        lldb.remote_platform = lldb.SBPlatform(configuration.lldb_platform_name)
986        lldb.selected_platform = lldb.remote_platform
987        if not lldb.remote_platform.IsValid():
988            print(
989                "error: unable to create the LLDB platform named '%s'."
990                % (configuration.lldb_platform_name)
991            )
992            exitTestSuite(1)
993        if configuration.lldb_platform_url:
994            # We must connect to a remote platform if a LLDB platform URL was
995            # specified
996            print(
997                "Connecting to remote platform '%s' at '%s'..."
998                % (configuration.lldb_platform_name, configuration.lldb_platform_url)
999            )
1000            platform_connect_options = lldb.SBPlatformConnectOptions(
1001                configuration.lldb_platform_url
1002            )
1003            err = lldb.remote_platform.ConnectRemote(platform_connect_options)
1004            if err.Success():
1005                print("Connected.")
1006            else:
1007                print(
1008                    "error: failed to connect to remote platform using URL '%s': %s"
1009                    % (configuration.lldb_platform_url, err)
1010                )
1011                exitTestSuite(1)
1012        else:
1013            configuration.lldb_platform_url = None
1014
1015    if configuration.lldb_platform_working_dir:
1016        print(
1017            "Setting remote platform working directory to '%s'..."
1018            % (configuration.lldb_platform_working_dir)
1019        )
1020        error = lldb.remote_platform.MakeDirectory(
1021            configuration.lldb_platform_working_dir, 448
1022        )  # 448 = 0o700
1023        if error.Fail():
1024            raise Exception(
1025                "making remote directory '%s': %s"
1026                % (configuration.lldb_platform_working_dir, error)
1027            )
1028
1029        if not lldb.remote_platform.SetWorkingDirectory(
1030            configuration.lldb_platform_working_dir
1031        ):
1032            raise Exception(
1033                "failed to set working directory '%s'"
1034                % configuration.lldb_platform_working_dir
1035            )
1036        lldb.selected_platform = lldb.remote_platform
1037    else:
1038        lldb.remote_platform = None
1039        configuration.lldb_platform_working_dir = None
1040        configuration.lldb_platform_url = None
1041
1042    # Set up the working directory.
1043    # Note that it's not dotest's job to clean this directory.
1044    lldbutil.mkdir_p(configuration.test_build_dir)
1045
1046    checkLibcxxSupport()
1047    checkLibstdcxxSupport()
1048    checkWatchpointSupport()
1049    checkDebugInfoSupport()
1050    checkDebugServerSupport()
1051    checkObjcSupport()
1052    checkForkVForkSupport()
1053    checkPexpectSupport()
1054    checkDAPSupport()
1055
1056    skipped_categories_list = ", ".join(configuration.skip_categories)
1057    print(
1058        "Skipping the following test categories: {}".format(
1059            configuration.skip_categories
1060        )
1061    )
1062
1063    for testdir in configuration.testdirs:
1064        for dirpath, dirnames, filenames in os.walk(testdir):
1065            visit("Test", dirpath, filenames)
1066
1067    #
1068    # Now that we have loaded all the test cases, run the whole test suite.
1069    #
1070
1071    # Install the control-c handler.
1072    unittest.signals.installHandler()
1073
1074    #
1075    # Invoke the default TextTestRunner to run the test suite
1076    #
1077    checkCompiler()
1078
1079    if configuration.verbose:
1080        print("compiler=%s" % configuration.compiler)
1081
1082    # Iterating over all possible architecture and compiler combinations.
1083    configString = "arch=%s compiler=%s" % (configuration.arch, configuration.compiler)
1084
1085    # Output the configuration.
1086    if configuration.verbose:
1087        sys.stderr.write("\nConfiguration: " + configString + "\n")
1088
1089    # First, write out the number of collected test cases.
1090    if configuration.verbose:
1091        sys.stderr.write(configuration.separator + "\n")
1092        sys.stderr.write(
1093            "Collected %d test%s\n\n"
1094            % (
1095                configuration.suite.countTestCases(),
1096                configuration.suite.countTestCases() != 1 and "s" or "",
1097            )
1098        )
1099
1100    if configuration.suite.countTestCases() == 0:
1101        logging.error("did not discover any matching tests")
1102        exitTestSuite(1)
1103
1104    # Invoke the test runner.
1105    if configuration.count == 1:
1106        result = unittest.TextTestRunner(
1107            stream=sys.stderr,
1108            verbosity=configuration.verbose,
1109            resultclass=test_result.LLDBTestResult,
1110        ).run(configuration.suite)
1111    else:
1112        # We are invoking the same test suite more than once.  In this case,
1113        # mark __ignore_singleton__ flag as True so the signleton pattern is
1114        # not enforced.
1115        test_result.LLDBTestResult.__ignore_singleton__ = True
1116        for i in range(configuration.count):
1117            result = unittest.TextTestRunner(
1118                stream=sys.stderr,
1119                verbosity=configuration.verbose,
1120                resultclass=test_result.LLDBTestResult,
1121            ).run(configuration.suite)
1122
1123    configuration.failed = not result.wasSuccessful()
1124
1125    if configuration.sdir_has_content and configuration.verbose:
1126        sys.stderr.write(
1127            "Session logs for test failures/errors/unexpected successes"
1128            " can be found in the test build directory\n"
1129        )
1130
1131    if configuration.use_categories and len(configuration.failures_per_category) > 0:
1132        sys.stderr.write("Failures per category:\n")
1133        for category in configuration.failures_per_category:
1134            sys.stderr.write(
1135                "%s - %d\n" % (category, configuration.failures_per_category[category])
1136            )
1137
1138    # Exiting.
1139    exitTestSuite(configuration.failed)
1140
1141
1142if __name__ == "__main__":
1143    print(
1144        __file__
1145        + " is for use as a module only.  It should not be run as a standalone script."
1146    )
1147    sys.exit(-1)
1148