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