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
420    # rerun-related arguments
421    configuration.rerun_all_issues = args.rerun_all_issues
422
423    if args.lldb_platform_name:
424        configuration.lldb_platform_name = args.lldb_platform_name
425    if args.lldb_platform_url:
426        configuration.lldb_platform_url = args.lldb_platform_url
427    if args.lldb_platform_working_dir:
428        configuration.lldb_platform_working_dir = args.lldb_platform_working_dir
429    if platform_system == 'Darwin'  and args.apple_sdk:
430        configuration.apple_sdk = args.apple_sdk
431    if args.test_build_dir:
432        configuration.test_build_dir = args.test_build_dir
433    if args.lldb_module_cache_dir:
434        configuration.lldb_module_cache_dir = args.lldb_module_cache_dir
435    else:
436        configuration.lldb_module_cache_dir = os.path.join(
437            configuration.test_build_dir, 'module-cache-lldb')
438    if args.clang_module_cache_dir:
439        configuration.clang_module_cache_dir = args.clang_module_cache_dir
440    else:
441        configuration.clang_module_cache_dir = os.path.join(
442            configuration.test_build_dir, 'module-cache-clang')
443
444    if args.lldb_libs_dir:
445        configuration.lldb_libs_dir = args.lldb_libs_dir
446
447    if args.enabled_plugins:
448        configuration.enabled_plugins = args.enabled_plugins
449
450    # Gather all the dirs passed on the command line.
451    if len(args.args) > 0:
452        configuration.testdirs = [os.path.realpath(os.path.abspath(x)) for x in args.args]
453
454    lldbtest_config.codesign_identity = args.codesign_identity
455
456def setupSysPath():
457    """
458    Add LLDB.framework/Resources/Python to the search paths for modules.
459    As a side effect, we also discover the 'lldb' executable and export it here.
460    """
461
462    # Get the directory containing the current script.
463    if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
464        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
465    else:
466        scriptPath = os.path.dirname(os.path.abspath(__file__))
467    if not scriptPath.endswith('test'):
468        print("This script expects to reside in lldb's test directory.")
469        sys.exit(-1)
470
471    os.environ["LLDB_TEST"] = scriptPath
472
473    # Set up the root build directory.
474    if not configuration.test_build_dir:
475        raise Exception("test_build_dir is not set")
476    configuration.test_build_dir = os.path.abspath(configuration.test_build_dir)
477
478    # Set up the LLDB_SRC environment variable, so that the tests can locate
479    # the LLDB source code.
480    os.environ["LLDB_SRC"] = lldbsuite.lldb_root
481
482    pluginPath = os.path.join(scriptPath, 'plugins')
483    toolsLLDBVSCode = os.path.join(scriptPath, 'tools', 'lldb-vscode')
484    toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server')
485
486    # Insert script dir, plugin dir and lldb-server dir to the sys.path.
487    sys.path.insert(0, pluginPath)
488    # Adding test/tools/lldb-vscode to the path makes it easy to
489    # "import lldb_vscode_testcase" from the VSCode tests
490    sys.path.insert(0, toolsLLDBVSCode)
491    # Adding test/tools/lldb-server to the path makes it easy
492    sys.path.insert(0, toolsLLDBServerPath)
493    # to "import lldbgdbserverutils" from the lldb-server tests
494
495    # This is the root of the lldb git/svn checkout
496    # When this changes over to a package instead of a standalone script, this
497    # will be `lldbsuite.lldb_root`
498    lldbRootDirectory = lldbsuite.lldb_root
499
500    # Some of the tests can invoke the 'lldb' command directly.
501    # We'll try to locate the appropriate executable right here.
502
503    # The lldb executable can be set from the command line
504    # if it's not set, we try to find it now
505    # first, we try the environment
506    if not lldbtest_config.lldbExec:
507        # First, you can define an environment variable LLDB_EXEC specifying the
508        # full pathname of the lldb executable.
509        if "LLDB_EXEC" in os.environ:
510            lldbtest_config.lldbExec = os.environ["LLDB_EXEC"]
511
512    if not lldbtest_config.lldbExec:
513        # Last, check the path
514        lldbtest_config.lldbExec = which('lldb')
515
516    if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec):
517        print(
518            "'{}' is not a path to a valid executable".format(
519                lldbtest_config.lldbExec))
520        lldbtest_config.lldbExec = None
521
522    if not lldbtest_config.lldbExec:
523        print("The 'lldb' executable cannot be located.  Some of the tests may not be run as a result.")
524        sys.exit(-1)
525
526    # confusingly, this is the "bin" directory
527    lldbLibDir = os.path.dirname(lldbtest_config.lldbExec)
528    os.environ["LLDB_LIB_DIR"] = lldbLibDir
529    lldbImpLibDir = configuration.lldb_libs_dir
530    os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir
531    print("LLDB library dir:", os.environ["LLDB_LIB_DIR"])
532    print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"])
533    os.system('%s -v' % lldbtest_config.lldbExec)
534
535    lldbDir = os.path.dirname(lldbtest_config.lldbExec)
536
537    lldbVSCodeExec = os.path.join(lldbDir, "lldb-vscode")
538    if is_exe(lldbVSCodeExec):
539        os.environ["LLDBVSCODE_EXEC"] = lldbVSCodeExec
540    else:
541        if not configuration.shouldSkipBecauseOfCategories(["lldb-vscode"]):
542            print(
543                "The 'lldb-vscode' executable cannot be located.  The lldb-vscode tests can not be run as a result.")
544            configuration.skip_categories.append("lldb-vscode")
545
546    lldbPythonDir = None  # The directory that contains 'lldb/__init__.py'
547    if not configuration.lldb_framework_path and os.path.exists(os.path.join(lldbLibDir, "LLDB.framework")):
548        configuration.lldb_framework_path = os.path.join(lldbLibDir, "LLDB.framework")
549    if configuration.lldb_framework_path:
550        lldbtest_config.lldb_framework_path = configuration.lldb_framework_path
551        candidatePath = os.path.join(
552            configuration.lldb_framework_path, 'Resources', 'Python')
553        if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')):
554            lldbPythonDir = candidatePath
555        if not lldbPythonDir:
556            print(
557                'Resources/Python/lldb/__init__.py was not found in ' +
558                configuration.lldb_framework_path)
559            sys.exit(-1)
560    else:
561        # If our lldb supports the -P option, use it to find the python path:
562        init_in_python_dir = os.path.join('lldb', '__init__.py')
563
564        lldb_dash_p_result = subprocess.check_output(
565            [lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True)
566
567        if lldb_dash_p_result and not lldb_dash_p_result.startswith(
568                ("<", "lldb: invalid option:")) and not lldb_dash_p_result.startswith("Traceback"):
569            lines = lldb_dash_p_result.splitlines()
570
571            # Workaround for readline vs libedit issue on FreeBSD.  If stdout
572            # is not a terminal Python executes
573            #     rl_variable_bind ("enable-meta-key", "off");
574            # This produces a warning with FreeBSD's libedit because the
575            # enable-meta-key variable is unknown.  Not an issue on Apple
576            # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__
577            # around the call.  See http://bugs.python.org/issue19884 for more
578            # information.  For now we just discard the warning output.
579            if len(lines) >= 1 and lines[0].startswith(
580                    "bind: Invalid command"):
581                lines.pop(0)
582
583            # Taking the last line because lldb outputs
584            # 'Cannot read termcap database;\nusing dumb terminal settings.\n'
585            # before the path
586            if len(lines) >= 1 and os.path.isfile(
587                    os.path.join(lines[-1], init_in_python_dir)):
588                lldbPythonDir = lines[-1]
589                if "freebsd" in sys.platform or "linux" in sys.platform:
590                    os.environ['LLDB_LIB_DIR'] = os.path.join(
591                        lldbPythonDir, '..', '..')
592
593        if not lldbPythonDir:
594            print(
595                "Unable to load lldb extension module.  Possible reasons for this include:")
596            print("  1) LLDB was built with LLDB_ENABLE_PYTHON=0")
597            print(
598                "  2) PYTHONPATH and PYTHONHOME are not set correctly.  PYTHONHOME should refer to")
599            print(
600                "     the version of Python that LLDB built and linked against, and PYTHONPATH")
601            print(
602                "     should contain the Lib directory for the same python distro, as well as the")
603            print("     location of LLDB\'s site-packages folder.")
604            print(
605                "  3) A different version of Python than that which was built against is exported in")
606            print("     the system\'s PATH environment variable, causing conflicts.")
607            print(
608                "  4) The executable '%s' could not be found.  Please check " %
609                lldbtest_config.lldbExec)
610            print("     that it exists and is executable.")
611
612    if lldbPythonDir:
613        lldbPythonDir = os.path.normpath(lldbPythonDir)
614        # Some of the code that uses this path assumes it hasn't resolved the Versions... link.
615        # If the path we've constructed looks like that, then we'll strip out
616        # the Versions/A part.
617        (before, frameWithVersion, after) = lldbPythonDir.rpartition(
618            "LLDB.framework/Versions/A")
619        if frameWithVersion != "":
620            lldbPythonDir = before + "LLDB.framework" + after
621
622        lldbPythonDir = os.path.abspath(lldbPythonDir)
623
624        # If tests need to find LLDB_FRAMEWORK, now they can do it
625        os.environ["LLDB_FRAMEWORK"] = os.path.dirname(
626            os.path.dirname(lldbPythonDir))
627
628        # This is to locate the lldb.py module.  Insert it right after
629        # sys.path[0].
630        sys.path[1:1] = [lldbPythonDir]
631
632
633def visit_file(dir, name):
634    # Try to match the regexp pattern, if specified.
635    if configuration.regexp:
636        if not re.search(configuration.regexp, name):
637            # We didn't match the regex, we're done.
638            return
639
640    if configuration.skip_tests:
641        for file_regexp in configuration.skip_tests:
642            if re.search(file_regexp, name):
643                return
644
645    # We found a match for our test.  Add it to the suite.
646
647    # Update the sys.path first.
648    if not sys.path.count(dir):
649        sys.path.insert(0, dir)
650    base = os.path.splitext(name)[0]
651
652    # Thoroughly check the filterspec against the base module and admit
653    # the (base, filterspec) combination only when it makes sense.
654
655    def check(obj, parts):
656        for part in parts:
657            try:
658                parent, obj = obj, getattr(obj, part)
659            except AttributeError:
660                # The filterspec has failed.
661                return False
662        return True
663
664    module = __import__(base)
665
666    def iter_filters():
667        for filterspec in configuration.filters:
668            parts = filterspec.split('.')
669            if check(module, parts):
670                yield filterspec
671            elif parts[0] == base and len(parts) > 1 and check(module, parts[1:]):
672                yield '.'.join(parts[1:])
673            else:
674                for key,value in module.__dict__.items():
675                    if check(value, parts):
676                        yield key + '.' + filterspec
677
678    filtered = False
679    for filterspec in iter_filters():
680        filtered = True
681        print("adding filter spec %s to module %s" % (filterspec, repr(module)))
682        tests = unittest2.defaultTestLoader.loadTestsFromName(filterspec, module)
683        configuration.suite.addTests(tests)
684
685    # Forgo this module if the (base, filterspec) combo is invalid
686    if configuration.filters and not filtered:
687        return
688
689    if not filtered:
690        # Add the entire file's worth of tests since we're not filtered.
691        # Also the fail-over case when the filterspec branch
692        # (base, filterspec) combo doesn't make sense.
693        configuration.suite.addTests(
694            unittest2.defaultTestLoader.loadTestsFromName(base))
695
696
697def visit(prefix, dir, names):
698    """Visitor function for os.path.walk(path, visit, arg)."""
699
700    dir_components = set(dir.split(os.sep))
701    excluded_components = set(['.svn', '.git'])
702    if dir_components.intersection(excluded_components):
703        return
704
705    # Gather all the Python test file names that follow the Test*.py pattern.
706    python_test_files = [
707        name
708        for name in names
709        if name.endswith('.py') and name.startswith(prefix)]
710
711    # Visit all the python test files.
712    for name in python_test_files:
713        # Ensure we error out if we have multiple tests with the same
714        # base name.
715        # Future improvement: find all the places where we work with base
716        # names and convert to full paths.  We have directory structure
717        # to disambiguate these, so we shouldn't need this constraint.
718        if name in configuration.all_tests:
719            raise Exception("Found multiple tests with the name %s" % name)
720        configuration.all_tests.add(name)
721
722        # Run the relevant tests in the python file.
723        visit_file(dir, name)
724
725
726# ======================================== #
727#                                          #
728# Execution of the test driver starts here #
729#                                          #
730# ======================================== #
731
732
733def checkDsymForUUIDIsNotOn():
734    cmd = ["defaults", "read", "com.apple.DebugSymbols"]
735    process = subprocess.Popen(
736        cmd,
737        stdout=subprocess.PIPE,
738        stderr=subprocess.STDOUT)
739    cmd_output = process.stdout.read()
740    output_str = cmd_output.decode("utf-8")
741    if "DBGFileMappedPaths = " in output_str:
742        print("%s =>" % ' '.join(cmd))
743        print(output_str)
744        print(
745            "Disable automatic lookup and caching of dSYMs before running the test suite!")
746        print("Exiting...")
747        sys.exit(0)
748
749
750def exitTestSuite(exitCode=None):
751    # lldb.py does SBDebugger.Initialize().
752    # Call SBDebugger.Terminate() on exit.
753    import lldb
754    lldb.SBDebugger.Terminate()
755    if exitCode:
756        sys.exit(exitCode)
757
758
759def getVersionForSDK(sdk):
760    sdk = str.lower(sdk)
761    full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk)
762    basename = os.path.basename(full_path)
763    basename = os.path.splitext(basename)[0]
764    basename = str.lower(basename)
765    ver = basename.replace(sdk, '')
766    return ver
767
768
769def checkCompiler():
770    # Add some intervention here to sanity check that the compiler requested is sane.
771    # If found not to be an executable program, we abort.
772    c = configuration.compiler
773    if which(c):
774        return
775
776    if not sys.platform.startswith("darwin"):
777        raise Exception(c + " is not a valid compiler")
778
779    pipe = subprocess.Popen(
780        ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
781    cmd_output = pipe.stdout.read()
782    if not cmd_output or "not found" in cmd_output:
783        raise Exception(c + " is not a valid compiler")
784
785    configuration.compiler = cmd_output.split('\n')[0]
786    print("'xcrun -find %s' returning %s" % (c, configuration.compiler))
787
788def canRunLibcxxTests():
789    from lldbsuite.test import lldbplatformutil
790
791    platform = lldbplatformutil.getPlatform()
792
793    if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin():
794        return True, "libc++ always present"
795
796    if platform == "linux":
797        if os.path.isdir("/usr/include/c++/v1"):
798            return True, "Headers found, let's hope they work"
799        with tempfile.NamedTemporaryFile() as f:
800            cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"]
801            p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
802            _, stderr = p.communicate("#include <algorithm>\nint main() {}")
803            if not p.returncode:
804                return True, "Compiling with -stdlib=libc++ works"
805            return False, "Compiling with -stdlib=libc++ fails with the error: %s" % stderr
806
807    return False, "Don't know how to build with libc++ on %s" % platform
808
809def checkLibcxxSupport():
810    result, reason = canRunLibcxxTests()
811    if result:
812        return # libc++ supported
813    if "libc++" in configuration.categories_list:
814        return # libc++ category explicitly requested, let it run.
815    print("Libc++ tests will not be run because: " + reason)
816    configuration.skip_categories.append("libc++")
817
818def canRunLibstdcxxTests():
819    from lldbsuite.test import lldbplatformutil
820
821    platform = lldbplatformutil.getPlatform()
822    if lldbplatformutil.target_is_android():
823        platform = "android"
824    if platform == "linux":
825        return True, "libstdcxx always present"
826    return False, "Don't know how to build with libstdcxx on %s" % platform
827
828def checkLibstdcxxSupport():
829    result, reason = canRunLibstdcxxTests()
830    if result:
831        return # libstdcxx supported
832    if "libstdcxx" in configuration.categories_list:
833        return # libstdcxx category explicitly requested, let it run.
834    print("libstdcxx tests will not be run because: " + reason)
835    configuration.skip_categories.append("libstdcxx")
836
837def canRunWatchpointTests():
838    from lldbsuite.test import lldbplatformutil
839
840    platform = lldbplatformutil.getPlatform()
841    if platform == "netbsd":
842        if os.geteuid() == 0:
843            return True, "root can always write dbregs"
844        try:
845            output = subprocess.check_output(["/sbin/sysctl", "-n",
846              "security.models.extensions.user_set_dbregs"]).decode().strip()
847            if output == "1":
848                return True, "security.models.extensions.user_set_dbregs enabled"
849        except subprocess.CalledProcessError:
850            pass
851        return False, "security.models.extensions.user_set_dbregs disabled"
852    return True, "watchpoint support available"
853
854def checkWatchpointSupport():
855    result, reason = canRunWatchpointTests()
856    if result:
857        return # watchpoints supported
858    if "watchpoint" in configuration.categories_list:
859        return # watchpoint category explicitly requested, let it run.
860    print("watchpoint tests will not be run because: " + reason)
861    configuration.skip_categories.append("watchpoint")
862
863def checkDebugInfoSupport():
864    import lldb
865
866    platform = lldb.selected_platform.GetTriple().split('-')[2]
867    compiler = configuration.compiler
868    skipped = []
869    for cat in test_categories.debug_info_categories:
870        if cat in configuration.categories_list:
871            continue # Category explicitly requested, let it run.
872        if test_categories.is_supported_on_platform(cat, platform, compiler):
873            continue
874        configuration.skip_categories.append(cat)
875        skipped.append(cat)
876    if skipped:
877        print("Skipping following debug info categories:", skipped)
878
879def run_suite():
880    # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
881    # does not exist before proceeding to running the test suite.
882    if sys.platform.startswith("darwin"):
883        checkDsymForUUIDIsNotOn()
884
885    # Start the actions by first parsing the options while setting up the test
886    # directories, followed by setting up the search paths for lldb utilities;
887    # then, we walk the directory trees and collect the tests into our test suite.
888    #
889    parseOptionsAndInitTestdirs()
890
891    setupSysPath()
892
893    import lldbconfig
894    if configuration.capture_path or configuration.replay_path:
895        lldbconfig.INITIALIZE = False
896    import lldb
897
898    if configuration.capture_path:
899        lldb.SBReproducer.Capture(configuration.capture_path)
900        lldb.SBReproducer.SetAutoGenerate(True)
901    elif configuration.replay_path:
902        lldb.SBReproducer.PassiveReplay(configuration.replay_path)
903
904    if not lldbconfig.INITIALIZE:
905        lldb.SBDebugger.Initialize()
906
907    # Use host platform by default.
908    lldb.selected_platform = lldb.SBPlatform.GetHostPlatform()
909
910    # Now we can also import lldbutil
911    from lldbsuite.test import lldbutil
912
913    if configuration.lldb_platform_name:
914        print("Setting up remote platform '%s'" %
915              (configuration.lldb_platform_name))
916        lldb.remote_platform = lldb.SBPlatform(
917            configuration.lldb_platform_name)
918        if not lldb.remote_platform.IsValid():
919            print(
920                "error: unable to create the LLDB platform named '%s'." %
921                (configuration.lldb_platform_name))
922            exitTestSuite(1)
923        if configuration.lldb_platform_url:
924            # We must connect to a remote platform if a LLDB platform URL was
925            # specified
926            print(
927                "Connecting to remote platform '%s' at '%s'..." %
928                (configuration.lldb_platform_name, configuration.lldb_platform_url))
929            platform_connect_options = lldb.SBPlatformConnectOptions(
930                configuration.lldb_platform_url)
931            err = lldb.remote_platform.ConnectRemote(platform_connect_options)
932            if err.Success():
933                print("Connected.")
934            else:
935                print("error: failed to connect to remote platform using URL '%s': %s" % (
936                    configuration.lldb_platform_url, err))
937                exitTestSuite(1)
938        else:
939            configuration.lldb_platform_url = None
940
941    if configuration.lldb_platform_working_dir:
942        print("Setting remote platform working directory to '%s'..." %
943              (configuration.lldb_platform_working_dir))
944        error = lldb.remote_platform.MakeDirectory(
945            configuration.lldb_platform_working_dir, 448)  # 448 = 0o700
946        if error.Fail():
947            raise Exception("making remote directory '%s': %s" % (
948                configuration.lldb_platform_working_dir, error))
949
950        if not lldb.remote_platform.SetWorkingDirectory(
951                configuration.lldb_platform_working_dir):
952            raise Exception("failed to set working directory '%s'" % configuration.lldb_platform_working_dir)
953        lldb.selected_platform = lldb.remote_platform
954    else:
955        lldb.remote_platform = None
956        configuration.lldb_platform_working_dir = None
957        configuration.lldb_platform_url = None
958
959    # Set up the working directory.
960    # Note that it's not dotest's job to clean this directory.
961    lldbutil.mkdir_p(configuration.test_build_dir)
962
963    target_platform = lldb.selected_platform.GetTriple().split('-')[2]
964
965    checkLibcxxSupport()
966    checkLibstdcxxSupport()
967    checkWatchpointSupport()
968    checkDebugInfoSupport()
969
970    # Don't do debugserver tests on anything except OS X.
971    configuration.dont_do_debugserver_test = (
972            "linux" in target_platform or
973            "freebsd" in target_platform or
974            "netbsd" in target_platform or
975            "windows" in target_platform)
976
977    # Don't do lldb-server (llgs) tests on anything except Linux and Windows.
978    configuration.dont_do_llgs_test = not (
979            "linux" in target_platform or
980            "netbsd" in target_platform or
981            "windows" in target_platform)
982
983    for testdir in configuration.testdirs:
984        for (dirpath, dirnames, filenames) in os.walk(testdir):
985            visit('Test', dirpath, filenames)
986
987    #
988    # Now that we have loaded all the test cases, run the whole test suite.
989    #
990
991    # Install the control-c handler.
992    unittest2.signals.installHandler()
993
994    lldbutil.mkdir_p(configuration.sdir_name)
995    os.environ["LLDB_SESSION_DIRNAME"] = configuration.sdir_name
996
997    sys.stderr.write(
998        "\nSession logs for test failures/errors/unexpected successes"
999        " will go into directory '%s'\n" %
1000        configuration.sdir_name)
1001
1002    #
1003    # Invoke the default TextTestRunner to run the test suite
1004    #
1005    checkCompiler()
1006
1007    if configuration.verbose:
1008        print("compiler=%s" % configuration.compiler)
1009
1010    # Iterating over all possible architecture and compiler combinations.
1011    configString = "arch=%s compiler=%s" % (configuration.arch,
1012                                            configuration.compiler)
1013
1014    # Output the configuration.
1015    if configuration.verbose:
1016        sys.stderr.write("\nConfiguration: " + configString + "\n")
1017
1018    # First, write out the number of collected test cases.
1019    if configuration.verbose:
1020        sys.stderr.write(configuration.separator + "\n")
1021        sys.stderr.write(
1022            "Collected %d test%s\n\n" %
1023            (configuration.suite.countTestCases(),
1024             configuration.suite.countTestCases() != 1 and "s" or ""))
1025
1026    # Invoke the test runner.
1027    if configuration.count == 1:
1028        result = unittest2.TextTestRunner(
1029            stream=sys.stderr,
1030            verbosity=configuration.verbose,
1031            resultclass=test_result.LLDBTestResult).run(
1032            configuration.suite)
1033    else:
1034        # We are invoking the same test suite more than once.  In this case,
1035        # mark __ignore_singleton__ flag as True so the signleton pattern is
1036        # not enforced.
1037        test_result.LLDBTestResult.__ignore_singleton__ = True
1038        for i in range(configuration.count):
1039
1040            result = unittest2.TextTestRunner(
1041                stream=sys.stderr,
1042                verbosity=configuration.verbose,
1043                resultclass=test_result.LLDBTestResult).run(
1044                configuration.suite)
1045
1046    configuration.failed = not result.wasSuccessful()
1047
1048    if configuration.sdir_has_content and configuration.verbose:
1049        sys.stderr.write(
1050            "Session logs for test failures/errors/unexpected successes"
1051            " can be found in directory '%s'\n" %
1052            configuration.sdir_name)
1053
1054    if configuration.use_categories and len(
1055            configuration.failures_per_category) > 0:
1056        sys.stderr.write("Failures per category:\n")
1057        for category in configuration.failures_per_category:
1058            sys.stderr.write(
1059                "%s - %d\n" %
1060                (category, configuration.failures_per_category[category]))
1061
1062    # Exiting.
1063    exitTestSuite(configuration.failed)
1064
1065if __name__ == "__main__":
1066    print(
1067        __file__ +
1068        " is for use as a module only.  It should not be run as a standalone script.")
1069    sys.exit(-1)
1070