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