blob: 25a7e450b50dec2ebe45fe4abfa62af824e56243 [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
Johnny Chene47649c2010-10-07 02:04:14 +0000102-w : insert some wait time (currently 0.5 sec) between consecutive test cases
Johnny Chen9707bb62010-06-25 21:14:08 +0000103
104and:
105args : specify a list of directory names to search for python Test*.py scripts
106 if empty, search from the curret working directory, instead
Johnny Chen58f93922010-06-29 23:10:39 +0000107
108Running of this script also sets up the LLDB_TEST environment variable so that
Johnny Chenaf149a02010-09-16 17:11:30 +0000109individual test cases can locate their supporting files correctly. The script
110tries to set up Python's search paths for modules by looking at the build tree
111relative to this script. See also the '-i' option.
Johnny Chenfde69bc2010-09-14 22:01:40 +0000112
113Environment variables related to loggings:
114
115o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem
116 with a default option of 'event process' if LLDB_LOG_OPTION is not defined.
117
118o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the
119 'process.gdb-remote' subsystem with a default option of 'packets' if
120 GDB_REMOTE_LOG_OPTION is not defined.
Johnny Chen9707bb62010-06-25 21:14:08 +0000121"""
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000122 sys.exit(0)
Johnny Chen9707bb62010-06-25 21:14:08 +0000123
124
Johnny Chenaf149a02010-09-16 17:11:30 +0000125def parseOptionsAndInitTestdirs():
126 """Initialize the list of directories containing our unittest scripts.
127
128 '-h/--help as the first option prints out usage info and exit the program.
129 """
130
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000131 global configFile
Johnny Chenaf149a02010-09-16 17:11:30 +0000132 global delay
Johnny Chenb62436b2010-10-06 20:40:56 +0000133 global filterspec
134 global fs4all
Johnny Chen7c52ff12010-09-27 23:29:54 +0000135 global ignore
Johnny Chen41998192010-10-01 22:59:49 +0000136 global skipLongRunningTest
Johnny Chen7c52ff12010-09-27 23:29:54 +0000137 global regexp
Johnny Chenaf149a02010-09-16 17:11:30 +0000138 global verbose
139 global testdirs
140
141 if len(sys.argv) == 1:
142 return
143
144 # Process possible trace and/or verbose flag, among other things.
145 index = 1
146 for i in range(1, len(sys.argv)):
147 if not sys.argv[index].startswith('-'):
148 # End of option processing.
149 break
150
151 if sys.argv[index].find('-h') != -1:
152 usage()
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000153 elif sys.argv[index].startswith('-c'):
154 # Increment by 1 to fetch the config file name option argument.
155 index += 1
156 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
157 usage()
158 configFile = sys.argv[index]
159 if not os.path.isfile(configFile):
160 print "Config file:", configFile, "does not exist!"
161 usage()
162 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000163 elif sys.argv[index].startswith('-d'):
164 delay = True
165 index += 1
Johnny Chenb62436b2010-10-06 20:40:56 +0000166 elif sys.argv[index].startswith('-f'):
167 # Increment by 1 to fetch the filter spec.
168 index += 1
169 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
170 usage()
171 filterspec = sys.argv[index]
172 index += 1
173 elif sys.argv[index].startswith('-g'):
174 fs4all = True
175 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000176 elif sys.argv[index].startswith('-i'):
177 ignore = True
178 index += 1
Johnny Chen41998192010-10-01 22:59:49 +0000179 elif sys.argv[index].startswith('-l'):
180 skipLongRunningTest = False
181 index += 1
Johnny Chen7c52ff12010-09-27 23:29:54 +0000182 elif sys.argv[index].startswith('-p'):
183 # Increment by 1 to fetch the reg exp pattern argument.
184 index += 1
185 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
186 usage()
187 regexp = sys.argv[index]
188 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000189 elif sys.argv[index].startswith('-t'):
190 os.environ["LLDB_COMMAND_TRACE"] = "YES"
191 index += 1
192 elif sys.argv[index].startswith('-v'):
193 verbose = 2
194 index += 1
Johnny Chene47649c2010-10-07 02:04:14 +0000195 elif sys.argv[index].startswith('-w'):
196 os.environ["LLDB_WAIT_BETWEEN_TEST_CASES"] = 'YES'
197 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000198 else:
199 print "Unknown option: ", sys.argv[index]
200 usage()
Johnny Chenaf149a02010-09-16 17:11:30 +0000201
202 # Gather all the dirs passed on the command line.
203 if len(sys.argv) > index:
204 testdirs = map(os.path.abspath, sys.argv[index:])
205
Johnny Chenb40056b2010-09-21 00:09:27 +0000206 # Source the configFile if specified.
207 # The side effect, if any, will be felt from this point on. An example
208 # config file may be these simple two lines:
209 #
210 # sys.stderr = open("/tmp/lldbtest-stderr", "w")
211 # sys.stdout = open("/tmp/lldbtest-stdout", "w")
212 #
213 # which will reassign the two file objects to sys.stderr and sys.stdout,
214 # respectively.
215 #
216 # See also lldb-trunk/example/test/usage-config.
217 global config
218 if configFile:
219 # Pass config (a dictionary) as the locals namespace for side-effect.
220 execfile(configFile, globals(), config)
221 #print "config:", config
222 #print "sys.stderr:", sys.stderr
223 #print "sys.stdout:", sys.stdout
224
Johnny Chenaf149a02010-09-16 17:11:30 +0000225
Johnny Chen9707bb62010-06-25 21:14:08 +0000226def setupSysPath():
227 """Add LLDB.framework/Resources/Python to the search paths for modules."""
228
229 # Get the directory containing the current script.
Johnny Chena1affab2010-07-03 03:41:59 +0000230 scriptPath = sys.path[0]
231 if not scriptPath.endswith('test'):
Johnny Chen9707bb62010-06-25 21:14:08 +0000232 print "This script expects to reside in lldb's test directory."
233 sys.exit(-1)
234
Johnny Chena1affab2010-07-03 03:41:59 +0000235 os.environ["LLDB_TEST"] = scriptPath
Johnny Chen9de4ede2010-08-31 17:42:54 +0000236 pluginPath = os.path.join(scriptPath, 'plugins')
Johnny Chen58f93922010-06-29 23:10:39 +0000237
Johnny Chenaf149a02010-09-16 17:11:30 +0000238 # Append script dir and plugin dir to the sys.path.
239 sys.path.append(scriptPath)
240 sys.path.append(pluginPath)
241
242 global ignore
243
244 # The '-i' option is used to skip looking for lldb.py in the build tree.
245 if ignore:
246 return
247
Johnny Chena1affab2010-07-03 03:41:59 +0000248 base = os.path.abspath(os.path.join(scriptPath, os.pardir))
Johnny Chen9707bb62010-06-25 21:14:08 +0000249 dbgPath = os.path.join(base, 'build', 'Debug', 'LLDB.framework',
250 'Resources', 'Python')
251 relPath = os.path.join(base, 'build', 'Release', 'LLDB.framework',
252 'Resources', 'Python')
Johnny Chenc202c462010-09-15 18:11:19 +0000253 baiPath = os.path.join(base, 'build', 'BuildAndIntegration',
254 'LLDB.framework', 'Resources', 'Python')
Johnny Chen9707bb62010-06-25 21:14:08 +0000255
256 lldbPath = None
257 if os.path.isfile(os.path.join(dbgPath, 'lldb.py')):
258 lldbPath = dbgPath
259 elif os.path.isfile(os.path.join(relPath, 'lldb.py')):
260 lldbPath = relPath
Johnny Chenc202c462010-09-15 18:11:19 +0000261 elif os.path.isfile(os.path.join(baiPath, 'lldb.py')):
262 lldbPath = baiPath
Johnny Chen9707bb62010-06-25 21:14:08 +0000263
264 if not lldbPath:
Johnny Chenc202c462010-09-15 18:11:19 +0000265 print 'This script requires lldb.py to be in either ' + dbgPath + ',',
266 print relPath + ', or ' + baiPath
Johnny Chen9707bb62010-06-25 21:14:08 +0000267 sys.exit(-1)
268
Johnny Chenaf149a02010-09-16 17:11:30 +0000269 # This is to locate the lldb.py module. Insert it right after sys.path[0].
270 sys.path[1:1] = [lldbPath]
Johnny Chen9707bb62010-06-25 21:14:08 +0000271
Johnny Chen9707bb62010-06-25 21:14:08 +0000272
Johnny Chencd0279d2010-09-20 18:07:50 +0000273def doDelay(delta):
274 """Delaying startup for delta-seconds to facilitate debugger attachment."""
275 def alarm_handler(*args):
276 raise Exception("timeout")
277
278 signal.signal(signal.SIGALRM, alarm_handler)
279 signal.alarm(delta)
280 sys.stdout.write("pid=%d\n" % os.getpid())
281 sys.stdout.write("Enter RET to proceed (or timeout after %d seconds):" %
282 delta)
283 sys.stdout.flush()
284 try:
285 text = sys.stdin.readline()
286 except:
287 text = ""
288 signal.alarm(0)
289 sys.stdout.write("proceeding...\n")
290 pass
291
292
Johnny Chen9707bb62010-06-25 21:14:08 +0000293def visit(prefix, dir, names):
294 """Visitor function for os.path.walk(path, visit, arg)."""
295
296 global suite
Johnny Chen7c52ff12010-09-27 23:29:54 +0000297 global regexp
Johnny Chenb62436b2010-10-06 20:40:56 +0000298 global filterspec
299 global fs4all
Johnny Chen9707bb62010-06-25 21:14:08 +0000300
301 for name in names:
302 if os.path.isdir(os.path.join(dir, name)):
303 continue
304
305 if '.py' == os.path.splitext(name)[1] and name.startswith(prefix):
Johnny Chen7c52ff12010-09-27 23:29:54 +0000306 # Try to match the regexp pattern, if specified.
307 if regexp:
308 import re
309 if re.search(regexp, name):
310 #print "Filename: '%s' matches pattern: '%s'" % (name, regexp)
311 pass
312 else:
313 #print "Filename: '%s' does not match pattern: '%s'" % (name, regexp)
314 continue
315
316 # We found a match for our test case. Add it to the suite.
Johnny Chena85d7ee2010-06-26 00:19:32 +0000317 if not sys.path.count(dir):
318 sys.path.append(dir)
Johnny Chen9707bb62010-06-25 21:14:08 +0000319 base = os.path.splitext(name)[0]
Johnny Chenb62436b2010-10-06 20:40:56 +0000320
321 # Thoroughly check the filterspec against the base module and admit
322 # the (base, filterspec) combination only when it makes sense.
323 if filterspec:
324 # Optimistically set the flag to True.
325 filtered = True
326 module = __import__(base)
327 parts = filterspec.split('.')
328 obj = module
329 for part in parts:
330 try:
331 parent, obj = obj, getattr(obj, part)
332 except AttributeError:
333 # The filterspec has failed.
334 filtered = False
335 break
336 # Forgo this module if the (base, filterspec) combo is invalid
337 # and the '-g' option is present.
338 if fs4all and not filtered:
339 continue
340
341 if filterspec and filtered:
342 suite.addTests(
343 unittest2.defaultTestLoader.loadTestsFromName(filterspec, module))
344 else:
345 # A simple case of just the module name. Also the failover case
346 # from the filterspec branch when the (base, filterspec) combo
347 # doesn't make sense.
348 suite.addTests(unittest2.defaultTestLoader.loadTestsFromName(base))
Johnny Chen9707bb62010-06-25 21:14:08 +0000349
350
Johnny Chencd0279d2010-09-20 18:07:50 +0000351def lldbLoggings():
352 """Check and do lldb loggings if necessary."""
353
354 # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
355 # defined. Use ${LLDB_LOG} to specify the log file.
356 ci = lldb.DBG.GetCommandInterpreter()
357 res = lldb.SBCommandReturnObject()
358 if ("LLDB_LOG" in os.environ):
359 if ("LLDB_LOG_OPTION" in os.environ):
360 lldb_log_option = os.environ["LLDB_LOG_OPTION"]
361 else:
362 lldb_log_option = "event process"
363 ci.HandleCommand(
364 "log enable -f " + os.environ["LLDB_LOG"] + " lldb " + lldb_log_option,
365 res)
366 if not res.Succeeded():
367 raise Exception('log enable failed (check LLDB_LOG env variable.')
368 # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
369 # Use ${GDB_REMOTE_LOG} to specify the log file.
370 if ("GDB_REMOTE_LOG" in os.environ):
371 if ("GDB_REMOTE_LOG_OPTION" in os.environ):
372 gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
373 else:
374 gdb_remote_log_option = "packets"
375 ci.HandleCommand(
376 "log enable -f " + os.environ["GDB_REMOTE_LOG"] + " process.gdb-remote "
377 + gdb_remote_log_option,
378 res)
379 if not res.Succeeded():
380 raise Exception('log enable failed (check GDB_REMOTE_LOG env variable.')
381
382
383############################################
384# #
385# Execution of the test driver starts here #
386# #
387############################################
388
Johnny Chen9707bb62010-06-25 21:14:08 +0000389#
Johnny Chenaf149a02010-09-16 17:11:30 +0000390# Start the actions by first parsing the options while setting up the test
391# directories, followed by setting up the search paths for lldb utilities;
392# then, we walk the directory trees and collect the tests into our test suite.
Johnny Chen9707bb62010-06-25 21:14:08 +0000393#
Johnny Chenaf149a02010-09-16 17:11:30 +0000394parseOptionsAndInitTestdirs()
Johnny Chen9707bb62010-06-25 21:14:08 +0000395setupSysPath()
Johnny Chen91960d32010-09-08 20:56:16 +0000396
397#
398# If '-d' is specified, do a delay of 10 seconds for the debugger to attach.
399#
400if delay:
Johnny Chencd0279d2010-09-20 18:07:50 +0000401 doDelay(10)
Johnny Chen91960d32010-09-08 20:56:16 +0000402
Johnny Chen49f2f7a2010-09-20 17:25:45 +0000403#
Johnny Chen41998192010-10-01 22:59:49 +0000404# If '-l' is specified, do not skip the long running tests.
405if not skipLongRunningTest:
406 os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO"
407
408#
Johnny Chen49f2f7a2010-09-20 17:25:45 +0000409# Walk through the testdirs while collecting test cases.
410#
Johnny Chen9707bb62010-06-25 21:14:08 +0000411for testdir in testdirs:
412 os.path.walk(testdir, visit, 'Test')
413
Johnny Chenb40056b2010-09-21 00:09:27 +0000414#
Johnny Chen9707bb62010-06-25 21:14:08 +0000415# Now that we have loaded all the test cases, run the whole test suite.
Johnny Chenb40056b2010-09-21 00:09:27 +0000416#
Johnny Chencd0279d2010-09-20 18:07:50 +0000417
418# First, write out the number of collected test cases.
Johnny Chenb40056b2010-09-21 00:09:27 +0000419sys.stderr.write(separator + "\n")
420sys.stderr.write("Collected %d test%s\n\n"
421 % (suite.countTestCases(),
422 suite.countTestCases() != 1 and "s" or ""))
Johnny Chen1bfbd412010-06-29 19:44:16 +0000423
424# For the time being, let's bracket the test runner within the
425# lldb.SBDebugger.Initialize()/Terminate() pair.
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000426import lldb, atexit
Johnny Chen1bfbd412010-06-29 19:44:16 +0000427lldb.SBDebugger.Initialize()
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000428atexit.register(lambda: lldb.SBDebugger.Terminate())
Johnny Chen1bfbd412010-06-29 19:44:16 +0000429
Johnny Chen909e5a62010-07-01 22:52:57 +0000430# Create a singleton SBDebugger in the lldb namespace.
431lldb.DBG = lldb.SBDebugger.Create()
432
Johnny Chencd0279d2010-09-20 18:07:50 +0000433# Turn on lldb loggings if necessary.
434lldbLoggings()
Johnny Chen909e5a62010-07-01 22:52:57 +0000435
Johnny Chen7987ac92010-08-09 20:40:52 +0000436# Install the control-c handler.
437unittest2.signals.installHandler()
438
Johnny Chenb40056b2010-09-21 00:09:27 +0000439#
440# Invoke the default TextTestRunner to run the test suite, possibly iterating
441# over different configurations.
442#
443
Johnny Chenb40056b2010-09-21 00:09:27 +0000444iterArchs = False
Johnny Chenf032d902010-09-21 00:16:09 +0000445iterCompilers = False
Johnny Chenb40056b2010-09-21 00:09:27 +0000446
447from types import *
448if "archs" in config:
449 archs = config["archs"]
450 if type(archs) is ListType and len(archs) >= 1:
451 iterArchs = True
452if "compilers" in config:
453 compilers = config["compilers"]
454 if type(compilers) is ListType and len(compilers) >= 1:
455 iterCompilers = True
456
457for ia in range(len(archs) if iterArchs else 1):
458 archConfig = ""
459 if iterArchs:
Johnny Chen18a921f2010-09-30 17:11:58 +0000460 os.environ["ARCH"] = archs[ia]
Johnny Chenb40056b2010-09-21 00:09:27 +0000461 archConfig = "arch=%s" % archs[ia]
462 for ic in range(len(compilers) if iterCompilers else 1):
463 if iterCompilers:
Johnny Chen18a921f2010-09-30 17:11:58 +0000464 os.environ["CC"] = compilers[ic]
Johnny Chenb40056b2010-09-21 00:09:27 +0000465 configString = "%s compiler=%s" % (archConfig, compilers[ic])
466 else:
467 configString = archConfig
468
469 # Invoke the test runner.
470 if iterArchs or iterCompilers:
471 sys.stderr.write("\nConfiguration: " + configString + "\n")
472 result = unittest2.TextTestRunner(stream=sys.stderr, verbosity=verbose).run(suite)
473
Johnny Chen1bfbd412010-06-29 19:44:16 +0000474
Johnny Chencd0279d2010-09-20 18:07:50 +0000475# Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
476# This should not be necessary now.
Johnny Chen83f6e512010-08-13 22:58:44 +0000477if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
478 import subprocess
479 print "Terminating Test suite..."
480 subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
481
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000482# Exiting.
483sys.exit(not result.wasSuccessful)