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