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