dotest.py revision a73ad66a4ee96ccc8e6be4a645cd6b9180a72e4b
1#!/usr/bin/env python
2
3"""
4A simple testing framework for lldb using python's unit testing framework.
5
6Tests for lldb are written as python scripts which take advantage of the script
7bridging provided by LLDB.framework to interact with lldb core.
8
9A specific naming pattern is followed by the .py script to be recognized as
10a module which implements a test scenario, namely, Test*.py.
11
12To specify the directories where "Test*.py" python test scripts are located,
13you need to pass in a list of directory names.  By default, the current
14working directory is searched if nothing is specified on the command line.
15
16Type:
17
18./dotest.py -h
19
20for available options.
21"""
22
23import os, signal, sys, time
24import subprocess
25import unittest2
26
27def is_exe(fpath):
28    """Returns true if fpath is an executable."""
29    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
30
31def which(program):
32    """Returns the full path to a program; None otherwise."""
33    fpath, fname = os.path.split(program)
34    if fpath:
35        if is_exe(program):
36            return program
37    else:
38        for path in os.environ["PATH"].split(os.pathsep):
39            exe_file = os.path.join(path, program)
40            if is_exe(exe_file):
41                return exe_file
42    return None
43
44class _WritelnDecorator(object):
45    """Used to decorate file-like objects with a handy 'writeln' method"""
46    def __init__(self,stream):
47        self.stream = stream
48
49    def __getattr__(self, attr):
50        if attr in ('stream', '__getstate__'):
51            raise AttributeError(attr)
52        return getattr(self.stream,attr)
53
54    def writeln(self, arg=None):
55        if arg:
56            self.write(arg)
57        self.write('\n') # text-mode streams translate to \r\n if needed
58
59#
60# Global variables:
61#
62
63# The test suite.
64suite = unittest2.TestSuite()
65
66# By default, both command line and Python API tests are performed.
67# Use @python_api_test decorator, defined in lldbtest.py, to mark a test as
68# a Python API test.
69dont_do_python_api_test = False
70
71# By default, both command line and Python API tests are performed.
72just_do_python_api_test = False
73
74# By default, benchmarks tests are not run.
75just_do_benchmarks_test = False
76
77# By default, both dsym and dwarf tests are performed.
78# Use @dsym_test or @dwarf_test decorators, defined in lldbtest.py, to mark a test
79# as a dsym or dwarf test.  Use '-N dsym' or '-N dwarf' to exclude dsym or dwarf
80# tests from running.
81dont_do_dsym_test = False
82dont_do_dwarf_test = False
83
84# The blacklist is optional (-b blacklistFile) and allows a central place to skip
85# testclass's and/or testclass.testmethod's.
86blacklist = None
87
88# The dictionary as a result of sourcing blacklistFile.
89blacklistConfig = {}
90
91# The config file is optional.
92configFile = None
93
94# Test suite repeat count.  Can be overwritten with '-# count'.
95count = 1
96
97# The dictionary as a result of sourcing configFile.
98config = {}
99# The pre_flight and post_flight functions come from reading a config file.
100pre_flight = None
101post_flight = None
102
103# The 'archs' and 'compilers' can be specified via either command line or configFile,
104# with the command line overriding the configFile.  When specified, they should be
105# of the list type.  For example, "-A x86_64^i386" => archs=['x86_64', 'i386'] and
106# "-C gcc^clang" => compilers=['gcc', 'clang'].
107archs = ['x86_64', 'i386']
108compilers = ['clang']
109
110# The arch might dictate some specific CFLAGS to be passed to the toolchain to build
111# the inferior programs.  The global variable cflags_extras provides a hook to do
112# just that.
113cflags_extras = ''
114
115# Delay startup in order for the debugger to attach.
116delay = False
117
118# Dump the Python sys.path variable.  Use '-D' to dump sys.path.
119dumpSysPath = False
120
121# Full path of the benchmark executable, as specified by the '-e' option.
122bmExecutable = None
123# The breakpoint specification of bmExecutable, as specified by the '-x' option.
124bmBreakpointSpec = None
125# The benchamrk iteration count, as specified by the '-y' option.
126bmIterationCount = -1
127
128# By default, don't exclude any directories.  Use '-X' to add one excluded directory.
129excluded = set(['.svn', '.git'])
130
131# By default, failfast is False.  Use '-F' to overwrite it.
132failfast = False
133
134# The filters (testclass.testmethod) used to admit tests into our test suite.
135filters = []
136
137# The runhooks is a list of lldb commands specifically for the debugger.
138# Use '-k' to specify a runhook.
139runHooks = []
140
141# If '-g' is specified, the filterspec is not exclusive.  If a test module does
142# not contain testclass.testmethod which matches the filterspec, the whole test
143# module is still admitted into our test suite.  fs4all flag defaults to True.
144fs4all = True
145
146# Ignore the build search path relative to this script to locate the lldb.py module.
147ignore = False
148
149# By default, we do not skip build and cleanup.  Use '-S' option to override.
150skip_build_and_cleanup = False
151
152# By default, we skip long running test case.  Use '-l' option to override.
153skip_long_running_test = True
154
155# By default, we print the build dir, lldb version, and svn info.  Use '-n' option to
156# turn it off.
157noHeaders = False
158
159# The regular expression pattern to match against eligible filenames as our test cases.
160regexp = None
161
162# By default, tests are executed in place and cleanups are performed afterwards.
163# Use '-r dir' option to relocate the tests and their intermediate files to a
164# different directory and to forgo any cleanups.  The directory specified must
165# not exist yet.
166rdir = None
167
168# By default, recorded session info for errored/failed test are dumped into its
169# own file under a session directory named after the timestamp of the test suite
170# run.  Use '-s session-dir-name' to specify a specific dir name.
171sdir_name = None
172
173# Set this flag if there is any session info dumped during the test run.
174sdir_has_content = False
175
176# svn_info stores the output from 'svn info lldb.base.dir'.
177svn_info = ''
178
179# The environment variables to unset before running the test cases.
180unsets = []
181
182# Default verbosity is 0.
183verbose = 0
184
185# Set to True only if verbose is 0 and LLDB trace mode is off.
186progress_bar = False
187
188# By default, search from the script directory.
189testdirs = [ sys.path[0] ]
190
191# Separator string.
192separator = '-' * 70
193
194
195def usage():
196    print """
197Usage: dotest.py [option] [args]
198where options:
199-h   : print this help message and exit.  Add '-v' for more detailed help.
200-A   : specify the architecture(s) to launch for the inferior process
201       -A i386 => launch inferior with i386 architecture
202       -A x86_64^i386 => launch inferior with x86_64 and i386 architectures
203-C   : specify the compiler(s) used to build the inferior executable
204       -C clang => build debuggee using clang compiler
205       -C /my/full/path/to/clang => specify a full path to the clang binary
206       -C clang^gcc => build debuggee using clang and gcc compilers
207-D   : dump the Python sys.path variable
208-E   : specify the extra flags to be passed to the toolchain when building the
209       inferior programs to be debugged
210       suggestions: do not lump the -A arch1^arch2 together such that the -E
211       option applies to only one of the architectures
212-N   : don't do test cases marked with the @dsym decorator by passing 'dsym' as the option arg, or
213       don't do test cases marked with the @dwarf decorator by passing 'dwarf' as the option arg
214-a   : don't do lldb Python API tests
215       use @python_api_test to decorate a test case as lldb Python API test
216+a   : just do lldb Python API tests
217       do not specify both '-a' and '+a' at the same time
218+b   : just do benchmark tests
219       use @benchmark_test to decorate a test case as such
220-b   : read a blacklist file specified after this option
221-c   : read a config file specified after this option
222       the architectures and compilers (note the plurals) specified via '-A' and '-C'
223       will override those specified via a config file
224       (see also lldb-trunk/example/test/usage-config)
225-d   : delay startup for 10 seconds (in order for the debugger to attach)
226-e   : specify the full path of an executable used for benchmark purpose;
227       see also '-x', which provides the breakpoint sepcification
228-F   : failfast, stop the test suite on the first error/failure
229-f   : specify a filter, which consists of the test class name, a dot, followed by
230       the test method, to only admit such test into the test suite
231       e.g., -f 'ClassTypesTestCase.test_with_dwarf_and_python_api'
232-g   : if specified, the filterspec by -f is not exclusive, i.e., if a test module
233       does not match the filterspec (testclass.testmethod), the whole module is
234       still admitted to the test suite
235-i   : ignore (don't bailout) if 'lldb.py' module cannot be located in the build
236       tree relative to this script; use PYTHONPATH to locate the module
237-k   : specify a runhook, which is an lldb command to be executed by the debugger;
238       '-k' option can occur multiple times, the commands are executed one after the
239       other to bring the debugger to a desired state, so that, for example, further
240       benchmarking can be done
241-l   : don't skip long running test
242-n   : don't print the headers like build dir, lldb version, and svn info at all
243-p   : specify a regexp filename pattern for inclusion in the test suite
244-R   : specify a dir to relocate the tests and their intermediate files to;
245       BE WARNED THAT the directory, if exists, will be deleted before running this test driver;
246       no cleanup of intermediate test files is performed in this case
247-r   : similar to '-R',
248       except that the directory must not exist before running this test driver
249-S   : skip the build and cleanup while running the test
250       use this option with care as you would need to build the inferior(s) by hand
251       and build the executable(s) with the correct name(s)
252       this can be used with '-# n' to stress test certain test cases for n number of
253       times
254-s   : specify the name of the dir created to store the session files of tests
255       with errored or failed status; if not specified, the test driver uses the
256       timestamp as the session dir name
257-t   : turn on tracing of lldb command and other detailed test executions
258-u   : specify an environment variable to unset before running the test cases
259       e.g., -u DYLD_INSERT_LIBRARIES -u MallocScribble'
260-v   : do verbose mode of unittest framework (print out each test case invocation)
261-X   : exclude a directory from consideration for test discovery
262       -X types => if 'types' appear in the pathname components of a potential testfile
263                   it will be ignored
264-x   : specify the breakpoint specification for the benchmark executable;
265       see also '-e', which provides the full path of the executable
266-y   : specify the iteration count used to collect our benchmarks; an example is
267       the number of times to do 'thread step-over' to measure stepping speed
268       see also '-e' and '-x' options
269-w   : insert some wait time (currently 0.5 sec) between consecutive test cases
270-#   : Repeat the test suite for a specified number of times
271
272and:
273args : specify a list of directory names to search for test modules named after
274       Test*.py (test discovery)
275       if empty, search from the current working directory, instead
276"""
277
278    if verbose > 0:
279        print """
280Examples:
281
282This is an example of using the -f option to pinpoint to a specfic test class
283and test method to be run:
284
285$ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command
286----------------------------------------------------------------------
287Collected 1 test
288
289test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase)
290Test 'frame variable this' when stopped on a class constructor. ... ok
291
292----------------------------------------------------------------------
293Ran 1 test in 1.396s
294
295OK
296
297And this is an example of using the -p option to run a single file (the filename
298matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'):
299
300$ ./dotest.py -v -p ObjC
301----------------------------------------------------------------------
302Collected 4 tests
303
304test_break_with_dsym (TestObjCMethods.FoundationTestCase)
305Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
306test_break_with_dwarf (TestObjCMethods.FoundationTestCase)
307Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
308test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase)
309Lookup objective-c data types and evaluate expressions. ... ok
310test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase)
311Lookup objective-c data types and evaluate expressions. ... ok
312
313----------------------------------------------------------------------
314Ran 4 tests in 16.661s
315
316OK
317
318Running of this script also sets up the LLDB_TEST environment variable so that
319individual test cases can locate their supporting files correctly.  The script
320tries to set up Python's search paths for modules by looking at the build tree
321relative to this script.  See also the '-i' option in the following example.
322
323Finally, this is an example of using the lldb.py module distributed/installed by
324Xcode4 to run against the tests under the 'forward' directory, and with the '-w'
325option to add some delay between two tests.  It uses ARCH=x86_64 to specify that
326as the architecture and CC=clang to specify the compiler used for the test run:
327
328$ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward
329
330Session logs for test failures/errors will go into directory '2010-11-11-13_56_16'
331----------------------------------------------------------------------
332Collected 2 tests
333
334test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
335Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
336test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
337Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
338
339----------------------------------------------------------------------
340Ran 2 tests in 5.659s
341
342OK
343
344The 'Session ...' verbiage is recently introduced (see also the '-s' option) to
345notify the directory containing the session logs for test failures or errors.
346In case there is any test failure/error, a similar message is appended at the
347end of the stderr output for your convenience.
348
349Environment variables related to loggings:
350
351o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem
352  with a default option of 'event process' if LLDB_LOG_OPTION is not defined.
353
354o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the
355  'process.gdb-remote' subsystem with a default option of 'packets' if
356  GDB_REMOTE_LOG_OPTION is not defined.
357"""
358    sys.exit(0)
359
360
361def parseOptionsAndInitTestdirs():
362    """Initialize the list of directories containing our unittest scripts.
363
364    '-h/--help as the first option prints out usage info and exit the program.
365    """
366
367    global dont_do_python_api_test
368    global just_do_python_api_test
369    global just_do_benchmarks_test
370    global dont_do_dsym_test
371    global dont_do_dwarf_test
372    global blacklist
373    global blacklistConfig
374    global configFile
375    global archs
376    global compilers
377    global count
378    global delay
379    global dumpSysPath
380    global bmExecutable
381    global bmBreakpointSpec
382    global bmIterationCount
383    global failfast
384    global filters
385    global fs4all
386    global ignore
387    global progress_bar
388    global runHooks
389    global skip_build_and_cleanup
390    global skip_long_running_test
391    global noHeaders
392    global regexp
393    global rdir
394    global sdir_name
395    global unsets
396    global verbose
397    global testdirs
398
399    do_help = False
400
401    # Process possible trace and/or verbose flag, among other things.
402    index = 1
403    while index < len(sys.argv):
404        if sys.argv[index].startswith('-') or sys.argv[index].startswith('+'):
405            # We should continue processing...
406            pass
407        else:
408            # End of option processing.
409            break
410
411        if sys.argv[index].find('-h') != -1:
412            index += 1
413            do_help = True
414        elif sys.argv[index].startswith('-A'):
415            # Increment by 1 to fetch the ARCH spec.
416            index += 1
417            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
418                usage()
419            archs = sys.argv[index].split('^')
420            index += 1
421        elif sys.argv[index].startswith('-C'):
422            # Increment by 1 to fetch the CC spec.
423            index += 1
424            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
425                usage()
426            compilers = sys.argv[index].split('^')
427            index += 1
428        elif sys.argv[index].startswith('-D'):
429            dumpSysPath = True
430            index += 1
431        elif sys.argv[index].startswith('-E'):
432            # Increment by 1 to fetch the CFLAGS_EXTRAS spec.
433            index += 1
434            if index >= len(sys.argv):
435                usage()
436            cflags_extras = sys.argv[index]
437            os.environ["CFLAGS_EXTRAS"] = cflags_extras
438            index += 1
439        elif sys.argv[index].startswith('-N'):
440            # Increment by 1 to fetch 'dsym' or 'dwarf'.
441            index += 1
442            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
443                usage()
444            dont_do = sys.argv[index]
445            if dont_do.lower() == 'dsym':
446                dont_do_dsym_test = True
447            elif dont_do.lower() == 'dwarf':
448                dont_do_dwarf_test = True
449            else:
450                print "!!!"
451                print "Warning: -N only accepts either 'dsym' or 'dwarf' as the option arg; you passed in '%s'?" % dont_do
452                print "!!!"
453            index += 1
454        elif sys.argv[index].startswith('-a'):
455            dont_do_python_api_test = True
456            index += 1
457        elif sys.argv[index].startswith('+a'):
458            just_do_python_api_test = True
459            index += 1
460        elif sys.argv[index].startswith('+b'):
461            just_do_benchmarks_test = True
462            index += 1
463        elif sys.argv[index].startswith('-b'):
464            # Increment by 1 to fetch the blacklist file name option argument.
465            index += 1
466            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
467                usage()
468            blacklistFile = sys.argv[index]
469            if not os.path.isfile(blacklistFile):
470                print "Blacklist file:", blacklistFile, "does not exist!"
471                usage()
472            index += 1
473            # Now read the blacklist contents and assign it to blacklist.
474            execfile(blacklistFile, globals(), blacklistConfig)
475            blacklist = blacklistConfig.get('blacklist')
476        elif sys.argv[index].startswith('-c'):
477            # Increment by 1 to fetch the config file name option argument.
478            index += 1
479            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
480                usage()
481            configFile = sys.argv[index]
482            if not os.path.isfile(configFile):
483                print "Config file:", configFile, "does not exist!"
484                usage()
485            index += 1
486        elif sys.argv[index].startswith('-d'):
487            delay = True
488            index += 1
489        elif sys.argv[index].startswith('-e'):
490            # Increment by 1 to fetch the full path of the benchmark executable.
491            index += 1
492            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
493                usage()
494            bmExecutable = sys.argv[index]
495            if not is_exe(bmExecutable):
496                usage()
497            index += 1
498        elif sys.argv[index].startswith('-F'):
499            failfast = True
500            index += 1
501        elif sys.argv[index].startswith('-f'):
502            # Increment by 1 to fetch the filter spec.
503            index += 1
504            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
505                usage()
506            filters.append(sys.argv[index])
507            index += 1
508        elif sys.argv[index].startswith('-g'):
509            fs4all = False
510            index += 1
511        elif sys.argv[index].startswith('-i'):
512            ignore = True
513            index += 1
514        elif sys.argv[index].startswith('-k'):
515            # Increment by 1 to fetch the runhook lldb command.
516            index += 1
517            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
518                usage()
519            runHooks.append(sys.argv[index])
520            index += 1
521        elif sys.argv[index].startswith('-l'):
522            skip_long_running_test = False
523            index += 1
524        elif sys.argv[index].startswith('-n'):
525            noHeaders = True
526            index += 1
527        elif sys.argv[index].startswith('-p'):
528            # Increment by 1 to fetch the reg exp pattern argument.
529            index += 1
530            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
531                usage()
532            regexp = sys.argv[index]
533            index += 1
534        elif sys.argv[index].startswith('-R'):
535            # Increment by 1 to fetch the relocated directory argument.
536            index += 1
537            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
538                usage()
539            rdir = os.path.abspath(sys.argv[index])
540            if os.path.exists(rdir):
541                import shutil
542                print "Removing tree:", rdir
543                shutil.rmtree(rdir)
544            index += 1
545        elif sys.argv[index].startswith('-r'):
546            # Increment by 1 to fetch the relocated directory argument.
547            index += 1
548            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
549                usage()
550            rdir = os.path.abspath(sys.argv[index])
551            if os.path.exists(rdir):
552                print "Relocated directory:", rdir, "must not exist!"
553                usage()
554            index += 1
555        elif sys.argv[index].startswith('-S'):
556            skip_build_and_cleanup = True
557            index += 1
558        elif sys.argv[index].startswith('-s'):
559            # Increment by 1 to fetch the session dir name.
560            index += 1
561            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
562                usage()
563            sdir_name = sys.argv[index]
564            index += 1
565        elif sys.argv[index].startswith('-t'):
566            os.environ["LLDB_COMMAND_TRACE"] = "YES"
567            index += 1
568        elif sys.argv[index].startswith('-u'):
569            # Increment by 1 to fetch the environment variable to unset.
570            index += 1
571            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
572                usage()
573            unsets.append(sys.argv[index])
574            index += 1
575        elif sys.argv[index].startswith('-v'):
576            verbose = 2
577            index += 1
578        elif sys.argv[index].startswith('-w'):
579            os.environ["LLDB_WAIT_BETWEEN_TEST_CASES"] = 'YES'
580            index += 1
581        elif sys.argv[index].startswith('-X'):
582            # Increment by 1 to fetch an excluded directory.
583            index += 1
584            if index >= len(sys.argv):
585                usage()
586            excluded.add(sys.argv[index])
587            index += 1
588        elif sys.argv[index].startswith('-x'):
589            # Increment by 1 to fetch the breakpoint specification of the benchmark executable.
590            index += 1
591            if index >= len(sys.argv):
592                usage()
593            bmBreakpointSpec = sys.argv[index]
594            index += 1
595        elif sys.argv[index].startswith('-y'):
596            # Increment by 1 to fetch the the benchmark iteration count.
597            index += 1
598            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
599                usage()
600            bmIterationCount = int(sys.argv[index])
601            index += 1
602        elif sys.argv[index].startswith('-#'):
603            # Increment by 1 to fetch the repeat count argument.
604            index += 1
605            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
606                usage()
607            count = int(sys.argv[index])
608            index += 1
609        else:
610            print "Unknown option: ", sys.argv[index]
611            usage()
612
613    if do_help == True:
614        usage()
615
616    # Do not specify both '-a' and '+a' at the same time.
617    if dont_do_python_api_test and just_do_python_api_test:
618        usage()
619
620    # The simple progress bar is turned on only if verbose == 0 and LLDB_COMMAND_TRACE is not 'YES'
621    if ("LLDB_COMMAND_TRACE" not in os.environ or os.environ["LLDB_COMMAND_TRACE"]!="YES") and verbose==0:
622        progress_bar = True
623
624    # Gather all the dirs passed on the command line.
625    if len(sys.argv) > index:
626        testdirs = map(os.path.abspath, sys.argv[index:])
627
628    # If '-r dir' is specified, the tests should be run under the relocated
629    # directory.  Let's copy the testdirs over.
630    if rdir:
631        from shutil import copytree, ignore_patterns
632
633        tmpdirs = []
634        orig_testdirs = testdirs[:]
635        for srcdir in testdirs:
636            # For example, /Volumes/data/lldb/svn/ToT/test/functionalities/watchpoint/hello_watchpoint
637            # shall be split into ['/Volumes/data/lldb/svn/ToT/', 'functionalities/watchpoint/hello_watchpoint'].
638            # Utilize the relative path to the 'test' directory to make our destination dir path.
639            if ("test"+os.sep) in srcdir:
640                to_split_on = "test"+os.sep
641            else:
642                to_split_on = "test"
643            dstdir = os.path.join(rdir, srcdir.split(to_split_on)[1])
644            dstdir = dstdir.rstrip(os.sep)
645            # Don't copy the *.pyc and .svn stuffs.
646            copytree(srcdir, dstdir, ignore=ignore_patterns('*.pyc', '.svn'))
647            tmpdirs.append(dstdir)
648
649        # This will be our modified testdirs.
650        testdirs = tmpdirs
651
652        # With '-r dir' specified, there's no cleanup of intermediate test files.
653        os.environ["LLDB_DO_CLEANUP"] = 'NO'
654
655        # If the original testdirs is ['test'], the make directory has already been copied
656        # recursively and is contained within the rdir/test dir.  For anything
657        # else, we would need to copy over the make directory and its contents,
658        # so that, os.listdir(rdir) looks like, for example:
659        #
660        #     array_types conditional_break make
661        #
662        # where the make directory contains the Makefile.rules file.
663        if len(testdirs) != 1 or os.path.basename(orig_testdirs[0]) != 'test':
664            scriptdir = os.path.dirname(__file__)
665            # Don't copy the .svn stuffs.
666            copytree(os.path.join(scriptdir, 'make'), os.path.join(rdir, 'make'),
667                     ignore=ignore_patterns('.svn'))
668
669    #print "testdirs:", testdirs
670
671    # Source the configFile if specified.
672    # The side effect, if any, will be felt from this point on.  An example
673    # config file may be these simple two lines:
674    #
675    # sys.stderr = open("/tmp/lldbtest-stderr", "w")
676    # sys.stdout = open("/tmp/lldbtest-stdout", "w")
677    #
678    # which will reassign the two file objects to sys.stderr and sys.stdout,
679    # respectively.
680    #
681    # See also lldb-trunk/example/test/usage-config.
682    global config, pre_flight, post_flight
683    if configFile:
684        # Pass config (a dictionary) as the locals namespace for side-effect.
685        execfile(configFile, globals(), config)
686        print "config:", config
687        if "pre_flight" in config:
688            pre_flight = config["pre_flight"]
689            if not callable(pre_flight):
690                print "fatal error: pre_flight is not callable, exiting."
691                sys.exit(1)
692        if "post_flight" in config:
693            post_flight = config["post_flight"]
694            if not callable(post_flight):
695                print "fatal error: post_flight is not callable, exiting."
696                sys.exit(1)
697        #print "sys.stderr:", sys.stderr
698        #print "sys.stdout:", sys.stdout
699
700
701def setupSysPath():
702    """
703    Add LLDB.framework/Resources/Python to the search paths for modules.
704    As a side effect, we also discover the 'lldb' executable and export it here.
705    """
706
707    global rdir
708    global testdirs
709    global dumpSysPath
710    global noHeaders
711    global svn_info
712
713    # Get the directory containing the current script.
714    if ("DOTEST_PROFILE" in os.environ or "DOTEST_PDB" in os.environ) and "DOTEST_SCRIPT_DIR" in os.environ:
715        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
716    else:
717        scriptPath = sys.path[0]
718    if not scriptPath.endswith('test'):
719        print "This script expects to reside in lldb's test directory."
720        sys.exit(-1)
721
722    if rdir:
723        # Set up the LLDB_TEST environment variable appropriately, so that the
724        # individual tests can be located relatively.
725        #
726        # See also lldbtest.TestBase.setUpClass(cls).
727        if len(testdirs) == 1 and os.path.basename(testdirs[0]) == 'test':
728            os.environ["LLDB_TEST"] = os.path.join(rdir, 'test')
729        else:
730            os.environ["LLDB_TEST"] = rdir
731    else:
732        os.environ["LLDB_TEST"] = scriptPath
733
734    # Set up the LLDB_SRC environment variable, so that the tests can locate
735    # the LLDB source code.
736    os.environ["LLDB_SRC"] = os.path.join(sys.path[0], os.pardir)
737
738    pluginPath = os.path.join(scriptPath, 'plugins')
739    pexpectPath = os.path.join(scriptPath, 'pexpect-2.4')
740
741    # Append script dir, plugin dir, and pexpect dir to the sys.path.
742    sys.path.append(scriptPath)
743    sys.path.append(pluginPath)
744    sys.path.append(pexpectPath)
745
746    # This is our base name component.
747    base = os.path.abspath(os.path.join(scriptPath, os.pardir))
748
749    # These are for xcode build directories.
750    xcode3_build_dir = ['build']
751    xcode4_build_dir = ['build', 'lldb', 'Build', 'Products']
752    dbg = ['Debug']
753    rel = ['Release']
754    bai = ['BuildAndIntegration']
755    python_resource_dir = ['LLDB.framework', 'Resources', 'Python']
756
757    # Some of the tests can invoke the 'lldb' command directly.
758    # We'll try to locate the appropriate executable right here.
759
760    # First, you can define an environment variable LLDB_EXEC specifying the
761    # full pathname of the lldb executable.
762    if "LLDB_EXEC" in os.environ and is_exe(os.environ["LLDB_EXEC"]):
763        lldbExec = os.environ["LLDB_EXEC"]
764    else:
765        lldbExec = None
766
767    executable = ['lldb']
768    dbgExec  = os.path.join(base, *(xcode3_build_dir + dbg + executable))
769    dbgExec2 = os.path.join(base, *(xcode4_build_dir + dbg + executable))
770    relExec  = os.path.join(base, *(xcode3_build_dir + rel + executable))
771    relExec2 = os.path.join(base, *(xcode4_build_dir + rel + executable))
772    baiExec  = os.path.join(base, *(xcode3_build_dir + bai + executable))
773    baiExec2 = os.path.join(base, *(xcode4_build_dir + bai + executable))
774
775    # The 'lldb' executable built here in the source tree.
776    lldbHere = None
777    if is_exe(dbgExec):
778        lldbHere = dbgExec
779    elif is_exe(dbgExec2):
780        lldbHere = dbgExec2
781    elif is_exe(relExec):
782        lldbHere = relExec
783    elif is_exe(relExec2):
784        lldbHere = relExec2
785    elif is_exe(baiExec):
786        lldbHere = baiExec
787    elif is_exe(baiExec2):
788        lldbHere = baiExec2
789    elif lldbExec:
790        lldbHere = lldbExec
791
792    if lldbHere:
793        os.environ["LLDB_HERE"] = lldbHere
794        os.environ["LLDB_BUILD_DIR"] = os.path.split(lldbHere)[0]
795        if not noHeaders:
796            print "LLDB build dir:", os.environ["LLDB_BUILD_DIR"]
797            os.system('%s -v' % lldbHere)
798
799    # One last chance to locate the 'lldb' executable.
800    if not lldbExec:
801        lldbExec = which('lldb')
802        if lldbHere and not lldbExec:
803            lldbExec = lldbHere
804
805
806    if not lldbExec:
807        print "The 'lldb' executable cannot be located.  Some of the tests may not be run as a result."
808    else:
809        os.environ["LLDB_EXEC"] = lldbExec
810        #print "The 'lldb' from PATH env variable", lldbExec
811
812    if os.path.isdir(os.path.join(base, '.svn')):
813        pipe = subprocess.Popen(["svn", "info", base], stdout = subprocess.PIPE)
814        svn_info = pipe.stdout.read()
815    elif os.path.isdir(os.path.join(base, '.git')):
816        pipe = subprocess.Popen(["git", "svn", "info", base], stdout = subprocess.PIPE)
817        svn_info = pipe.stdout.read()
818    if not noHeaders:
819        print svn_info
820
821    global ignore
822
823    # The '-i' option is used to skip looking for lldb.py in the build tree.
824    if ignore:
825        return
826
827    dbgPath  = os.path.join(base, *(xcode3_build_dir + dbg + python_resource_dir))
828    dbgPath2 = os.path.join(base, *(xcode4_build_dir + dbg + python_resource_dir))
829    relPath  = os.path.join(base, *(xcode3_build_dir + rel + python_resource_dir))
830    relPath2 = os.path.join(base, *(xcode4_build_dir + rel + python_resource_dir))
831    baiPath  = os.path.join(base, *(xcode3_build_dir + bai + python_resource_dir))
832    baiPath2 = os.path.join(base, *(xcode4_build_dir + bai + python_resource_dir))
833
834    lldbPath = None
835    if os.path.isfile(os.path.join(dbgPath, 'lldb/__init__.py')):
836        lldbPath = dbgPath
837    elif os.path.isfile(os.path.join(dbgPath2, 'lldb/__init__.py')):
838        lldbPath = dbgPath2
839    elif os.path.isfile(os.path.join(relPath, 'lldb/__init__.py')):
840        lldbPath = relPath
841    elif os.path.isfile(os.path.join(relPath2, 'lldb/__init__.py')):
842        lldbPath = relPath2
843    elif os.path.isfile(os.path.join(baiPath, 'lldb/__init__.py')):
844        lldbPath = baiPath
845    elif os.path.isfile(os.path.join(baiPath2, 'lldb/__init__.py')):
846        lldbPath = baiPath2
847
848    if not lldbPath:
849        print 'This script requires lldb.py to be in either ' + dbgPath + ',',
850        print relPath + ', or ' + baiPath
851        sys.exit(-1)
852
853    # This is to locate the lldb.py module.  Insert it right after sys.path[0].
854    sys.path[1:1] = [lldbPath]
855    if dumpSysPath:
856        print "sys.path:", sys.path
857
858
859def doDelay(delta):
860    """Delaying startup for delta-seconds to facilitate debugger attachment."""
861    def alarm_handler(*args):
862        raise Exception("timeout")
863
864    signal.signal(signal.SIGALRM, alarm_handler)
865    signal.alarm(delta)
866    sys.stdout.write("pid=%d\n" % os.getpid())
867    sys.stdout.write("Enter RET to proceed (or timeout after %d seconds):" %
868                     delta)
869    sys.stdout.flush()
870    try:
871        text = sys.stdin.readline()
872    except:
873        text = ""
874    signal.alarm(0)
875    sys.stdout.write("proceeding...\n")
876    pass
877
878
879def visit(prefix, dir, names):
880    """Visitor function for os.path.walk(path, visit, arg)."""
881
882    global suite
883    global regexp
884    global filters
885    global fs4all
886    global excluded
887
888    if set(dir.split(os.sep)).intersection(excluded):
889        #print "Detected an excluded dir component: %s" % dir
890        return
891
892    for name in names:
893        if os.path.isdir(os.path.join(dir, name)):
894            continue
895
896        if '.py' == os.path.splitext(name)[1] and name.startswith(prefix):
897            # Try to match the regexp pattern, if specified.
898            if regexp:
899                import re
900                if re.search(regexp, name):
901                    #print "Filename: '%s' matches pattern: '%s'" % (name, regexp)
902                    pass
903                else:
904                    #print "Filename: '%s' does not match pattern: '%s'" % (name, regexp)
905                    continue
906
907            # We found a match for our test.  Add it to the suite.
908
909            # Update the sys.path first.
910            if not sys.path.count(dir):
911                sys.path.insert(0, dir)
912            base = os.path.splitext(name)[0]
913
914            # Thoroughly check the filterspec against the base module and admit
915            # the (base, filterspec) combination only when it makes sense.
916            filterspec = None
917            for filterspec in filters:
918                # Optimistically set the flag to True.
919                filtered = True
920                module = __import__(base)
921                parts = filterspec.split('.')
922                obj = module
923                for part in parts:
924                    try:
925                        parent, obj = obj, getattr(obj, part)
926                    except AttributeError:
927                        # The filterspec has failed.
928                        filtered = False
929                        break
930
931                # If filtered, we have a good filterspec.  Add it.
932                if filtered:
933                    #print "adding filter spec %s to module %s" % (filterspec, module)
934                    suite.addTests(
935                        unittest2.defaultTestLoader.loadTestsFromName(filterspec, module))
936                    continue
937
938            # Forgo this module if the (base, filterspec) combo is invalid
939            # and no '-g' option is specified
940            if filters and fs4all and not filtered:
941                continue
942
943            # Add either the filtered test case(s) (which is done before) or the entire test class.
944            if not filterspec or not filtered:
945                # A simple case of just the module name.  Also the failover case
946                # from the filterspec branch when the (base, filterspec) combo
947                # doesn't make sense.
948                suite.addTests(unittest2.defaultTestLoader.loadTestsFromName(base))
949
950
951def lldbLoggings():
952    """Check and do lldb loggings if necessary."""
953
954    # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
955    # defined.  Use ${LLDB_LOG} to specify the log file.
956    ci = lldb.DBG.GetCommandInterpreter()
957    res = lldb.SBCommandReturnObject()
958    if ("LLDB_LOG" in os.environ):
959        if ("LLDB_LOG_OPTION" in os.environ):
960            lldb_log_option = os.environ["LLDB_LOG_OPTION"]
961        else:
962            lldb_log_option = "event process expr state api"
963        ci.HandleCommand(
964            "log enable -n -f " + os.environ["LLDB_LOG"] + " lldb " + lldb_log_option,
965            res)
966        if not res.Succeeded():
967            raise Exception('log enable failed (check LLDB_LOG env variable.')
968    # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
969    # Use ${GDB_REMOTE_LOG} to specify the log file.
970    if ("GDB_REMOTE_LOG" in os.environ):
971        if ("GDB_REMOTE_LOG_OPTION" in os.environ):
972            gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
973        else:
974            gdb_remote_log_option = "packets process"
975        ci.HandleCommand(
976            "log enable -n -f " + os.environ["GDB_REMOTE_LOG"] + " gdb-remote "
977            + gdb_remote_log_option,
978            res)
979        if not res.Succeeded():
980            raise Exception('log enable failed (check GDB_REMOTE_LOG env variable.')
981
982def getMyCommandLine():
983    ps = subprocess.Popen(['ps', '-o', "command=CMD", str(os.getpid())], stdout=subprocess.PIPE).communicate()[0]
984    lines = ps.split('\n')
985    cmd_line = lines[1]
986    return cmd_line
987
988# ======================================== #
989#                                          #
990# Execution of the test driver starts here #
991#                                          #
992# ======================================== #
993
994def checkDsymForUUIDIsNotOn():
995    cmd = ["defaults", "read", "com.apple.DebugSymbols"]
996    pipe = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
997    cmd_output = pipe.stdout.read()
998    if cmd_output and "DBGFileMappedPaths = " in cmd_output:
999        print "%s =>" % ' '.join(cmd)
1000        print cmd_output
1001        print "Disable automatic lookup and caching of dSYMs before running the test suite!"
1002        print "Exiting..."
1003        sys.exit(0)
1004
1005# On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
1006# does not exist before proceeding to running the test suite.
1007if sys.platform.startswith("darwin"):
1008    checkDsymForUUIDIsNotOn()
1009
1010#
1011# Start the actions by first parsing the options while setting up the test
1012# directories, followed by setting up the search paths for lldb utilities;
1013# then, we walk the directory trees and collect the tests into our test suite.
1014#
1015parseOptionsAndInitTestdirs()
1016setupSysPath()
1017
1018#
1019# If '-d' is specified, do a delay of 10 seconds for the debugger to attach.
1020#
1021if delay:
1022    doDelay(10)
1023
1024#
1025# If '-l' is specified, do not skip the long running tests.
1026if not skip_long_running_test:
1027    os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO"
1028
1029#
1030# Walk through the testdirs while collecting tests.
1031#
1032for testdir in testdirs:
1033    os.path.walk(testdir, visit, 'Test')
1034
1035#
1036# Now that we have loaded all the test cases, run the whole test suite.
1037#
1038
1039# For the time being, let's bracket the test runner within the
1040# lldb.SBDebugger.Initialize()/Terminate() pair.
1041import lldb, atexit
1042# Update: the act of importing lldb now executes lldb.SBDebugger.Initialize(),
1043# there's no need to call it a second time.
1044#lldb.SBDebugger.Initialize()
1045atexit.register(lambda: lldb.SBDebugger.Terminate())
1046
1047# Create a singleton SBDebugger in the lldb namespace.
1048lldb.DBG = lldb.SBDebugger.Create()
1049
1050# Put the blacklist in the lldb namespace, to be used by lldb.TestBase.
1051lldb.blacklist = blacklist
1052
1053# The pre_flight and post_flight come from reading a config file.
1054lldb.pre_flight = pre_flight
1055lldb.post_flight = post_flight
1056def getsource_if_available(obj):
1057    """
1058    Return the text of the source code for an object if available.  Otherwise,
1059    a print representation is returned.
1060    """
1061    import inspect
1062    try:
1063        return inspect.getsource(obj)
1064    except:
1065        return repr(obj)
1066
1067print "lldb.pre_flight:", getsource_if_available(lldb.pre_flight)
1068print "lldb.post_flight:", getsource_if_available(lldb.post_flight)
1069
1070# Put all these test decorators in the lldb namespace.
1071lldb.dont_do_python_api_test = dont_do_python_api_test
1072lldb.just_do_python_api_test = just_do_python_api_test
1073lldb.just_do_benchmarks_test = just_do_benchmarks_test
1074lldb.dont_do_dsym_test = dont_do_dsym_test
1075lldb.dont_do_dwarf_test = dont_do_dwarf_test
1076
1077# Do we need to skip build and cleanup?
1078lldb.skip_build_and_cleanup = skip_build_and_cleanup
1079
1080# Put bmExecutable, bmBreakpointSpec, and bmIterationCount into the lldb namespace, too.
1081lldb.bmExecutable = bmExecutable
1082lldb.bmBreakpointSpec = bmBreakpointSpec
1083lldb.bmIterationCount = bmIterationCount
1084
1085# And don't forget the runHooks!
1086lldb.runHooks = runHooks
1087
1088# Turn on lldb loggings if necessary.
1089lldbLoggings()
1090
1091# Install the control-c handler.
1092unittest2.signals.installHandler()
1093
1094# If sdir_name is not specified through the '-s sdir_name' option, get a
1095# timestamp string and export it as LLDB_SESSION_DIR environment var.  This will
1096# be used when/if we want to dump the session info of individual test cases
1097# later on.
1098#
1099# See also TestBase.dumpSessionInfo() in lldbtest.py.
1100import datetime
1101# The windows platforms don't like ':' in the pathname.
1102timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
1103if not sdir_name:
1104    sdir_name = timestamp_started
1105os.environ["LLDB_SESSION_DIRNAME"] = os.path.join(os.getcwd(), sdir_name)
1106
1107if not noHeaders:
1108    sys.stderr.write("\nSession logs for test failures/errors/unexpected successes"
1109                     " will go into directory '%s'\n" % sdir_name)
1110    sys.stderr.write("Command invoked: %s\n" % getMyCommandLine())
1111
1112if not os.path.isdir(sdir_name):
1113    os.mkdir(sdir_name)
1114fname = os.path.join(sdir_name, "TestStarted")
1115with open(fname, "w") as f:
1116    print >> f, "Test started at: %s\n" % timestamp_started
1117    print >> f, svn_info
1118    print >> f, "Command invoked: %s\n" % getMyCommandLine()
1119
1120#
1121# If we have environment variables to unset, do it here before we invoke the test runner.
1122#
1123for env_var in unsets :
1124    if env_var in os.environ:
1125        # From Python Doc: When unsetenv() is supported, deletion of items in os.environ
1126        # is automatically translated into a corresponding call to unsetenv().
1127        del os.environ[env_var]
1128        #os.unsetenv(env_var)
1129
1130#
1131# Invoke the default TextTestRunner to run the test suite, possibly iterating
1132# over different configurations.
1133#
1134
1135iterArchs = False
1136iterCompilers = False
1137
1138if not archs and "archs" in config:
1139    archs = config["archs"]
1140
1141if isinstance(archs, list) and len(archs) >= 1:
1142    iterArchs = True
1143
1144if not compilers and "compilers" in config:
1145    compilers = config["compilers"]
1146
1147#
1148# Add some intervention here to sanity check that the compilers requested are sane.
1149# If found not to be an executable program, the invalid one is dropped from the list.
1150for i in range(len(compilers)):
1151    c = compilers[i]
1152    if which(c):
1153        continue
1154    else:
1155        if sys.platform.startswith("darwin"):
1156            pipe = subprocess.Popen(['xcrun', '-find', c], stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
1157            cmd_output = pipe.stdout.read()
1158            if cmd_output:
1159                if "not found" in cmd_output:
1160                    print "dropping %s from the compilers used" % c
1161                    compilers.remove(i)
1162                else:
1163                    compilers[i] = cmd_output.split('\n')[0]
1164                    print "'xcrun -find %s' returning %s" % (c, compilers[i])
1165
1166print "compilers=%s" % str(compilers)
1167
1168if not compilers or len(compilers) == 0:
1169    print "No eligible compiler found, exiting."
1170    sys.exit(1)
1171
1172if isinstance(compilers, list) and len(compilers) >= 1:
1173    iterCompilers = True
1174
1175# Make a shallow copy of sys.path, we need to manipulate the search paths later.
1176# This is only necessary if we are relocated and with different configurations.
1177if rdir:
1178    old_sys_path = sys.path[:]
1179# If we iterate on archs or compilers, there is a chance we want to split stderr/stdout.
1180if iterArchs or iterCompilers:
1181    old_stderr = sys.stderr
1182    old_stdout = sys.stdout
1183    new_stderr = None
1184    new_stdout = None
1185
1186# Iterating over all possible architecture and compiler combinations.
1187for ia in range(len(archs) if iterArchs else 1):
1188    archConfig = ""
1189    if iterArchs:
1190        os.environ["ARCH"] = archs[ia]
1191        archConfig = "arch=%s" % archs[ia]
1192    for ic in range(len(compilers) if iterCompilers else 1):
1193        if iterCompilers:
1194            os.environ["CC"] = compilers[ic]
1195            configString = "%s compiler=%s" % (archConfig, compilers[ic])
1196        else:
1197            configString = archConfig
1198
1199        if iterArchs or iterCompilers:
1200            # Translate ' ' to '-' for pathname component.
1201            from string import maketrans
1202            tbl = maketrans(' ', '-')
1203            configPostfix = configString.translate(tbl)
1204
1205            # Check whether we need to split stderr/stdout into configuration
1206            # specific files.
1207            if old_stderr.name != '<stderr>' and config.get('split_stderr'):
1208                if new_stderr:
1209                    new_stderr.close()
1210                new_stderr = open("%s.%s" % (old_stderr.name, configPostfix), "w")
1211                sys.stderr = new_stderr
1212            if old_stdout.name != '<stdout>' and config.get('split_stdout'):
1213                if new_stdout:
1214                    new_stdout.close()
1215                new_stdout = open("%s.%s" % (old_stdout.name, configPostfix), "w")
1216                sys.stdout = new_stdout
1217
1218            # If we specified a relocated directory to run the test suite, do
1219            # the extra housekeeping to copy the testdirs to a configStringified
1220            # directory and to update sys.path before invoking the test runner.
1221            # The purpose is to separate the configuration-specific directories
1222            # from each other.
1223            if rdir:
1224                from shutil import copytree, rmtree, ignore_patterns
1225
1226                newrdir = "%s.%s" % (rdir, configPostfix)
1227
1228                # Copy the tree to a new directory with postfix name configPostfix.
1229                if os.path.exists(newrdir):
1230                    rmtree(newrdir)
1231                copytree(rdir, newrdir, ignore=ignore_patterns('*.pyc', '*.o', '*.d'))
1232
1233                # Update the LLDB_TEST environment variable to reflect new top
1234                # level test directory.
1235                #
1236                # See also lldbtest.TestBase.setUpClass(cls).
1237                if len(testdirs) == 1 and os.path.basename(testdirs[0]) == 'test':
1238                    os.environ["LLDB_TEST"] = os.path.join(newrdir, 'test')
1239                else:
1240                    os.environ["LLDB_TEST"] = newrdir
1241
1242                # And update the Python search paths for modules.
1243                sys.path = [x.replace(rdir, newrdir, 1) for x in old_sys_path]
1244
1245            # Output the configuration.
1246            sys.stderr.write("\nConfiguration: " + configString + "\n")
1247
1248        #print "sys.stderr name is", sys.stderr.name
1249        #print "sys.stdout name is", sys.stdout.name
1250
1251        # First, write out the number of collected test cases.
1252        sys.stderr.write(separator + "\n")
1253        sys.stderr.write("Collected %d test%s\n\n"
1254                         % (suite.countTestCases(),
1255                            suite.countTestCases() != 1 and "s" or ""))
1256
1257        class LLDBTestResult(unittest2.TextTestResult):
1258            """
1259            Enforce a singleton pattern to allow introspection of test progress.
1260
1261            Overwrite addError(), addFailure(), and addExpectedFailure() methods
1262            to enable each test instance to track its failure/error status.  It
1263            is used in the LLDB test framework to emit detailed trace messages
1264            to a log file for easier human inspection of test failres/errors.
1265            """
1266            __singleton__ = None
1267            __ignore_singleton__ = False
1268
1269            def __init__(self, *args):
1270                if not LLDBTestResult.__ignore_singleton__ and LLDBTestResult.__singleton__:
1271                    raise Exception("LLDBTestResult instantiated more than once")
1272                super(LLDBTestResult, self).__init__(*args)
1273                LLDBTestResult.__singleton__ = self
1274                # Now put this singleton into the lldb module namespace.
1275                lldb.test_result = self
1276                # Computes the format string for displaying the counter.
1277                global suite
1278                counterWidth = len(str(suite.countTestCases()))
1279                self.fmt = "%" + str(counterWidth) + "d: "
1280                self.indentation = ' ' * (counterWidth + 2)
1281                # This counts from 1 .. suite.countTestCases().
1282                self.counter = 0
1283
1284            def _exc_info_to_string(self, err, test):
1285                """Overrides superclass TestResult's method in order to append
1286                our test config info string to the exception info string."""
1287                modified_exc_string = '%sConfig=%s-%s' % (super(LLDBTestResult, self)._exc_info_to_string(err, test),
1288                                                          test.getArchitecture(),
1289                                                          test.getCompiler())
1290                return modified_exc_string
1291
1292            def getDescription(self, test):
1293                doc_first_line = test.shortDescription()
1294                if self.descriptions and doc_first_line:
1295                    return '\n'.join((str(test), self.indentation + doc_first_line))
1296                else:
1297                    return str(test)
1298
1299            def startTest(self, test):
1300                self.counter += 1
1301                if self.showAll:
1302                    self.stream.write(self.fmt % self.counter)
1303                super(LLDBTestResult, self).startTest(test)
1304
1305            def addError(self, test, err):
1306                global sdir_has_content
1307                sdir_has_content = True
1308                super(LLDBTestResult, self).addError(test, err)
1309                method = getattr(test, "markError", None)
1310                if method:
1311                    method()
1312
1313            def addFailure(self, test, err):
1314                global sdir_has_content
1315                sdir_has_content = True
1316                super(LLDBTestResult, self).addFailure(test, err)
1317                method = getattr(test, "markFailure", None)
1318                if method:
1319                    method()
1320
1321            def addExpectedFailure(self, test, err):
1322                global sdir_has_content
1323                sdir_has_content = True
1324                super(LLDBTestResult, self).addExpectedFailure(test, err)
1325                method = getattr(test, "markExpectedFailure", None)
1326                if method:
1327                    method()
1328
1329            def addSkip(self, test, reason):
1330                global sdir_has_content
1331                sdir_has_content = True
1332                super(LLDBTestResult, self).addSkip(test, reason)
1333                method = getattr(test, "markSkippedTest", None)
1334                if method:
1335                    method()
1336
1337            def addUnexpectedSuccess(self, test):
1338                global sdir_has_content
1339                sdir_has_content = True
1340                super(LLDBTestResult, self).addUnexpectedSuccess(test)
1341                method = getattr(test, "markUnexpectedSuccess", None)
1342                if method:
1343                    method()
1344
1345        # Invoke the test runner.
1346        if count == 1:
1347            result = unittest2.TextTestRunner(stream=sys.stderr,
1348                                              verbosity=(1 if progress_bar else verbose),
1349                                              failfast=failfast,
1350                                              resultclass=LLDBTestResult).run(suite)
1351        else:
1352            # We are invoking the same test suite more than once.  In this case,
1353            # mark __ignore_singleton__ flag as True so the signleton pattern is
1354            # not enforced.
1355            LLDBTestResult.__ignore_singleton__ = True
1356            for i in range(count):
1357                result = unittest2.TextTestRunner(stream=sys.stderr,
1358                                                  verbosity=(1 if progress_bar else verbose),
1359                                                  failfast=failfast,
1360                                                  resultclass=LLDBTestResult).run(suite)
1361
1362
1363if sdir_has_content:
1364    sys.stderr.write("Session logs for test failures/errors/unexpected successes"
1365                     " can be found in directory '%s'\n" % sdir_name)
1366
1367fname = os.path.join(sdir_name, "TestFinished")
1368with open(fname, "w") as f:
1369    print >> f, "Test finished at: %s\n" % datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
1370
1371# Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
1372# This should not be necessary now.
1373if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
1374    print "Terminating Test suite..."
1375    subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
1376
1377# Exiting.
1378sys.exit(not result.wasSuccessful)
1379