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