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