dotest.py revision c5fa005707f9378e03328fb0ee492b8a6c230057
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 unittest2
25
26def is_exe(fpath):
27    """Returns true if fpath is an executable."""
28    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
29
30def which(program):
31    """Returns the full path to a program; None otherwise."""
32    fpath, fname = os.path.split(program)
33    if fpath:
34        if is_exe(program):
35            return program
36    else:
37        for path in os.environ["PATH"].split(os.pathsep):
38            exe_file = os.path.join(path, program)
39            if is_exe(exe_file):
40                return exe_file
41    return None
42
43class _WritelnDecorator(object):
44    """Used to decorate file-like objects with a handy 'writeln' method"""
45    def __init__(self,stream):
46        self.stream = stream
47
48    def __getattr__(self, attr):
49        if attr in ('stream', '__getstate__'):
50            raise AttributeError(attr)
51        return getattr(self.stream,attr)
52
53    def writeln(self, arg=None):
54        if arg:
55            self.write(arg)
56        self.write('\n') # text-mode streams translate to \r\n if needed
57
58#
59# Global variables:
60#
61
62# The test suite.
63suite = unittest2.TestSuite()
64
65# By default, both command line and Python API tests are performed.
66# Use @python_api_test decorator, defined in lldbtest.py, to mark a test as
67# a Python API test.
68dont_do_python_api_test = False
69
70# By default, both command line and Python API tests are performed.
71just_do_python_api_test = False
72
73# The blacklist is optional (-b blacklistFile) and allows a central place to skip
74# testclass's and/or testclass.testmethod's.
75blacklist = None
76
77# The dictionary as a result of sourcing blacklistFile.
78blacklistConfig = {}
79
80# The config file is optional.
81configFile = None
82
83# Test suite repeat count.  Can be overwritten with '-# count'.
84count = 1
85
86# The dictionary as a result of sourcing configFile.
87config = {}
88
89# The 'archs' and 'compilers' can be specified via either command line or configFile,
90# with the command line overriding the configFile.  When specified, they should be
91# of the list type.  For example, "-A x86_64^i386" => archs=['x86_64', 'i386'] and
92# "-C gcc^clang" => compilers=['gcc', 'clang'].
93archs = None
94compilers = None
95
96# Delay startup in order for the debugger to attach.
97delay = False
98
99# Dump the Python sys.path variable.  Use '-D' to dump sys.path.
100dumpSysPath = False
101
102# By default, failfast is False.  Use '-F' to overwrite it.
103failfast = False
104
105# The filters (testclass.testmethod) used to admit tests into our test suite.
106filters = []
107
108# If '-g' is specified, the filterspec is not exclusive.  If a test module does
109# not contain testclass.testmethod which matches the filterspec, the whole test
110# module is still admitted into our test suite.  fs4all flag defaults to True.
111fs4all = True
112
113# Ignore the build search path relative to this script to locate the lldb.py module.
114ignore = False
115
116# By default, we skip long running test case.  Use '-l' option to override.
117skipLongRunningTest = True
118
119# The regular expression pattern to match against eligible filenames as our test cases.
120regexp = None
121
122# By default, tests are executed in place and cleanups are performed afterwards.
123# Use '-r dir' option to relocate the tests and their intermediate files to a
124# different directory and to forgo any cleanups.  The directory specified must
125# not exist yet.
126rdir = None
127
128# By default, recorded session info for errored/failed test are dumped into its
129# own file under a session directory named after the timestamp of the test suite
130# run.  Use '-s session-dir-name' to specify a specific dir name.
131sdir_name = None
132
133# Set this flag if there is any session info dumped during the test run.
134sdir_has_content = False
135
136# svn_info stores the output from 'svn info lldb.base.dir'.
137svn_info = ''
138
139# Default verbosity is 0.
140verbose = 0
141
142# By default, search from the script directory.
143testdirs = [ sys.path[0] ]
144
145# Separator string.
146separator = '-' * 70
147
148
149def usage():
150    print """
151Usage: dotest.py [option] [args]
152where options:
153-h   : print this help message and exit.  Add '-v' for more detailed help.
154-A   : specify the architecture(s) to launch for the inferior process
155       -A i386 => launch inferior with i386 architecture
156       -A x86_64^i386 => launch inferior with x86_64 and i386 architectures
157-C   : specify the compiler(s) used to build the inferior executable
158       -C clang => build debuggee using clang compiler
159       -C clang^gcc => build debuggee using clang and gcc compilers
160-D   : dump the Python sys.path variable
161-a   : don't do lldb Python API tests
162       use @python_api_test to decorate a test case as lldb Python API test
163+a   : just do lldb Python API tests
164       do not specify both '-a' and '+a' at the same time
165-b   : read a blacklist file specified after this option
166-c   : read a config file specified after this option
167       the architectures and compilers (note the plurals) specified via '-A' and '-C'
168       will override those specified via a config file
169       (see also lldb-trunk/example/test/usage-config)
170-d   : delay startup for 10 seconds (in order for the debugger to attach)
171-F   : failfast, stop the test suite on the first error/failure
172-f   : specify a filter, which consists of the test class name, a dot, followed by
173       the test method, to only admit such test into the test suite
174       e.g., -f 'ClassTypesTestCase.test_with_dwarf_and_python_api'
175-g   : if specified, the filterspec by -f is not exclusive, i.e., if a test module
176       does not match the filterspec (testclass.testmethod), the whole module is
177       still admitted to the test suite
178-i   : ignore (don't bailout) if 'lldb.py' module cannot be located in the build
179       tree relative to this script; use PYTHONPATH to locate the module
180-l   : don't skip long running test
181-p   : specify a regexp filename pattern for inclusion in the test suite
182-r   : specify a dir to relocate the tests and their intermediate files to;
183       the directory must not exist before running this test driver;
184       no cleanup of intermediate test files is performed in this case
185-s   : specify the name of the dir created to store the session files of tests
186       with errored or failed status; if not specified, the test driver uses the
187       timestamp as the session dir name
188-t   : turn on tracing of lldb command and other detailed test executions
189-v   : do verbose mode of unittest framework (print out each test case invocation)
190-w   : insert some wait time (currently 0.5 sec) between consecutive test cases
191-#   : Repeat the test suite for a specified number of times
192
193and:
194args : specify a list of directory names to search for test modules named after
195       Test*.py (test discovery)
196       if empty, search from the current working directory, instead
197"""
198
199    if verbose > 0:
200        print """
201Examples:
202
203This is an example of using the -f option to pinpoint to a specfic test class
204and test method to be run:
205
206$ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command
207----------------------------------------------------------------------
208Collected 1 test
209
210test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase)
211Test 'frame variable this' when stopped on a class constructor. ... ok
212
213----------------------------------------------------------------------
214Ran 1 test in 1.396s
215
216OK
217
218And this is an example of using the -p option to run a single file (the filename
219matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'):
220
221$ ./dotest.py -v -p ObjC
222----------------------------------------------------------------------
223Collected 4 tests
224
225test_break_with_dsym (TestObjCMethods.FoundationTestCase)
226Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
227test_break_with_dwarf (TestObjCMethods.FoundationTestCase)
228Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok
229test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase)
230Lookup objective-c data types and evaluate expressions. ... ok
231test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase)
232Lookup objective-c data types and evaluate expressions. ... ok
233
234----------------------------------------------------------------------
235Ran 4 tests in 16.661s
236
237OK
238
239Running of this script also sets up the LLDB_TEST environment variable so that
240individual test cases can locate their supporting files correctly.  The script
241tries to set up Python's search paths for modules by looking at the build tree
242relative to this script.  See also the '-i' option in the following example.
243
244Finally, this is an example of using the lldb.py module distributed/installed by
245Xcode4 to run against the tests under the 'forward' directory, and with the '-w'
246option to add some delay between two tests.  It uses ARCH=x86_64 to specify that
247as the architecture and CC=clang to specify the compiler used for the test run:
248
249$ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward
250
251Session logs for test failures/errors will go into directory '2010-11-11-13_56_16'
252----------------------------------------------------------------------
253Collected 2 tests
254
255test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
256Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
257test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase)
258Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok
259
260----------------------------------------------------------------------
261Ran 2 tests in 5.659s
262
263OK
264
265The 'Session ...' verbiage is recently introduced (see also the '-s' option) to
266notify the directory containing the session logs for test failures or errors.
267In case there is any test failure/error, a similar message is appended at the
268end of the stderr output for your convenience.
269
270Environment variables related to loggings:
271
272o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem
273  with a default option of 'event process' if LLDB_LOG_OPTION is not defined.
274
275o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the
276  'process.gdb-remote' subsystem with a default option of 'packets' if
277  GDB_REMOTE_LOG_OPTION is not defined.
278"""
279    sys.exit(0)
280
281
282def parseOptionsAndInitTestdirs():
283    """Initialize the list of directories containing our unittest scripts.
284
285    '-h/--help as the first option prints out usage info and exit the program.
286    """
287
288    global dont_do_python_api_test
289    global just_do_python_api_test
290    global blacklist
291    global blacklistConfig
292    global configFile
293    global archs
294    global compilers
295    global count
296    global delay
297    global dumpSysPath
298    global failfast
299    global filters
300    global fs4all
301    global ignore
302    global skipLongRunningTest
303    global regexp
304    global rdir
305    global sdir_name
306    global verbose
307    global testdirs
308
309    do_help = False
310
311    if len(sys.argv) == 1:
312        return
313
314    # Process possible trace and/or verbose flag, among other things.
315    index = 1
316    while index < len(sys.argv):
317        if sys.argv[index].startswith('-') or sys.argv[index].startswith('+'):
318            # We should continue processing...
319            pass
320        else:
321            # End of option processing.
322            break
323
324        if sys.argv[index].find('-h') != -1:
325            index += 1
326            do_help = True
327        elif sys.argv[index].startswith('-A'):
328            # Increment by 1 to fetch the ARCH spec.
329            index += 1
330            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
331                usage()
332            archs = sys.argv[index].split('^')
333            index += 1
334        elif sys.argv[index].startswith('-C'):
335            # Increment by 1 to fetch the CC spec.
336            index += 1
337            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
338                usage()
339            compilers = sys.argv[index].split('^')
340            index += 1
341        elif sys.argv[index].startswith('-D'):
342            dumpSysPath = True
343            index += 1
344        elif sys.argv[index].startswith('-a'):
345            dont_do_python_api_test = True
346            index += 1
347        elif sys.argv[index].startswith('+a'):
348            just_do_python_api_test = True
349            index += 1
350        elif sys.argv[index].startswith('-b'):
351            # Increment by 1 to fetch the blacklist file name option argument.
352            index += 1
353            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
354                usage()
355            blacklistFile = sys.argv[index]
356            if not os.path.isfile(blacklistFile):
357                print "Blacklist file:", blacklistFile, "does not exist!"
358                usage()
359            index += 1
360            # Now read the blacklist contents and assign it to blacklist.
361            execfile(blacklistFile, globals(), blacklistConfig)
362            blacklist = blacklistConfig.get('blacklist')
363        elif sys.argv[index].startswith('-c'):
364            # Increment by 1 to fetch the config file name option argument.
365            index += 1
366            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
367                usage()
368            configFile = sys.argv[index]
369            if not os.path.isfile(configFile):
370                print "Config file:", configFile, "does not exist!"
371                usage()
372            index += 1
373        elif sys.argv[index].startswith('-d'):
374            delay = True
375            index += 1
376        elif sys.argv[index].startswith('-F'):
377            failfast = True
378            index += 1
379        elif sys.argv[index].startswith('-f'):
380            # Increment by 1 to fetch the filter spec.
381            index += 1
382            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
383                usage()
384            filters.append(sys.argv[index])
385            index += 1
386        elif sys.argv[index].startswith('-g'):
387            fs4all = False
388            index += 1
389        elif sys.argv[index].startswith('-i'):
390            ignore = True
391            index += 1
392        elif sys.argv[index].startswith('-l'):
393            skipLongRunningTest = False
394            index += 1
395        elif sys.argv[index].startswith('-p'):
396            # Increment by 1 to fetch the reg exp pattern argument.
397            index += 1
398            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
399                usage()
400            regexp = sys.argv[index]
401            index += 1
402        elif sys.argv[index].startswith('-r'):
403            # Increment by 1 to fetch the relocated directory argument.
404            index += 1
405            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
406                usage()
407            rdir = os.path.abspath(sys.argv[index])
408            if os.path.exists(rdir):
409                print "Relocated directory:", rdir, "must not exist!"
410                usage()
411            index += 1
412        elif sys.argv[index].startswith('-s'):
413            # Increment by 1 to fetch the session dir name.
414            index += 1
415            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
416                usage()
417            sdir_name = sys.argv[index]
418            index += 1
419        elif sys.argv[index].startswith('-t'):
420            os.environ["LLDB_COMMAND_TRACE"] = "YES"
421            index += 1
422        elif sys.argv[index].startswith('-v'):
423            verbose = 2
424            index += 1
425        elif sys.argv[index].startswith('-w'):
426            os.environ["LLDB_WAIT_BETWEEN_TEST_CASES"] = 'YES'
427            index += 1
428        elif sys.argv[index].startswith('-#'):
429            # Increment by 1 to fetch the repeat count argument.
430            index += 1
431            if index >= len(sys.argv) or sys.argv[index].startswith('-'):
432                usage()
433            count = int(sys.argv[index])
434            index += 1
435        else:
436            print "Unknown option: ", sys.argv[index]
437            usage()
438
439    if do_help == True:
440        usage()
441
442    # Do not specify both '-a' and '+a' at the same time.
443    if dont_do_python_api_test and just_do_python_api_test:
444        usage()
445
446    # Gather all the dirs passed on the command line.
447    if len(sys.argv) > index:
448        testdirs = map(os.path.abspath, sys.argv[index:])
449
450    # If '-r dir' is specified, the tests should be run under the relocated
451    # directory.  Let's copy the testdirs over.
452    if rdir:
453        from shutil import copytree, ignore_patterns
454
455        tmpdirs = []
456        for srcdir in testdirs:
457            dstdir = os.path.join(rdir, os.path.basename(srcdir))
458            # Don't copy the *.pyc and .svn stuffs.
459            copytree(srcdir, dstdir, ignore=ignore_patterns('*.pyc', '.svn'))
460            tmpdirs.append(dstdir)
461
462        # This will be our modified testdirs.
463        testdirs = tmpdirs
464
465        # With '-r dir' specified, there's no cleanup of intermediate test files.
466        os.environ["LLDB_DO_CLEANUP"] = 'NO'
467
468        # If testdirs is ['test'], the make directory has already been copied
469        # recursively and is contained within the rdir/test dir.  For anything
470        # else, we would need to copy over the make directory and its contents,
471        # so that, os.listdir(rdir) looks like, for example:
472        #
473        #     array_types conditional_break make
474        #
475        # where the make directory contains the Makefile.rules file.
476        if len(testdirs) != 1 or os.path.basename(testdirs[0]) != 'test':
477            # Don't copy the .svn stuffs.
478            copytree('make', os.path.join(rdir, 'make'),
479                     ignore=ignore_patterns('.svn'))
480
481    #print "testdirs:", testdirs
482
483    # Source the configFile if specified.
484    # The side effect, if any, will be felt from this point on.  An example
485    # config file may be these simple two lines:
486    #
487    # sys.stderr = open("/tmp/lldbtest-stderr", "w")
488    # sys.stdout = open("/tmp/lldbtest-stdout", "w")
489    #
490    # which will reassign the two file objects to sys.stderr and sys.stdout,
491    # respectively.
492    #
493    # See also lldb-trunk/example/test/usage-config.
494    global config
495    if configFile:
496        # Pass config (a dictionary) as the locals namespace for side-effect.
497        execfile(configFile, globals(), config)
498        #print "config:", config
499        #print "sys.stderr:", sys.stderr
500        #print "sys.stdout:", sys.stdout
501
502
503def setupSysPath():
504    """
505    Add LLDB.framework/Resources/Python to the search paths for modules.
506    As a side effect, we also discover the 'lldb' executable and export it here.
507    """
508
509    global rdir
510    global testdirs
511    global dumpSysPath
512    global svn_info
513
514    # Get the directory containing the current script.
515    if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ:
516        scriptPath = os.environ["DOTEST_SCRIPT_DIR"]
517    else:
518        scriptPath = sys.path[0]
519    if not scriptPath.endswith('test'):
520        print "This script expects to reside in lldb's test directory."
521        sys.exit(-1)
522
523    if rdir:
524        # Set up the LLDB_TEST environment variable appropriately, so that the
525        # individual tests can be located relatively.
526        #
527        # See also lldbtest.TestBase.setUpClass(cls).
528        if len(testdirs) == 1 and os.path.basename(testdirs[0]) == 'test':
529            os.environ["LLDB_TEST"] = os.path.join(rdir, 'test')
530        else:
531            os.environ["LLDB_TEST"] = rdir
532    else:
533        os.environ["LLDB_TEST"] = scriptPath
534
535    # Set up the LLDB_SRC environment variable, so that the tests can locate
536    # the LLDB source code.
537    os.environ["LLDB_SRC"] = os.path.join(sys.path[0], os.pardir)
538
539    pluginPath = os.path.join(scriptPath, 'plugins')
540    pexpectPath = os.path.join(scriptPath, 'pexpect-2.4')
541
542    # Append script dir, plugin dir, and pexpect dir to the sys.path.
543    sys.path.append(scriptPath)
544    sys.path.append(pluginPath)
545    sys.path.append(pexpectPath)
546
547    # This is our base name component.
548    base = os.path.abspath(os.path.join(scriptPath, os.pardir))
549
550    # These are for xcode build directories.
551    xcode3_build_dir = ['build']
552    xcode4_build_dir = ['build', 'lldb', 'Build', 'Products']
553    dbg = ['Debug']
554    rel = ['Release']
555    bai = ['BuildAndIntegration']
556    python_resource_dir = ['LLDB.framework', 'Resources', 'Python']
557
558    # Some of the tests can invoke the 'lldb' command directly.
559    # We'll try to locate the appropriate executable right here.
560
561    executable = ['lldb']
562    dbgExec  = os.path.join(base, *(xcode3_build_dir + dbg + executable))
563    dbgExec2 = os.path.join(base, *(xcode4_build_dir + dbg + executable))
564    relExec  = os.path.join(base, *(xcode3_build_dir + rel + executable))
565    relExec2 = os.path.join(base, *(xcode4_build_dir + rel + executable))
566    baiExec  = os.path.join(base, *(xcode3_build_dir + bai + executable))
567    baiExec2 = os.path.join(base, *(xcode4_build_dir + bai + executable))
568
569    lldbExec = None
570    if is_exe(dbgExec):
571        lldbExec = dbgExec
572    elif is_exe(dbgExec2):
573        lldbExec = dbgExec2
574    elif is_exe(relExec):
575        lldbExec = relExec
576    elif is_exe(relExec2):
577        lldbExec = relExec2
578    elif is_exe(baiExec):
579        lldbExec = baiExec
580    elif is_exe(baiExec2):
581        lldbExec = baiExec2
582
583    if not lldbExec:
584        lldbExec = which('lldb')
585
586    if not lldbExec:
587        print "The 'lldb' executable cannot be located.  Some of the tests may not be run as a result."
588    else:
589        os.environ["LLDB_EXEC"] = lldbExec
590        #print "The 'lldb' executable path is", lldbExec
591        os.system('%s -v' % lldbExec)
592
593    import subprocess
594    if os.path.isdir(os.path.join(base, '.svn')):
595        pipe = subprocess.Popen(["svn", "info", base], stdout = subprocess.PIPE)
596        svn_info = pipe.stdout.read()
597    elif os.path.isdir(os.path.join(base, '.git')):
598        pipe = subprocess.Popen(["git", "svn", "info", base], stdout = subprocess.PIPE)
599        svn_info = pipe.stdout.read()
600    print svn_info
601
602    global ignore
603
604    # The '-i' option is used to skip looking for lldb.py in the build tree.
605    if ignore:
606        return
607
608    dbgPath  = os.path.join(base, *(xcode3_build_dir + dbg + python_resource_dir))
609    dbgPath2 = os.path.join(base, *(xcode4_build_dir + dbg + python_resource_dir))
610    relPath  = os.path.join(base, *(xcode3_build_dir + rel + python_resource_dir))
611    relPath2 = os.path.join(base, *(xcode4_build_dir + rel + python_resource_dir))
612    baiPath  = os.path.join(base, *(xcode3_build_dir + bai + python_resource_dir))
613    baiPath2 = os.path.join(base, *(xcode4_build_dir + bai + python_resource_dir))
614
615    lldbPath = None
616    if os.path.isfile(os.path.join(dbgPath, 'lldb.py')):
617        lldbPath = dbgPath
618    elif os.path.isfile(os.path.join(dbgPath2, 'lldb.py')):
619        lldbPath = dbgPath2
620    elif os.path.isfile(os.path.join(relPath, 'lldb.py')):
621        lldbPath = relPath
622    elif os.path.isfile(os.path.join(relPath2, 'lldb.py')):
623        lldbPath = relPath2
624    elif os.path.isfile(os.path.join(baiPath, 'lldb.py')):
625        lldbPath = baiPath
626    elif os.path.isfile(os.path.join(baiPath2, 'lldb.py')):
627        lldbPath = baiPath2
628
629    if not lldbPath:
630        print 'This script requires lldb.py to be in either ' + dbgPath + ',',
631        print relPath + ', or ' + baiPath
632        sys.exit(-1)
633
634    # This is to locate the lldb.py module.  Insert it right after sys.path[0].
635    sys.path[1:1] = [lldbPath]
636    if dumpSysPath:
637        print "sys.path:", sys.path
638
639
640def doDelay(delta):
641    """Delaying startup for delta-seconds to facilitate debugger attachment."""
642    def alarm_handler(*args):
643        raise Exception("timeout")
644
645    signal.signal(signal.SIGALRM, alarm_handler)
646    signal.alarm(delta)
647    sys.stdout.write("pid=%d\n" % os.getpid())
648    sys.stdout.write("Enter RET to proceed (or timeout after %d seconds):" %
649                     delta)
650    sys.stdout.flush()
651    try:
652        text = sys.stdin.readline()
653    except:
654        text = ""
655    signal.alarm(0)
656    sys.stdout.write("proceeding...\n")
657    pass
658
659
660def visit(prefix, dir, names):
661    """Visitor function for os.path.walk(path, visit, arg)."""
662
663    global suite
664    global regexp
665    global filters
666    global fs4all
667
668    for name in names:
669        if os.path.isdir(os.path.join(dir, name)):
670            continue
671
672        if '.py' == os.path.splitext(name)[1] and name.startswith(prefix):
673            # Try to match the regexp pattern, if specified.
674            if regexp:
675                import re
676                if re.search(regexp, name):
677                    #print "Filename: '%s' matches pattern: '%s'" % (name, regexp)
678                    pass
679                else:
680                    #print "Filename: '%s' does not match pattern: '%s'" % (name, regexp)
681                    continue
682
683            # We found a match for our test.  Add it to the suite.
684
685            # Update the sys.path first.
686            if not sys.path.count(dir):
687                sys.path.insert(0, dir)
688            base = os.path.splitext(name)[0]
689
690            # Thoroughly check the filterspec against the base module and admit
691            # the (base, filterspec) combination only when it makes sense.
692            filterspec = None
693            for filterspec in filters:
694                # Optimistically set the flag to True.
695                filtered = True
696                module = __import__(base)
697                parts = filterspec.split('.')
698                obj = module
699                for part in parts:
700                    try:
701                        parent, obj = obj, getattr(obj, part)
702                    except AttributeError:
703                        # The filterspec has failed.
704                        filtered = False
705                        break
706
707                # If we reach here, we have a good filterspec.  Add it.
708                if filtered:
709                    break
710
711            # Forgo this module if the (base, filterspec) combo is invalid
712            # and no '-g' option is specified
713            if filters and fs4all and not filtered:
714                continue
715
716            # Add either the filtered test case or the entire test class.
717            if filterspec and filtered:
718                #print "adding filter spec %s to module %s" % (filterspec, module)
719                suite.addTests(
720                    unittest2.defaultTestLoader.loadTestsFromName(filterspec, module))
721            else:
722                # A simple case of just the module name.  Also the failover case
723                # from the filterspec branch when the (base, filterspec) combo
724                # doesn't make sense.
725                suite.addTests(unittest2.defaultTestLoader.loadTestsFromName(base))
726
727
728def lldbLoggings():
729    """Check and do lldb loggings if necessary."""
730
731    # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
732    # defined.  Use ${LLDB_LOG} to specify the log file.
733    ci = lldb.DBG.GetCommandInterpreter()
734    res = lldb.SBCommandReturnObject()
735    if ("LLDB_LOG" in os.environ):
736        if ("LLDB_LOG_OPTION" in os.environ):
737            lldb_log_option = os.environ["LLDB_LOG_OPTION"]
738        else:
739            lldb_log_option = "event process expr state api"
740        ci.HandleCommand(
741            "log enable -n -f " + os.environ["LLDB_LOG"] + " lldb " + lldb_log_option,
742            res)
743        if not res.Succeeded():
744            raise Exception('log enable failed (check LLDB_LOG env variable.')
745    # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
746    # Use ${GDB_REMOTE_LOG} to specify the log file.
747    if ("GDB_REMOTE_LOG" in os.environ):
748        if ("GDB_REMOTE_LOG_OPTION" in os.environ):
749            gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
750        else:
751            gdb_remote_log_option = "packets process"
752        ci.HandleCommand(
753            "log enable -n -f " + os.environ["GDB_REMOTE_LOG"] + " gdb-remote "
754            + gdb_remote_log_option,
755            res)
756        if not res.Succeeded():
757            raise Exception('log enable failed (check GDB_REMOTE_LOG env variable.')
758
759def getMyCommandLine():
760    import subprocess
761    ps = subprocess.Popen(['ps', '-o', "command=CMD", str(os.getpid())], stdout=subprocess.PIPE).communicate()[0]
762    lines = ps.split('\n')
763    cmd_line = lines[1]
764    return cmd_line
765
766# ======================================== #
767#                                          #
768# Execution of the test driver starts here #
769#                                          #
770# ======================================== #
771
772#
773# Start the actions by first parsing the options while setting up the test
774# directories, followed by setting up the search paths for lldb utilities;
775# then, we walk the directory trees and collect the tests into our test suite.
776#
777parseOptionsAndInitTestdirs()
778setupSysPath()
779
780#
781# If '-d' is specified, do a delay of 10 seconds for the debugger to attach.
782#
783if delay:
784    doDelay(10)
785
786#
787# If '-l' is specified, do not skip the long running tests.
788if not skipLongRunningTest:
789    os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO"
790
791#
792# Walk through the testdirs while collecting tests.
793#
794for testdir in testdirs:
795    os.path.walk(testdir, visit, 'Test')
796
797#
798# Now that we have loaded all the test cases, run the whole test suite.
799#
800
801# For the time being, let's bracket the test runner within the
802# lldb.SBDebugger.Initialize()/Terminate() pair.
803import lldb, atexit
804# Update: the act of importing lldb now executes lldb.SBDebugger.Initialize(),
805# there's no need to call it a second time.
806#lldb.SBDebugger.Initialize()
807atexit.register(lambda: lldb.SBDebugger.Terminate())
808
809# Create a singleton SBDebugger in the lldb namespace.
810lldb.DBG = lldb.SBDebugger.Create()
811
812# Put the blacklist in the lldb namespace, to be used by lldb.TestBase.
813lldb.blacklist = blacklist
814
815# Put dont/just_do_python_api_test in the lldb namespace, too.
816lldb.dont_do_python_api_test = dont_do_python_api_test
817lldb.just_do_python_api_test = just_do_python_api_test
818
819# Turn on lldb loggings if necessary.
820lldbLoggings()
821
822# Install the control-c handler.
823unittest2.signals.installHandler()
824
825# If sdir_name is not specified through the '-s sdir_name' option, get a
826# timestamp string and export it as LLDB_SESSION_DIR environment var.  This will
827# be used when/if we want to dump the session info of individual test cases
828# later on.
829#
830# See also TestBase.dumpSessionInfo() in lldbtest.py.
831if not sdir_name:
832    import datetime
833    # The windows platforms don't like ':' in the pathname.
834    timestamp = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
835    sdir_name = timestamp
836os.environ["LLDB_SESSION_DIRNAME"] = os.path.join(os.getcwd(), sdir_name)
837
838sys.stderr.write("\nSession logs for test failures/errors/unexpected successes"
839                 " will go into directory '%s'\n" % sdir_name)
840sys.stderr.write("Command invoked: %s\n" % getMyCommandLine())
841
842if not os.path.isdir(sdir_name):
843    os.mkdir(sdir_name)
844fname = os.path.join(sdir_name, "svn-info")
845with open(fname, "w") as f:
846    print >> f, svn_info
847    print >> f, "Command invoked: %s\n" % getMyCommandLine()
848
849#
850# Invoke the default TextTestRunner to run the test suite, possibly iterating
851# over different configurations.
852#
853
854iterArchs = False
855iterCompilers = False
856
857if not archs and "archs" in config:
858    archs = config["archs"]
859
860if isinstance(archs, list) and len(archs) >= 1:
861    iterArchs = True
862
863if not compilers and "compilers" in config:
864    compilers = config["compilers"]
865
866if isinstance(compilers, list) and len(compilers) >= 1:
867    iterCompilers = True
868
869# Make a shallow copy of sys.path, we need to manipulate the search paths later.
870# This is only necessary if we are relocated and with different configurations.
871if rdir:
872    old_sys_path = sys.path[:]
873# If we iterate on archs or compilers, there is a chance we want to split stderr/stdout.
874if iterArchs or iterCompilers:
875    old_stderr = sys.stderr
876    old_stdout = sys.stdout
877    new_stderr = None
878    new_stdout = None
879
880# Iterating over all possible architecture and compiler combinations.
881for ia in range(len(archs) if iterArchs else 1):
882    archConfig = ""
883    if iterArchs:
884        os.environ["ARCH"] = archs[ia]
885        archConfig = "arch=%s" % archs[ia]
886    for ic in range(len(compilers) if iterCompilers else 1):
887        if iterCompilers:
888            os.environ["CC"] = compilers[ic]
889            configString = "%s compiler=%s" % (archConfig, compilers[ic])
890        else:
891            configString = archConfig
892
893        if iterArchs or iterCompilers:
894            # Translate ' ' to '-' for pathname component.
895            from string import maketrans
896            tbl = maketrans(' ', '-')
897            configPostfix = configString.translate(tbl)
898
899            # Check whether we need to split stderr/stdout into configuration
900            # specific files.
901            if old_stderr.name != '<stderr>' and config.get('split_stderr'):
902                if new_stderr:
903                    new_stderr.close()
904                new_stderr = open("%s.%s" % (old_stderr.name, configPostfix), "w")
905                sys.stderr = new_stderr
906            if old_stdout.name != '<stdout>' and config.get('split_stdout'):
907                if new_stdout:
908                    new_stdout.close()
909                new_stdout = open("%s.%s" % (old_stdout.name, configPostfix), "w")
910                sys.stdout = new_stdout
911
912            # If we specified a relocated directory to run the test suite, do
913            # the extra housekeeping to copy the testdirs to a configStringified
914            # directory and to update sys.path before invoking the test runner.
915            # The purpose is to separate the configuration-specific directories
916            # from each other.
917            if rdir:
918                from shutil import copytree, ignore_patterns
919
920                newrdir = "%s.%s" % (rdir, configPostfix)
921
922                # Copy the tree to a new directory with postfix name configPostfix.
923                copytree(rdir, newrdir, ignore=ignore_patterns('*.pyc', '*.o', '*.d'))
924
925               # Update the LLDB_TEST environment variable to reflect new top
926                # level test directory.
927                #
928                # See also lldbtest.TestBase.setUpClass(cls).
929                if len(testdirs) == 1 and os.path.basename(testdirs[0]) == 'test':
930                    os.environ["LLDB_TEST"] = os.path.join(newrdir, 'test')
931                else:
932                    os.environ["LLDB_TEST"] = newrdir
933
934                # And update the Python search paths for modules.
935                sys.path = [x.replace(rdir, newrdir, 1) for x in old_sys_path]
936
937            # Output the configuration.
938            sys.stderr.write("\nConfiguration: " + configString + "\n")
939
940        #print "sys.stderr name is", sys.stderr.name
941        #print "sys.stdout name is", sys.stdout.name
942
943        # First, write out the number of collected test cases.
944        sys.stderr.write(separator + "\n")
945        sys.stderr.write("Collected %d test%s\n\n"
946                         % (suite.countTestCases(),
947                            suite.countTestCases() != 1 and "s" or ""))
948
949        class LLDBTestResult(unittest2.TextTestResult):
950            """
951            Enforce a singleton pattern to allow introspection of test progress.
952
953            Overwrite addError(), addFailure(), and addExpectedFailure() methods
954            to enable each test instance to track its failure/error status.  It
955            is used in the LLDB test framework to emit detailed trace messages
956            to a log file for easier human inspection of test failres/errors.
957            """
958            __singleton__ = None
959            __ignore_singleton__ = False
960
961            def __init__(self, *args):
962                if not LLDBTestResult.__ignore_singleton__ and LLDBTestResult.__singleton__:
963                    raise Exception("LLDBTestResult instantiated more than once")
964                super(LLDBTestResult, self).__init__(*args)
965                LLDBTestResult.__singleton__ = self
966                # Now put this singleton into the lldb module namespace.
967                lldb.test_result = self
968                # Computes the format string for displaying the counter.
969                global suite
970                counterWidth = len(str(suite.countTestCases()))
971                self.fmt = "%" + str(counterWidth) + "d: "
972                self.indentation = ' ' * (counterWidth + 2)
973                # This counts from 1 .. suite.countTestCases().
974                self.counter = 0
975
976            def getDescription(self, test):
977                doc_first_line = test.shortDescription()
978                if self.descriptions and doc_first_line:
979                    return '\n'.join((str(test), self.indentation + doc_first_line))
980                else:
981                    return str(test)
982
983            def startTest(self, test):
984                self.counter += 1
985                if self.showAll:
986                    self.stream.write(self.fmt % self.counter)
987                super(LLDBTestResult, self).startTest(test)
988
989            def addError(self, test, err):
990                global sdir_has_content
991                sdir_has_content = True
992                super(LLDBTestResult, self).addError(test, err)
993                method = getattr(test, "markError", None)
994                if method:
995                    method()
996
997            def addFailure(self, test, err):
998                global sdir_has_content
999                sdir_has_content = True
1000                super(LLDBTestResult, self).addFailure(test, err)
1001                method = getattr(test, "markFailure", None)
1002                if method:
1003                    method()
1004
1005            def addExpectedFailure(self, test, err):
1006                global sdir_has_content
1007                sdir_has_content = True
1008                super(LLDBTestResult, self).addExpectedFailure(test, err)
1009                method = getattr(test, "markExpectedFailure", None)
1010                if method:
1011                    method()
1012
1013            def addUnexpectedSuccess(self, test):
1014                global sdir_has_content
1015                sdir_has_content = True
1016                super(LLDBTestResult, self).addUnexpectedSuccess(test)
1017                method = getattr(test, "markUnexpectedSuccess", None)
1018                if method:
1019                    method()
1020
1021        # Invoke the test runner.
1022        if count == 1:
1023            result = unittest2.TextTestRunner(stream=sys.stderr,
1024                                              verbosity=verbose,
1025                                              failfast=failfast,
1026                                              resultclass=LLDBTestResult).run(suite)
1027        else:
1028            # We are invoking the same test suite more than once.  In this case,
1029            # mark __ignore_singleton__ flag as True so the signleton pattern is
1030            # not enforced.
1031            LLDBTestResult.__ignore_singleton__ = True
1032            for i in range(count):
1033                result = unittest2.TextTestRunner(stream=sys.stderr,
1034                                                  verbosity=verbose,
1035                                                  failfast=failfast,
1036                                                  resultclass=LLDBTestResult).run(suite)
1037
1038
1039if sdir_has_content:
1040    sys.stderr.write("Session logs for test failures/errors/unexpected successes"
1041                     " can be found in directory '%s'\n" % sdir_name)
1042
1043# Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
1044# This should not be necessary now.
1045if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
1046    import subprocess
1047    print "Terminating Test suite..."
1048    subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
1049
1050# Exiting.
1051sys.exit(not result.wasSuccessful)
1052