4
lit - LLVM Integrated Tester.
6
See lit.pod for more information.
9
import math, os, platform, random, re, sys, time, threading, traceback
15
from TestingConfig import TestingConfig
19
# Configuration files to look for when discovering test suites. These can be
20
# overridden with --config-prefix.
22
# FIXME: Rename to 'config.lit', 'site.lit', and 'local.lit' ?
23
gConfigName = 'lit.cfg'
24
gSiteConfigName = 'lit.site.cfg'
26
kLocalConfigName = 'lit.local.cfg'
28
class TestingProgressDisplay:
29
def __init__(self, opts, numTests, progressBar=None):
31
self.numTests = numTests
33
self.lock = threading.Lock()
34
self.progressBar = progressBar
37
def update(self, test):
38
# Avoid locking overhead in quiet mode
39
if self.opts.quiet and not test.result.isFailure:
46
self.handleUpdate(test)
52
self.progressBar.clear()
55
elif self.opts.succinct:
56
sys.stdout.write('\n')
58
def handleUpdate(self, test):
61
self.progressBar.update(float(self.completed)/self.numTests,
64
if self.opts.succinct and not test.result.isFailure:
68
self.progressBar.clear()
70
print '%s: %s (%d of %d)' % (test.result.name, test.getFullName(),
71
self.completed, self.numTests)
73
if test.result.isFailure and self.opts.showOutput:
74
print "%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
82
def __init__(self, tests, maxTime):
83
self.maxTime = maxTime
84
self.iter = iter(tests)
85
self.lock = threading.Lock()
86
self.startTime = time.time()
89
# Check if we have run out of time.
90
if self.maxTime is not None:
91
if time.time() - self.startTime > self.maxTime:
94
# Otherwise take the next test.
97
item = self.iter.next()
103
class Tester(threading.Thread):
104
def __init__(self, litConfig, provider, display):
105
threading.Thread.__init__(self)
106
self.litConfig = litConfig
107
self.provider = provider
108
self.display = display
112
item = self.provider.get()
117
def runTest(self, test):
119
startTime = time.time()
121
result, output = test.config.test_format.execute(test,
123
except KeyboardInterrupt:
124
# This is a sad hack. Unfortunately subprocess goes
125
# bonkers with ctrl-c and we start forking merrily.
126
print '\nCtrl-C detected, goodbye.'
129
if self.litConfig.debug:
131
result = Test.UNRESOLVED
132
output = 'Exception during script execution:\n'
133
output += traceback.format_exc()
135
elapsed = time.time() - startTime
137
test.setResult(result, output, elapsed)
138
self.display.update(test)
140
def dirContainsTestSuite(path):
141
cfgpath = os.path.join(path, gSiteConfigName)
142
if os.path.exists(cfgpath):
144
cfgpath = os.path.join(path, gConfigName)
145
if os.path.exists(cfgpath):
148
def getTestSuite(item, litConfig, cache):
149
"""getTestSuite(item, litConfig, cache) -> (suite, relative_path)
151
Find the test suite containing @arg item.
153
@retval (None, ...) - Indicates no test suite contains @arg item.
154
@retval (suite, relative_path) - The suite that @arg item is in, and its
155
relative path inside that suite.
158
# Check for a site config or a lit config.
159
cfgpath = dirContainsTestSuite(path)
161
# If we didn't find a config file, keep looking.
163
parent,base = os.path.split(path)
167
ts, relative = search(parent)
168
return (ts, relative + (base,))
170
# We found a config file, load it.
172
litConfig.note('loading suite config %r' % cfgpath)
174
cfg = TestingConfig.frompath(cfgpath, None, litConfig, mustExist = True)
175
source_root = os.path.realpath(cfg.test_source_root or path)
176
exec_root = os.path.realpath(cfg.test_exec_root or path)
177
return Test.TestSuite(cfg.name, source_root, exec_root, cfg), ()
180
# Check for an already instantiated test suite.
181
res = cache.get(path)
183
cache[path] = res = search1(path)
186
# Canonicalize the path.
187
item = os.path.realpath(item)
189
# Skip files and virtual components.
191
while not os.path.isdir(item):
192
parent,base = os.path.split(item)
195
components.append(base)
199
ts, relative = search(item)
200
return ts, tuple(relative + tuple(components))
202
def getLocalConfig(ts, path_in_suite, litConfig, cache):
203
def search1(path_in_suite):
204
# Get the parent config.
205
if not path_in_suite:
208
parent = search(path_in_suite[:-1])
210
# Load the local configuration.
211
source_path = ts.getSourcePath(path_in_suite)
212
cfgpath = os.path.join(source_path, kLocalConfigName)
214
litConfig.note('loading local config %r' % cfgpath)
215
return TestingConfig.frompath(cfgpath, parent, litConfig,
217
config = parent.clone(cfgpath))
219
def search(path_in_suite):
220
key = (ts, path_in_suite)
223
cache[key] = res = search1(path_in_suite)
226
return search(path_in_suite)
228
def getTests(path, litConfig, testSuiteCache, localConfigCache):
229
# Find the test suite for this input and its relative path.
230
ts,path_in_suite = getTestSuite(path, litConfig, testSuiteCache)
232
litConfig.warning('unable to find test suite for %r' % path)
236
litConfig.note('resolved input %r to %r::%r' % (path, ts.name,
239
return ts, getTestsInSuite(ts, path_in_suite, litConfig,
240
testSuiteCache, localConfigCache)
242
def getTestsInSuite(ts, path_in_suite, litConfig,
243
testSuiteCache, localConfigCache):
244
# Check that the source path exists (errors here are reported by the
246
source_path = ts.getSourcePath(path_in_suite)
247
if not os.path.exists(source_path):
250
# Check if the user named a test directly.
251
if not os.path.isdir(source_path):
252
lc = getLocalConfig(ts, path_in_suite[:-1], litConfig, localConfigCache)
253
yield Test.Test(ts, path_in_suite, lc)
256
# Otherwise we have a directory to search for tests, start by getting the
257
# local configuration.
258
lc = getLocalConfig(ts, path_in_suite, litConfig, localConfigCache)
261
for res in lc.test_format.getTestsInDirectory(ts, path_in_suite,
265
# Search subdirectories.
266
for filename in os.listdir(source_path):
267
# FIXME: This doesn't belong here?
268
if filename in ('Output', '.svn') or filename in lc.excludes:
271
# Ignore non-directories.
272
file_sourcepath = os.path.join(source_path, filename)
273
if not os.path.isdir(file_sourcepath):
276
# Check for nested test suites, first in the execpath in case there is a
277
# site configuration and then in the source path.
278
file_execpath = ts.getExecPath(path_in_suite + (filename,))
279
if dirContainsTestSuite(file_execpath):
280
sub_ts, subiter = getTests(file_execpath, litConfig,
281
testSuiteCache, localConfigCache)
282
elif dirContainsTestSuite(file_sourcepath):
283
sub_ts, subiter = getTests(file_sourcepath, litConfig,
284
testSuiteCache, localConfigCache)
286
# Otherwise, continue loading from inside this test suite.
287
subiter = getTestsInSuite(ts, path_in_suite + (filename,),
288
litConfig, testSuiteCache,
297
litConfig.warning('test suite %r contained no tests' % sub_ts.name)
299
def runTests(numThreads, litConfig, provider, display):
300
# If only using one testing thread, don't use threads at all; this lets us
301
# profile, among other things.
303
t = Tester(litConfig, provider, display)
307
# Otherwise spin up the testing threads and wait for them to finish.
308
testers = [Tester(litConfig, provider, display)
309
for i in range(numThreads)]
315
except KeyboardInterrupt:
319
# Bump the GIL check interval, its more important to get any one thread to a
320
# blocking operation (hopefully exec) than to try and unblock other threads.
322
# FIXME: This is a hack.
324
sys.setcheckinterval(1000)
327
from optparse import OptionParser, OptionGroup
328
parser = OptionParser("usage: %prog [options] {file-or-path}")
330
parser.add_option("-j", "--threads", dest="numThreads", metavar="N",
331
help="Number of testing threads",
332
type=int, action="store", default=None)
333
parser.add_option("", "--config-prefix", dest="configPrefix",
334
metavar="NAME", help="Prefix for 'lit' config files",
335
action="store", default=None)
336
parser.add_option("", "--param", dest="userParameters",
338
help="Add 'NAME' = 'VAL' to the user defined parameters",
339
type=str, action="append", default=[])
341
group = OptionGroup(parser, "Output Format")
342
# FIXME: I find these names very confusing, although I like the
344
group.add_option("-q", "--quiet", dest="quiet",
345
help="Suppress no error output",
346
action="store_true", default=False)
347
group.add_option("-s", "--succinct", dest="succinct",
348
help="Reduce amount of output",
349
action="store_true", default=False)
350
group.add_option("-v", "--verbose", dest="showOutput",
351
help="Show all test output",
352
action="store_true", default=False)
353
group.add_option("", "--no-progress-bar", dest="useProgressBar",
354
help="Do not use curses based progress bar",
355
action="store_false", default=True)
356
parser.add_option_group(group)
358
group = OptionGroup(parser, "Test Execution")
359
group.add_option("", "--path", dest="path",
360
help="Additional paths to add to testing environment",
361
action="append", type=str, default=[])
362
group.add_option("", "--vg", dest="useValgrind",
363
help="Run tests under valgrind",
364
action="store_true", default=False)
365
group.add_option("", "--vg-arg", dest="valgrindArgs", metavar="ARG",
366
help="Specify an extra argument for valgrind",
367
type=str, action="append", default=[])
368
group.add_option("", "--time-tests", dest="timeTests",
369
help="Track elapsed wall time for each test",
370
action="store_true", default=False)
371
group.add_option("", "--no-execute", dest="noExecute",
372
help="Don't execute any tests (assume PASS)",
373
action="store_true", default=False)
374
parser.add_option_group(group)
376
group = OptionGroup(parser, "Test Selection")
377
group.add_option("", "--max-tests", dest="maxTests", metavar="N",
378
help="Maximum number of tests to run",
379
action="store", type=int, default=None)
380
group.add_option("", "--max-time", dest="maxTime", metavar="N",
381
help="Maximum time to spend testing (in seconds)",
382
action="store", type=float, default=None)
383
group.add_option("", "--shuffle", dest="shuffle",
384
help="Run tests in random order",
385
action="store_true", default=False)
386
parser.add_option_group(group)
388
group = OptionGroup(parser, "Debug and Experimental Options")
389
group.add_option("", "--debug", dest="debug",
390
help="Enable debugging (for 'lit' development)",
391
action="store_true", default=False)
392
group.add_option("", "--show-suites", dest="showSuites",
393
help="Show discovered test suites",
394
action="store_true", default=False)
395
group.add_option("", "--no-tcl-as-sh", dest="useTclAsSh",
396
help="Don't run Tcl scripts using 'sh'",
397
action="store_false", default=True)
398
group.add_option("", "--repeat", dest="repeatTests", metavar="N",
399
help="Repeat tests N times (for timing)",
400
action="store", default=None, type=int)
401
parser.add_option_group(group)
403
(opts, args) = parser.parse_args()
406
parser.error('No inputs specified')
408
if opts.configPrefix is not None:
409
global gConfigName, gSiteConfigName
410
gConfigName = '%s.cfg' % opts.configPrefix
411
gSiteConfigName = '%s.site.cfg' % opts.configPrefix
413
if opts.numThreads is None:
414
opts.numThreads = Util.detectCPUs()
418
# Create the user defined parameters.
420
for entry in opts.userParameters:
424
name,val = entry.split('=', 1)
425
userParams[name] = val
427
# Create the global config object.
428
litConfig = LitConfig.LitConfig(progname = os.path.basename(sys.argv[0]),
431
useValgrind = opts.useValgrind,
432
valgrindArgs = opts.valgrindArgs,
433
useTclAsSh = opts.useTclAsSh,
434
noExecute = opts.noExecute,
436
isWindows = (platform.system()=='Windows'),
439
# Load the tests from the inputs.
442
localConfigCache = {}
445
tests.extend(getTests(input, litConfig,
446
testSuiteCache, localConfigCache)[1])
447
if prev == len(tests):
448
litConfig.warning('input %r contained no tests' % input)
450
# If there were any errors during test discovery, exit now.
451
if litConfig.numErrors:
452
print >>sys.stderr, '%d errors, exiting.' % litConfig.numErrors
456
suitesAndTests = dict([(ts,[])
457
for ts,_ in testSuiteCache.values()
460
suitesAndTests[t.suite].append(t)
462
print '-- Test Suites --'
463
suitesAndTests = suitesAndTests.items()
464
suitesAndTests.sort(key = lambda (ts,_): ts.name)
465
for ts,ts_tests in suitesAndTests:
466
print ' %s - %d tests' %(ts.name, len(ts_tests))
467
print ' Source Root: %s' % ts.source_root
468
print ' Exec Root : %s' % ts.exec_root
470
# Select and order the tests.
471
numTotalTests = len(tests)
473
random.shuffle(tests)
475
tests.sort(key = lambda t: t.getFullName())
476
if opts.maxTests is not None:
477
tests = tests[:opts.maxTests]
480
if len(tests) != numTotalTests:
481
extra = ' of %d' % numTotalTests
482
header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
486
tests = [t.copyWithIndex(i)
488
for i in range(opts.repeatTests)]
492
if opts.succinct and opts.useProgressBar:
494
tc = ProgressBar.TerminalController()
495
progressBar = ProgressBar.ProgressBar(tc, header)
498
progressBar = ProgressBar.SimpleProgressBar('Testing: ')
502
# Don't create more threads than tests.
503
opts.numThreads = min(len(tests), opts.numThreads)
505
startTime = time.time()
506
display = TestingProgressDisplay(opts, len(tests), progressBar)
507
provider = TestProvider(tests, opts.maxTime)
508
runTests(opts.numThreads, litConfig, provider, display)
512
print 'Testing Time: %.2fs'%(time.time() - startTime)
514
# Update results for any tests which weren't run.
517
t.setResult(Test.UNRESOLVED, '', 0.0)
519
# List test results organized by kind.
523
if t.result not in byCode:
524
byCode[t.result] = []
525
byCode[t.result].append(t)
526
if t.result.isFailure:
529
# FIXME: Show unresolved and (optionally) unsupported tests.
530
for title,code in (('Unexpected Passing Tests', Test.XPASS),
531
('Failing Tests', Test.FAIL)):
532
elts = byCode.get(code)
536
print '%s (%d):' % (title, len(elts))
538
print ' %s' % t.getFullName()
542
# Collate, in case we repeated tests.
545
key = t.getFullName()
546
times[key] = times.get(key, 0.) + t.elapsed
548
byTime = list(times.items())
549
byTime.sort(key = lambda (name,elapsed): elapsed)
551
Util.printHistogram(byTime, title='Tests')
553
for name,code in (('Expected Passes ', Test.PASS),
554
('Expected Failures ', Test.XFAIL),
555
('Unsupported Tests ', Test.UNSUPPORTED),
556
('Unresolved Tests ', Test.UNRESOLVED),
557
('Unexpected Passes ', Test.XPASS),
558
('Unexpected Failures', Test.FAIL),):
559
if opts.quiet and not code.isFailure:
561
N = len(byCode.get(code,[]))
563
print ' %s: %d' % (name,N)
565
# If we encountered any additional errors, exit abnormally.
566
if litConfig.numErrors:
567
print >>sys.stderr, '\n%d error(s), exiting.' % litConfig.numErrors
570
# Warn about warnings.
571
if litConfig.numWarnings:
572
print >>sys.stderr, '\n%d warning(s) in tests.' % litConfig.numWarnings
578
if __name__=='__main__':