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