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