xref: /llvm-project/lldb/packages/Python/lldbsuite/test/dotest.py (revision 586114510c5fa71d1377c7f53e68a3b12c472aa2)
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.filecheck = shutil.which("FileCheck", path=args.llvm_tools_dir)
284        configuration.yaml2obj = shutil.which("yaml2obj", path=args.llvm_tools_dir)
285
286    if not configuration.get_filecheck_path():
287        logging.warning("No valid FileCheck executable; some tests may fail...")
288        logging.warning("(Double-check the --llvm-tools-dir argument to dotest.py)")
289
290    if args.libcxx_include_dir or args.libcxx_library_dir:
291        if args.lldb_platform_name:
292            logging.warning(
293                "Custom libc++ is not supported for remote runs: ignoring --libcxx arguments"
294            )
295        elif not (args.libcxx_include_dir and args.libcxx_library_dir):
296            logging.error(
297                "Custom libc++ requires both --libcxx-include-dir and --libcxx-library-dir"
298            )
299            sys.exit(-1)
300    configuration.libcxx_include_dir = args.libcxx_include_dir
301    configuration.libcxx_include_target_dir = args.libcxx_include_target_dir
302    configuration.libcxx_library_dir = args.libcxx_library_dir
303
304    if args.channels:
305        lldbtest_config.channels = args.channels
306
307    if args.log_success:
308        lldbtest_config.log_success = args.log_success
309
310    if args.out_of_tree_debugserver:
311        lldbtest_config.out_of_tree_debugserver = args.out_of_tree_debugserver
312
313    # Set SDKROOT if we are using an Apple SDK
314    if args.sysroot is not None:
315        configuration.sdkroot = args.sysroot
316    elif platform_system == "Darwin" and args.apple_sdk:
317        configuration.sdkroot = seven.get_command_output(
318            'xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % (args.apple_sdk)
319        )
320        if not configuration.sdkroot:
321            logging.error("No SDK found with the name %s; aborting...", args.apple_sdk)
322            sys.exit(-1)
323
324    if args.arch:
325        configuration.arch = args.arch
326    else:
327        configuration.arch = platform_machine
328
329    if args.categories_list:
330        configuration.categories_list = set(
331            test_categories.validate(args.categories_list, False)
332        )
333        configuration.use_categories = True
334    else:
335        configuration.categories_list = []
336
337    if args.skip_categories:
338        configuration.skip_categories += test_categories.validate(
339            args.skip_categories, False
340        )
341
342    if args.xfail_categories:
343        configuration.xfail_categories += test_categories.validate(
344            args.xfail_categories, False
345        )
346
347    if args.E:
348        os.environ["CFLAGS_EXTRAS"] = args.E
349
350    if args.dwarf_version:
351        configuration.dwarf_version = args.dwarf_version
352        # We cannot modify CFLAGS_EXTRAS because they're used in test cases
353        # that explicitly require no debug info.
354        os.environ["CFLAGS"] = "-gdwarf-{}".format(configuration.dwarf_version)
355
356    if args.settings:
357        for setting in args.settings:
358            if not len(setting) == 1 or not setting[0].count("="):
359                logging.error(
360                    '"%s" is not a setting in the form "key=value"', setting[0]
361                )
362                sys.exit(-1)
363            setting_list = setting[0].split("=", 1)
364            configuration.settings.append((setting_list[0], setting_list[1]))
365
366    if args.d:
367        sys.stdout.write(
368            "Suspending the process %d to wait for debugger to attach...\n"
369            % os.getpid()
370        )
371        sys.stdout.flush()
372        os.kill(os.getpid(), signal.SIGSTOP)
373
374    if args.f:
375        if any([x.startswith("-") for x in args.f]):
376            usage(parser)
377        configuration.filters.extend(args.f)
378
379    if args.framework:
380        configuration.lldb_framework_path = args.framework
381
382    if args.executable:
383        # lldb executable is passed explicitly
384        lldbtest_config.lldbExec = os.path.abspath(args.executable)
385        if not is_exe(lldbtest_config.lldbExec):
386            lldbtest_config.lldbExec = which(args.executable)
387        if not is_exe(lldbtest_config.lldbExec):
388            logging.error(
389                "%s is not a valid executable to test; aborting...", args.executable
390            )
391            sys.exit(-1)
392
393    if args.excluded:
394        for excl_file in args.excluded:
395            parseExclusion(excl_file)
396
397    if args.p:
398        if args.p.startswith("-"):
399            usage(parser)
400        configuration.regexp = args.p
401
402    if args.t:
403        os.environ["LLDB_COMMAND_TRACE"] = "YES"
404
405    if args.v:
406        configuration.verbose = 2
407
408    # argparse makes sure we have a number
409    if args.sharp:
410        configuration.count = args.sharp
411
412    if sys.platform.startswith("win32"):
413        os.environ["LLDB_DISABLE_CRASH_DIALOG"] = str(args.disable_crash_dialog)
414        os.environ["LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"] = str(True)
415
416    if do_help:
417        usage(parser)
418
419    if args.lldb_platform_name:
420        configuration.lldb_platform_name = args.lldb_platform_name
421    if args.lldb_platform_url:
422        configuration.lldb_platform_url = args.lldb_platform_url
423    if args.lldb_platform_working_dir:
424        configuration.lldb_platform_working_dir = args.lldb_platform_working_dir
425    if platform_system == "Darwin" and args.apple_sdk:
426        configuration.apple_sdk = args.apple_sdk
427    if args.test_build_dir:
428        configuration.test_build_dir = args.test_build_dir
429    if args.lldb_module_cache_dir:
430        configuration.lldb_module_cache_dir = args.lldb_module_cache_dir
431    else:
432        configuration.lldb_module_cache_dir = os.path.join(
433            configuration.test_build_dir, "module-cache-lldb"
434        )
435
436    if args.clang_module_cache_dir:
437        configuration.clang_module_cache_dir = args.clang_module_cache_dir
438    else:
439        configuration.clang_module_cache_dir = os.path.join(
440            configuration.test_build_dir, "module-cache-clang"
441        )
442
443    if args.lldb_libs_dir:
444        configuration.lldb_libs_dir = args.lldb_libs_dir
445    if args.lldb_obj_root:
446        configuration.lldb_obj_root = args.lldb_obj_root
447
448    if args.enabled_plugins:
449        configuration.enabled_plugins = args.enabled_plugins
450
451    # Gather all the dirs passed on the command line.
452    if len(args.args) > 0:
453        configuration.testdirs = [
454            os.path.realpath(os.path.abspath(x)) for x in args.args
455        ]
456
457
458def registerFaulthandler():
459    try:
460        import faulthandler
461    except ImportError:
462        # faulthandler is not available until python3
463        return
464
465    faulthandler.enable()
466    # faulthandler.register is not available on Windows.
467    if getattr(faulthandler, "register", None):
468        faulthandler.register(signal.SIGTERM, chain=True)
469
470
471def setupSysPath():
472    """
473    Add LLDB.framework/Resources/Python to the search paths for modules.
474    As a side effect, we also discover the 'lldb' executable and export it here.
475    """
476
477    # Get the directory containing the current script.
478    if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
479        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
480    else:
481        scriptPath = os.path.dirname(os.path.abspath(__file__))
482    if not scriptPath.endswith("test"):
483        print("This script expects to reside in lldb's test directory.")
484        sys.exit(-1)
485
486    os.environ["LLDB_TEST"] = scriptPath
487
488    # Set up the root build directory.
489    if not configuration.test_build_dir:
490        raise Exception("test_build_dir is not set")
491    configuration.test_build_dir = os.path.abspath(configuration.test_build_dir)
492
493    # Set up the LLDB_SRC environment variable, so that the tests can locate
494    # the LLDB source code.
495    os.environ["LLDB_SRC"] = lldbsuite.lldb_root
496
497    pluginPath = os.path.join(scriptPath, "plugins")
498    toolsLLDBDAP = os.path.join(scriptPath, "tools", "lldb-dap")
499    toolsLLDBServerPath = os.path.join(scriptPath, "tools", "lldb-server")
500    intelpt = os.path.join(scriptPath, "tools", "intelpt")
501
502    # Insert script dir, plugin dir and lldb-server dir to the sys.path.
503    sys.path.insert(0, pluginPath)
504    # Adding test/tools/lldb-dap to the path makes it easy to
505    # "import lldb_dap_testcase" from the DAP tests
506    sys.path.insert(0, toolsLLDBDAP)
507    # Adding test/tools/lldb-server to the path makes it easy
508    # to "import lldbgdbserverutils" from the lldb-server tests
509    sys.path.insert(0, toolsLLDBServerPath)
510    # Adding test/tools/intelpt to the path makes it easy
511    # to "import intelpt_testcase" from the lldb-server tests
512    sys.path.insert(0, intelpt)
513
514    # This is the root of the lldb git/svn checkout
515    # When this changes over to a package instead of a standalone script, this
516    # will be `lldbsuite.lldb_root`
517    lldbRootDirectory = lldbsuite.lldb_root
518
519    # Some of the tests can invoke the 'lldb' command directly.
520    # We'll try to locate the appropriate executable right here.
521
522    # The lldb executable can be set from the command line
523    # if it's not set, we try to find it now
524    # first, we try the environment
525    if not lldbtest_config.lldbExec:
526        # First, you can define an environment variable LLDB_EXEC specifying the
527        # full pathname of the lldb executable.
528        if "LLDB_EXEC" in os.environ:
529            lldbtest_config.lldbExec = os.environ["LLDB_EXEC"]
530
531    if not lldbtest_config.lldbExec:
532        # Last, check the path
533        lldbtest_config.lldbExec = which("lldb")
534
535    if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec):
536        print(
537            "'{}' is not a path to a valid executable".format(lldbtest_config.lldbExec)
538        )
539        lldbtest_config.lldbExec = None
540
541    if not lldbtest_config.lldbExec:
542        print(
543            "The 'lldb' executable cannot be located.  Some of the tests may not be run as a result."
544        )
545        sys.exit(-1)
546
547    os.system("%s -v" % lldbtest_config.lldbExec)
548
549    lldbDir = os.path.dirname(lldbtest_config.lldbExec)
550
551    lldbDAPExec = os.path.join(lldbDir, "lldb-dap")
552    if is_exe(lldbDAPExec):
553        os.environ["LLDBDAP_EXEC"] = lldbDAPExec
554
555    lldbPythonDir = None  # The directory that contains 'lldb/__init__.py'
556
557    # If our lldb supports the -P option, use it to find the python path:
558    lldb_dash_p_result = subprocess.check_output(
559        [lldbtest_config.lldbExec, "-P"], universal_newlines=True
560    )
561    if lldb_dash_p_result:
562        for line in lldb_dash_p_result.splitlines():
563            if os.path.isdir(line) and os.path.exists(
564                os.path.join(line, "lldb", "__init__.py")
565            ):
566                lldbPythonDir = line
567                break
568
569    if not lldbPythonDir:
570        print(
571            "Unable to load lldb extension module.  Possible reasons for this include:"
572        )
573        print("  1) LLDB was built with LLDB_ENABLE_PYTHON=0")
574        print(
575            "  2) PYTHONPATH and PYTHONHOME are not set correctly.  PYTHONHOME should refer to"
576        )
577        print(
578            "     the version of Python that LLDB built and linked against, and PYTHONPATH"
579        )
580        print(
581            "     should contain the Lib directory for the same python distro, as well as the"
582        )
583        print("     location of LLDB's site-packages folder.")
584        print(
585            "  3) A different version of Python than that which was built against is exported in"
586        )
587        print("     the system's PATH environment variable, causing conflicts.")
588        print(
589            "  4) The executable '%s' could not be found.  Please check "
590            % lldbtest_config.lldbExec
591        )
592        print("     that it exists and is executable.")
593
594    if lldbPythonDir:
595        lldbPythonDir = os.path.normpath(lldbPythonDir)
596        # Some of the code that uses this path assumes it hasn't resolved the Versions... link.
597        # If the path we've constructed looks like that, then we'll strip out
598        # the Versions/A part.
599        (before, frameWithVersion, after) = lldbPythonDir.rpartition(
600            "LLDB.framework/Versions/A"
601        )
602        if frameWithVersion != "":
603            lldbPythonDir = before + "LLDB.framework" + after
604
605        lldbPythonDir = os.path.abspath(lldbPythonDir)
606
607        if "freebsd" in sys.platform or "linux" in sys.platform:
608            os.environ["LLDB_LIB_DIR"] = os.path.join(lldbPythonDir, "..", "..")
609
610        # If tests need to find LLDB_FRAMEWORK, now they can do it
611        os.environ["LLDB_FRAMEWORK"] = os.path.dirname(os.path.dirname(lldbPythonDir))
612
613        # This is to locate the lldb.py module.  Insert it right after
614        # sys.path[0].
615        sys.path[1:1] = [lldbPythonDir]
616
617
618def visit_file(dir, name):
619    # Try to match the regexp pattern, if specified.
620    if configuration.regexp:
621        if not re.search(configuration.regexp, name):
622            # We didn't match the regex, we're done.
623            return
624
625    if configuration.skip_tests:
626        for file_regexp in configuration.skip_tests:
627            if re.search(file_regexp, name):
628                return
629
630    # We found a match for our test.  Add it to the suite.
631
632    # Update the sys.path first.
633    if not sys.path.count(dir):
634        sys.path.insert(0, dir)
635    base = os.path.splitext(name)[0]
636
637    # Thoroughly check the filterspec against the base module and admit
638    # the (base, filterspec) combination only when it makes sense.
639
640    def check(obj, parts):
641        for part in parts:
642            try:
643                parent, obj = obj, getattr(obj, part)
644            except AttributeError:
645                # The filterspec has failed.
646                return False
647        return True
648
649    module = __import__(base)
650
651    def iter_filters():
652        for filterspec in configuration.filters:
653            parts = filterspec.split(".")
654            if check(module, parts):
655                yield filterspec
656            elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]):
657                yield ".".join(parts[1:])
658            else:
659                for key, value in module.__dict__.items():
660                    if check(value, parts):
661                        yield key + "." + filterspec
662
663    filtered = False
664    for filterspec in iter_filters():
665        filtered = True
666        print("adding filter spec %s to module %s" % (filterspec, repr(module)))
667        tests = unittest.defaultTestLoader.loadTestsFromName(filterspec, module)
668        configuration.suite.addTests(tests)
669
670    # Forgo this module if the (base, filterspec) combo is invalid
671    if configuration.filters and not filtered:
672        return
673
674    if not filtered:
675        # Add the entire file's worth of tests since we're not filtered.
676        # Also the fail-over case when the filterspec branch
677        # (base, filterspec) combo doesn't make sense.
678        configuration.suite.addTests(unittest.defaultTestLoader.loadTestsFromName(base))
679
680
681def visit(prefix, dir, names):
682    """Visitor function for os.path.walk(path, visit, arg)."""
683
684    dir_components = set(dir.split(os.sep))
685    excluded_components = set([".svn", ".git"])
686    if dir_components.intersection(excluded_components):
687        return
688
689    # Gather all the Python test file names that follow the Test*.py pattern.
690    python_test_files = [
691        name for name in names if name.endswith(".py") and name.startswith(prefix)
692    ]
693
694    # Visit all the python test files.
695    for name in python_test_files:
696        # Ensure we error out if we have multiple tests with the same
697        # base name.
698        # Future improvement: find all the places where we work with base
699        # names and convert to full paths.  We have directory structure
700        # to disambiguate these, so we shouldn't need this constraint.
701        if name in configuration.all_tests:
702            raise Exception("Found multiple tests with the name %s" % name)
703        configuration.all_tests.add(name)
704
705        # Run the relevant tests in the python file.
706        visit_file(dir, name)
707
708
709# ======================================== #
710#                                          #
711# Execution of the test driver starts here #
712#                                          #
713# ======================================== #
714
715
716def checkDsymForUUIDIsNotOn():
717    cmd = ["defaults", "read", "com.apple.DebugSymbols"]
718    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
719    cmd_output = process.stdout.read()
720    output_str = cmd_output.decode("utf-8")
721    if "DBGFileMappedPaths = " in output_str:
722        print("%s =>" % " ".join(cmd))
723        print(output_str)
724        print(
725            "Disable automatic lookup and caching of dSYMs before running the test suite!"
726        )
727        print("Exiting...")
728        sys.exit(0)
729
730
731def exitTestSuite(exitCode=None):
732    # lldb.py does SBDebugger.Initialize().
733    # Call SBDebugger.Terminate() on exit.
734    import lldb
735
736    lldb.SBDebugger.Terminate()
737    if exitCode:
738        sys.exit(exitCode)
739
740
741def getVersionForSDK(sdk):
742    sdk = str.lower(sdk)
743    full_path = seven.get_command_output("xcrun -sdk %s --show-sdk-path" % sdk)
744    basename = os.path.basename(full_path)
745    basename = os.path.splitext(basename)[0]
746    basename = str.lower(basename)
747    ver = basename.replace(sdk, "")
748    return ver
749
750
751def checkCompiler():
752    # Add some intervention here to sanity check that the compiler requested is sane.
753    # If found not to be an executable program, we abort.
754    c = configuration.compiler
755    if which(c):
756        return
757
758    if not sys.platform.startswith("darwin"):
759        raise Exception(c + " is not a valid compiler")
760
761    pipe = subprocess.Popen(
762        ["xcrun", "-find", c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
763    )
764    cmd_output = pipe.stdout.read()
765    if not cmd_output or "not found" in cmd_output:
766        raise Exception(c + " is not a valid compiler")
767
768    configuration.compiler = cmd_output.split("\n")[0]
769    print("'xcrun -find %s' returning %s" % (c, configuration.compiler))
770
771
772def canRunLibcxxTests():
773    from lldbsuite.test import lldbplatformutil
774
775    platform = lldbplatformutil.getPlatform()
776
777    if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin():
778        return True, "libc++ always present"
779
780    if platform == "linux":
781        with tempfile.NamedTemporaryFile() as f:
782            cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"]
783            p = subprocess.Popen(
784                cmd,
785                stdin=subprocess.PIPE,
786                stdout=subprocess.PIPE,
787                stderr=subprocess.PIPE,
788                universal_newlines=True,
789            )
790            _, stderr = p.communicate("#include <cassert>\nint main() {}")
791            if not p.returncode:
792                return True, "Compiling with -stdlib=libc++ works"
793            return (
794                False,
795                "Compiling with -stdlib=libc++ fails with the error: %s" % stderr,
796            )
797
798    return False, "Don't know how to build with libc++ on %s" % platform
799
800
801def checkLibcxxSupport():
802    result, reason = canRunLibcxxTests()
803    if result:
804        return  # libc++ supported
805    if "libc++" in configuration.categories_list:
806        return  # libc++ category explicitly requested, let it run.
807    if configuration.verbose:
808        print("libc++ tests will not be run because: " + reason)
809    configuration.skip_categories.append("libc++")
810
811
812def canRunLibstdcxxTests():
813    from lldbsuite.test import lldbplatformutil
814
815    platform = lldbplatformutil.getPlatform()
816    if lldbplatformutil.target_is_android():
817        platform = "android"
818    if platform == "linux":
819        return True, "libstdcxx always present"
820    return False, "Don't know how to build with libstdcxx on %s" % platform
821
822
823def checkLibstdcxxSupport():
824    result, reason = canRunLibstdcxxTests()
825    if result:
826        return  # libstdcxx supported
827    if "libstdcxx" in configuration.categories_list:
828        return  # libstdcxx category explicitly requested, let it run.
829    if configuration.verbose:
830        print("libstdcxx tests will not be run because: " + reason)
831    configuration.skip_categories.append("libstdcxx")
832
833
834def canRunWatchpointTests():
835    from lldbsuite.test import lldbplatformutil
836
837    platform = lldbplatformutil.getPlatform()
838    if platform == "netbsd":
839        if os.geteuid() == 0:
840            return True, "root can always write dbregs"
841        try:
842            output = (
843                subprocess.check_output(
844                    ["/sbin/sysctl", "-n", "security.models.extensions.user_set_dbregs"]
845                )
846                .decode()
847                .strip()
848            )
849            if output == "1":
850                return True, "security.models.extensions.user_set_dbregs enabled"
851        except subprocess.CalledProcessError:
852            pass
853        return False, "security.models.extensions.user_set_dbregs disabled"
854    elif platform == "freebsd" and configuration.arch == "aarch64":
855        import lldb
856
857        if lldb.SBPlatform.GetHostPlatform().GetOSMajorVersion() < 13:
858            return False, "Watchpoint support on arm64 requires FreeBSD 13.0"
859    return True, "watchpoint support available"
860
861
862def checkWatchpointSupport():
863    result, reason = canRunWatchpointTests()
864    if result:
865        return  # watchpoints supported
866    if "watchpoint" in configuration.categories_list:
867        return  # watchpoint category explicitly requested, let it run.
868    if configuration.verbose:
869        print("watchpoint tests will not be run because: " + reason)
870    configuration.skip_categories.append("watchpoint")
871
872
873def checkObjcSupport():
874    from lldbsuite.test import lldbplatformutil
875
876    if not lldbplatformutil.platformIsDarwin():
877        if configuration.verbose:
878            print("objc tests will be skipped because of unsupported platform")
879        configuration.skip_categories.append("objc")
880
881
882def checkDebugInfoSupport():
883    from lldbsuite.test import lldbplatformutil
884
885    platform = lldbplatformutil.getPlatform()
886    compiler = configuration.compiler
887    for cat in test_categories.debug_info_categories:
888        if cat in configuration.categories_list:
889            continue  # Category explicitly requested, let it run.
890        if test_categories.is_supported_on_platform(cat, platform, compiler):
891            continue
892        configuration.skip_categories.append(cat)
893
894
895def checkDebugServerSupport():
896    from lldbsuite.test import lldbplatformutil
897    import lldb
898
899    skip_msg = "Skipping %s tests, as they are not compatible with remote testing on this platform"
900    if lldbplatformutil.platformIsDarwin():
901        configuration.skip_categories.append("llgs")
902        if lldb.remote_platform:
903            # <rdar://problem/34539270>
904            configuration.skip_categories.append("debugserver")
905            if configuration.verbose:
906                print(skip_msg % "debugserver")
907    else:
908        configuration.skip_categories.append("debugserver")
909        if lldb.remote_platform and lldbplatformutil.getPlatform() == "windows":
910            configuration.skip_categories.append("llgs")
911            if configuration.verbose:
912                print(skip_msg % "lldb-server")
913
914
915def checkForkVForkSupport():
916    from lldbsuite.test import lldbplatformutil
917
918    platform = lldbplatformutil.getPlatform()
919    if platform not in ["freebsd", "linux", "netbsd"]:
920        configuration.skip_categories.append("fork")
921
922
923def checkPexpectSupport():
924    from lldbsuite.test import lldbplatformutil
925
926    platform = lldbplatformutil.getPlatform()
927
928    # llvm.org/pr22274: need a pexpect replacement for windows
929    if platform in ["windows"]:
930        if configuration.verbose:
931            print("pexpect tests will be skipped because of unsupported platform")
932        configuration.skip_categories.append("pexpect")
933
934
935def checkDAPSupport():
936    import lldb
937
938    if "LLDBDAP_EXEC" not in os.environ:
939        msg = (
940            "The 'lldb-dap' executable cannot be located and its tests will not be run."
941        )
942    elif lldb.remote_platform:
943        msg = "lldb-dap tests are not compatible with remote platforms and will not be run."
944    else:
945        msg = None
946
947    if msg:
948        if configuration.verbose:
949            print(msg)
950        configuration.skip_categories.append("lldb-dap")
951
952
953def run_suite():
954    # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
955    # does not exist before proceeding to running the test suite.
956    if sys.platform.startswith("darwin"):
957        checkDsymForUUIDIsNotOn()
958
959    # Start the actions by first parsing the options while setting up the test
960    # directories, followed by setting up the search paths for lldb utilities;
961    # then, we walk the directory trees and collect the tests into our test suite.
962    #
963    parseOptionsAndInitTestdirs()
964
965    # Print a stack trace if the test hangs or is passed SIGTERM.
966    registerFaulthandler()
967
968    setupSysPath()
969
970    import lldb
971
972    lldb.SBDebugger.Initialize()
973    lldb.SBDebugger.PrintStackTraceOnError()
974
975    # Use host platform by default.
976    lldb.remote_platform = None
977    lldb.selected_platform = lldb.SBPlatform.GetHostPlatform()
978
979    # Now we can also import lldbutil
980    from lldbsuite.test import lldbutil
981
982    if configuration.lldb_platform_name:
983        print("Setting up remote platform '%s'" % (configuration.lldb_platform_name))
984        lldb.remote_platform = lldb.SBPlatform(configuration.lldb_platform_name)
985        lldb.selected_platform = lldb.remote_platform
986        if not lldb.remote_platform.IsValid():
987            print(
988                "error: unable to create the LLDB platform named '%s'."
989                % (configuration.lldb_platform_name)
990            )
991            exitTestSuite(1)
992        if configuration.lldb_platform_url:
993            # We must connect to a remote platform if a LLDB platform URL was
994            # specified
995            print(
996                "Connecting to remote platform '%s' at '%s'..."
997                % (configuration.lldb_platform_name, configuration.lldb_platform_url)
998            )
999            platform_connect_options = lldb.SBPlatformConnectOptions(
1000                configuration.lldb_platform_url
1001            )
1002            err = lldb.remote_platform.ConnectRemote(platform_connect_options)
1003            if err.Success():
1004                print("Connected.")
1005            else:
1006                print(
1007                    "error: failed to connect to remote platform using URL '%s': %s"
1008                    % (configuration.lldb_platform_url, err)
1009                )
1010                exitTestSuite(1)
1011        else:
1012            configuration.lldb_platform_url = None
1013
1014    if configuration.lldb_platform_working_dir:
1015        print(
1016            "Setting remote platform working directory to '%s'..."
1017            % (configuration.lldb_platform_working_dir)
1018        )
1019        error = lldb.remote_platform.MakeDirectory(
1020            configuration.lldb_platform_working_dir, 448
1021        )  # 448 = 0o700
1022        if error.Fail():
1023            raise Exception(
1024                "making remote directory '%s': %s"
1025                % (configuration.lldb_platform_working_dir, error)
1026            )
1027
1028        if not lldb.remote_platform.SetWorkingDirectory(
1029            configuration.lldb_platform_working_dir
1030        ):
1031            raise Exception(
1032                "failed to set working directory '%s'"
1033                % configuration.lldb_platform_working_dir
1034            )
1035        lldb.selected_platform = lldb.remote_platform
1036    else:
1037        lldb.remote_platform = None
1038        configuration.lldb_platform_working_dir = None
1039        configuration.lldb_platform_url = None
1040
1041    # Set up the working directory.
1042    # Note that it's not dotest's job to clean this directory.
1043    lldbutil.mkdir_p(configuration.test_build_dir)
1044
1045    checkLibcxxSupport()
1046    checkLibstdcxxSupport()
1047    checkWatchpointSupport()
1048    checkDebugInfoSupport()
1049    checkDebugServerSupport()
1050    checkObjcSupport()
1051    checkForkVForkSupport()
1052    checkPexpectSupport()
1053    checkDAPSupport()
1054
1055    skipped_categories_list = ", ".join(configuration.skip_categories)
1056    print(
1057        "Skipping the following test categories: {}".format(
1058            configuration.skip_categories
1059        )
1060    )
1061
1062    for testdir in configuration.testdirs:
1063        for dirpath, dirnames, filenames in os.walk(testdir):
1064            visit("Test", dirpath, filenames)
1065
1066    #
1067    # Now that we have loaded all the test cases, run the whole test suite.
1068    #
1069
1070    # Install the control-c handler.
1071    unittest.signals.installHandler()
1072
1073    #
1074    # Invoke the default TextTestRunner to run the test suite
1075    #
1076    checkCompiler()
1077
1078    if configuration.verbose:
1079        print("compiler=%s" % configuration.compiler)
1080
1081    # Iterating over all possible architecture and compiler combinations.
1082    configString = "arch=%s compiler=%s" % (configuration.arch, configuration.compiler)
1083
1084    # Output the configuration.
1085    if configuration.verbose:
1086        sys.stderr.write("\nConfiguration: " + configString + "\n")
1087
1088    # First, write out the number of collected test cases.
1089    if configuration.verbose:
1090        sys.stderr.write(configuration.separator + "\n")
1091        sys.stderr.write(
1092            "Collected %d test%s\n\n"
1093            % (
1094                configuration.suite.countTestCases(),
1095                configuration.suite.countTestCases() != 1 and "s" or "",
1096            )
1097        )
1098
1099    if configuration.suite.countTestCases() == 0:
1100        logging.error("did not discover any matching tests")
1101        exitTestSuite(1)
1102
1103    # Invoke the test runner.
1104    if configuration.count == 1:
1105        result = unittest.TextTestRunner(
1106            stream=sys.stderr,
1107            verbosity=configuration.verbose,
1108            resultclass=test_result.LLDBTestResult,
1109        ).run(configuration.suite)
1110    else:
1111        # We are invoking the same test suite more than once.  In this case,
1112        # mark __ignore_singleton__ flag as True so the signleton pattern is
1113        # not enforced.
1114        test_result.LLDBTestResult.__ignore_singleton__ = True
1115        for i in range(configuration.count):
1116            result = unittest.TextTestRunner(
1117                stream=sys.stderr,
1118                verbosity=configuration.verbose,
1119                resultclass=test_result.LLDBTestResult,
1120            ).run(configuration.suite)
1121
1122    configuration.failed = not result.wasSuccessful()
1123
1124    if configuration.sdir_has_content and configuration.verbose:
1125        sys.stderr.write(
1126            "Session logs for test failures/errors/unexpected successes"
1127            " can be found in the test build directory\n"
1128        )
1129
1130    if configuration.use_categories and len(configuration.failures_per_category) > 0:
1131        sys.stderr.write("Failures per category:\n")
1132        for category in configuration.failures_per_category:
1133            sys.stderr.write(
1134                "%s - %d\n" % (category, configuration.failures_per_category[category])
1135            )
1136
1137    # Exiting.
1138    exitTestSuite(configuration.failed)
1139
1140
1141if __name__ == "__main__":
1142    print(
1143        __file__
1144        + " is for use as a module only.  It should not be run as a standalone script."
1145    )
1146    sys.exit(-1)
1147