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