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