blob: 5d46cc410b626468a5ab4881fe871ce725861cb2 [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 Chenaf149a02010-09-16 17:11:30 +000057# Ignore the build search path relative to this script to locate the lldb.py module.
58ignore = False
59
Johnny Chen7c52ff12010-09-27 23:29:54 +000060# The regular expression pattern to match against eligible filenames as our test cases.
61regexp = None
62
Johnny Chen9707bb62010-06-25 21:14:08 +000063# Default verbosity is 0.
64verbose = 0
65
66# By default, search from the current working directory.
67testdirs = [ os.getcwd() ]
68
Johnny Chen877c7e42010-08-07 00:16:07 +000069# Separator string.
70separator = '-' * 70
71
Johnny Chen9707bb62010-06-25 21:14:08 +000072
73def usage():
74 print """
75Usage: dotest.py [option] [args]
76where options:
77-h : print this help message and exit (also --help)
Johnny Chen9fdb0a92010-09-18 00:16:47 +000078-c : read a config file specified after this option
Johnny Chenb40056b2010-09-21 00:09:27 +000079 (see also lldb-trunk/example/test/usage-config)
Johnny Chen91960d32010-09-08 20:56:16 +000080-d : delay startup for 10 seconds (in order for the debugger to attach)
Johnny Chenaf149a02010-09-16 17:11:30 +000081-i : ignore (don't bailout) if 'lldb.py' module cannot be located in the build
82 tree relative to this script; use PYTHONPATH to locate the module
Johnny Chen7c52ff12010-09-27 23:29:54 +000083-p : specify a regexp filename pattern for inclusion in the test suite
Johnny Chend0c24b22010-08-23 17:10:44 +000084-t : trace lldb command execution and result
Johnny Chen9707bb62010-06-25 21:14:08 +000085-v : do verbose mode of unittest framework
86
87and:
88args : specify a list of directory names to search for python Test*.py scripts
89 if empty, search from the curret working directory, instead
Johnny Chen58f93922010-06-29 23:10:39 +000090
91Running of this script also sets up the LLDB_TEST environment variable so that
Johnny Chenaf149a02010-09-16 17:11:30 +000092individual test cases can locate their supporting files correctly. The script
93tries to set up Python's search paths for modules by looking at the build tree
94relative to this script. See also the '-i' option.
Johnny Chenfde69bc2010-09-14 22:01:40 +000095
96Environment variables related to loggings:
97
98o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem
99 with a default option of 'event process' if LLDB_LOG_OPTION is not defined.
100
101o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the
102 'process.gdb-remote' subsystem with a default option of 'packets' if
103 GDB_REMOTE_LOG_OPTION is not defined.
Johnny Chen9707bb62010-06-25 21:14:08 +0000104"""
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000105 sys.exit(0)
Johnny Chen9707bb62010-06-25 21:14:08 +0000106
107
Johnny Chenaf149a02010-09-16 17:11:30 +0000108def parseOptionsAndInitTestdirs():
109 """Initialize the list of directories containing our unittest scripts.
110
111 '-h/--help as the first option prints out usage info and exit the program.
112 """
113
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000114 global configFile
Johnny Chenaf149a02010-09-16 17:11:30 +0000115 global delay
Johnny Chen7c52ff12010-09-27 23:29:54 +0000116 global ignore
117 global regexp
Johnny Chenaf149a02010-09-16 17:11:30 +0000118 global verbose
119 global testdirs
120
121 if len(sys.argv) == 1:
122 return
123
124 # Process possible trace and/or verbose flag, among other things.
125 index = 1
126 for i in range(1, len(sys.argv)):
127 if not sys.argv[index].startswith('-'):
128 # End of option processing.
129 break
130
131 if sys.argv[index].find('-h') != -1:
132 usage()
Johnny Chen9fdb0a92010-09-18 00:16:47 +0000133 elif sys.argv[index].startswith('-c'):
134 # Increment by 1 to fetch the config file name option argument.
135 index += 1
136 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
137 usage()
138 configFile = sys.argv[index]
139 if not os.path.isfile(configFile):
140 print "Config file:", configFile, "does not exist!"
141 usage()
142 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000143 elif sys.argv[index].startswith('-d'):
144 delay = True
145 index += 1
146 elif sys.argv[index].startswith('-i'):
147 ignore = True
148 index += 1
Johnny Chen7c52ff12010-09-27 23:29:54 +0000149 elif sys.argv[index].startswith('-p'):
150 # Increment by 1 to fetch the reg exp pattern argument.
151 index += 1
152 if index >= len(sys.argv) or sys.argv[index].startswith('-'):
153 usage()
154 regexp = sys.argv[index]
155 index += 1
Johnny Chenaf149a02010-09-16 17:11:30 +0000156 elif sys.argv[index].startswith('-t'):
157 os.environ["LLDB_COMMAND_TRACE"] = "YES"
158 index += 1
159 elif sys.argv[index].startswith('-v'):
160 verbose = 2
161 index += 1
162 else:
163 print "Unknown option: ", sys.argv[index]
164 usage()
Johnny Chenaf149a02010-09-16 17:11:30 +0000165
166 # Gather all the dirs passed on the command line.
167 if len(sys.argv) > index:
168 testdirs = map(os.path.abspath, sys.argv[index:])
169
Johnny Chenb40056b2010-09-21 00:09:27 +0000170 # Source the configFile if specified.
171 # The side effect, if any, will be felt from this point on. An example
172 # config file may be these simple two lines:
173 #
174 # sys.stderr = open("/tmp/lldbtest-stderr", "w")
175 # sys.stdout = open("/tmp/lldbtest-stdout", "w")
176 #
177 # which will reassign the two file objects to sys.stderr and sys.stdout,
178 # respectively.
179 #
180 # See also lldb-trunk/example/test/usage-config.
181 global config
182 if configFile:
183 # Pass config (a dictionary) as the locals namespace for side-effect.
184 execfile(configFile, globals(), config)
185 #print "config:", config
186 #print "sys.stderr:", sys.stderr
187 #print "sys.stdout:", sys.stdout
188
Johnny Chenaf149a02010-09-16 17:11:30 +0000189
Johnny Chen9707bb62010-06-25 21:14:08 +0000190def setupSysPath():
191 """Add LLDB.framework/Resources/Python to the search paths for modules."""
192
193 # Get the directory containing the current script.
Johnny Chena1affab2010-07-03 03:41:59 +0000194 scriptPath = sys.path[0]
195 if not scriptPath.endswith('test'):
Johnny Chen9707bb62010-06-25 21:14:08 +0000196 print "This script expects to reside in lldb's test directory."
197 sys.exit(-1)
198
Johnny Chena1affab2010-07-03 03:41:59 +0000199 os.environ["LLDB_TEST"] = scriptPath
Johnny Chen9de4ede2010-08-31 17:42:54 +0000200 pluginPath = os.path.join(scriptPath, 'plugins')
Johnny Chen58f93922010-06-29 23:10:39 +0000201
Johnny Chenaf149a02010-09-16 17:11:30 +0000202 # Append script dir and plugin dir to the sys.path.
203 sys.path.append(scriptPath)
204 sys.path.append(pluginPath)
205
206 global ignore
207
208 # The '-i' option is used to skip looking for lldb.py in the build tree.
209 if ignore:
210 return
211
Johnny Chena1affab2010-07-03 03:41:59 +0000212 base = os.path.abspath(os.path.join(scriptPath, os.pardir))
Johnny Chen9707bb62010-06-25 21:14:08 +0000213 dbgPath = os.path.join(base, 'build', 'Debug', 'LLDB.framework',
214 'Resources', 'Python')
215 relPath = os.path.join(base, 'build', 'Release', 'LLDB.framework',
216 'Resources', 'Python')
Johnny Chenc202c462010-09-15 18:11:19 +0000217 baiPath = os.path.join(base, 'build', 'BuildAndIntegration',
218 'LLDB.framework', 'Resources', 'Python')
Johnny Chen9707bb62010-06-25 21:14:08 +0000219
220 lldbPath = None
221 if os.path.isfile(os.path.join(dbgPath, 'lldb.py')):
222 lldbPath = dbgPath
223 elif os.path.isfile(os.path.join(relPath, 'lldb.py')):
224 lldbPath = relPath
Johnny Chenc202c462010-09-15 18:11:19 +0000225 elif os.path.isfile(os.path.join(baiPath, 'lldb.py')):
226 lldbPath = baiPath
Johnny Chen9707bb62010-06-25 21:14:08 +0000227
228 if not lldbPath:
Johnny Chenc202c462010-09-15 18:11:19 +0000229 print 'This script requires lldb.py to be in either ' + dbgPath + ',',
230 print relPath + ', or ' + baiPath
Johnny Chen9707bb62010-06-25 21:14:08 +0000231 sys.exit(-1)
232
Johnny Chenaf149a02010-09-16 17:11:30 +0000233 # This is to locate the lldb.py module. Insert it right after sys.path[0].
234 sys.path[1:1] = [lldbPath]
Johnny Chen9707bb62010-06-25 21:14:08 +0000235
Johnny Chen9707bb62010-06-25 21:14:08 +0000236
Johnny Chencd0279d2010-09-20 18:07:50 +0000237def doDelay(delta):
238 """Delaying startup for delta-seconds to facilitate debugger attachment."""
239 def alarm_handler(*args):
240 raise Exception("timeout")
241
242 signal.signal(signal.SIGALRM, alarm_handler)
243 signal.alarm(delta)
244 sys.stdout.write("pid=%d\n" % os.getpid())
245 sys.stdout.write("Enter RET to proceed (or timeout after %d seconds):" %
246 delta)
247 sys.stdout.flush()
248 try:
249 text = sys.stdin.readline()
250 except:
251 text = ""
252 signal.alarm(0)
253 sys.stdout.write("proceeding...\n")
254 pass
255
256
Johnny Chen9707bb62010-06-25 21:14:08 +0000257def visit(prefix, dir, names):
258 """Visitor function for os.path.walk(path, visit, arg)."""
259
260 global suite
Johnny Chen7c52ff12010-09-27 23:29:54 +0000261 global regexp
Johnny Chen9707bb62010-06-25 21:14:08 +0000262
263 for name in names:
264 if os.path.isdir(os.path.join(dir, name)):
265 continue
266
267 if '.py' == os.path.splitext(name)[1] and name.startswith(prefix):
Johnny Chen7c52ff12010-09-27 23:29:54 +0000268 # Try to match the regexp pattern, if specified.
269 if regexp:
270 import re
271 if re.search(regexp, name):
272 #print "Filename: '%s' matches pattern: '%s'" % (name, regexp)
273 pass
274 else:
275 #print "Filename: '%s' does not match pattern: '%s'" % (name, regexp)
276 continue
277
278 # We found a match for our test case. Add it to the suite.
Johnny Chena85d7ee2010-06-26 00:19:32 +0000279 if not sys.path.count(dir):
280 sys.path.append(dir)
Johnny Chen9707bb62010-06-25 21:14:08 +0000281 base = os.path.splitext(name)[0]
Johnny Chen75e28f92010-08-05 23:42:46 +0000282 suite.addTests(unittest2.defaultTestLoader.loadTestsFromName(base))
Johnny Chen9707bb62010-06-25 21:14:08 +0000283
284
Johnny Chencd0279d2010-09-20 18:07:50 +0000285def lldbLoggings():
286 """Check and do lldb loggings if necessary."""
287
288 # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is
289 # defined. Use ${LLDB_LOG} to specify the log file.
290 ci = lldb.DBG.GetCommandInterpreter()
291 res = lldb.SBCommandReturnObject()
292 if ("LLDB_LOG" in os.environ):
293 if ("LLDB_LOG_OPTION" in os.environ):
294 lldb_log_option = os.environ["LLDB_LOG_OPTION"]
295 else:
296 lldb_log_option = "event process"
297 ci.HandleCommand(
298 "log enable -f " + os.environ["LLDB_LOG"] + " lldb " + lldb_log_option,
299 res)
300 if not res.Succeeded():
301 raise Exception('log enable failed (check LLDB_LOG env variable.')
302 # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined.
303 # Use ${GDB_REMOTE_LOG} to specify the log file.
304 if ("GDB_REMOTE_LOG" in os.environ):
305 if ("GDB_REMOTE_LOG_OPTION" in os.environ):
306 gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"]
307 else:
308 gdb_remote_log_option = "packets"
309 ci.HandleCommand(
310 "log enable -f " + os.environ["GDB_REMOTE_LOG"] + " process.gdb-remote "
311 + gdb_remote_log_option,
312 res)
313 if not res.Succeeded():
314 raise Exception('log enable failed (check GDB_REMOTE_LOG env variable.')
315
316
317############################################
318# #
319# Execution of the test driver starts here #
320# #
321############################################
322
Johnny Chen9707bb62010-06-25 21:14:08 +0000323#
Johnny Chenaf149a02010-09-16 17:11:30 +0000324# Start the actions by first parsing the options while setting up the test
325# directories, followed by setting up the search paths for lldb utilities;
326# then, we walk the directory trees and collect the tests into our test suite.
Johnny Chen9707bb62010-06-25 21:14:08 +0000327#
Johnny Chenaf149a02010-09-16 17:11:30 +0000328parseOptionsAndInitTestdirs()
Johnny Chen9707bb62010-06-25 21:14:08 +0000329setupSysPath()
Johnny Chen91960d32010-09-08 20:56:16 +0000330
331#
332# If '-d' is specified, do a delay of 10 seconds for the debugger to attach.
333#
334if delay:
Johnny Chencd0279d2010-09-20 18:07:50 +0000335 doDelay(10)
Johnny Chen91960d32010-09-08 20:56:16 +0000336
Johnny Chen49f2f7a2010-09-20 17:25:45 +0000337#
338# Walk through the testdirs while collecting test cases.
339#
Johnny Chen9707bb62010-06-25 21:14:08 +0000340for testdir in testdirs:
341 os.path.walk(testdir, visit, 'Test')
342
Johnny Chenb40056b2010-09-21 00:09:27 +0000343#
Johnny Chen9707bb62010-06-25 21:14:08 +0000344# Now that we have loaded all the test cases, run the whole test suite.
Johnny Chenb40056b2010-09-21 00:09:27 +0000345#
Johnny Chencd0279d2010-09-20 18:07:50 +0000346
347# First, write out the number of collected test cases.
Johnny Chenb40056b2010-09-21 00:09:27 +0000348sys.stderr.write(separator + "\n")
349sys.stderr.write("Collected %d test%s\n\n"
350 % (suite.countTestCases(),
351 suite.countTestCases() != 1 and "s" or ""))
Johnny Chen1bfbd412010-06-29 19:44:16 +0000352
353# For the time being, let's bracket the test runner within the
354# lldb.SBDebugger.Initialize()/Terminate() pair.
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000355import lldb, atexit
Johnny Chen1bfbd412010-06-29 19:44:16 +0000356lldb.SBDebugger.Initialize()
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000357atexit.register(lambda: lldb.SBDebugger.Terminate())
Johnny Chen1bfbd412010-06-29 19:44:16 +0000358
Johnny Chen909e5a62010-07-01 22:52:57 +0000359# Create a singleton SBDebugger in the lldb namespace.
360lldb.DBG = lldb.SBDebugger.Create()
361
Johnny Chencd0279d2010-09-20 18:07:50 +0000362# Turn on lldb loggings if necessary.
363lldbLoggings()
Johnny Chen909e5a62010-07-01 22:52:57 +0000364
Johnny Chen7987ac92010-08-09 20:40:52 +0000365# Install the control-c handler.
366unittest2.signals.installHandler()
367
Johnny Chenb40056b2010-09-21 00:09:27 +0000368#
369# Invoke the default TextTestRunner to run the test suite, possibly iterating
370# over different configurations.
371#
372
Johnny Chenb40056b2010-09-21 00:09:27 +0000373iterArchs = False
Johnny Chenf032d902010-09-21 00:16:09 +0000374iterCompilers = False
Johnny Chenb40056b2010-09-21 00:09:27 +0000375
376from types import *
377if "archs" in config:
378 archs = config["archs"]
379 if type(archs) is ListType and len(archs) >= 1:
380 iterArchs = True
381if "compilers" in config:
382 compilers = config["compilers"]
383 if type(compilers) is ListType and len(compilers) >= 1:
384 iterCompilers = True
385
386for ia in range(len(archs) if iterArchs else 1):
387 archConfig = ""
388 if iterArchs:
389 os.environ["LLDB_ARCH"] = archs[ia]
390 archConfig = "arch=%s" % archs[ia]
391 for ic in range(len(compilers) if iterCompilers else 1):
392 if iterCompilers:
393 os.environ["LLDB_CC"] = compilers[ic]
394 configString = "%s compiler=%s" % (archConfig, compilers[ic])
395 else:
396 configString = archConfig
397
398 # Invoke the test runner.
399 if iterArchs or iterCompilers:
400 sys.stderr.write("\nConfiguration: " + configString + "\n")
401 result = unittest2.TextTestRunner(stream=sys.stderr, verbosity=verbose).run(suite)
402
Johnny Chen1bfbd412010-06-29 19:44:16 +0000403
Johnny Chencd0279d2010-09-20 18:07:50 +0000404# Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
405# This should not be necessary now.
Johnny Chen83f6e512010-08-13 22:58:44 +0000406if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ):
407 import subprocess
408 print "Terminating Test suite..."
409 subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())])
410
Johnny Chen01f2a6a2010-08-10 20:23:55 +0000411# Exiting.
412sys.exit(not result.wasSuccessful)