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