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