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