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