blob: 804b67693720ca23a14f1f12d86ed039c2c54894 [file] [log] [blame]
Johnny Chen9707bb62010-06-25 21:14:08 +00001#!/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.
Johnny Chen872aee12010-09-16 15:44:23 +000015
16Type:
17
18./dotest.py -h
19
20for available options.
Johnny Chen9707bb62010-06-25 21:14:08 +000021"""
22
Johnny Chen91960d32010-09-08 20:56:16 +000023import os, signal, sys, time
Johnny Chen75e28f92010-08-05 23:42:46 +000024import unittest2
Johnny Chen9707bb62010-06-25 21:14:08 +000025
Johnny Chen877c7e42010-08-07 00:16:07 +000026class _WritelnDecorator(object):
27 """Used to decorate file-like objects with a handy 'writeln' method"""
28 def __init__(self,stream):
29 self.stream = stream
30
31 def __getattr__(self, attr):
32 if attr in ('stream', '__getstate__'):
33 raise AttributeError(attr)
34 return getattr(self.stream,attr)
35
36 def writeln(self, arg=None):
37 if arg:
38 self.write(arg)
39 self.write('\n') # text-mode streams translate to \r\n if needed
40
Johnny Chen9707bb62010-06-25 21:14:08 +000041#
42# Global variables:
43#
44
45# The test suite.
Johnny Chen75e28f92010-08-05 23:42:46 +000046suite = unittest2.TestSuite()
Johnny Chen9707bb62010-06-25 21:14:08 +000047
Johnny Chen9fdb0a92010-09-18 00:16:47 +000048# The config file is optional.
49configFile = None
50
Johnny Chenb40056b2010-09-21 00:09:27 +000051# The dictionary as a result of sourcing configFile.
52config = {}
53
Johnny Chen91960d32010-09-08 20:56:16 +000054# Delay startup in order for the debugger to attach.
55delay = False
56
Johnny Chenb62436b2010-10-06 20:40:56 +000057# The filter (testcase.testmethod) used to admit tests into our test suite.
58filterspec = None
59
60# If '-g' is specified, the filterspec must be consulted for each test module, default to False.
61fs4all = False
62
Johnny Chenaf149a02010-09-16 17:11:30 +000063# Ignore the build search path relative to this script to locate the lldb.py module.
64ignore = False
65
Johnny Chen41998192010-10-01 22:59:49 +000066# By default, we skip long running test case. Use "-l" option to override.
67skipLongRunningTest = True
68
Johnny Chen7c52ff12010-09-27 23:29:54 +000069# The regular expression pattern to match against eligible filenames as our test cases.
70regexp = None
71
Johnny Chen9707bb62010-06-25 21:14:08 +000072# Default verbosity is 0.
73verbose = 0
74
75# By default, search from the current working directory.
76testdirs = [ os.getcwd() ]
77
Johnny Chen877c7e42010-08-07 00:16:07 +000078# Separator string.
79separator = '-' * 70
80
Johnny Chen9707bb62010-06-25 21:14:08 +000081
82def usage():
83 print """
84Usage: dotest.py [option] [args]
85where options:
86-h : print this help message and exit (also --help)
Johnny Chen9fdb0a92010-09-18 00:16:47 +000087-c : read a config file specified after this option
Johnny Chenb40056b2010-09-21 00:09:27 +000088 (see also lldb-trunk/example/test/usage-config)
Johnny Chen91960d32010-09-08 20:56:16 +000089-d : delay startup for 10 seconds (in order for the debugger to attach)
Johnny Chenb62436b2010-10-06 20:40:56 +000090-f : specify a filter to admit tests into the test suite
91 e.g., -f 'ClassTypesTestCase.test_with_dwarf_and_python_api'
92-g : if specified, only the modules with the corect filter will be run
93 it has no effect if no '-f' option is present
94 '-f filterspec -g' can be used with '-p filename-regexp' to select only
95 the testfile.testclass.testmethod to run
Johnny Chenaf149a02010-09-16 17:11:30 +000096-i : ignore (don't bailout) if 'lldb.py' module cannot be located in the build
97 tree relative to this script; use PYTHONPATH to locate the module
Johnny Chen41998192010-10-01 22:59:49 +000098-l : don't skip long running test
Johnny Chen7c52ff12010-09-27 23:29:54 +000099-p : specify a regexp filename pattern for inclusion in the test suite
Johnny Chend0c24b22010-08-23 17:10:44 +0000100-t : trace lldb command execution and result
Johnny Chen9707bb62010-06-25 21:14:08 +0000101-v : do verbose mode of unittest framework
102
103and:
104args : specify a list of directory names to search for python Test*.py scripts
105 if empty, search from the curret working directory, instead
Johnny Chen58f93922010-06-29 23:10:39 +0000106
107Running of this script also sets up the LLDB_TEST environment variable so that
Johnny Chenaf149a02010-09-16 17:11:30 +0000108individual test cases can locate their supporting files correctly. The script
109tries to set up Python's search paths for modules by looking at the build tree
110relative to this script. See also the '-i' option.
Johnny Chenfde69bc2010-09-14 22:01:40 +0000111
112Environment variables related to loggings:
113
114o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem
115 with a default option of 'event process' if LLDB_LOG_OPTION is not defined.
116
117o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the
118 'process.gdb-remote' subsystem with a default option of 'packets' if
119 GDB_REMOTE_LOG_OPTION is not defined.
Johnny Chen9707bb62010-06-25 21:14:08 +0000120"""
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000121 sys.exit(0)
Johnny Chen9707bb62010-06-25 21:14:08 +0000122
123
Johnny Chenaf149a02010-09-16 17:11:30 +0000124def parseOptionsAndInitTestdirs():
125 """Initialize the list of directories containing our unittest scripts.
126
127 '-h/--help as the first option prints out usage info and exit the program.
128 """
129
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000130 global configFile
Johnny Chenaf149a02010-09-16 17:11:30 +0000131 global delay
Johnny Chenb62436b2010-10-06 20:40:56 +0000132 global filterspec
133 global fs4all
Johnny Chen7c52ff12010-09-27 23:29:54 +0000134 global ignore
Johnny Chen41998192010-10-01 22:59:49 +0000135 global skipLongRunningTest
Johnny Chen7c52ff12010-09-27 23:29:54 +0000136 global regexp
Johnny Chenaf149a02010-09-16 17:11:30 +0000137 global verbose
138 global testdirs
139
140 if len(sys.argv) == 1:
141 return
142
143 # Process possible trace and/or verbose flag, among other things.
144 index = 1
145 for i in range(1, len(sys.argv)):
146 if not sys.argv[index].startswith('-'):
147 # End of option processing.
148 break
149
150 if sys.argv[index].find('-h') != -1:
151 usage()
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000152 elif sys.argv[index].startswith('-c'):
153 # Increment by 1 to fetch the config file name option argument.
154 index += 1
155 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
156 usage()
157 configFile = sys.argv[index]
158 if not os.path.isfile(configFile):
159 print "Config file:", configFile, "does not exist!"
160 usage()
161 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000162 elif sys.argv[index].startswith('-d'):
163 delay = True
164 index += 1
Johnny Chenb62436b2010-10-06 20:40:56 +0000165 elif sys.argv[index].startswith('-f'):
166 # Increment by 1 to fetch the filter spec.
167 index += 1
168 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
169 usage()
170 filterspec = sys.argv[index]
171 index += 1
172 elif sys.argv[index].startswith('-g'):
173 fs4all = True
174 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000175 elif sys.argv[index].startswith('-i'):
176 ignore = True
177 index += 1
Johnny Chen41998192010-10-01 22:59:49 +0000178 elif sys.argv[index].startswith('-l'):
179 skipLongRunningTest = False
180 index += 1
Johnny Chen7c52ff12010-09-27 23:29:54 +0000181 elif sys.argv[index].startswith('-p'):
182 # Increment by 1 to fetch the reg exp pattern argument.
183 index += 1
184 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
185 usage()
186 regexp = sys.argv[index]
187 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000188 elif sys.argv[index].startswith('-t'):
189 os.environ["LLDB_COMMAND_TRACE"] = "YES"
190 index += 1
191 elif sys.argv[index].startswith('-v'):
192 verbose = 2
193 index += 1
194 else:
195 print "Unknown option: ", sys.argv[index]
196 usage()
Johnny Chenaf149a02010-09-16 17:11:30 +0000197
198 # Gather all the dirs passed on the command line.
199 if len(sys.argv) > index:
200 testdirs = map(os.path.abspath, sys.argv[index:])
201
Johnny Chenb40056b2010-09-21 00:09:27 +0000202 # Source the configFile if specified.
203 # The side effect, if any, will be felt from this point on. An example
204 # config file may be these simple two lines:
205 #
206 # sys.stderr = open("/tmp/lldbtest-stderr", "w")
207 # sys.stdout = open("/tmp/lldbtest-stdout", "w")
208 #
209 # which will reassign the two file objects to sys.stderr and sys.stdout,
210 # respectively.
211 #
212 # See also lldb-trunk/example/test/usage-config.
213 global config
214 if configFile:
215 # Pass config (a dictionary) as the locals namespace for side-effect.
216 execfile(configFile, globals(), config)
217 #print "config:", config
218 #print "sys.stderr:", sys.stderr
219 #print "sys.stdout:", sys.stdout
220
Johnny Chenaf149a02010-09-16 17:11:30 +0000221
Johnny Chen9707bb62010-06-25 21:14:08 +0000222def setupSysPath():
223 """Add LLDB.framework/Resources/Python to the search paths for modules."""
224
225 # Get the directory containing the current script.
Johnny Chena1affab2010-07-03 03:41:59 +0000226 scriptPath = sys.path[0]
227 if not scriptPath.endswith('test'):
Johnny Chen9707bb62010-06-25 21:14:08 +0000228 print "This script expects to reside in lldb's test directory."
229 sys.exit(-1)
230
Johnny Chena1affab2010-07-03 03:41:59 +0000231 os.environ["LLDB_TEST"] = scriptPath
Johnny Chen9de4ede2010-08-31 17:42:54 +0000232 pluginPath = os.path.join(scriptPath, 'plugins')
Johnny Chen58f93922010-06-29 23:10:39 +0000233
Johnny Chenaf149a02010-09-16 17:11:30 +0000234 # Append script dir and plugin dir to the sys.path.
235 sys.path.append(scriptPath)
236 sys.path.append(pluginPath)
237
238 global ignore
239
240 # The '-i' option is used to skip looking for lldb.py in the build tree.
241 if ignore:
242 return
243
Johnny Chena1affab2010-07-03 03:41:59 +0000244 base = os.path.abspath(os.path.join(scriptPath, os.pardir))
Johnny Chen9707bb62010-06-25 21:14:08 +0000245 dbgPath = os.path.join(base, 'build', 'Debug', 'LLDB.framework',
246 'Resources', 'Python')
247 relPath = os.path.join(base, 'build', 'Release', 'LLDB.framework',
248 'Resources', 'Python')
Johnny Chenc202c462010-09-15 18:11:19 +0000249 baiPath = os.path.join(base, 'build', 'BuildAndIntegration',
250 'LLDB.framework', 'Resources', 'Python')
Johnny Chen9707bb62010-06-25 21:14:08 +0000251
252 lldbPath = None
253 if os.path.isfile(os.path.join(dbgPath, 'lldb.py')):
254 lldbPath = dbgPath
255 elif os.path.isfile(os.path.join(relPath, 'lldb.py')):
256 lldbPath = relPath
Johnny Chenc202c462010-09-15 18:11:19 +0000257 elif os.path.isfile(os.path.join(baiPath, 'lldb.py')):
258 lldbPath = baiPath
Johnny Chen9707bb62010-06-25 21:14:08 +0000259
260 if not lldbPath:
Johnny Chenc202c462010-09-15 18:11:19 +0000261 print 'This script requires lldb.py to be in either ' + dbgPath + ',',
262 print relPath + ', or ' + baiPath
Johnny Chen9707bb62010-06-25 21:14:08 +0000263 sys.exit(-1)
264
Johnny Chenaf149a02010-09-16 17:11:30 +0000265 # This is to locate the lldb.py module. Insert it right after sys.path[0].
266 sys.path[1:1] = [lldbPath]
Johnny Chen9707bb62010-06-25 21:14:08 +0000267
Johnny Chen9707bb62010-06-25 21:14:08 +0000268
Johnny Chencd0279d2010-09-20 18:07:50 +0000269def doDelay(delta):
270 """Delaying startup for delta-seconds to facilitate debugger attachment."""
271 def alarm_handler(*args):
272 raise Exception("timeout")
273
274 signal.signal(signal.SIGALRM, alarm_handler)
275 signal.alarm(delta)
276 sys.stdout.write("pid=%d\n" % os.getpid())
277 sys.stdout.write("Enter RET to proceed (or timeout after %d seconds):" %
278 delta)
279 sys.stdout.flush()
280 try:
281 text = sys.stdin.readline()
282 except:
283 text = ""
284 signal.alarm(0)
285 sys.stdout.write("proceeding...\n")
286 pass
287
288
Johnny Chen9707bb62010-06-25 21:14:08 +0000289def visit(prefix, dir, names):
290 """Visitor function for os.path.walk(path, visit, arg)."""
291
292 global suite
Johnny Chen7c52ff12010-09-27 23:29:54 +0000293 global regexp
Johnny Chenb62436b2010-10-06 20:40:56 +0000294 global filterspec
295 global fs4all
Johnny Chen9707bb62010-06-25 21:14:08 +0000296
297 for name in names:
298 if os.path.isdir(os.path.join(dir, name)):
299 continue
300
301 if '.py' == os.path.splitext(name)[1] and name.startswith(prefix):
Johnny Chen7c52ff12010-09-27 23:29:54 +0000302 # Try to match the regexp pattern, if specified.
303 if regexp:
304 import re
305 if re.search(regexp, name):
306 #print "Filename: '%s' matches pattern: '%s'" % (name, regexp)
307 pass
308 else:
309 #print "Filename: '%s' does not match pattern: '%s'" % (name, regexp)
310 continue
311
312 # We found a match for our test case. Add it to the suite.
Johnny Chena85d7ee2010-06-26 00:19:32 +0000313 if not sys.path.count(dir):
314 sys.path.append(dir)
Johnny Chen9707bb62010-06-25 21:14:08 +0000315 base = os.path.splitext(name)[0]
Johnny Chenb62436b2010-10-06 20:40:56 +0000316
317 # Thoroughly check the filterspec against the base module and admit
318 # the (base, filterspec) combination only when it makes sense.
319 if filterspec:
320 # Optimistically set the flag to True.
321 filtered = True
322 module = __import__(base)
323 parts = filterspec.split('.')
324 obj = module
325 for part in parts:
326 try:
327 parent, obj = obj, getattr(obj, part)
328 except AttributeError:
329 # The filterspec has failed.
330 filtered = False
331 break
332 # Forgo this module if the (base, filterspec) combo is invalid
333 # and the '-g' option is present.
334 if fs4all and not filtered:
335 continue
336
337 if filterspec and filtered:
338 suite.addTests(
339 unittest2.defaultTestLoader.loadTestsFromName(filterspec, module))
340 else:
341 # A simple case of just the module name. Also the failover case
342 # from the filterspec branch when the (base, filterspec) combo
343 # doesn't make sense.
344 suite.addTests(unittest2.defaultTestLoader.loadTestsFromName(base))
Johnny Chen9707bb62010-06-25 21:14:08 +0000345
346
Johnny Chencd0279d2010-09-20 18:07:50 +0000347def lldbLoggings():
348 """Check and do lldb loggings if necessary."""
349
350 # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
351 # defined. Use ${LLDB_LOG} to specify the log file.
352 ci = lldb.DBG.GetCommandInterpreter()
353 res = lldb.SBCommandReturnObject()
354 if ("LLDB_LOG" in os.environ):
355 if ("LLDB_LOG_OPTION" in os.environ):
356 lldb_log_option = os.environ["LLDB_LOG_OPTION"]
357 else:
358 lldb_log_option = "event process"
359 ci.HandleCommand(
360 "log enable -f " + os.environ["LLDB_LOG"] + " lldb " + lldb_log_option,
361 res)
362 if not res.Succeeded():
363 raise Exception('log enable failed (check LLDB_LOG env variable.')
364 # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
365 # Use ${GDB_REMOTE_LOG} to specify the log file.
366 if ("GDB_REMOTE_LOG" in os.environ):
367 if ("GDB_REMOTE_LOG_OPTION" in os.environ):
368 gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
369 else:
370 gdb_remote_log_option = "packets"
371 ci.HandleCommand(
372 "log enable -f " + os.environ["GDB_REMOTE_LOG"] + " process.gdb-remote "
373 + gdb_remote_log_option,
374 res)
375 if not res.Succeeded():
376 raise Exception('log enable failed (check GDB_REMOTE_LOG env variable.')
377
378
379############################################
380# #
381# Execution of the test driver starts here #
382# #
383############################################
384
Johnny Chen9707bb62010-06-25 21:14:08 +0000385#
Johnny Chenaf149a02010-09-16 17:11:30 +0000386# Start the actions by first parsing the options while setting up the test
387# directories, followed by setting up the search paths for lldb utilities;
388# then, we walk the directory trees and collect the tests into our test suite.
Johnny Chen9707bb62010-06-25 21:14:08 +0000389#
Johnny Chenaf149a02010-09-16 17:11:30 +0000390parseOptionsAndInitTestdirs()
Johnny Chen9707bb62010-06-25 21:14:08 +0000391setupSysPath()
Johnny Chen91960d32010-09-08 20:56:16 +0000392
393#
394# If '-d' is specified, do a delay of 10 seconds for the debugger to attach.
395#
396if delay:
Johnny Chencd0279d2010-09-20 18:07:50 +0000397 doDelay(10)
Johnny Chen91960d32010-09-08 20:56:16 +0000398
Johnny Chen49f2f7a2010-09-20 17:25:45 +0000399#
Johnny Chen41998192010-10-01 22:59:49 +0000400# If '-l' is specified, do not skip the long running tests.
401if not skipLongRunningTest:
402 os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO"
403
404#
Johnny Chen49f2f7a2010-09-20 17:25:45 +0000405# Walk through the testdirs while collecting test cases.
406#
Johnny Chen9707bb62010-06-25 21:14:08 +0000407for testdir in testdirs:
408 os.path.walk(testdir, visit, 'Test')
409
Johnny Chenb40056b2010-09-21 00:09:27 +0000410#
Johnny Chen9707bb62010-06-25 21:14:08 +0000411# Now that we have loaded all the test cases, run the whole test suite.
Johnny Chenb40056b2010-09-21 00:09:27 +0000412#
Johnny Chencd0279d2010-09-20 18:07:50 +0000413
414# First, write out the number of collected test cases.
Johnny Chenb40056b2010-09-21 00:09:27 +0000415sys.stderr.write(separator + "\n")
416sys.stderr.write("Collected %d test%s\n\n"
417 % (suite.countTestCases(),
418 suite.countTestCases() != 1 and "s" or ""))
Johnny Chen1bfbd412010-06-29 19:44:16 +0000419
420# For the time being, let's bracket the test runner within the
421# lldb.SBDebugger.Initialize()/Terminate() pair.
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000422import lldb, atexit
Johnny Chen1bfbd412010-06-29 19:44:16 +0000423lldb.SBDebugger.Initialize()
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000424atexit.register(lambda: lldb.SBDebugger.Terminate())
Johnny Chen1bfbd412010-06-29 19:44:16 +0000425
Johnny Chen909e5a62010-07-01 22:52:57 +0000426# Create a singleton SBDebugger in the lldb namespace.
427lldb.DBG = lldb.SBDebugger.Create()
428
Johnny Chencd0279d2010-09-20 18:07:50 +0000429# Turn on lldb loggings if necessary.
430lldbLoggings()
Johnny Chen909e5a62010-07-01 22:52:57 +0000431
Johnny Chen7987ac92010-08-09 20:40:52 +0000432# Install the control-c handler.
433unittest2.signals.installHandler()
434
Johnny Chenb40056b2010-09-21 00:09:27 +0000435#
436# Invoke the default TextTestRunner to run the test suite, possibly iterating
437# over different configurations.
438#
439
Johnny Chenb40056b2010-09-21 00:09:27 +0000440iterArchs = False
Johnny Chenf032d902010-09-21 00:16:09 +0000441iterCompilers = False
Johnny Chenb40056b2010-09-21 00:09:27 +0000442
443from types import *
444if "archs" in config:
445 archs = config["archs"]
446 if type(archs) is ListType and len(archs) >= 1:
447 iterArchs = True
448if "compilers" in config:
449 compilers = config["compilers"]
450 if type(compilers) is ListType and len(compilers) >= 1:
451 iterCompilers = True
452
453for ia in range(len(archs) if iterArchs else 1):
454 archConfig = ""
455 if iterArchs:
Johnny Chen18a921f2010-09-30 17:11:58 +0000456 os.environ["ARCH"] = archs[ia]
Johnny Chenb40056b2010-09-21 00:09:27 +0000457 archConfig = "arch=%s" % archs[ia]
458 for ic in range(len(compilers) if iterCompilers else 1):
459 if iterCompilers:
Johnny Chen18a921f2010-09-30 17:11:58 +0000460 os.environ["CC"] = compilers[ic]
Johnny Chenb40056b2010-09-21 00:09:27 +0000461 configString = "%s compiler=%s" % (archConfig, compilers[ic])
462 else:
463 configString = archConfig
464
465 # Invoke the test runner.
466 if iterArchs or iterCompilers:
467 sys.stderr.write("\nConfiguration: " + configString + "\n")
468 result = unittest2.TextTestRunner(stream=sys.stderr, verbosity=verbose).run(suite)
469
Johnny Chen1bfbd412010-06-29 19:44:16 +0000470
Johnny Chencd0279d2010-09-20 18:07:50 +0000471# Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
472# This should not be necessary now.
Johnny Chen83f6e512010-08-13 22:58:44 +0000473if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
474 import subprocess
475 print "Terminating Test suite..."
476 subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
477
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000478# Exiting.
479sys.exit(not result.wasSuccessful)