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