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