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 os
27import errno
28import platform
29import signal
30import socket
31import subprocess
32import sys
33
34# Third-party modules
35import six
36import unittest2
37
38# LLDB Modules
39import lldbsuite
40from . import configuration
41from . import dotest_args
42from . import lldbtest_config
43from . import test_categories
44from lldbsuite.test_event import formatter
45from . import test_result
46from lldbsuite.test_event.event_builder import EventBuilder
47from ..support import seven
48
49def is_exe(fpath):
50    """Returns true if fpath is an executable."""
51    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
52
53def which(program):
54    """Returns the full path to a program; None otherwise."""
55    fpath, fname = os.path.split(program)
56    if fpath:
57        if is_exe(program):
58            return program
59    else:
60        for path in os.environ["PATH"].split(os.pathsep):
61            exe_file = os.path.join(path, program)
62            if is_exe(exe_file):
63                return exe_file
64    return None
65
66class _WritelnDecorator(object):
67    """Used to decorate file-like objects with a handy 'writeln' method"""
68    def __init__(self,stream):
69        self.stream = stream
70
71    def __getattr__(self, attr):
72        if attr in ('stream', '__getstate__'):
73            raise AttributeError(attr)
74        return getattr(self.stream,attr)
75
76    def writeln(self, arg=None):
77        if arg:
78            self.write(arg)
79        self.write('\n') # text-mode streams translate to \r\n if needed
80
81#
82# Global variables:
83#
84def usage(parser):
85    parser.print_help()
86    if configuration.verbose > 0:
87        print("""
88Examples:
89
90This is an example of using the -f option to pinpoint to a specific test class
91and test method to be run:
92
93$ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command
94----------------------------------------------------------------------
95Collected 1 test
96
97test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase)
98Test 'frame variable this' when stopped on a class constructor. ... ok
99
100----------------------------------------------------------------------
101Ran 1 test in 1.396s
102
103OK
104
105And this is an example of using the -p option to run a single file (the filename
106matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'):
107
108$ ./dotest.py -v -p ObjC
109----------------------------------------------------------------------
110Collected 4 tests
111
112test_break_with_dsym (TestObjCMethods.FoundationTestCase)
113Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
114test_break_with_dwarf (TestObjCMethods.FoundationTestCase)
115Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
116test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase)
117Lookup objective-c data types and evaluate expressions. ... ok
118test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase)
119Lookup objective-c data types and evaluate expressions. ... ok
120
121----------------------------------------------------------------------
122Ran 4 tests in 16.661s
123
124OK
125
126Running of this script also sets up the LLDB_TEST environment variable so that
127individual test cases can locate their supporting files correctly.  The script
128tries to set up Python's search paths for modules by looking at the build tree
129relative to this script.  See also the '-i' option in the following example.
130
131Finally, this is an example of using the lldb.py module distributed/installed by
132Xcode4 to run against the tests under the 'forward' directory, and with the '-w'
133option to add some delay between two tests.  It uses ARCH=x86_64 to specify that
134as the architecture and CC=clang to specify the compiler used for the test run:
135
136$ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward
137
138Session logs for test failures/errors will go into directory '2010-11-11-13_56_16'
139----------------------------------------------------------------------
140Collected 2 tests
141
142test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
143Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
144test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
145Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
146
147----------------------------------------------------------------------
148Ran 2 tests in 5.659s
149
150OK
151
152The 'Session ...' verbiage is recently introduced (see also the '-s' option) to
153notify the directory containing the session logs for test failures or errors.
154In case there is any test failure/error, a similar message is appended at the
155end of the stderr output for your convenience.
156
157ENABLING LOGS FROM TESTS
158
159Option 1:
160
161Writing logs into different files per test case::
162
163This option is particularly useful when multiple dotest instances are created
164by dosep.py
165
166$ ./dotest.py --channel "lldb all"
167
168$ ./dotest.py --channel "lldb all" --channel "gdb-remote packets"
169
170These log files are written to:
171
172<session-dir>/<test-id>-host.log (logs from lldb host process)
173<session-dir>/<test-id>-server.log (logs from debugserver/lldb-server)
174<session-dir>/<test-id>-<test-result>.log (console logs)
175
176By default, logs from successful runs are deleted.  Use the --log-success flag
177to create reference logs for debugging.
178
179$ ./dotest.py --log-success
180
181Option 2: (DEPRECATED)
182
183The following options can only enable logs from the host lldb process.
184Only categories from the "lldb" or "gdb-remote" channels can be enabled
185They also do not automatically enable logs in locally running debug servers.
186Also, logs from all test case are written into each log file
187
188o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem
189  with a default option of 'event process' if LLDB_LOG_OPTION is not defined.
190
191o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the
192  'process.gdb-remote' subsystem with a default option of 'packets' if
193  GDB_REMOTE_LOG_OPTION is not defined.
194
195""")
196    sys.exit(0)
197
198def parseOptionsAndInitTestdirs():
199    """Initialize the list of directories containing our unittest scripts.
200
201    '-h/--help as the first option prints out usage info and exit the program.
202    """
203
204    do_help = False
205
206    platform_system = platform.system()
207    platform_machine = platform.machine()
208
209    parser = dotest_args.create_parser()
210    args = dotest_args.parse_args(parser, sys.argv[1:])
211
212    if args.unset_env_varnames:
213        for env_var in args.unset_env_varnames:
214            if env_var in os.environ:
215                # From Python Doc: When unsetenv() is supported, deletion of items in os.environ
216                # is automatically translated into a corresponding call to unsetenv().
217                del os.environ[env_var]
218                #os.unsetenv(env_var)
219
220    if args.set_env_vars:
221        for env_var in args.set_env_vars:
222            parts = env_var.split('=', 1)
223            if len(parts) == 1:
224                os.environ[parts[0]] = ""
225            else:
226                os.environ[parts[0]] = parts[1]
227
228    # only print the args if being verbose (and parsable is off)
229    if args.v and not args.q:
230        print(sys.argv)
231
232    if args.h:
233        do_help = True
234
235    if args.compilers:
236        configuration.compilers = args.compilers
237    else:
238        # Use a compiler appropriate appropriate for the Apple SDK if one was specified
239        if platform_system == 'Darwin' and args.apple_sdk:
240            configuration.compilers = [seven.get_command_output('xcrun -sdk "%s" -find clang 2> /dev/null' % (args.apple_sdk))]
241        else:
242            # 'clang' on ubuntu 14.04 is 3.4 so we try clang-3.5 first
243            candidateCompilers = ['clang-3.5', 'clang', 'gcc']
244            for candidate in candidateCompilers:
245                if which(candidate):
246                    configuration.compilers = [candidate]
247                    break
248
249    if args.channels:
250        lldbtest_config.channels = args.channels
251
252    if args.log_success:
253        lldbtest_config.log_success = args.log_success
254
255    # Set SDKROOT if we are using an Apple SDK
256    if platform_system == 'Darwin' and args.apple_sdk:
257        os.environ['SDKROOT'] = seven.get_command_output('xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % (args.apple_sdk))
258
259    if args.archs:
260        configuration.archs = args.archs
261        for arch in configuration.archs:
262            if arch.startswith('arm') and platform_system == 'Darwin' and not args.apple_sdk:
263                os.environ['SDKROOT'] = seven.get_command_output('xcrun --sdk iphoneos.internal --show-sdk-path 2> /dev/null')
264                if not os.path.exists(os.environ['SDKROOT']):
265                    os.environ['SDKROOT'] = seven.get_command_output('xcrun --sdk iphoneos --show-sdk-path 2> /dev/null')
266    else:
267        configuration.archs = [platform_machine]
268
269    if args.categoriesList:
270        configuration.categoriesList = set(test_categories.validate(args.categoriesList, False))
271        configuration.useCategories = True
272    else:
273        configuration.categoriesList = []
274
275    if args.skipCategories:
276        configuration.skipCategories = test_categories.validate(args.skipCategories, False)
277
278    if args.E:
279        cflags_extras = args.E
280        os.environ['CFLAGS_EXTRAS'] = cflags_extras
281
282    if args.d:
283        sys.stdout.write("Suspending the process %d to wait for debugger to attach...\n" % os.getpid())
284        sys.stdout.flush()
285        os.kill(os.getpid(), signal.SIGSTOP)
286
287    if args.f:
288        if any([x.startswith('-') for x in args.f]):
289            usage(parser)
290        configuration.filters.extend(args.f)
291        # Shut off multiprocessing mode when additional filters are specified.
292        # The rational is that the user is probably going after a very specific
293        # test and doesn't need a bunch of parallel test runners all looking for
294        # it in a frenzy.  Also, '-v' now spits out all test run output even
295        # on success, so the standard recipe for redoing a failing test (with -v
296        # and a -f to filter to the specific test) now causes all test scanning
297        # (in parallel) to print results for do-nothing runs in a very distracting
298        # manner.  If we really need filtered parallel runs in the future, consider
299        # adding a --no-output-on-success that prevents -v from setting
300        # output-on-success.
301        configuration.no_multiprocess_test_runner = True
302
303    if args.l:
304        configuration.skip_long_running_test = False
305
306    if args.framework:
307        configuration.lldbFrameworkPath = args.framework
308
309    if args.executable:
310        lldbtest_config.lldbExec = os.path.realpath(args.executable)
311
312    if args.p:
313        if args.p.startswith('-'):
314            usage(parser)
315        configuration.regexp = args.p
316
317    if args.q:
318        configuration.parsable = True
319
320    if args.s:
321        if args.s.startswith('-'):
322            usage(parser)
323        configuration.sdir_name = args.s
324
325    if args.t:
326        os.environ['LLDB_COMMAND_TRACE'] = 'YES'
327
328    if args.v:
329        configuration.verbose = 2
330
331    # argparse makes sure we have a number
332    if args.sharp:
333        configuration.count = args.sharp
334
335    if sys.platform.startswith('win32'):
336        os.environ['LLDB_DISABLE_CRASH_DIALOG'] = str(args.disable_crash_dialog)
337        os.environ['LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE'] = str(True)
338
339    if do_help == True:
340        usage(parser)
341
342    if args.no_multiprocess:
343        configuration.no_multiprocess_test_runner = True
344
345    if args.inferior:
346        configuration.is_inferior_test_runner = True
347
348    if args.num_threads:
349        configuration.num_threads = args.num_threads
350
351    if args.test_subdir:
352        configuration.multiprocess_test_subdir = args.test_subdir
353
354    if args.test_runner_name:
355        configuration.test_runner_name = args.test_runner_name
356
357    # Capture test results-related args.
358    if args.curses and not args.inferior:
359        # Act as if the following args were set.
360        args.results_formatter = "lldbsuite.test_event.formatter.curses.Curses"
361        args.results_file = "stdout"
362
363    if args.results_file:
364        configuration.results_filename = args.results_file
365
366    if args.results_port:
367        configuration.results_port = args.results_port
368
369    if args.results_file and args.results_port:
370        sys.stderr.write(
371            "only one of --results-file and --results-port should "
372            "be specified\n")
373        usage(args)
374
375    if args.results_formatter:
376        configuration.results_formatter_name = args.results_formatter
377    if args.results_formatter_options:
378        configuration.results_formatter_options = args.results_formatter_options
379
380    # Default to using the BasicResultsFormatter if no formatter is specified
381    # and we're not a test inferior.
382    if not args.inferior and configuration.results_formatter_name is None:
383        configuration.results_formatter_name = (
384            "lldbsuite.test_event.formatter.results_formatter.ResultsFormatter")
385
386    # rerun-related arguments
387    configuration.rerun_all_issues = args.rerun_all_issues
388    configuration.rerun_max_file_threshold = args.rerun_max_file_threshold
389
390    if args.lldb_platform_name:
391        configuration.lldb_platform_name = args.lldb_platform_name
392    if args.lldb_platform_url:
393        configuration.lldb_platform_url = args.lldb_platform_url
394    if args.lldb_platform_working_dir:
395        configuration.lldb_platform_working_dir = args.lldb_platform_working_dir
396
397    if args.event_add_entries and len(args.event_add_entries) > 0:
398        entries = {}
399        # Parse out key=val pairs, separated by comma
400        for keyval in args.event_add_entries.split(","):
401            key_val_entry = keyval.split("=")
402            if len(key_val_entry) == 2:
403                (key, val) = key_val_entry
404                val_parts = val.split(':')
405                if len(val_parts) > 1:
406                    (val, val_type) = val_parts
407                    if val_type == 'int':
408                        val = int(val)
409                entries[key] = val
410        # Tell the event builder to create all events with these
411        # key/val pairs in them.
412        if len(entries) > 0:
413            EventBuilder.add_entries_to_all_events(entries)
414
415    # Gather all the dirs passed on the command line.
416    if len(args.args) > 0:
417        configuration.testdirs = list(map(lambda x: os.path.realpath(os.path.abspath(x)), args.args))
418        # Shut off multiprocessing mode when test directories are specified.
419        configuration.no_multiprocess_test_runner = True
420
421    #print("testdirs:", testdirs)
422
423def getXcodeOutputPaths(lldbRootDirectory):
424    result = []
425
426    # These are for xcode build directories.
427    xcode3_build_dir = ['build']
428    xcode4_build_dir = ['build', 'lldb', 'Build', 'Products']
429
430    configurations = [['Debug'], ['DebugClang'], ['Release'], ['BuildAndIntegration']]
431    xcode_build_dirs = [xcode3_build_dir, xcode4_build_dir]
432    for configuration in configurations:
433        for xcode_build_dir in xcode_build_dirs:
434            outputPath = os.path.join(lldbRootDirectory, *(xcode_build_dir+configuration) )
435            result.append(outputPath)
436
437    return result
438
439
440def createSocketToLocalPort(port):
441    def socket_closer(s):
442        """Close down an opened socket properly."""
443        s.shutdown(socket.SHUT_RDWR)
444        s.close()
445
446    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
447    sock.connect(("localhost", port))
448    return (sock, lambda: socket_closer(sock))
449
450
451def setupTestResults():
452    """Sets up test results-related objects based on arg settings."""
453    # Setup the results formatter configuration.
454    formatter_config = formatter.FormatterConfig()
455    formatter_config.filename = configuration.results_filename
456    formatter_config.formatter_name = configuration.results_formatter_name
457    formatter_config.formatter_options = (
458        configuration.results_formatter_options)
459    formatter_config.port = configuration.results_port
460
461    # Create the results formatter.
462    formatter_spec = formatter.create_results_formatter(
463        formatter_config)
464    if formatter_spec is not None and formatter_spec.formatter is not None:
465        configuration.results_formatter_object = formatter_spec.formatter
466
467        # Send an initialize message to the formatter.
468        initialize_event = EventBuilder.bare_event("initialize")
469        if isMultiprocessTestRunner():
470            if (configuration.test_runner_name is not None and
471                    configuration.test_runner_name == "serial"):
472                # Only one worker queue here.
473                worker_count = 1
474            else:
475                # Workers will be the number of threads specified.
476                worker_count = configuration.num_threads
477        else:
478            worker_count = 1
479        initialize_event["worker_count"] = worker_count
480
481        formatter_spec.formatter.handle_event(initialize_event)
482
483        # Make sure we clean up the formatter on shutdown.
484        if formatter_spec.cleanup_func is not None:
485            atexit.register(formatter_spec.cleanup_func)
486
487
488def getOutputPaths(lldbRootDirectory):
489    """
490    Returns typical build output paths for the lldb executable
491
492    lldbDirectory - path to the root of the lldb svn/git repo
493    """
494    result = []
495
496    if sys.platform == 'darwin':
497        result.extend(getXcodeOutputPaths(lldbRootDirectory))
498
499    # cmake builds?  look for build or build/host folder next to llvm directory
500    # lldb is located in llvm/tools/lldb so we need to go up three levels
501    llvmParentDir = os.path.abspath(os.path.join(lldbRootDirectory, os.pardir, os.pardir, os.pardir))
502    result.append(os.path.join(llvmParentDir, 'build', 'bin'))
503    result.append(os.path.join(llvmParentDir, 'build', 'host', 'bin'))
504
505    # some cmake developers keep their build directory beside their lldb directory
506    lldbParentDir = os.path.abspath(os.path.join(lldbRootDirectory, os.pardir))
507    result.append(os.path.join(lldbParentDir, 'build', 'bin'))
508    result.append(os.path.join(lldbParentDir, 'build', 'host', 'bin'))
509
510    return result
511
512def setupSysPath():
513    """
514    Add LLDB.framework/Resources/Python to the search paths for modules.
515    As a side effect, we also discover the 'lldb' executable and export it here.
516    """
517
518    # Get the directory containing the current script.
519    if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
520        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
521    else:
522        scriptPath = os.path.dirname(os.path.realpath(__file__))
523    if not scriptPath.endswith('test'):
524        print("This script expects to reside in lldb's test directory.")
525        sys.exit(-1)
526
527    os.environ["LLDB_TEST"] = scriptPath
528
529    # Set up the LLDB_SRC environment variable, so that the tests can locate
530    # the LLDB source code.
531    os.environ["LLDB_SRC"] = lldbsuite.lldb_root
532
533    pluginPath = os.path.join(scriptPath, 'plugins')
534    toolsLLDBMIPath = os.path.join(scriptPath, 'tools', 'lldb-mi')
535    toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server')
536
537    # Insert script dir, plugin dir, lldb-mi dir and lldb-server dir to the sys.path.
538    sys.path.insert(0, pluginPath)
539    sys.path.insert(0, toolsLLDBMIPath)      # Adding test/tools/lldb-mi to the path makes it easy
540                                             # to "import lldbmi_testcase" from the MI tests
541    sys.path.insert(0, toolsLLDBServerPath)  # Adding test/tools/lldb-server to the path makes it easy
542                                             # to "import lldbgdbserverutils" from the lldb-server tests
543
544    # This is the root of the lldb git/svn checkout
545    # When this changes over to a package instead of a standalone script, this
546    # will be `lldbsuite.lldb_root`
547    lldbRootDirectory = lldbsuite.lldb_root
548
549    # Some of the tests can invoke the 'lldb' command directly.
550    # We'll try to locate the appropriate executable right here.
551
552    # The lldb executable can be set from the command line
553    # if it's not set, we try to find it now
554    # first, we try the environment
555    if not lldbtest_config.lldbExec:
556        # First, you can define an environment variable LLDB_EXEC specifying the
557        # full pathname of the lldb executable.
558        if "LLDB_EXEC" in os.environ:
559            lldbtest_config.lldbExec = os.environ["LLDB_EXEC"]
560
561    if not lldbtest_config.lldbExec:
562        outputPaths = getOutputPaths(lldbRootDirectory)
563        for outputPath in outputPaths:
564            candidatePath = os.path.join(outputPath, 'lldb')
565            if is_exe(candidatePath):
566                lldbtest_config.lldbExec = candidatePath
567                break
568
569    if not lldbtest_config.lldbExec:
570        # Last, check the path
571        lldbtest_config.lldbExec = which('lldb')
572
573    if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec):
574        print("'{}' is not a path to a valid executable".format(lldbtest_config.lldbExec))
575        lldbtest_config.lldbExec = None
576
577    if not lldbtest_config.lldbExec:
578        print("The 'lldb' executable cannot be located.  Some of the tests may not be run as a result.")
579        sys.exit(-1)
580
581    lldbLibDir = os.path.dirname(lldbtest_config.lldbExec)  # confusingly, this is the "bin" directory
582    os.environ["LLDB_LIB_DIR"] = lldbLibDir
583    lldbImpLibDir = os.path.join(lldbLibDir, '..', 'lib') if sys.platform.startswith('win32') else lldbLibDir
584    os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir
585    print("LLDB library dir:", os.environ["LLDB_LIB_DIR"])
586    print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"])
587    os.system('%s -v' % lldbtest_config.lldbExec)
588
589    # Assume lldb-mi is in same place as lldb
590    # If not found, disable the lldb-mi tests
591    lldbMiExec = None
592    if lldbtest_config.lldbExec and is_exe(lldbtest_config.lldbExec + "-mi"):
593        lldbMiExec = lldbtest_config.lldbExec + "-mi"
594    if not lldbMiExec:
595        if not configuration.shouldSkipBecauseOfCategories(["lldb-mi"]):
596            print("The 'lldb-mi' executable cannot be located.  The lldb-mi tests can not be run as a result.")
597            configuration.skipCategories.append("lldb-mi")
598    else:
599        os.environ["LLDBMI_EXEC"] = lldbMiExec
600
601    lldbPythonDir = None # The directory that contains 'lldb/__init__.py'
602    if configuration.lldbFrameworkPath:
603        candidatePath = os.path.join(configuration.lldbFrameworkPath, 'Resources', 'Python')
604        if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')):
605            lldbPythonDir = candidatePath
606        if not lldbPythonDir:
607            print('Resources/Python/lldb/__init__.py was not found in ' + configuration.lldbFrameworkPath)
608            sys.exit(-1)
609    else:
610        # If our lldb supports the -P option, use it to find the python path:
611        init_in_python_dir = os.path.join('lldb', '__init__.py')
612
613        lldb_dash_p_result = subprocess.check_output([lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True)
614
615        if lldb_dash_p_result and not lldb_dash_p_result.startswith(("<", "lldb: invalid option:")) \
616							  and not lldb_dash_p_result.startswith("Traceback"):
617            lines = lldb_dash_p_result.splitlines()
618
619            # Workaround for readline vs libedit issue on FreeBSD.  If stdout
620            # is not a terminal Python executes
621            #     rl_variable_bind ("enable-meta-key", "off");
622            # This produces a warning with FreeBSD's libedit because the
623            # enable-meta-key variable is unknown.  Not an issue on Apple
624            # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__
625            # around the call.  See http://bugs.python.org/issue19884 for more
626            # information.  For now we just discard the warning output.
627            if len(lines) >= 1 and lines[0].startswith("bind: Invalid command"):
628                lines.pop(0)
629
630            # Taking the last line because lldb outputs
631            # 'Cannot read termcap database;\nusing dumb terminal settings.\n'
632            # before the path
633            if len(lines) >= 1 and os.path.isfile(os.path.join(lines[-1], init_in_python_dir)):
634                lldbPythonDir = lines[-1]
635                if "freebsd" in sys.platform or "linux" in sys.platform:
636                    os.environ['LLDB_LIB_DIR'] = os.path.join(lldbPythonDir, '..', '..')
637
638        if not lldbPythonDir:
639            if platform.system() == "Darwin":
640                python_resource_dir = ['LLDB.framework', 'Resources', 'Python']
641                outputPaths = getXcodeOutputPaths()
642                for outputPath in outputPaths:
643                    candidatePath = os.path.join(outputPath, python_resource_dir)
644                    if os.path.isfile(os.path.join(candidatePath, init_in_python_dir)):
645                        lldbPythonDir = candidatePath
646                        break
647
648                if not lldbPythonDir:
649                    print('This script requires lldb.py to be in either ' + dbgPath + ',', end=' ')
650                    print(relPath + ', or ' + baiPath + '. Some tests might fail.')
651            else:
652                print("Unable to load lldb extension module.  Possible reasons for this include:")
653                print("  1) LLDB was built with LLDB_DISABLE_PYTHON=1")
654                print("  2) PYTHONPATH and PYTHONHOME are not set correctly.  PYTHONHOME should refer to")
655                print("     the version of Python that LLDB built and linked against, and PYTHONPATH")
656                print("     should contain the Lib directory for the same python distro, as well as the")
657                print("     location of LLDB\'s site-packages folder.")
658                print("  3) A different version of Python than that which was built against is exported in")
659                print("     the system\'s PATH environment variable, causing conflicts.")
660                print("  4) The executable '%s' could not be found.  Please check " % lldbtest_config.lldbExec)
661                print("     that it exists and is executable.")
662
663    if lldbPythonDir:
664        lldbPythonDir = os.path.normpath(lldbPythonDir)
665        # Some of the code that uses this path assumes it hasn't resolved the Versions... link.
666        # If the path we've constructed looks like that, then we'll strip out the Versions/A part.
667        (before, frameWithVersion, after) = lldbPythonDir.rpartition("LLDB.framework/Versions/A")
668        if frameWithVersion != "" :
669            lldbPythonDir = before + "LLDB.framework" + after
670
671        lldbPythonDir = os.path.abspath(lldbPythonDir)
672
673        # If tests need to find LLDB_FRAMEWORK, now they can do it
674        os.environ["LLDB_FRAMEWORK"] = os.path.dirname(os.path.dirname(lldbPythonDir))
675
676        # This is to locate the lldb.py module.  Insert it right after sys.path[0].
677        sys.path[1:1] = [lldbPythonDir]
678
679def visit(prefix, dir, names):
680    """Visitor function for os.path.walk(path, visit, arg)."""
681
682    dir_components = set(dir.split(os.sep))
683    excluded_components = set(['.svn', '.git'])
684    if dir_components.intersection(excluded_components):
685        #print("Detected an excluded dir component: %s" % dir)
686        return
687
688    for name in names:
689        if '.py' == os.path.splitext(name)[1] and name.startswith(prefix):
690
691            if name in configuration.all_tests:
692                raise Exception("Found multiple tests with the name %s" % name)
693            configuration.all_tests.add(name)
694
695            # Try to match the regexp pattern, if specified.
696            if configuration.regexp:
697                import re
698                if re.search(configuration.regexp, name):
699                    #print("Filename: '%s' matches pattern: '%s'" % (name, regexp))
700                    pass
701                else:
702                    #print("Filename: '%s' does not match pattern: '%s'" % (name, regexp))
703                    continue
704
705            # We found a match for our test.  Add it to the suite.
706
707            # Update the sys.path first.
708            if not sys.path.count(dir):
709                sys.path.insert(0, dir)
710            base = os.path.splitext(name)[0]
711
712            # Thoroughly check the filterspec against the base module and admit
713            # the (base, filterspec) combination only when it makes sense.
714            filterspec = None
715            for filterspec in configuration.filters:
716                # Optimistically set the flag to True.
717                filtered = True
718                module = __import__(base)
719                parts = filterspec.split('.')
720                obj = module
721                for part in parts:
722                    try:
723                        parent, obj = obj, getattr(obj, part)
724                    except AttributeError:
725                        # The filterspec has failed.
726                        filtered = False
727                        break
728
729                # If filtered, we have a good filterspec.  Add it.
730                if filtered:
731                    #print("adding filter spec %s to module %s" % (filterspec, module))
732                    configuration.suite.addTests(
733                        unittest2.defaultTestLoader.loadTestsFromName(filterspec, module))
734                    continue
735
736            # Forgo this module if the (base, filterspec) combo is invalid
737            if configuration.filters and not filtered:
738                continue
739
740            # Add either the filtered test case(s) (which is done before) or the entire test class.
741            if not filterspec or not filtered:
742                # A simple case of just the module name.  Also the failover case
743                # from the filterspec branch when the (base, filterspec) combo
744                # doesn't make sense.
745                configuration.suite.addTests(unittest2.defaultTestLoader.loadTestsFromName(base))
746
747
748def disabledynamics():
749    import lldb
750    ci = lldb.DBG.GetCommandInterpreter()
751    res = lldb.SBCommandReturnObject()
752    ci.HandleCommand("setting set target.prefer-dynamic-value no-dynamic-values", res, False)
753    if not res.Succeeded():
754        raise Exception('disabling dynamic type support failed')
755
756def lldbLoggings():
757    import lldb
758    """Check and do lldb loggings if necessary."""
759
760    # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
761    # defined.  Use ${LLDB_LOG} to specify the log file.
762    ci = lldb.DBG.GetCommandInterpreter()
763    res = lldb.SBCommandReturnObject()
764    if ("LLDB_LOG" in os.environ):
765        open(os.environ["LLDB_LOG"], 'w').close()
766        if ("LLDB_LOG_OPTION" in os.environ):
767            lldb_log_option = os.environ["LLDB_LOG_OPTION"]
768        else:
769            lldb_log_option = "event process expr state api"
770        ci.HandleCommand(
771            "log enable -n -f " + os.environ["LLDB_LOG"] + " lldb " + lldb_log_option,
772            res)
773        if not res.Succeeded():
774            raise Exception('log enable failed (check LLDB_LOG env variable)')
775
776    if ("LLDB_LINUX_LOG" in os.environ):
777        open(os.environ["LLDB_LINUX_LOG"], 'w').close()
778        if ("LLDB_LINUX_LOG_OPTION" in os.environ):
779            lldb_log_option = os.environ["LLDB_LINUX_LOG_OPTION"]
780        else:
781            lldb_log_option = "event process expr state api"
782        ci.HandleCommand(
783            "log enable -n -f " + os.environ["LLDB_LINUX_LOG"] + " linux " + lldb_log_option,
784            res)
785        if not res.Succeeded():
786            raise Exception('log enable failed (check LLDB_LINUX_LOG env variable)')
787
788    # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
789    # Use ${GDB_REMOTE_LOG} to specify the log file.
790    if ("GDB_REMOTE_LOG" in os.environ):
791        if ("GDB_REMOTE_LOG_OPTION" in os.environ):
792            gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
793        else:
794            gdb_remote_log_option = "packets process"
795        ci.HandleCommand(
796            "log enable -n -f " + os.environ["GDB_REMOTE_LOG"] + " gdb-remote "
797            + gdb_remote_log_option,
798            res)
799        if not res.Succeeded():
800            raise Exception('log enable failed (check GDB_REMOTE_LOG env variable)')
801
802def getMyCommandLine():
803    return ' '.join(sys.argv)
804
805# ======================================== #
806#                                          #
807# Execution of the test driver starts here #
808#                                          #
809# ======================================== #
810
811def checkDsymForUUIDIsNotOn():
812    cmd = ["defaults", "read", "com.apple.DebugSymbols"]
813    pipe = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
814    cmd_output = pipe.stdout.read()
815    if cmd_output and "DBGFileMappedPaths = " in cmd_output:
816        print("%s =>" % ' '.join(cmd))
817        print(cmd_output)
818        print("Disable automatic lookup and caching of dSYMs before running the test suite!")
819        print("Exiting...")
820        sys.exit(0)
821
822def exitTestSuite(exitCode = None):
823    import lldb
824    lldb.SBDebugger.Terminate()
825    if exitCode:
826        sys.exit(exitCode)
827
828
829def isMultiprocessTestRunner():
830    # We're not multiprocess when we're either explicitly
831    # the inferior (as specified by the multiprocess test
832    # runner) OR we've been told to skip using the multiprocess
833    # test runner
834    return not (configuration.is_inferior_test_runner or configuration.no_multiprocess_test_runner)
835
836def getVersionForSDK(sdk):
837    sdk = str.lower(sdk)
838    full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk)
839    basename = os.path.basename(full_path)
840    basename = os.path.splitext(basename)[0]
841    basename = str.lower(basename)
842    ver = basename.replace(sdk, '')
843    return ver
844
845def getPathForSDK(sdk):
846    sdk = str.lower(sdk)
847    full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk)
848    if os.path.exists(full_path): return full_path
849    return None
850
851def setDefaultTripleForPlatform():
852    if configuration.lldb_platform_name == 'ios-simulator':
853        triple_str = 'x86_64-apple-ios%s' % (getVersionForSDK('iphonesimulator'))
854        os.environ['TRIPLE'] = triple_str
855        return {'TRIPLE':triple_str}
856    return {}
857
858def run_suite():
859    # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
860    # does not exist before proceeding to running the test suite.
861    if sys.platform.startswith("darwin"):
862        checkDsymForUUIDIsNotOn()
863
864    #
865    # Start the actions by first parsing the options while setting up the test
866    # directories, followed by setting up the search paths for lldb utilities;
867    # then, we walk the directory trees and collect the tests into our test suite.
868    #
869    parseOptionsAndInitTestdirs()
870
871    # Setup test results (test results formatter and output handling).
872    setupTestResults()
873
874    # If we are running as the multiprocess test runner, kick off the
875    # multiprocess test runner here.
876    if isMultiprocessTestRunner():
877        from . import dosep
878        dosep.main(configuration.num_threads, configuration.multiprocess_test_subdir,
879                   configuration.test_runner_name, configuration.results_formatter_object)
880        raise Exception("should never get here")
881    elif configuration.is_inferior_test_runner:
882        # Shut off Ctrl-C processing in inferiors.  The parallel
883        # test runner handles this more holistically.
884        signal.signal(signal.SIGINT, signal.SIG_IGN)
885
886    setupSysPath()
887    configuration.setupCrashInfoHook()
888
889    #
890    # If '-l' is specified, do not skip the long running tests.
891    if not configuration.skip_long_running_test:
892        os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO"
893
894    # For the time being, let's bracket the test runner within the
895    # lldb.SBDebugger.Initialize()/Terminate() pair.
896    import lldb
897
898    # Create a singleton SBDebugger in the lldb namespace.
899    lldb.DBG = lldb.SBDebugger.Create()
900
901    if configuration.lldb_platform_name:
902        print("Setting up remote platform '%s'" % (configuration.lldb_platform_name))
903        lldb.remote_platform = lldb.SBPlatform(configuration.lldb_platform_name)
904        if not lldb.remote_platform.IsValid():
905            print("error: unable to create the LLDB platform named '%s'." % (configuration.lldb_platform_name))
906            exitTestSuite(1)
907        if configuration.lldb_platform_url:
908            # We must connect to a remote platform if a LLDB platform URL was specified
909            print("Connecting to remote platform '%s' at '%s'..." % (configuration.lldb_platform_name, configuration.lldb_platform_url))
910            platform_connect_options = lldb.SBPlatformConnectOptions(configuration.lldb_platform_url)
911            err = lldb.remote_platform.ConnectRemote(platform_connect_options)
912            if err.Success():
913                print("Connected.")
914            else:
915                print("error: failed to connect to remote platform using URL '%s': %s" % (configuration.lldb_platform_url, err))
916                exitTestSuite(1)
917        else:
918            configuration.lldb_platform_url = None
919
920    platform_changes = setDefaultTripleForPlatform()
921    first = True
922    for key in platform_changes:
923        if first:
924            print("Environment variables setup for platform support:")
925            first = False
926        print("%s = %s" % (key,platform_changes[key]))
927
928    if configuration.lldb_platform_working_dir:
929        print("Setting remote platform working directory to '%s'..." % (configuration.lldb_platform_working_dir))
930        lldb.remote_platform.SetWorkingDirectory(configuration.lldb_platform_working_dir)
931        lldb.DBG.SetSelectedPlatform(lldb.remote_platform)
932    else:
933        lldb.remote_platform = None
934        configuration.lldb_platform_working_dir = None
935        configuration.lldb_platform_url = None
936
937    target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2]
938
939    # Don't do debugserver tests on everything except OS X.
940    configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform
941
942    # Don't do lldb-server (llgs) tests on anything except Linux.
943    configuration.dont_do_llgs_test = not ("linux" in target_platform)
944
945    #
946    # Walk through the testdirs while collecting tests.
947    #
948    for testdir in configuration.testdirs:
949        for (dirpath, dirnames, filenames) in os.walk(testdir):
950            visit('Test', dirpath, filenames)
951
952    #
953    # Now that we have loaded all the test cases, run the whole test suite.
954    #
955
956    # Turn on lldb loggings if necessary.
957    lldbLoggings()
958
959    # Disable default dynamic types for testing purposes
960    disabledynamics()
961
962    # Install the control-c handler.
963    unittest2.signals.installHandler()
964
965    # If sdir_name is not specified through the '-s sdir_name' option, get a
966    # timestamp string and export it as LLDB_SESSION_DIR environment var.  This will
967    # be used when/if we want to dump the session info of individual test cases
968    # later on.
969    #
970    # See also TestBase.dumpSessionInfo() in lldbtest.py.
971    import datetime
972    # The windows platforms don't like ':' in the pathname.
973    timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
974    if not configuration.sdir_name:
975        configuration.sdir_name = timestamp_started
976    os.environ["LLDB_SESSION_DIRNAME"] = os.path.join(os.getcwd(), configuration.sdir_name)
977
978    sys.stderr.write("\nSession logs for test failures/errors/unexpected successes"
979                        " will go into directory '%s'\n" % configuration.sdir_name)
980    sys.stderr.write("Command invoked: %s\n" % getMyCommandLine())
981
982    if not os.path.isdir(configuration.sdir_name):
983        try:
984            os.mkdir(configuration.sdir_name)
985        except OSError as exception:
986            if exception.errno != errno.EEXIST:
987                raise
988
989    #
990    # Invoke the default TextTestRunner to run the test suite, possibly iterating
991    # over different configurations.
992    #
993
994    iterArchs = False
995    iterCompilers = False
996
997    if isinstance(configuration.archs, list) and len(configuration.archs) >= 1:
998        iterArchs = True
999
1000    #
1001    # Add some intervention here to sanity check that the compilers requested are sane.
1002    # If found not to be an executable program, the invalid one is dropped from the list.
1003    for i in range(len(configuration.compilers)):
1004        c = configuration.compilers[i]
1005        if which(c):
1006            continue
1007        else:
1008            if sys.platform.startswith("darwin"):
1009                pipe = subprocess.Popen(['xcrun', '-find', c], stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
1010                cmd_output = pipe.stdout.read()
1011                if cmd_output:
1012                    if "not found" in cmd_output:
1013                        print("dropping %s from the compilers used" % c)
1014                        configuration.compilers.remove(i)
1015                    else:
1016                        configuration.compilers[i] = cmd_output.split('\n')[0]
1017                        print("'xcrun -find %s' returning %s" % (c, configuration.compilers[i]))
1018
1019    if not configuration.parsable:
1020        print("compilers=%s" % str(configuration.compilers))
1021
1022    if not configuration.compilers or len(configuration.compilers) == 0:
1023        print("No eligible compiler found, exiting.")
1024        exitTestSuite(1)
1025
1026    if isinstance(configuration.compilers, list) and len(configuration.compilers) >= 1:
1027        iterCompilers = True
1028
1029    # If we iterate on archs or compilers, there is a chance we want to split stderr/stdout.
1030    if iterArchs or iterCompilers:
1031        old_stderr = sys.stderr
1032        old_stdout = sys.stdout
1033        new_stderr = None
1034        new_stdout = None
1035
1036    # Iterating over all possible architecture and compiler combinations.
1037    for ia in range(len(configuration.archs) if iterArchs else 1):
1038        archConfig = ""
1039        if iterArchs:
1040            os.environ["ARCH"] = configuration.archs[ia]
1041            archConfig = "arch=%s" % configuration.archs[ia]
1042        for ic in range(len(configuration.compilers) if iterCompilers else 1):
1043            if iterCompilers:
1044                os.environ["CC"] = configuration.compilers[ic]
1045                configString = "%s compiler=%s" % (archConfig, configuration.compilers[ic])
1046            else:
1047                configString = archConfig
1048
1049            if iterArchs or iterCompilers:
1050                # Translate ' ' to '-' for pathname component.
1051                if six.PY2:
1052                    import string
1053                    tbl = string.maketrans(' ', '-')
1054                else:
1055                    tbl = str.maketrans(' ', '-')
1056                configPostfix = configString.translate(tbl)
1057
1058                # Output the configuration.
1059                if not configuration.parsable:
1060                    sys.stderr.write("\nConfiguration: " + configString + "\n")
1061
1062            #print("sys.stderr name is", sys.stderr.name)
1063            #print("sys.stdout name is", sys.stdout.name)
1064
1065            # First, write out the number of collected test cases.
1066            if not configuration.parsable:
1067                sys.stderr.write(configuration.separator + "\n")
1068                sys.stderr.write("Collected %d test%s\n\n"
1069                                 % (configuration.suite.countTestCases(),
1070                                    configuration.suite.countTestCases() != 1 and "s" or ""))
1071
1072            if configuration.parsable:
1073                v = 0
1074            else:
1075                v = configuration.verbose
1076
1077            # Invoke the test runner.
1078            if configuration.count == 1:
1079                result = unittest2.TextTestRunner(stream=sys.stderr,
1080                                                  verbosity=v,
1081                                                  resultclass=test_result.LLDBTestResult).run(configuration.suite)
1082            else:
1083                # We are invoking the same test suite more than once.  In this case,
1084                # mark __ignore_singleton__ flag as True so the signleton pattern is
1085                # not enforced.
1086                test_result.LLDBTestResult.__ignore_singleton__ = True
1087                for i in range(count):
1088
1089                    result = unittest2.TextTestRunner(stream=sys.stderr,
1090                                                      verbosity=v,
1091                                                      resultclass=test_result.LLDBTestResult).run(configuration.suite)
1092
1093            configuration.failed = configuration.failed or not result.wasSuccessful()
1094
1095    if configuration.sdir_has_content and not configuration.parsable:
1096        sys.stderr.write("Session logs for test failures/errors/unexpected successes"
1097                         " can be found in directory '%s'\n" % configuration.sdir_name)
1098
1099    if configuration.useCategories and len(configuration.failuresPerCategory) > 0:
1100        sys.stderr.write("Failures per category:\n")
1101        for category in configuration.failuresPerCategory:
1102            sys.stderr.write("%s - %d\n" % (category, configuration.failuresPerCategory[category]))
1103
1104    # Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
1105    # This should not be necessary now.
1106    if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
1107        print("Terminating Test suite...")
1108        subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
1109
1110    # Exiting.
1111    exitTestSuite(configuration.failed)
1112
1113if __name__ == "__main__":
1114    print(__file__ + " is for use as a module only.  It should not be run as a standalone script.")
1115    sys.exit(-1)
1116