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    lldbtest_config.codesign_identity = args.codesign_identity
485
486    #print("testdirs:", testdirs)
487
488
489def getXcodeOutputPaths(lldbRootDirectory):
490    result = []
491
492    # These are for xcode build directories.
493    xcode3_build_dir = ['build']
494    xcode4_build_dir = ['build', 'lldb', 'Build', 'Products']
495
496    configurations = [
497        ['Debug'],
498        ['DebugClang'],
499        ['Release'],
500        ['BuildAndIntegration']]
501    xcode_build_dirs = [xcode3_build_dir, xcode4_build_dir]
502    for configuration in configurations:
503        for xcode_build_dir in xcode_build_dirs:
504            outputPath = os.path.join(
505                lldbRootDirectory, *(xcode_build_dir + configuration))
506            result.append(outputPath)
507
508    return result
509
510
511def createSocketToLocalPort(port):
512    def socket_closer(s):
513        """Close down an opened socket properly."""
514        s.shutdown(socket.SHUT_RDWR)
515        s.close()
516
517    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
518    sock.connect(("localhost", port))
519    return (sock, lambda: socket_closer(sock))
520
521
522def setupTestResults():
523    """Sets up test results-related objects based on arg settings."""
524    # Setup the results formatter configuration.
525    formatter_config = formatter.FormatterConfig()
526    formatter_config.filename = configuration.results_filename
527    formatter_config.formatter_name = configuration.results_formatter_name
528    formatter_config.formatter_options = (
529        configuration.results_formatter_options)
530    formatter_config.port = configuration.results_port
531
532    # Create the results formatter.
533    formatter_spec = formatter.create_results_formatter(
534        formatter_config)
535    if formatter_spec is not None and formatter_spec.formatter is not None:
536        configuration.results_formatter_object = formatter_spec.formatter
537
538        # Send an initialize message to the formatter.
539        initialize_event = EventBuilder.bare_event("initialize")
540        if isMultiprocessTestRunner():
541            if (configuration.test_runner_name is not None and
542                    configuration.test_runner_name == "serial"):
543                # Only one worker queue here.
544                worker_count = 1
545            else:
546                # Workers will be the number of threads specified.
547                worker_count = configuration.num_threads
548        else:
549            worker_count = 1
550        initialize_event["worker_count"] = worker_count
551
552        formatter_spec.formatter.handle_event(initialize_event)
553
554        # Make sure we clean up the formatter on shutdown.
555        if formatter_spec.cleanup_func is not None:
556            atexit.register(formatter_spec.cleanup_func)
557
558
559def getOutputPaths(lldbRootDirectory):
560    """
561    Returns typical build output paths for the lldb executable
562
563    lldbDirectory - path to the root of the lldb svn/git repo
564    """
565    result = []
566
567    if sys.platform == 'darwin':
568        result.extend(getXcodeOutputPaths(lldbRootDirectory))
569
570    # cmake builds?  look for build or build/host folder next to llvm directory
571    # lldb is located in llvm/tools/lldb so we need to go up three levels
572    llvmParentDir = os.path.abspath(
573        os.path.join(
574            lldbRootDirectory,
575            os.pardir,
576            os.pardir,
577            os.pardir))
578    result.append(os.path.join(llvmParentDir, 'build', 'bin'))
579    result.append(os.path.join(llvmParentDir, 'build', 'host', 'bin'))
580
581    # some cmake developers keep their build directory beside their lldb
582    # directory
583    lldbParentDir = os.path.abspath(os.path.join(lldbRootDirectory, os.pardir))
584    result.append(os.path.join(lldbParentDir, 'build', 'bin'))
585    result.append(os.path.join(lldbParentDir, 'build', 'host', 'bin'))
586
587    return result
588
589
590def setupSysPath():
591    """
592    Add LLDB.framework/Resources/Python to the search paths for modules.
593    As a side effect, we also discover the 'lldb' executable and export it here.
594    """
595
596    # Get the directory containing the current script.
597    if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
598        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
599    else:
600        scriptPath = os.path.dirname(os.path.realpath(__file__))
601    if not scriptPath.endswith('test'):
602        print("This script expects to reside in lldb's test directory.")
603        sys.exit(-1)
604
605    os.environ["LLDB_TEST"] = scriptPath
606
607    # Set up the LLDB_SRC environment variable, so that the tests can locate
608    # the LLDB source code.
609    os.environ["LLDB_SRC"] = lldbsuite.lldb_root
610
611    pluginPath = os.path.join(scriptPath, 'plugins')
612    toolsLLDBMIPath = os.path.join(scriptPath, 'tools', 'lldb-mi')
613    toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server')
614
615    # Insert script dir, plugin dir, lldb-mi dir and lldb-server dir to the
616    # sys.path.
617    sys.path.insert(0, pluginPath)
618    # Adding test/tools/lldb-mi to the path makes it easy
619    sys.path.insert(0, toolsLLDBMIPath)
620    # to "import lldbmi_testcase" from the MI tests
621    # Adding test/tools/lldb-server to the path makes it easy
622    sys.path.insert(0, toolsLLDBServerPath)
623    # to "import lldbgdbserverutils" from the lldb-server tests
624
625    # This is the root of the lldb git/svn checkout
626    # When this changes over to a package instead of a standalone script, this
627    # will be `lldbsuite.lldb_root`
628    lldbRootDirectory = lldbsuite.lldb_root
629
630    # Some of the tests can invoke the 'lldb' command directly.
631    # We'll try to locate the appropriate executable right here.
632
633    # The lldb executable can be set from the command line
634    # if it's not set, we try to find it now
635    # first, we try the environment
636    if not lldbtest_config.lldbExec:
637        # First, you can define an environment variable LLDB_EXEC specifying the
638        # full pathname of the lldb executable.
639        if "LLDB_EXEC" in os.environ:
640            lldbtest_config.lldbExec = os.environ["LLDB_EXEC"]
641
642    if not lldbtest_config.lldbExec:
643        outputPaths = getOutputPaths(lldbRootDirectory)
644        for outputPath in outputPaths:
645            candidatePath = os.path.join(outputPath, 'lldb')
646            if is_exe(candidatePath):
647                lldbtest_config.lldbExec = candidatePath
648                break
649
650    if not lldbtest_config.lldbExec:
651        # Last, check the path
652        lldbtest_config.lldbExec = which('lldb')
653
654    if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec):
655        print(
656            "'{}' is not a path to a valid executable".format(
657                lldbtest_config.lldbExec))
658        lldbtest_config.lldbExec = None
659
660    if not lldbtest_config.lldbExec:
661        print("The 'lldb' executable cannot be located.  Some of the tests may not be run as a result.")
662        sys.exit(-1)
663
664    # confusingly, this is the "bin" directory
665    lldbLibDir = os.path.dirname(lldbtest_config.lldbExec)
666    os.environ["LLDB_LIB_DIR"] = lldbLibDir
667    lldbImpLibDir = os.path.join(
668        lldbLibDir,
669        '..',
670        'lib') if sys.platform.startswith('win32') else lldbLibDir
671    os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir
672    print("LLDB library dir:", os.environ["LLDB_LIB_DIR"])
673    print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"])
674    os.system('%s -v' % lldbtest_config.lldbExec)
675
676    # Assume lldb-mi is in same place as lldb
677    # If not found, disable the lldb-mi tests
678    # TODO: Append .exe on Windows
679    #   - this will be in a separate commit in case the mi tests fail horribly
680    lldbDir = os.path.dirname(lldbtest_config.lldbExec)
681    lldbMiExec = os.path.join(lldbDir, "lldb-mi")
682    if is_exe(lldbMiExec):
683        os.environ["LLDBMI_EXEC"] = lldbMiExec
684    else:
685        if not configuration.shouldSkipBecauseOfCategories(["lldb-mi"]):
686            print(
687                "The 'lldb-mi' executable cannot be located.  The lldb-mi tests can not be run as a result.")
688            configuration.skipCategories.append("lldb-mi")
689
690    lldbPythonDir = None  # The directory that contains 'lldb/__init__.py'
691    if not configuration.lldbFrameworkPath and os.path.exists(os.path.join(lldbLibDir, "LLDB.framework")):
692        configuration.lldbFrameworkPath = os.path.join(lldbLibDir, "LLDB.framework")
693    if configuration.lldbFrameworkPath:
694        lldbtest_config.lldbFrameworkPath = configuration.lldbFrameworkPath
695        candidatePath = os.path.join(
696            configuration.lldbFrameworkPath, 'Resources', 'Python')
697        if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')):
698            lldbPythonDir = candidatePath
699        if not lldbPythonDir:
700            print(
701                'Resources/Python/lldb/__init__.py was not found in ' +
702                configuration.lldbFrameworkPath)
703            sys.exit(-1)
704    else:
705        # If our lldb supports the -P option, use it to find the python path:
706        init_in_python_dir = os.path.join('lldb', '__init__.py')
707
708        lldb_dash_p_result = subprocess.check_output(
709            [lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True)
710
711        if lldb_dash_p_result and not lldb_dash_p_result.startswith(
712                ("<", "lldb: invalid option:")) and not lldb_dash_p_result.startswith("Traceback"):
713            lines = lldb_dash_p_result.splitlines()
714
715            # Workaround for readline vs libedit issue on FreeBSD.  If stdout
716            # is not a terminal Python executes
717            #     rl_variable_bind ("enable-meta-key", "off");
718            # This produces a warning with FreeBSD's libedit because the
719            # enable-meta-key variable is unknown.  Not an issue on Apple
720            # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__
721            # around the call.  See http://bugs.python.org/issue19884 for more
722            # information.  For now we just discard the warning output.
723            if len(lines) >= 1 and lines[0].startswith(
724                    "bind: Invalid command"):
725                lines.pop(0)
726
727            # Taking the last line because lldb outputs
728            # 'Cannot read termcap database;\nusing dumb terminal settings.\n'
729            # before the path
730            if len(lines) >= 1 and os.path.isfile(
731                    os.path.join(lines[-1], init_in_python_dir)):
732                lldbPythonDir = lines[-1]
733                if "freebsd" in sys.platform or "linux" in sys.platform:
734                    os.environ['LLDB_LIB_DIR'] = os.path.join(
735                        lldbPythonDir, '..', '..')
736
737        if not lldbPythonDir:
738            if platform.system() == "Darwin":
739                python_resource_dir = ['LLDB.framework', 'Resources', 'Python']
740                outputPaths = getXcodeOutputPaths(lldbRootDirectory)
741                for outputPath in outputPaths:
742                    candidatePath = os.path.join(
743                        outputPath, *python_resource_dir)
744                    if os.path.isfile(
745                        os.path.join(
746                            candidatePath,
747                            init_in_python_dir)):
748                        lldbPythonDir = candidatePath
749                        break
750
751                if not lldbPythonDir:
752                    print("lldb.py is not found, some tests may fail.")
753            else:
754                print(
755                    "Unable to load lldb extension module.  Possible reasons for this include:")
756                print("  1) LLDB was built with LLDB_DISABLE_PYTHON=1")
757                print(
758                    "  2) PYTHONPATH and PYTHONHOME are not set correctly.  PYTHONHOME should refer to")
759                print(
760                    "     the version of Python that LLDB built and linked against, and PYTHONPATH")
761                print(
762                    "     should contain the Lib directory for the same python distro, as well as the")
763                print("     location of LLDB\'s site-packages folder.")
764                print(
765                    "  3) A different version of Python than that which was built against is exported in")
766                print("     the system\'s PATH environment variable, causing conflicts.")
767                print(
768                    "  4) The executable '%s' could not be found.  Please check " %
769                    lldbtest_config.lldbExec)
770                print("     that it exists and is executable.")
771
772    if lldbPythonDir:
773        lldbPythonDir = os.path.normpath(lldbPythonDir)
774        # Some of the code that uses this path assumes it hasn't resolved the Versions... link.
775        # If the path we've constructed looks like that, then we'll strip out
776        # the Versions/A part.
777        (before, frameWithVersion, after) = lldbPythonDir.rpartition(
778            "LLDB.framework/Versions/A")
779        if frameWithVersion != "":
780            lldbPythonDir = before + "LLDB.framework" + after
781
782        lldbPythonDir = os.path.abspath(lldbPythonDir)
783
784        # If tests need to find LLDB_FRAMEWORK, now they can do it
785        os.environ["LLDB_FRAMEWORK"] = os.path.dirname(
786            os.path.dirname(lldbPythonDir))
787
788        # This is to locate the lldb.py module.  Insert it right after
789        # sys.path[0].
790        sys.path[1:1] = [lldbPythonDir]
791
792
793def visit_file(dir, name):
794    # Try to match the regexp pattern, if specified.
795    if configuration.regexp:
796        if not re.search(configuration.regexp, name):
797            # We didn't match the regex, we're done.
798            return
799
800    if configuration.skip_tests:
801        for file_regexp in configuration.skip_tests:
802            if re.search(file_regexp, name):
803                return
804
805    # We found a match for our test.  Add it to the suite.
806
807    # Update the sys.path first.
808    if not sys.path.count(dir):
809        sys.path.insert(0, dir)
810    base = os.path.splitext(name)[0]
811
812    # Thoroughly check the filterspec against the base module and admit
813    # the (base, filterspec) combination only when it makes sense.
814    filterspec = None
815    for filterspec in configuration.filters:
816        # Optimistically set the flag to True.
817        filtered = True
818        module = __import__(base)
819        parts = filterspec.split('.')
820        obj = module
821        for part in parts:
822            try:
823                parent, obj = obj, getattr(obj, part)
824            except AttributeError:
825                # The filterspec has failed.
826                filtered = False
827                break
828
829        # If filtered, we have a good filterspec.  Add it.
830        if filtered:
831            # print("adding filter spec %s to module %s" % (filterspec, module))
832            configuration.suite.addTests(
833                unittest2.defaultTestLoader.loadTestsFromName(
834                    filterspec, module))
835            continue
836
837    # Forgo this module if the (base, filterspec) combo is invalid
838    if configuration.filters and not filtered:
839        return
840
841    if not filterspec or not filtered:
842        # Add the entire file's worth of tests since we're not filtered.
843        # Also the fail-over case when the filterspec branch
844        # (base, filterspec) combo doesn't make sense.
845        configuration.suite.addTests(
846            unittest2.defaultTestLoader.loadTestsFromName(base))
847
848
849def visit(prefix, dir, names):
850    """Visitor function for os.path.walk(path, visit, arg)."""
851
852    dir_components = set(dir.split(os.sep))
853    excluded_components = set(['.svn', '.git'])
854    if dir_components.intersection(excluded_components):
855        return
856
857    # Gather all the Python test file names that follow the Test*.py pattern.
858    python_test_files = [
859        name
860        for name in names
861        if name.endswith('.py') and name.startswith(prefix)]
862
863    # Visit all the python test files.
864    for name in python_test_files:
865        try:
866            # Ensure we error out if we have multiple tests with the same
867            # base name.
868            # Future improvement: find all the places where we work with base
869            # names and convert to full paths.  We have directory structure
870            # to disambiguate these, so we shouldn't need this constraint.
871            if name in configuration.all_tests:
872                raise Exception("Found multiple tests with the name %s" % name)
873            configuration.all_tests.add(name)
874
875            # Run the relevant tests in the python file.
876            visit_file(dir, name)
877        except Exception as ex:
878            # Convert this exception to a test event error for the file.
879            test_filename = os.path.abspath(os.path.join(dir, name))
880            if configuration.results_formatter_object is not None:
881                # Grab the backtrace for the exception.
882                import traceback
883                backtrace = traceback.format_exc()
884
885                # Generate the test event.
886                configuration.results_formatter_object.handle_event(
887                    EventBuilder.event_for_job_test_add_error(
888                        test_filename, ex, backtrace))
889            raise
890
891
892def disabledynamics():
893    import lldb
894    ci = lldb.DBG.GetCommandInterpreter()
895    res = lldb.SBCommandReturnObject()
896    ci.HandleCommand(
897        "setting set target.prefer-dynamic-value no-dynamic-values",
898        res,
899        False)
900    if not res.Succeeded():
901        raise Exception('disabling dynamic type support failed')
902
903
904def lldbLoggings():
905    import lldb
906    """Check and do lldb loggings if necessary."""
907
908    # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
909    # defined.  Use ${LLDB_LOG} to specify the log file.
910    ci = lldb.DBG.GetCommandInterpreter()
911    res = lldb.SBCommandReturnObject()
912    if ("LLDB_LOG" in os.environ):
913        open(os.environ["LLDB_LOG"], 'w').close()
914        if ("LLDB_LOG_OPTION" in os.environ):
915            lldb_log_option = os.environ["LLDB_LOG_OPTION"]
916        else:
917            lldb_log_option = "event process expr state api"
918        ci.HandleCommand(
919            "log enable -n -f " +
920            os.environ["LLDB_LOG"] +
921            " lldb " +
922            lldb_log_option,
923            res)
924        if not res.Succeeded():
925            raise Exception('log enable failed (check LLDB_LOG env variable)')
926
927    if ("LLDB_LINUX_LOG" in os.environ):
928        open(os.environ["LLDB_LINUX_LOG"], 'w').close()
929        if ("LLDB_LINUX_LOG_OPTION" in os.environ):
930            lldb_log_option = os.environ["LLDB_LINUX_LOG_OPTION"]
931        else:
932            lldb_log_option = "event process expr state api"
933        ci.HandleCommand(
934            "log enable -n -f " +
935            os.environ["LLDB_LINUX_LOG"] +
936            " linux " +
937            lldb_log_option,
938            res)
939        if not res.Succeeded():
940            raise Exception(
941                'log enable failed (check LLDB_LINUX_LOG env variable)')
942
943    # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
944    # Use ${GDB_REMOTE_LOG} to specify the log file.
945    if ("GDB_REMOTE_LOG" in os.environ):
946        if ("GDB_REMOTE_LOG_OPTION" in os.environ):
947            gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
948        else:
949            gdb_remote_log_option = "packets process"
950        ci.HandleCommand(
951            "log enable -n -f " + os.environ["GDB_REMOTE_LOG"] + " gdb-remote "
952            + gdb_remote_log_option,
953            res)
954        if not res.Succeeded():
955            raise Exception(
956                'log enable failed (check GDB_REMOTE_LOG env variable)')
957
958
959def getMyCommandLine():
960    return ' '.join(sys.argv)
961
962# ======================================== #
963#                                          #
964# Execution of the test driver starts here #
965#                                          #
966# ======================================== #
967
968
969def checkDsymForUUIDIsNotOn():
970    cmd = ["defaults", "read", "com.apple.DebugSymbols"]
971    pipe = subprocess.Popen(
972        cmd,
973        stdout=subprocess.PIPE,
974        stderr=subprocess.STDOUT)
975    cmd_output = pipe.stdout.read()
976    if cmd_output and "DBGFileMappedPaths = " in cmd_output:
977        print("%s =>" % ' '.join(cmd))
978        print(cmd_output)
979        print(
980            "Disable automatic lookup and caching of dSYMs before running the test suite!")
981        print("Exiting...")
982        sys.exit(0)
983
984
985def exitTestSuite(exitCode=None):
986    import lldb
987    lldb.SBDebugger.Terminate()
988    if exitCode:
989        sys.exit(exitCode)
990
991
992def isMultiprocessTestRunner():
993    # We're not multiprocess when we're either explicitly
994    # the inferior (as specified by the multiprocess test
995    # runner) OR we've been told to skip using the multiprocess
996    # test runner
997    return not (
998        configuration.is_inferior_test_runner or configuration.no_multiprocess_test_runner)
999
1000
1001def getVersionForSDK(sdk):
1002    sdk = str.lower(sdk)
1003    full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk)
1004    basename = os.path.basename(full_path)
1005    basename = os.path.splitext(basename)[0]
1006    basename = str.lower(basename)
1007    ver = basename.replace(sdk, '')
1008    return ver
1009
1010
1011def getPathForSDK(sdk):
1012    sdk = str.lower(sdk)
1013    full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk)
1014    if os.path.exists(full_path):
1015        return full_path
1016    return None
1017
1018
1019def setDefaultTripleForPlatform():
1020    if configuration.lldb_platform_name == 'ios-simulator':
1021        triple_str = 'x86_64-apple-ios%s' % (
1022            getVersionForSDK('iphonesimulator'))
1023        os.environ['TRIPLE'] = triple_str
1024        return {'TRIPLE': triple_str}
1025    return {}
1026
1027
1028def run_suite():
1029    # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
1030    # does not exist before proceeding to running the test suite.
1031    if sys.platform.startswith("darwin"):
1032        checkDsymForUUIDIsNotOn()
1033
1034    #
1035    # Start the actions by first parsing the options while setting up the test
1036    # directories, followed by setting up the search paths for lldb utilities;
1037    # then, we walk the directory trees and collect the tests into our test suite.
1038    #
1039    parseOptionsAndInitTestdirs()
1040
1041    # Setup test results (test results formatter and output handling).
1042    setupTestResults()
1043
1044    # If we are running as the multiprocess test runner, kick off the
1045    # multiprocess test runner here.
1046    if isMultiprocessTestRunner():
1047        from . import dosep
1048        dosep.main(
1049            configuration.num_threads,
1050            configuration.multiprocess_test_subdir,
1051            configuration.test_runner_name,
1052            configuration.results_formatter_object)
1053        raise Exception("should never get here")
1054    elif configuration.is_inferior_test_runner:
1055        # Shut off Ctrl-C processing in inferiors.  The parallel
1056        # test runner handles this more holistically.
1057        signal.signal(signal.SIGINT, signal.SIG_IGN)
1058
1059    setupSysPath()
1060    configuration.setupCrashInfoHook()
1061
1062    #
1063    # If '-l' is specified, do not skip the long running tests.
1064    if not configuration.skip_long_running_test:
1065        os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO"
1066
1067    # For the time being, let's bracket the test runner within the
1068    # lldb.SBDebugger.Initialize()/Terminate() pair.
1069    import lldb
1070
1071    # Create a singleton SBDebugger in the lldb namespace.
1072    lldb.DBG = lldb.SBDebugger.Create()
1073
1074    if configuration.lldb_platform_name:
1075        print("Setting up remote platform '%s'" %
1076              (configuration.lldb_platform_name))
1077        lldb.remote_platform = lldb.SBPlatform(
1078            configuration.lldb_platform_name)
1079        if not lldb.remote_platform.IsValid():
1080            print(
1081                "error: unable to create the LLDB platform named '%s'." %
1082                (configuration.lldb_platform_name))
1083            exitTestSuite(1)
1084        if configuration.lldb_platform_url:
1085            # We must connect to a remote platform if a LLDB platform URL was
1086            # specified
1087            print(
1088                "Connecting to remote platform '%s' at '%s'..." %
1089                (configuration.lldb_platform_name, configuration.lldb_platform_url))
1090            platform_connect_options = lldb.SBPlatformConnectOptions(
1091                configuration.lldb_platform_url)
1092            err = lldb.remote_platform.ConnectRemote(platform_connect_options)
1093            if err.Success():
1094                print("Connected.")
1095            else:
1096                print("error: failed to connect to remote platform using URL '%s': %s" % (
1097                    configuration.lldb_platform_url, err))
1098                exitTestSuite(1)
1099        else:
1100            configuration.lldb_platform_url = None
1101
1102    platform_changes = setDefaultTripleForPlatform()
1103    first = True
1104    for key in platform_changes:
1105        if first:
1106            print("Environment variables setup for platform support:")
1107            first = False
1108        print("%s = %s" % (key, platform_changes[key]))
1109
1110    if configuration.lldb_platform_working_dir:
1111        print("Setting remote platform working directory to '%s'..." %
1112              (configuration.lldb_platform_working_dir))
1113        lldb.remote_platform.SetWorkingDirectory(
1114            configuration.lldb_platform_working_dir)
1115        lldb.DBG.SetSelectedPlatform(lldb.remote_platform)
1116    else:
1117        lldb.remote_platform = None
1118        configuration.lldb_platform_working_dir = None
1119        configuration.lldb_platform_url = None
1120
1121    target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2]
1122
1123    # Don't do debugserver tests on everything except OS X.
1124    configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform
1125
1126    # Don't do lldb-server (llgs) tests on anything except Linux.
1127    configuration.dont_do_llgs_test = not ("linux" in target_platform)
1128
1129    #
1130    # Walk through the testdirs while collecting tests.
1131    #
1132    for testdir in configuration.testdirs:
1133        for (dirpath, dirnames, filenames) in os.walk(testdir):
1134            visit('Test', dirpath, filenames)
1135
1136    #
1137    # Now that we have loaded all the test cases, run the whole test suite.
1138    #
1139
1140    # Turn on lldb loggings if necessary.
1141    lldbLoggings()
1142
1143    # Disable default dynamic types for testing purposes
1144    disabledynamics()
1145
1146    # Install the control-c handler.
1147    unittest2.signals.installHandler()
1148
1149    # If sdir_name is not specified through the '-s sdir_name' option, get a
1150    # timestamp string and export it as LLDB_SESSION_DIR environment var.  This will
1151    # be used when/if we want to dump the session info of individual test cases
1152    # later on.
1153    #
1154    # See also TestBase.dumpSessionInfo() in lldbtest.py.
1155    import datetime
1156    # The windows platforms don't like ':' in the pathname.
1157    timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
1158    if not configuration.sdir_name:
1159        configuration.sdir_name = timestamp_started
1160    os.environ["LLDB_SESSION_DIRNAME"] = os.path.join(
1161        os.getcwd(), configuration.sdir_name)
1162
1163    sys.stderr.write(
1164        "\nSession logs for test failures/errors/unexpected successes"
1165        " will go into directory '%s'\n" %
1166        configuration.sdir_name)
1167    sys.stderr.write("Command invoked: %s\n" % getMyCommandLine())
1168
1169    if not os.path.isdir(configuration.sdir_name):
1170        try:
1171            os.mkdir(configuration.sdir_name)
1172        except OSError as exception:
1173            if exception.errno != errno.EEXIST:
1174                raise
1175
1176    #
1177    # Invoke the default TextTestRunner to run the test suite, possibly iterating
1178    # over different configurations.
1179    #
1180
1181    iterArchs = False
1182    iterCompilers = False
1183
1184    if isinstance(configuration.archs, list) and len(configuration.archs) >= 1:
1185        iterArchs = True
1186
1187    #
1188    # Add some intervention here to sanity check that the compilers requested are sane.
1189    # If found not to be an executable program, the invalid one is dropped
1190    # from the list.
1191    for i in range(len(configuration.compilers)):
1192        c = configuration.compilers[i]
1193        if which(c):
1194            continue
1195        else:
1196            if sys.platform.startswith("darwin"):
1197                pipe = subprocess.Popen(
1198                    ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1199                cmd_output = pipe.stdout.read()
1200                if cmd_output:
1201                    if "not found" in cmd_output:
1202                        print("dropping %s from the compilers used" % c)
1203                        configuration.compilers.remove(i)
1204                    else:
1205                        configuration.compilers[i] = cmd_output.split('\n')[0]
1206                        print(
1207                            "'xcrun -find %s' returning %s" %
1208                            (c, configuration.compilers[i]))
1209
1210    if not configuration.parsable:
1211        print("compilers=%s" % str(configuration.compilers))
1212
1213    if not configuration.compilers or len(configuration.compilers) == 0:
1214        print("No eligible compiler found, exiting.")
1215        exitTestSuite(1)
1216
1217    if isinstance(
1218        configuration.compilers,
1219        list) and len(
1220            configuration.compilers) >= 1:
1221        iterCompilers = True
1222
1223    # If we iterate on archs or compilers, there is a chance we want to split
1224    # stderr/stdout.
1225    if iterArchs or iterCompilers:
1226        old_stderr = sys.stderr
1227        old_stdout = sys.stdout
1228        new_stderr = None
1229        new_stdout = None
1230
1231    # Iterating over all possible architecture and compiler combinations.
1232    for ia in range(len(configuration.archs) if iterArchs else 1):
1233        archConfig = ""
1234        if iterArchs:
1235            os.environ["ARCH"] = configuration.archs[ia]
1236            archConfig = "arch=%s" % configuration.archs[ia]
1237        for ic in range(len(configuration.compilers) if iterCompilers else 1):
1238            if iterCompilers:
1239                os.environ["CC"] = configuration.compilers[ic]
1240                configString = "%s compiler=%s" % (
1241                    archConfig, configuration.compilers[ic])
1242            else:
1243                configString = archConfig
1244
1245            if iterArchs or iterCompilers:
1246                # Translate ' ' to '-' for pathname component.
1247                if six.PY2:
1248                    import string
1249                    tbl = string.maketrans(' ', '-')
1250                else:
1251                    tbl = str.maketrans(' ', '-')
1252                configPostfix = configString.translate(tbl)
1253
1254                # Output the configuration.
1255                if not configuration.parsable:
1256                    sys.stderr.write("\nConfiguration: " + configString + "\n")
1257
1258            #print("sys.stderr name is", sys.stderr.name)
1259            #print("sys.stdout name is", sys.stdout.name)
1260
1261            # First, write out the number of collected test cases.
1262            if not configuration.parsable:
1263                sys.stderr.write(configuration.separator + "\n")
1264                sys.stderr.write(
1265                    "Collected %d test%s\n\n" %
1266                    (configuration.suite.countTestCases(),
1267                     configuration.suite.countTestCases() != 1 and "s" or ""))
1268
1269            if configuration.parsable:
1270                v = 0
1271            else:
1272                v = configuration.verbose
1273
1274            # Invoke the test runner.
1275            if configuration.count == 1:
1276                result = unittest2.TextTestRunner(
1277                    stream=sys.stderr,
1278                    verbosity=v,
1279                    resultclass=test_result.LLDBTestResult).run(
1280                    configuration.suite)
1281            else:
1282                # We are invoking the same test suite more than once.  In this case,
1283                # mark __ignore_singleton__ flag as True so the signleton pattern is
1284                # not enforced.
1285                test_result.LLDBTestResult.__ignore_singleton__ = True
1286                for i in range(configuration.count):
1287
1288                    result = unittest2.TextTestRunner(
1289                        stream=sys.stderr,
1290                        verbosity=v,
1291                        resultclass=test_result.LLDBTestResult).run(
1292                        configuration.suite)
1293
1294            configuration.failed = configuration.failed or not result.wasSuccessful()
1295
1296    if configuration.sdir_has_content and not configuration.parsable:
1297        sys.stderr.write(
1298            "Session logs for test failures/errors/unexpected successes"
1299            " can be found in directory '%s'\n" %
1300            configuration.sdir_name)
1301
1302    if configuration.useCategories and len(
1303            configuration.failuresPerCategory) > 0:
1304        sys.stderr.write("Failures per category:\n")
1305        for category in configuration.failuresPerCategory:
1306            sys.stderr.write(
1307                "%s - %d\n" %
1308                (category, configuration.failuresPerCategory[category]))
1309
1310    # Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
1311    # This should not be necessary now.
1312    if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
1313        print("Terminating Test suite...")
1314        subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
1315
1316    # Exiting.
1317    exitTestSuite(configuration.failed)
1318
1319if __name__ == "__main__":
1320    print(
1321        __file__ +
1322        " is for use as a module only.  It should not be run as a standalone script.")
1323    sys.exit(-1)
1324