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