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