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