blob: b477e10f34eec07d13ce7a2f8cc3635686d76447 [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 Chen46be75d2010-10-11 16:19:48 +000090-f : specify a filter, which consists of the test class name, a dot, followed by
91 the test method, to admit tests into the test suite
Johnny Chenb62436b2010-10-06 20:40:56 +000092 e.g., -f 'ClassTypesTestCase.test_with_dwarf_and_python_api'
93-g : if specified, only the modules with the corect filter will be run
94 it has no effect if no '-f' option is present
95 '-f filterspec -g' can be used with '-p filename-regexp' to select only
96 the testfile.testclass.testmethod to run
Johnny Chenaf149a02010-09-16 17:11:30 +000097-i : ignore (don't bailout) if 'lldb.py' module cannot be located in the build
98 tree relative to this script; use PYTHONPATH to locate the module
Johnny Chen41998192010-10-01 22:59:49 +000099-l : don't skip long running test
Johnny Chen7c52ff12010-09-27 23:29:54 +0000100-p : specify a regexp filename pattern for inclusion in the test suite
Johnny Chend0c24b22010-08-23 17:10:44 +0000101-t : trace lldb command execution and result
Johnny Chen9707bb62010-06-25 21:14:08 +0000102-v : do verbose mode of unittest framework
Johnny Chene47649c2010-10-07 02:04:14 +0000103-w : insert some wait time (currently 0.5 sec) between consecutive test cases
Johnny Chen9707bb62010-06-25 21:14:08 +0000104
105and:
106args : specify a list of directory names to search for python Test*.py scripts
107 if empty, search from the curret working directory, instead
Johnny Chen58f93922010-06-29 23:10:39 +0000108
109Running of this script also sets up the LLDB_TEST environment variable so that
Johnny Chenaf149a02010-09-16 17:11:30 +0000110individual test cases can locate their supporting files correctly. The script
111tries to set up Python's search paths for modules by looking at the build tree
112relative to this script. See also the '-i' option.
Johnny Chenfde69bc2010-09-14 22:01:40 +0000113
114Environment variables related to loggings:
115
116o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem
117 with a default option of 'event process' if LLDB_LOG_OPTION is not defined.
118
119o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the
120 'process.gdb-remote' subsystem with a default option of 'packets' if
121 GDB_REMOTE_LOG_OPTION is not defined.
Johnny Chen9707bb62010-06-25 21:14:08 +0000122"""
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000123 sys.exit(0)
Johnny Chen9707bb62010-06-25 21:14:08 +0000124
125
Johnny Chenaf149a02010-09-16 17:11:30 +0000126def parseOptionsAndInitTestdirs():
127 """Initialize the list of directories containing our unittest scripts.
128
129 '-h/--help as the first option prints out usage info and exit the program.
130 """
131
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000132 global configFile
Johnny Chenaf149a02010-09-16 17:11:30 +0000133 global delay
Johnny Chenb62436b2010-10-06 20:40:56 +0000134 global filterspec
135 global fs4all
Johnny Chen7c52ff12010-09-27 23:29:54 +0000136 global ignore
Johnny Chen41998192010-10-01 22:59:49 +0000137 global skipLongRunningTest
Johnny Chen7c52ff12010-09-27 23:29:54 +0000138 global regexp
Johnny Chenaf149a02010-09-16 17:11:30 +0000139 global verbose
140 global testdirs
141
142 if len(sys.argv) == 1:
143 return
144
145 # Process possible trace and/or verbose flag, among other things.
146 index = 1
Johnny Chence2212c2010-10-07 15:41:55 +0000147 while index < len(sys.argv):
Johnny Chenaf149a02010-09-16 17:11:30 +0000148 if not sys.argv[index].startswith('-'):
149 # End of option processing.
150 break
151
152 if sys.argv[index].find('-h') != -1:
153 usage()
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000154 elif sys.argv[index].startswith('-c'):
155 # Increment by 1 to fetch the config file name option argument.
156 index += 1
157 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
158 usage()
159 configFile = sys.argv[index]
160 if not os.path.isfile(configFile):
161 print "Config file:", configFile, "does not exist!"
162 usage()
163 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000164 elif sys.argv[index].startswith('-d'):
165 delay = True
166 index += 1
Johnny Chenb62436b2010-10-06 20:40:56 +0000167 elif sys.argv[index].startswith('-f'):
168 # Increment by 1 to fetch the filter spec.
169 index += 1
170 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
171 usage()
172 filterspec = sys.argv[index]
173 index += 1
174 elif sys.argv[index].startswith('-g'):
175 fs4all = True
176 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000177 elif sys.argv[index].startswith('-i'):
178 ignore = True
179 index += 1
Johnny Chen41998192010-10-01 22:59:49 +0000180 elif sys.argv[index].startswith('-l'):
181 skipLongRunningTest = False
182 index += 1
Johnny Chen7c52ff12010-09-27 23:29:54 +0000183 elif sys.argv[index].startswith('-p'):
184 # Increment by 1 to fetch the reg exp pattern argument.
185 index += 1
186 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
187 usage()
188 regexp = sys.argv[index]
189 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000190 elif sys.argv[index].startswith('-t'):
191 os.environ["LLDB_COMMAND_TRACE"] = "YES"
192 index += 1
193 elif sys.argv[index].startswith('-v'):
194 verbose = 2
195 index += 1
Johnny Chene47649c2010-10-07 02:04:14 +0000196 elif sys.argv[index].startswith('-w'):
197 os.environ["LLDB_WAIT_BETWEEN_TEST_CASES"] = 'YES'
198 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000199 else:
200 print "Unknown option: ", sys.argv[index]
201 usage()
Johnny Chenaf149a02010-09-16 17:11:30 +0000202
203 # Gather all the dirs passed on the command line.
204 if len(sys.argv) > index:
205 testdirs = map(os.path.abspath, sys.argv[index:])
206
Johnny Chenb40056b2010-09-21 00:09:27 +0000207 # Source the configFile if specified.
208 # The side effect, if any, will be felt from this point on. An example
209 # config file may be these simple two lines:
210 #
211 # sys.stderr = open("/tmp/lldbtest-stderr", "w")
212 # sys.stdout = open("/tmp/lldbtest-stdout", "w")
213 #
214 # which will reassign the two file objects to sys.stderr and sys.stdout,
215 # respectively.
216 #
217 # See also lldb-trunk/example/test/usage-config.
218 global config
219 if configFile:
220 # Pass config (a dictionary) as the locals namespace for side-effect.
221 execfile(configFile, globals(), config)
222 #print "config:", config
223 #print "sys.stderr:", sys.stderr
224 #print "sys.stdout:", sys.stdout
225
Johnny Chenaf149a02010-09-16 17:11:30 +0000226
Johnny Chen9707bb62010-06-25 21:14:08 +0000227def setupSysPath():
228 """Add LLDB.framework/Resources/Python to the search paths for modules."""
229
230 # Get the directory containing the current script.
Johnny Chena1affab2010-07-03 03:41:59 +0000231 scriptPath = sys.path[0]
232 if not scriptPath.endswith('test'):
Johnny Chen9707bb62010-06-25 21:14:08 +0000233 print "This script expects to reside in lldb's test directory."
234 sys.exit(-1)
235
Johnny Chena1affab2010-07-03 03:41:59 +0000236 os.environ["LLDB_TEST"] = scriptPath
Johnny Chen9de4ede2010-08-31 17:42:54 +0000237 pluginPath = os.path.join(scriptPath, 'plugins')
Johnny Chen58f93922010-06-29 23:10:39 +0000238
Johnny Chenaf149a02010-09-16 17:11:30 +0000239 # Append script dir and plugin dir to the sys.path.
240 sys.path.append(scriptPath)
241 sys.path.append(pluginPath)
242
243 global ignore
244
245 # The '-i' option is used to skip looking for lldb.py in the build tree.
246 if ignore:
247 return
248
Johnny Chena1affab2010-07-03 03:41:59 +0000249 base = os.path.abspath(os.path.join(scriptPath, os.pardir))
Johnny Chen9707bb62010-06-25 21:14:08 +0000250 dbgPath = os.path.join(base, 'build', 'Debug', 'LLDB.framework',
251 'Resources', 'Python')
252 relPath = os.path.join(base, 'build', 'Release', 'LLDB.framework',
253 'Resources', 'Python')
Johnny Chenc202c462010-09-15 18:11:19 +0000254 baiPath = os.path.join(base, 'build', 'BuildAndIntegration',
255 'LLDB.framework', 'Resources', 'Python')
Johnny Chen9707bb62010-06-25 21:14:08 +0000256
257 lldbPath = None
258 if os.path.isfile(os.path.join(dbgPath, 'lldb.py')):
259 lldbPath = dbgPath
260 elif os.path.isfile(os.path.join(relPath, 'lldb.py')):
261 lldbPath = relPath
Johnny Chenc202c462010-09-15 18:11:19 +0000262 elif os.path.isfile(os.path.join(baiPath, 'lldb.py')):
263 lldbPath = baiPath
Johnny Chen9707bb62010-06-25 21:14:08 +0000264
265 if not lldbPath:
Johnny Chenc202c462010-09-15 18:11:19 +0000266 print 'This script requires lldb.py to be in either ' + dbgPath + ',',
267 print relPath + ', or ' + baiPath
Johnny Chen9707bb62010-06-25 21:14:08 +0000268 sys.exit(-1)
269
Johnny Chenaf149a02010-09-16 17:11:30 +0000270 # This is to locate the lldb.py module. Insert it right after sys.path[0].
271 sys.path[1:1] = [lldbPath]
Johnny Chen9707bb62010-06-25 21:14:08 +0000272
Johnny Chen9707bb62010-06-25 21:14:08 +0000273
Johnny Chencd0279d2010-09-20 18:07:50 +0000274def doDelay(delta):
275 """Delaying startup for delta-seconds to facilitate debugger attachment."""
276 def alarm_handler(*args):
277 raise Exception("timeout")
278
279 signal.signal(signal.SIGALRM, alarm_handler)
280 signal.alarm(delta)
281 sys.stdout.write("pid=%d\n" % os.getpid())
282 sys.stdout.write("Enter RET to proceed (or timeout after %d seconds):" %
283 delta)
284 sys.stdout.flush()
285 try:
286 text = sys.stdin.readline()
287 except:
288 text = ""
289 signal.alarm(0)
290 sys.stdout.write("proceeding...\n")
291 pass
292
293
Johnny Chen9707bb62010-06-25 21:14:08 +0000294def visit(prefix, dir, names):
295 """Visitor function for os.path.walk(path, visit, arg)."""
296
297 global suite
Johnny Chen7c52ff12010-09-27 23:29:54 +0000298 global regexp
Johnny Chenb62436b2010-10-06 20:40:56 +0000299 global filterspec
300 global fs4all
Johnny Chen9707bb62010-06-25 21:14:08 +0000301
302 for name in names:
303 if os.path.isdir(os.path.join(dir, name)):
304 continue
305
306 if '.py' == os.path.splitext(name)[1] and name.startswith(prefix):
Johnny Chen7c52ff12010-09-27 23:29:54 +0000307 # Try to match the regexp pattern, if specified.
308 if regexp:
309 import re
310 if re.search(regexp, name):
311 #print "Filename: '%s' matches pattern: '%s'" % (name, regexp)
312 pass
313 else:
314 #print "Filename: '%s' does not match pattern: '%s'" % (name, regexp)
315 continue
316
317 # We found a match for our test case. Add it to the suite.
Johnny Chena85d7ee2010-06-26 00:19:32 +0000318 if not sys.path.count(dir):
319 sys.path.append(dir)
Johnny Chen9707bb62010-06-25 21:14:08 +0000320 base = os.path.splitext(name)[0]
Johnny Chenb62436b2010-10-06 20:40:56 +0000321
322 # Thoroughly check the filterspec against the base module and admit
323 # the (base, filterspec) combination only when it makes sense.
324 if filterspec:
325 # Optimistically set the flag to True.
326 filtered = True
327 module = __import__(base)
328 parts = filterspec.split('.')
329 obj = module
330 for part in parts:
331 try:
332 parent, obj = obj, getattr(obj, part)
333 except AttributeError:
334 # The filterspec has failed.
335 filtered = False
336 break
337 # Forgo this module if the (base, filterspec) combo is invalid
338 # and the '-g' option is present.
339 if fs4all and not filtered:
340 continue
341
342 if filterspec and filtered:
343 suite.addTests(
344 unittest2.defaultTestLoader.loadTestsFromName(filterspec, module))
345 else:
346 # A simple case of just the module name. Also the failover case
347 # from the filterspec branch when the (base, filterspec) combo
348 # doesn't make sense.
349 suite.addTests(unittest2.defaultTestLoader.loadTestsFromName(base))
Johnny Chen9707bb62010-06-25 21:14:08 +0000350
351
Johnny Chencd0279d2010-09-20 18:07:50 +0000352def lldbLoggings():
353 """Check and do lldb loggings if necessary."""
354
355 # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
356 # defined. Use ${LLDB_LOG} to specify the log file.
357 ci = lldb.DBG.GetCommandInterpreter()
358 res = lldb.SBCommandReturnObject()
359 if ("LLDB_LOG" in os.environ):
360 if ("LLDB_LOG_OPTION" in os.environ):
361 lldb_log_option = os.environ["LLDB_LOG_OPTION"]
362 else:
363 lldb_log_option = "event process"
364 ci.HandleCommand(
365 "log enable -f " + os.environ["LLDB_LOG"] + " lldb " + lldb_log_option,
366 res)
367 if not res.Succeeded():
368 raise Exception('log enable failed (check LLDB_LOG env variable.')
369 # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
370 # Use ${GDB_REMOTE_LOG} to specify the log file.
371 if ("GDB_REMOTE_LOG" in os.environ):
372 if ("GDB_REMOTE_LOG_OPTION" in os.environ):
373 gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
374 else:
375 gdb_remote_log_option = "packets"
376 ci.HandleCommand(
377 "log enable -f " + os.environ["GDB_REMOTE_LOG"] + " process.gdb-remote "
378 + gdb_remote_log_option,
379 res)
380 if not res.Succeeded():
381 raise Exception('log enable failed (check GDB_REMOTE_LOG env variable.')
382
383
384############################################
385# #
386# Execution of the test driver starts here #
387# #
388############################################
389
Johnny Chen9707bb62010-06-25 21:14:08 +0000390#
Johnny Chenaf149a02010-09-16 17:11:30 +0000391# Start the actions by first parsing the options while setting up the test
392# directories, followed by setting up the search paths for lldb utilities;
393# then, we walk the directory trees and collect the tests into our test suite.
Johnny Chen9707bb62010-06-25 21:14:08 +0000394#
Johnny Chenaf149a02010-09-16 17:11:30 +0000395parseOptionsAndInitTestdirs()
Johnny Chen9707bb62010-06-25 21:14:08 +0000396setupSysPath()
Johnny Chen91960d32010-09-08 20:56:16 +0000397
398#
399# If '-d' is specified, do a delay of 10 seconds for the debugger to attach.
400#
401if delay:
Johnny Chencd0279d2010-09-20 18:07:50 +0000402 doDelay(10)
Johnny Chen91960d32010-09-08 20:56:16 +0000403
Johnny Chen49f2f7a2010-09-20 17:25:45 +0000404#
Johnny Chen41998192010-10-01 22:59:49 +0000405# If '-l' is specified, do not skip the long running tests.
406if not skipLongRunningTest:
407 os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO"
408
409#
Johnny Chen49f2f7a2010-09-20 17:25:45 +0000410# Walk through the testdirs while collecting test cases.
411#
Johnny Chen9707bb62010-06-25 21:14:08 +0000412for testdir in testdirs:
413 os.path.walk(testdir, visit, 'Test')
414
Johnny Chenb40056b2010-09-21 00:09:27 +0000415#
Johnny Chen9707bb62010-06-25 21:14:08 +0000416# Now that we have loaded all the test cases, run the whole test suite.
Johnny Chenb40056b2010-09-21 00:09:27 +0000417#
Johnny Chencd0279d2010-09-20 18:07:50 +0000418
419# First, write out the number of collected test cases.
Johnny Chenb40056b2010-09-21 00:09:27 +0000420sys.stderr.write(separator + "\n")
421sys.stderr.write("Collected %d test%s\n\n"
422 % (suite.countTestCases(),
423 suite.countTestCases() != 1 and "s" or ""))
Johnny Chen1bfbd412010-06-29 19:44:16 +0000424
425# For the time being, let's bracket the test runner within the
426# lldb.SBDebugger.Initialize()/Terminate() pair.
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000427import lldb, atexit
Johnny Chen1bfbd412010-06-29 19:44:16 +0000428lldb.SBDebugger.Initialize()
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000429atexit.register(lambda: lldb.SBDebugger.Terminate())
Johnny Chen1bfbd412010-06-29 19:44:16 +0000430
Johnny Chen909e5a62010-07-01 22:52:57 +0000431# Create a singleton SBDebugger in the lldb namespace.
432lldb.DBG = lldb.SBDebugger.Create()
433
Johnny Chencd0279d2010-09-20 18:07:50 +0000434# Turn on lldb loggings if necessary.
435lldbLoggings()
Johnny Chen909e5a62010-07-01 22:52:57 +0000436
Johnny Chen7987ac92010-08-09 20:40:52 +0000437# Install the control-c handler.
438unittest2.signals.installHandler()
439
Johnny Chenb40056b2010-09-21 00:09:27 +0000440#
441# Invoke the default TextTestRunner to run the test suite, possibly iterating
442# over different configurations.
443#
444
Johnny Chenb40056b2010-09-21 00:09:27 +0000445iterArchs = False
Johnny Chenf032d902010-09-21 00:16:09 +0000446iterCompilers = False
Johnny Chenb40056b2010-09-21 00:09:27 +0000447
448from types import *
449if "archs" in config:
450 archs = config["archs"]
451 if type(archs) is ListType and len(archs) >= 1:
452 iterArchs = True
453if "compilers" in config:
454 compilers = config["compilers"]
455 if type(compilers) is ListType and len(compilers) >= 1:
456 iterCompilers = True
457
458for ia in range(len(archs) if iterArchs else 1):
459 archConfig = ""
460 if iterArchs:
Johnny Chen18a921f2010-09-30 17:11:58 +0000461 os.environ["ARCH"] = archs[ia]
Johnny Chenb40056b2010-09-21 00:09:27 +0000462 archConfig = "arch=%s" % archs[ia]
463 for ic in range(len(compilers) if iterCompilers else 1):
464 if iterCompilers:
Johnny Chen18a921f2010-09-30 17:11:58 +0000465 os.environ["CC"] = compilers[ic]
Johnny Chenb40056b2010-09-21 00:09:27 +0000466 configString = "%s compiler=%s" % (archConfig, compilers[ic])
467 else:
468 configString = archConfig
469
470 # Invoke the test runner.
471 if iterArchs or iterCompilers:
472 sys.stderr.write("\nConfiguration: " + configString + "\n")
473 result = unittest2.TextTestRunner(stream=sys.stderr, verbosity=verbose).run(suite)
474
Johnny Chen1bfbd412010-06-29 19:44:16 +0000475
Johnny Chencd0279d2010-09-20 18:07:50 +0000476# Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
477# This should not be necessary now.
Johnny Chen83f6e512010-08-13 22:58:44 +0000478if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
479 import subprocess
480 print "Terminating Test suite..."
481 subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
482
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000483# Exiting.
484sys.exit(not result.wasSuccessful)