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        os.environ['CFLAGS_EXTRAS'] = args.E
347
348    if args.dwarf_version:
349        configuration.dwarf_version = args.dwarf_version
350        # We cannot modify CFLAGS_EXTRAS because they're used in test cases
351        # that explicitly require no debug info.
352        os.environ['CFLAGS'] = '-gdwarf-{}'.format(configuration.dwarf_version)
353
354    if args.d:
355        sys.stdout.write(
356            "Suspending the process %d to wait for debugger to attach...\n" %
357            os.getpid())
358        sys.stdout.flush()
359        os.kill(os.getpid(), signal.SIGSTOP)
360
361    if args.f:
362        if any([x.startswith('-') for x in args.f]):
363            usage(parser)
364        configuration.filters.extend(args.f)
365
366    if args.l:
367        configuration.skip_long_running_test = False
368
369    if args.framework:
370        configuration.lldbFrameworkPath = args.framework
371
372    if args.executable:
373        # lldb executable is passed explicitly
374        lldbtest_config.lldbExec = os.path.realpath(args.executable)
375        if not is_exe(lldbtest_config.lldbExec):
376            lldbtest_config.lldbExec = which(args.executable)
377        if not is_exe(lldbtest_config.lldbExec):
378            logging.error(
379                    '%s is not a valid executable to test; aborting...',
380                    args.executable)
381            sys.exit(-1)
382
383    if args.server:
384        os.environ['LLDB_DEBUGSERVER_PATH'] = args.server
385
386    if args.excluded:
387        for excl_file in args.excluded:
388            parseExclusion(excl_file)
389
390    if args.p:
391        if args.p.startswith('-'):
392            usage(parser)
393        configuration.regexp = args.p
394
395    if args.q:
396        configuration.parsable = True
397
398    if args.s:
399        if args.s.startswith('-'):
400            usage(parser)
401        configuration.sdir_name = args.s
402    configuration.session_file_format = args.session_file_format
403
404    if args.t:
405        os.environ['LLDB_COMMAND_TRACE'] = 'YES'
406
407    if args.v:
408        configuration.verbose = 2
409
410    # argparse makes sure we have a number
411    if args.sharp:
412        configuration.count = args.sharp
413
414    if sys.platform.startswith('win32'):
415        os.environ['LLDB_DISABLE_CRASH_DIALOG'] = str(
416            args.disable_crash_dialog)
417        os.environ['LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE'] = str(True)
418
419    if do_help:
420        usage(parser)
421
422    # Capture test results-related args.
423    if args.curses:
424        # Act as if the following args were set.
425        args.results_formatter = "lldbsuite.test_event.formatter.curses.Curses"
426        args.results_file = "stdout"
427
428    if args.results_file:
429        configuration.results_filename = args.results_file
430
431    if args.results_port:
432        configuration.results_port = args.results_port
433
434    if args.results_file and args.results_port:
435        sys.stderr.write(
436            "only one of --results-file and --results-port should "
437            "be specified\n")
438        usage(args)
439
440    if args.results_formatter:
441        configuration.results_formatter_name = args.results_formatter
442    if args.results_formatter_options:
443        configuration.results_formatter_options = args.results_formatter_options
444
445    # Default to using the BasicResultsFormatter if no formatter is specified.
446    if configuration.results_formatter_name is None:
447        configuration.results_formatter_name = (
448            "lldbsuite.test_event.formatter.results_formatter.ResultsFormatter")
449
450    # rerun-related arguments
451    configuration.rerun_all_issues = args.rerun_all_issues
452    configuration.rerun_max_file_threshold = args.rerun_max_file_threshold
453
454    if args.lldb_platform_name:
455        configuration.lldb_platform_name = args.lldb_platform_name
456    if args.lldb_platform_url:
457        configuration.lldb_platform_url = args.lldb_platform_url
458    if args.lldb_platform_working_dir:
459        configuration.lldb_platform_working_dir = args.lldb_platform_working_dir
460    if args.test_build_dir:
461        configuration.test_build_dir = args.test_build_dir
462
463    if args.event_add_entries and len(args.event_add_entries) > 0:
464        entries = {}
465        # Parse out key=val pairs, separated by comma
466        for keyval in args.event_add_entries.split(","):
467            key_val_entry = keyval.split("=")
468            if len(key_val_entry) == 2:
469                (key, val) = key_val_entry
470                val_parts = val.split(':')
471                if len(val_parts) > 1:
472                    (val, val_type) = val_parts
473                    if val_type == 'int':
474                        val = int(val)
475                entries[key] = val
476        # Tell the event builder to create all events with these
477        # key/val pairs in them.
478        if len(entries) > 0:
479            EventBuilder.add_entries_to_all_events(entries)
480
481    # Gather all the dirs passed on the command line.
482    if len(args.args) > 0:
483        configuration.testdirs = [os.path.realpath(os.path.abspath(x)) for x in args.args]
484
485    lldbtest_config.codesign_identity = args.codesign_identity
486
487
488def getXcodeOutputPaths(lldbRootDirectory):
489    result = []
490
491    # These are for xcode build directories.
492    xcode3_build_dir = ['build']
493    xcode4_build_dir = ['build', 'lldb', 'Build', 'Products']
494
495    configurations = [
496        ['Debug'],
497        ['DebugClang'],
498        ['Release'],
499        ['BuildAndIntegration']]
500    xcode_build_dirs = [xcode3_build_dir, xcode4_build_dir]
501    for configuration in configurations:
502        for xcode_build_dir in xcode_build_dirs:
503            outputPath = os.path.join(
504                lldbRootDirectory, *(xcode_build_dir + configuration))
505            result.append(outputPath)
506
507    return result
508
509
510def setupTestResults():
511    """Sets up test results-related objects based on arg settings."""
512    # Setup the results formatter configuration.
513    formatter_config = formatter.FormatterConfig()
514    formatter_config.filename = configuration.results_filename
515    formatter_config.formatter_name = configuration.results_formatter_name
516    formatter_config.formatter_options = (
517        configuration.results_formatter_options)
518    formatter_config.port = configuration.results_port
519
520    # Create the results formatter.
521    formatter_spec = formatter.create_results_formatter(
522        formatter_config)
523    if formatter_spec is not None and formatter_spec.formatter is not None:
524        configuration.results_formatter_object = formatter_spec.formatter
525
526        # Send an initialize message to the formatter.
527        initialize_event = EventBuilder.bare_event("initialize")
528        initialize_event["worker_count"] = 1
529
530        formatter_spec.formatter.handle_event(initialize_event)
531
532        # Make sure we clean up the formatter on shutdown.
533        if formatter_spec.cleanup_func is not None:
534            atexit.register(formatter_spec.cleanup_func)
535
536
537def getOutputPaths(lldbRootDirectory):
538    """
539    Returns typical build output paths for the lldb executable
540
541    lldbDirectory - path to the root of the lldb svn/git repo
542    """
543    result = []
544
545    if sys.platform == 'darwin':
546        result.extend(getXcodeOutputPaths(lldbRootDirectory))
547
548    # cmake builds?  look for build or build/host folder next to llvm directory
549    # lldb is located in llvm/tools/lldb so we need to go up three levels
550    llvmParentDir = os.path.abspath(
551        os.path.join(
552            lldbRootDirectory,
553            os.pardir,
554            os.pardir,
555            os.pardir))
556    result.append(os.path.join(llvmParentDir, 'build', 'bin'))
557    result.append(os.path.join(llvmParentDir, 'build', 'host', 'bin'))
558
559    # some cmake developers keep their build directory beside their lldb
560    # directory
561    lldbParentDir = os.path.abspath(os.path.join(lldbRootDirectory, os.pardir))
562    result.append(os.path.join(lldbParentDir, 'build', 'bin'))
563    result.append(os.path.join(lldbParentDir, 'build', 'host', 'bin'))
564
565    return result
566
567def get_llvm_bin_dirs():
568    """
569    Returns an array of paths that may have the llvm/clang/etc binaries
570    in them, relative to this current file.
571    Returns an empty array if none are found.
572    """
573    result = []
574
575    lldb_root_path = os.path.join(
576        os.path.dirname(__file__), "..", "..", "..", "..")
577    paths_to_try = [
578        "llvm-build/Release+Asserts/x86_64/bin",
579        "llvm-build/Debug+Asserts/x86_64/bin",
580        "llvm-build/Release/x86_64/bin",
581        "llvm-build/Debug/x86_64/bin",
582        "llvm-build/Ninja-DebugAssert/llvm-macosx-x86_64/bin",
583        "llvm-build/Ninja-DebugAssert+asan/llvm-macosx-x86_64/bin",
584        "llvm-build/Ninja-ReleaseAssert/llvm-macosx-x86_64/bin",
585        "llvm-build/Ninja-ReleaseAssert+asan/llvm-macosx-x86_64/bin",
586        "llvm-build/Ninja-RelWithDebInfoAssert/llvm-macosx-x86_64/bin",
587        "llvm-build/Ninja-RelWithDebInfoAssert+asan/llvm-macosx-x86_64/bin",
588    ]
589    for p in paths_to_try:
590        path = os.path.join(lldb_root_path, p)
591        if os.path.exists(path):
592            result.append(path)
593
594    return result
595
596def setupSysPath():
597    """
598    Add LLDB.framework/Resources/Python to the search paths for modules.
599    As a side effect, we also discover the 'lldb' executable and export it here.
600    """
601
602    # Get the directory containing the current script.
603    if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
604        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
605    else:
606        scriptPath = os.path.dirname(os.path.realpath(__file__))
607    if not scriptPath.endswith('test'):
608        print("This script expects to reside in lldb's test directory.")
609        sys.exit(-1)
610
611    os.environ["LLDB_TEST"] = scriptPath
612
613    # Set up the root build directory.
614    builddir = configuration.test_build_dir
615    if not configuration.test_build_dir:
616        raise Exception("test_build_dir is not set")
617    os.environ["LLDB_BUILD"] = os.path.abspath(configuration.test_build_dir)
618
619    # Set up the LLDB_SRC environment variable, so that the tests can locate
620    # the LLDB source code.
621    os.environ["LLDB_SRC"] = lldbsuite.lldb_root
622
623    pluginPath = os.path.join(scriptPath, 'plugins')
624    toolsLLDBVSCode = os.path.join(scriptPath, 'tools', 'lldb-vscode')
625    toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server')
626
627    # Insert script dir, plugin dir and lldb-server dir to the sys.path.
628    sys.path.insert(0, pluginPath)
629    # Adding test/tools/lldb-vscode to the path makes it easy to
630    # "import lldb_vscode_testcase" from the VSCode tests
631    sys.path.insert(0, toolsLLDBVSCode)
632    # Adding test/tools/lldb-server to the path makes it easy
633    sys.path.insert(0, toolsLLDBServerPath)
634    # to "import lldbgdbserverutils" from the lldb-server tests
635
636    # This is the root of the lldb git/svn checkout
637    # When this changes over to a package instead of a standalone script, this
638    # will be `lldbsuite.lldb_root`
639    lldbRootDirectory = lldbsuite.lldb_root
640
641    # Some of the tests can invoke the 'lldb' command directly.
642    # We'll try to locate the appropriate executable right here.
643
644    # The lldb executable can be set from the command line
645    # if it's not set, we try to find it now
646    # first, we try the environment
647    if not lldbtest_config.lldbExec:
648        # First, you can define an environment variable LLDB_EXEC specifying the
649        # full pathname of the lldb executable.
650        if "LLDB_EXEC" in os.environ:
651            lldbtest_config.lldbExec = os.environ["LLDB_EXEC"]
652
653    if not lldbtest_config.lldbExec:
654        outputPaths = getOutputPaths(lldbRootDirectory)
655        for outputPath in outputPaths:
656            candidatePath = os.path.join(outputPath, 'lldb')
657            if is_exe(candidatePath):
658                lldbtest_config.lldbExec = candidatePath
659                break
660
661    if not lldbtest_config.lldbExec:
662        # Last, check the path
663        lldbtest_config.lldbExec = which('lldb')
664
665    if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec):
666        print(
667            "'{}' is not a path to a valid executable".format(
668                lldbtest_config.lldbExec))
669        lldbtest_config.lldbExec = None
670
671    if not lldbtest_config.lldbExec:
672        print("The 'lldb' executable cannot be located.  Some of the tests may not be run as a result.")
673        sys.exit(-1)
674
675    # confusingly, this is the "bin" directory
676    lldbLibDir = os.path.dirname(lldbtest_config.lldbExec)
677    os.environ["LLDB_LIB_DIR"] = lldbLibDir
678    lldbImpLibDir = os.path.join(
679        lldbLibDir,
680        '..',
681        'lib') if sys.platform.startswith('win32') else lldbLibDir
682    os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir
683    print("LLDB library dir:", os.environ["LLDB_LIB_DIR"])
684    print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"])
685    os.system('%s -v' % lldbtest_config.lldbExec)
686
687    lldbDir = os.path.dirname(lldbtest_config.lldbExec)
688
689    lldbVSCodeExec = os.path.join(lldbDir, "lldb-vscode")
690    if is_exe(lldbVSCodeExec):
691        os.environ["LLDBVSCODE_EXEC"] = lldbVSCodeExec
692    else:
693        if not configuration.shouldSkipBecauseOfCategories(["lldb-vscode"]):
694            print(
695                "The 'lldb-vscode' executable cannot be located.  The lldb-vscode tests can not be run as a result.")
696            configuration.skipCategories.append("lldb-vscode")
697
698    lldbPythonDir = None  # The directory that contains 'lldb/__init__.py'
699    if not configuration.lldbFrameworkPath and os.path.exists(os.path.join(lldbLibDir, "LLDB.framework")):
700        configuration.lldbFrameworkPath = os.path.join(lldbLibDir, "LLDB.framework")
701    if configuration.lldbFrameworkPath:
702        lldbtest_config.lldbFrameworkPath = configuration.lldbFrameworkPath
703        candidatePath = os.path.join(
704            configuration.lldbFrameworkPath, 'Resources', 'Python')
705        if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')):
706            lldbPythonDir = candidatePath
707        if not lldbPythonDir:
708            print(
709                'Resources/Python/lldb/__init__.py was not found in ' +
710                configuration.lldbFrameworkPath)
711            sys.exit(-1)
712    else:
713        # If our lldb supports the -P option, use it to find the python path:
714        init_in_python_dir = os.path.join('lldb', '__init__.py')
715
716        lldb_dash_p_result = subprocess.check_output(
717            [lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True)
718
719        if lldb_dash_p_result and not lldb_dash_p_result.startswith(
720                ("<", "lldb: invalid option:")) and not lldb_dash_p_result.startswith("Traceback"):
721            lines = lldb_dash_p_result.splitlines()
722
723            # Workaround for readline vs libedit issue on FreeBSD.  If stdout
724            # is not a terminal Python executes
725            #     rl_variable_bind ("enable-meta-key", "off");
726            # This produces a warning with FreeBSD's libedit because the
727            # enable-meta-key variable is unknown.  Not an issue on Apple
728            # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__
729            # around the call.  See http://bugs.python.org/issue19884 for more
730            # information.  For now we just discard the warning output.
731            if len(lines) >= 1 and lines[0].startswith(
732                    "bind: Invalid command"):
733                lines.pop(0)
734
735            # Taking the last line because lldb outputs
736            # 'Cannot read termcap database;\nusing dumb terminal settings.\n'
737            # before the path
738            if len(lines) >= 1 and os.path.isfile(
739                    os.path.join(lines[-1], init_in_python_dir)):
740                lldbPythonDir = lines[-1]
741                if "freebsd" in sys.platform or "linux" in sys.platform:
742                    os.environ['LLDB_LIB_DIR'] = os.path.join(
743                        lldbPythonDir, '..', '..')
744
745        if not lldbPythonDir:
746            if platform.system() == "Darwin":
747                python_resource_dir = ['LLDB.framework', 'Resources', 'Python']
748                outputPaths = getXcodeOutputPaths(lldbRootDirectory)
749                for outputPath in outputPaths:
750                    candidatePath = os.path.join(
751                        outputPath, *python_resource_dir)
752                    if os.path.isfile(
753                        os.path.join(
754                            candidatePath,
755                            init_in_python_dir)):
756                        lldbPythonDir = candidatePath
757                        break
758
759                if not lldbPythonDir:
760                    print("lldb.py is not found, some tests may fail.")
761            else:
762                print(
763                    "Unable to load lldb extension module.  Possible reasons for this include:")
764                print("  1) LLDB was built with LLDB_DISABLE_PYTHON=1")
765                print(
766                    "  2) PYTHONPATH and PYTHONHOME are not set correctly.  PYTHONHOME should refer to")
767                print(
768                    "     the version of Python that LLDB built and linked against, and PYTHONPATH")
769                print(
770                    "     should contain the Lib directory for the same python distro, as well as the")
771                print("     location of LLDB\'s site-packages folder.")
772                print(
773                    "  3) A different version of Python than that which was built against is exported in")
774                print("     the system\'s PATH environment variable, causing conflicts.")
775                print(
776                    "  4) The executable '%s' could not be found.  Please check " %
777                    lldbtest_config.lldbExec)
778                print("     that it exists and is executable.")
779
780    if lldbPythonDir:
781        lldbPythonDir = os.path.normpath(lldbPythonDir)
782        # Some of the code that uses this path assumes it hasn't resolved the Versions... link.
783        # If the path we've constructed looks like that, then we'll strip out
784        # the Versions/A part.
785        (before, frameWithVersion, after) = lldbPythonDir.rpartition(
786            "LLDB.framework/Versions/A")
787        if frameWithVersion != "":
788            lldbPythonDir = before + "LLDB.framework" + after
789
790        lldbPythonDir = os.path.abspath(lldbPythonDir)
791
792        # If tests need to find LLDB_FRAMEWORK, now they can do it
793        os.environ["LLDB_FRAMEWORK"] = os.path.dirname(
794            os.path.dirname(lldbPythonDir))
795
796        # This is to locate the lldb.py module.  Insert it right after
797        # sys.path[0].
798        sys.path[1:1] = [lldbPythonDir]
799
800
801def visit_file(dir, name):
802    # Try to match the regexp pattern, if specified.
803    if configuration.regexp:
804        if not re.search(configuration.regexp, name):
805            # We didn't match the regex, we're done.
806            return
807
808    if configuration.skip_tests:
809        for file_regexp in configuration.skip_tests:
810            if re.search(file_regexp, name):
811                return
812
813    # We found a match for our test.  Add it to the suite.
814
815    # Update the sys.path first.
816    if not sys.path.count(dir):
817        sys.path.insert(0, dir)
818    base = os.path.splitext(name)[0]
819
820    # Thoroughly check the filterspec against the base module and admit
821    # the (base, filterspec) combination only when it makes sense.
822    filterspec = None
823    for filterspec in configuration.filters:
824        # Optimistically set the flag to True.
825        filtered = True
826        module = __import__(base)
827        parts = filterspec.split('.')
828        obj = module
829        for part in parts:
830            try:
831                parent, obj = obj, getattr(obj, part)
832            except AttributeError:
833                # The filterspec has failed.
834                filtered = False
835                break
836
837        # If filtered, we have a good filterspec.  Add it.
838        if filtered:
839            # print("adding filter spec %s to module %s" % (filterspec, module))
840            configuration.suite.addTests(
841                unittest2.defaultTestLoader.loadTestsFromName(
842                    filterspec, module))
843            continue
844
845    # Forgo this module if the (base, filterspec) combo is invalid
846    if configuration.filters and not filtered:
847        return
848
849    if not filterspec or not filtered:
850        # Add the entire file's worth of tests since we're not filtered.
851        # Also the fail-over case when the filterspec branch
852        # (base, filterspec) combo doesn't make sense.
853        configuration.suite.addTests(
854            unittest2.defaultTestLoader.loadTestsFromName(base))
855
856
857# TODO: This should be replaced with a call to find_test_files_in_dir_tree.
858def visit(prefix, dir, names):
859    """Visitor function for os.path.walk(path, visit, arg)."""
860
861    dir_components = set(dir.split(os.sep))
862    excluded_components = set(['.svn', '.git'])
863    if dir_components.intersection(excluded_components):
864        return
865
866    # Gather all the Python test file names that follow the Test*.py pattern.
867    python_test_files = [
868        name
869        for name in names
870        if name.endswith('.py') and name.startswith(prefix)]
871
872    # Visit all the python test files.
873    for name in python_test_files:
874        try:
875            # Ensure we error out if we have multiple tests with the same
876            # base name.
877            # Future improvement: find all the places where we work with base
878            # names and convert to full paths.  We have directory structure
879            # to disambiguate these, so we shouldn't need this constraint.
880            if name in configuration.all_tests:
881                raise Exception("Found multiple tests with the name %s" % name)
882            configuration.all_tests.add(name)
883
884            # Run the relevant tests in the python file.
885            visit_file(dir, name)
886        except Exception as ex:
887            # Convert this exception to a test event error for the file.
888            test_filename = os.path.abspath(os.path.join(dir, name))
889            if configuration.results_formatter_object is not None:
890                # Grab the backtrace for the exception.
891                import traceback
892                backtrace = traceback.format_exc()
893
894                # Generate the test event.
895                configuration.results_formatter_object.handle_event(
896                    EventBuilder.event_for_job_test_add_error(
897                        test_filename, ex, backtrace))
898            raise
899
900
901def disabledynamics():
902    import lldb
903    ci = lldb.DBG.GetCommandInterpreter()
904    res = lldb.SBCommandReturnObject()
905    ci.HandleCommand(
906        "setting set target.prefer-dynamic-value no-dynamic-values",
907        res,
908        False)
909    if not res.Succeeded():
910        raise Exception('disabling dynamic type support failed')
911
912
913def lldbLoggings():
914    import lldb
915    """Check and do lldb loggings if necessary."""
916
917    # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
918    # defined.  Use ${LLDB_LOG} to specify the log file.
919    ci = lldb.DBG.GetCommandInterpreter()
920    res = lldb.SBCommandReturnObject()
921    if ("LLDB_LOG" in os.environ):
922        open(os.environ["LLDB_LOG"], 'w').close()
923        if ("LLDB_LOG_OPTION" in os.environ):
924            lldb_log_option = os.environ["LLDB_LOG_OPTION"]
925        else:
926            lldb_log_option = "event process expr state api"
927        ci.HandleCommand(
928            "log enable -n -f " +
929            os.environ["LLDB_LOG"] +
930            " lldb " +
931            lldb_log_option,
932            res)
933        if not res.Succeeded():
934            raise Exception('log enable failed (check LLDB_LOG env variable)')
935
936    if ("LLDB_LINUX_LOG" in os.environ):
937        open(os.environ["LLDB_LINUX_LOG"], 'w').close()
938        if ("LLDB_LINUX_LOG_OPTION" in os.environ):
939            lldb_log_option = os.environ["LLDB_LINUX_LOG_OPTION"]
940        else:
941            lldb_log_option = "event process expr state api"
942        ci.HandleCommand(
943            "log enable -n -f " +
944            os.environ["LLDB_LINUX_LOG"] +
945            " linux " +
946            lldb_log_option,
947            res)
948        if not res.Succeeded():
949            raise Exception(
950                'log enable failed (check LLDB_LINUX_LOG env variable)')
951
952    # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
953    # Use ${GDB_REMOTE_LOG} to specify the log file.
954    if ("GDB_REMOTE_LOG" in os.environ):
955        if ("GDB_REMOTE_LOG_OPTION" in os.environ):
956            gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
957        else:
958            gdb_remote_log_option = "packets process"
959        ci.HandleCommand(
960            "log enable -n -f " + os.environ["GDB_REMOTE_LOG"] + " gdb-remote "
961            + gdb_remote_log_option,
962            res)
963        if not res.Succeeded():
964            raise Exception(
965                'log enable failed (check GDB_REMOTE_LOG env variable)')
966
967
968def getMyCommandLine():
969    return ' '.join(sys.argv)
970
971# ======================================== #
972#                                          #
973# Execution of the test driver starts here #
974#                                          #
975# ======================================== #
976
977
978def checkDsymForUUIDIsNotOn():
979    cmd = ["defaults", "read", "com.apple.DebugSymbols"]
980    process = subprocess.Popen(
981        cmd,
982        stdout=subprocess.PIPE,
983        stderr=subprocess.STDOUT)
984    cmd_output = process.stdout.read()
985    output_str = cmd_output.decode("utf-8")
986    if "DBGFileMappedPaths = " in output_str:
987        print("%s =>" % ' '.join(cmd))
988        print(output_str)
989        print(
990            "Disable automatic lookup and caching of dSYMs before running the test suite!")
991        print("Exiting...")
992        sys.exit(0)
993
994
995def exitTestSuite(exitCode=None):
996    import lldb
997    lldb.SBDebugger.Terminate()
998    if exitCode:
999        sys.exit(exitCode)
1000
1001
1002def getVersionForSDK(sdk):
1003    sdk = str.lower(sdk)
1004    full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk)
1005    basename = os.path.basename(full_path)
1006    basename = os.path.splitext(basename)[0]
1007    basename = str.lower(basename)
1008    ver = basename.replace(sdk, '')
1009    return ver
1010
1011
1012def setDefaultTripleForPlatform():
1013    if configuration.lldb_platform_name == 'ios-simulator':
1014        triple_str = 'x86_64-apple-ios%s' % (
1015            getVersionForSDK('iphonesimulator'))
1016        os.environ['TRIPLE'] = triple_str
1017        return {'TRIPLE': triple_str}
1018    return {}
1019
1020
1021def checkCompiler():
1022    # Add some intervention here to sanity check that the compiler requested is sane.
1023    # If found not to be an executable program, we abort.
1024    c = configuration.compiler
1025    if which(c):
1026        return
1027
1028    if not sys.platform.startswith("darwin"):
1029        raise Exception(c + " is not a valid compiler")
1030
1031    pipe = subprocess.Popen(
1032        ['xcrun', '-find', c], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1033    cmd_output = pipe.stdout.read()
1034    if not cmd_output or "not found" in cmd_output:
1035        raise Exception(c + " is not a valid compiler")
1036
1037    configuration.compiler = cmd_output.split('\n')[0]
1038    print("'xcrun -find %s' returning %s" % (c, configuration.compiler))
1039
1040def canRunLibcxxTests():
1041    from lldbsuite.test import lldbplatformutil
1042
1043    platform = lldbplatformutil.getPlatform()
1044
1045    if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin():
1046        return True, "libc++ always present"
1047
1048    if platform == "linux":
1049        if not os.path.isdir("/usr/include/c++/v1"):
1050            return False, "Unable to find libc++ installation"
1051        return True, "Headers found, let's hope they work"
1052
1053    return False, "Don't know how to build with libc++ on %s" % platform
1054
1055def checkLibcxxSupport():
1056    result, reason = canRunLibcxxTests()
1057    if result:
1058        return # libc++ supported
1059    if "libc++" in configuration.categoriesList:
1060        return # libc++ category explicitly requested, let it run.
1061    print("Libc++ tests will not be run because: " + reason)
1062    configuration.skipCategories.append("libc++")
1063
1064def canRunLibstdcxxTests():
1065    from lldbsuite.test import lldbplatformutil
1066
1067    platform = lldbplatformutil.getPlatform()
1068    if lldbplatformutil.target_is_android():
1069        platform = "android"
1070    if platform == "linux":
1071        return True, "libstdcxx always present"
1072    return False, "Don't know how to build with libstdcxx on %s" % platform
1073
1074def checkLibstdcxxSupport():
1075    result, reason = canRunLibstdcxxTests()
1076    if result:
1077        return # libstdcxx supported
1078    if "libstdcxx" in configuration.categoriesList:
1079        return # libstdcxx category explicitly requested, let it run.
1080    print("libstdcxx tests will not be run because: " + reason)
1081    configuration.skipCategories.append("libstdcxx")
1082
1083def canRunWatchpointTests():
1084    from lldbsuite.test import lldbplatformutil
1085
1086    platform = lldbplatformutil.getPlatform()
1087    if platform == "netbsd":
1088      if os.geteuid() == 0:
1089        return True, "root can always write dbregs"
1090      try:
1091        output = subprocess.check_output(["/sbin/sysctl", "-n",
1092          "security.models.extensions.user_set_dbregs"]).decode().strip()
1093        if output == "1":
1094          return True, "security.models.extensions.user_set_dbregs enabled"
1095      except subprocess.CalledProcessError:
1096        pass
1097      return False, "security.models.extensions.user_set_dbregs disabled"
1098    return True, "watchpoint support available"
1099
1100def checkWatchpointSupport():
1101    result, reason = canRunWatchpointTests()
1102    if result:
1103        return # watchpoints supported
1104    if "watchpoint" in configuration.categoriesList:
1105        return # watchpoint category explicitly requested, let it run.
1106    print("watchpoint tests will not be run because: " + reason)
1107    configuration.skipCategories.append("watchpoint")
1108
1109def checkDebugInfoSupport():
1110    import lldb
1111
1112    platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2]
1113    compiler = configuration.compiler
1114    skipped = []
1115    for cat in test_categories.debug_info_categories:
1116        if cat in configuration.categoriesList:
1117            continue # Category explicitly requested, let it run.
1118        if test_categories.is_supported_on_platform(cat, platform, compiler):
1119            continue
1120        configuration.skipCategories.append(cat)
1121        skipped.append(cat)
1122    if skipped:
1123        print("Skipping following debug info categories:", skipped)
1124
1125def run_suite():
1126    # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
1127    # does not exist before proceeding to running the test suite.
1128    if sys.platform.startswith("darwin"):
1129        checkDsymForUUIDIsNotOn()
1130
1131    # Start the actions by first parsing the options while setting up the test
1132    # directories, followed by setting up the search paths for lldb utilities;
1133    # then, we walk the directory trees and collect the tests into our test suite.
1134    #
1135    parseOptionsAndInitTestdirs()
1136
1137    # Setup test results (test results formatter and output handling).
1138    setupTestResults()
1139
1140    setupSysPath()
1141
1142    #
1143    # If '-l' is specified, do not skip the long running tests.
1144    if not configuration.skip_long_running_test:
1145        os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO"
1146
1147    # For the time being, let's bracket the test runner within the
1148    # lldb.SBDebugger.Initialize()/Terminate() pair.
1149    import lldb
1150
1151    # Create a singleton SBDebugger in the lldb namespace.
1152    lldb.DBG = lldb.SBDebugger.Create()
1153
1154    if configuration.lldb_platform_name:
1155        print("Setting up remote platform '%s'" %
1156              (configuration.lldb_platform_name))
1157        lldb.remote_platform = lldb.SBPlatform(
1158            configuration.lldb_platform_name)
1159        if not lldb.remote_platform.IsValid():
1160            print(
1161                "error: unable to create the LLDB platform named '%s'." %
1162                (configuration.lldb_platform_name))
1163            exitTestSuite(1)
1164        if configuration.lldb_platform_url:
1165            # We must connect to a remote platform if a LLDB platform URL was
1166            # specified
1167            print(
1168                "Connecting to remote platform '%s' at '%s'..." %
1169                (configuration.lldb_platform_name, configuration.lldb_platform_url))
1170            platform_connect_options = lldb.SBPlatformConnectOptions(
1171                configuration.lldb_platform_url)
1172            err = lldb.remote_platform.ConnectRemote(platform_connect_options)
1173            if err.Success():
1174                print("Connected.")
1175            else:
1176                print("error: failed to connect to remote platform using URL '%s': %s" % (
1177                    configuration.lldb_platform_url, err))
1178                exitTestSuite(1)
1179        else:
1180            configuration.lldb_platform_url = None
1181
1182    platform_changes = setDefaultTripleForPlatform()
1183    first = True
1184    for key in platform_changes:
1185        if first:
1186            print("Environment variables setup for platform support:")
1187            first = False
1188        print("%s = %s" % (key, platform_changes[key]))
1189
1190    if configuration.lldb_platform_working_dir:
1191        print("Setting remote platform working directory to '%s'..." %
1192              (configuration.lldb_platform_working_dir))
1193        error = lldb.remote_platform.MakeDirectory(
1194            configuration.lldb_platform_working_dir, 448)  # 448 = 0o700
1195        if error.Fail():
1196            raise Exception("making remote directory '%s': %s" % (
1197                configuration.lldb_platform_working_dir, error))
1198
1199        if not lldb.remote_platform.SetWorkingDirectory(
1200                configuration.lldb_platform_working_dir):
1201            raise Exception("failed to set working directory '%s'" % configuration.lldb_platform_working_dir)
1202        lldb.DBG.SetSelectedPlatform(lldb.remote_platform)
1203    else:
1204        lldb.remote_platform = None
1205        configuration.lldb_platform_working_dir = None
1206        configuration.lldb_platform_url = None
1207
1208    # Set up the working directory.
1209    # Note that it's not dotest's job to clean this directory.
1210    import lldbsuite.test.lldbutil as lldbutil
1211    build_dir = configuration.test_build_dir
1212    lldbutil.mkdir_p(build_dir)
1213
1214    target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2]
1215
1216    checkLibcxxSupport()
1217    checkLibstdcxxSupport()
1218    checkWatchpointSupport()
1219    checkDebugInfoSupport()
1220
1221    # Don't do debugserver tests on anything except OS X.
1222    configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform
1223
1224    # Don't do lldb-server (llgs) tests on anything except Linux and Windows.
1225    configuration.dont_do_llgs_test = not ("linux" in target_platform) and not ("windows" in target_platform)
1226
1227    # Collect tests from the specified testing directories. If a test
1228    # subdirectory filter is explicitly specified, limit the search to that
1229    # subdirectory.
1230    exclusive_test_subdir = configuration.get_absolute_path_to_exclusive_test_subdir()
1231    if exclusive_test_subdir:
1232        dirs_to_search = [exclusive_test_subdir]
1233    else:
1234        dirs_to_search = configuration.testdirs
1235    for testdir in dirs_to_search:
1236        for (dirpath, dirnames, filenames) in os.walk(testdir):
1237            visit('Test', dirpath, filenames)
1238
1239    #
1240    # Now that we have loaded all the test cases, run the whole test suite.
1241    #
1242
1243    # Turn on lldb loggings if necessary.
1244    lldbLoggings()
1245
1246    # Disable default dynamic types for testing purposes
1247    disabledynamics()
1248
1249    # Install the control-c handler.
1250    unittest2.signals.installHandler()
1251
1252    # If sdir_name is not specified through the '-s sdir_name' option, get a
1253    # timestamp string and export it as LLDB_SESSION_DIR environment var.  This will
1254    # be used when/if we want to dump the session info of individual test cases
1255    # later on.
1256    #
1257    # See also TestBase.dumpSessionInfo() in lldbtest.py.
1258    import datetime
1259    # The windows platforms don't like ':' in the pathname.
1260    timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
1261    if not configuration.sdir_name:
1262        configuration.sdir_name = timestamp_started
1263    os.environ["LLDB_SESSION_DIRNAME"] = os.path.join(
1264        os.getcwd(), configuration.sdir_name)
1265
1266    sys.stderr.write(
1267        "\nSession logs for test failures/errors/unexpected successes"
1268        " will go into directory '%s'\n" %
1269        configuration.sdir_name)
1270    sys.stderr.write("Command invoked: %s\n" % getMyCommandLine())
1271
1272    if not os.path.isdir(configuration.sdir_name):
1273        try:
1274            os.mkdir(configuration.sdir_name)
1275        except OSError as exception:
1276            if exception.errno != errno.EEXIST:
1277                raise
1278
1279    #
1280    # Invoke the default TextTestRunner to run the test suite
1281    #
1282    checkCompiler()
1283
1284    if not configuration.parsable:
1285        print("compiler=%s" % configuration.compiler)
1286
1287    # Iterating over all possible architecture and compiler combinations.
1288    os.environ["ARCH"] = configuration.arch
1289    os.environ["CC"] = configuration.compiler
1290    configString = "arch=%s compiler=%s" % (configuration.arch,
1291                                            configuration.compiler)
1292
1293    # Translate ' ' to '-' for pathname component.
1294    if six.PY2:
1295        import string
1296        tbl = string.maketrans(' ', '-')
1297    else:
1298        tbl = str.maketrans(' ', '-')
1299    configPostfix = configString.translate(tbl)
1300
1301    # Output the configuration.
1302    if not configuration.parsable:
1303        sys.stderr.write("\nConfiguration: " + configString + "\n")
1304
1305    # First, write out the number of collected test cases.
1306    if not configuration.parsable:
1307        sys.stderr.write(configuration.separator + "\n")
1308        sys.stderr.write(
1309            "Collected %d test%s\n\n" %
1310            (configuration.suite.countTestCases(),
1311             configuration.suite.countTestCases() != 1 and "s" or ""))
1312
1313    if configuration.parsable:
1314        v = 0
1315    else:
1316        v = configuration.verbose
1317
1318    # Invoke the test runner.
1319    if configuration.count == 1:
1320        result = unittest2.TextTestRunner(
1321            stream=sys.stderr,
1322            verbosity=v,
1323            resultclass=test_result.LLDBTestResult).run(
1324            configuration.suite)
1325    else:
1326        # We are invoking the same test suite more than once.  In this case,
1327        # mark __ignore_singleton__ flag as True so the signleton pattern is
1328        # not enforced.
1329        test_result.LLDBTestResult.__ignore_singleton__ = True
1330        for i in range(configuration.count):
1331
1332            result = unittest2.TextTestRunner(
1333                stream=sys.stderr,
1334                verbosity=v,
1335                resultclass=test_result.LLDBTestResult).run(
1336                configuration.suite)
1337
1338    configuration.failed = not result.wasSuccessful()
1339
1340    if configuration.sdir_has_content and not configuration.parsable:
1341        sys.stderr.write(
1342            "Session logs for test failures/errors/unexpected successes"
1343            " can be found in directory '%s'\n" %
1344            configuration.sdir_name)
1345
1346    if configuration.useCategories and len(
1347            configuration.failuresPerCategory) > 0:
1348        sys.stderr.write("Failures per category:\n")
1349        for category in configuration.failuresPerCategory:
1350            sys.stderr.write(
1351                "%s - %d\n" %
1352                (category, configuration.failuresPerCategory[category]))
1353
1354    # Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
1355    # This should not be necessary now.
1356    if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
1357        print("Terminating Test suite...")
1358        subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
1359
1360    # Exiting.
1361    exitTestSuite(configuration.failed)
1362
1363if __name__ == "__main__":
1364    print(
1365        __file__ +
1366        " is for use as a module only.  It should not be run as a standalone script.")
1367    sys.exit(-1)
1368