blob: 462f912e0b8a7123cdf0cecc8cbf701e5cc7f9d7 [file] [log] [blame]
Daniel Dunbarbe7ada72009-09-08 05:31:18 +00001#!/usr/bin/env python
2
3"""
4lit - LLVM Integrated Tester.
5
6See lit.pod for more information.
7"""
8
9import math, os, platform, random, re, sys, time, threading, traceback
10
11import ProgressBar
12import TestRunner
13import Util
14
15from TestingConfig import TestingConfig
16import LitConfig
17import Test
18
Daniel Dunbaredb89972009-10-25 03:30:55 +000019# Configuration files to look for when discovering test suites. These can be
20# overridden with --config-prefix.
21#
Daniel Dunbarbe7ada72009-09-08 05:31:18 +000022# FIXME: Rename to 'config.lit', 'site.lit', and 'local.lit' ?
Daniel Dunbaredb89972009-10-25 03:30:55 +000023gConfigName = 'lit.cfg'
24gSiteConfigName = 'lit.site.cfg'
25
Daniel Dunbarbe7ada72009-09-08 05:31:18 +000026kLocalConfigName = 'lit.local.cfg'
27
28class TestingProgressDisplay:
29 def __init__(self, opts, numTests, progressBar=None):
30 self.opts = opts
31 self.numTests = numTests
32 self.current = None
33 self.lock = threading.Lock()
34 self.progressBar = progressBar
35 self.completed = 0
36
37 def update(self, test):
38 # Avoid locking overhead in quiet mode
39 if self.opts.quiet and not test.result.isFailure:
Daniel Dunbarc60535a2009-09-09 02:41:42 +000040 self.completed += 1
Daniel Dunbarbe7ada72009-09-08 05:31:18 +000041 return
42
43 # Output lock.
44 self.lock.acquire()
45 try:
46 self.handleUpdate(test)
47 finally:
48 self.lock.release()
49
50 def finish(self):
51 if self.progressBar:
52 self.progressBar.clear()
53 elif self.opts.quiet:
54 pass
55 elif self.opts.succinct:
56 sys.stdout.write('\n')
57
58 def handleUpdate(self, test):
59 self.completed += 1
60 if self.progressBar:
61 self.progressBar.update(float(self.completed)/self.numTests,
62 test.getFullName())
63
64 if self.opts.succinct and not test.result.isFailure:
65 return
66
67 if self.progressBar:
68 self.progressBar.clear()
69
70 print '%s: %s (%d of %d)' % (test.result.name, test.getFullName(),
71 self.completed, self.numTests)
72
73 if test.result.isFailure and self.opts.showOutput:
74 print "%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
75 '*'*20)
76 print test.output
77 print "*" * 20
78
79 sys.stdout.flush()
80
81class TestProvider:
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()
87
88 def get(self):
89 # Check if we have run out of time.
90 if self.maxTime is not None:
91 if time.time() - self.startTime > self.maxTime:
92 return None
93
94 # Otherwise take the next test.
95 self.lock.acquire()
96 try:
97 item = self.iter.next()
98 except StopIteration:
99 item = None
100 self.lock.release()
101 return item
102
103class 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
109
110 def run(self):
111 while 1:
112 item = self.provider.get()
113 if item is None:
114 break
115 self.runTest(item)
116
117 def runTest(self, test):
118 result = None
119 startTime = time.time()
120 try:
121 result, output = test.config.test_format.execute(test,
122 self.litConfig)
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.'
127 os.kill(0,9)
128 except:
129 if self.litConfig.debug:
130 raise
131 result = Test.UNRESOLVED
132 output = 'Exception during script execution:\n'
133 output += traceback.format_exc()
134 output += '\n'
135 elapsed = time.time() - startTime
136
137 test.setResult(result, output, elapsed)
138 self.display.update(test)
139
140def dirContainsTestSuite(path):
Daniel Dunbaredb89972009-10-25 03:30:55 +0000141 cfgpath = os.path.join(path, gSiteConfigName)
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000142 if os.path.exists(cfgpath):
143 return cfgpath
Daniel Dunbaredb89972009-10-25 03:30:55 +0000144 cfgpath = os.path.join(path, gConfigName)
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000145 if os.path.exists(cfgpath):
146 return cfgpath
147
148def getTestSuite(item, litConfig, cache):
149 """getTestSuite(item, litConfig, cache) -> (suite, relative_path)
150
151 Find the test suite containing @arg item.
152
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.
156 """
157 def search1(path):
158 # Check for a site config or a lit config.
159 cfgpath = dirContainsTestSuite(path)
160
161 # If we didn't find a config file, keep looking.
162 if not cfgpath:
163 parent,base = os.path.split(path)
Daniel Dunbar07d0bd42009-09-09 02:41:32 +0000164 if parent == path:
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000165 return (None, ())
166
167 ts, relative = search(parent)
168 return (ts, relative + (base,))
169
170 # We found a config file, load it.
171 if litConfig.debug:
172 litConfig.note('loading suite config %r' % cfgpath)
173
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), ()
178
179 def search(path):
180 # Check for an already instantiated test suite.
181 res = cache.get(path)
182 if res is None:
183 cache[path] = res = search1(path)
184 return res
185
186 # Canonicalize the path.
187 item = os.path.realpath(item)
188
189 # Skip files and virtual components.
190 components = []
191 while not os.path.isdir(item):
192 parent,base = os.path.split(item)
193 if parent == item:
194 return (None, ())
195 components.append(base)
196 item = parent
197 components.reverse()
198
199 ts, relative = search(item)
200 return ts, tuple(relative + tuple(components))
201
202def getLocalConfig(ts, path_in_suite, litConfig, cache):
203 def search1(path_in_suite):
204 # Get the parent config.
205 if not path_in_suite:
206 parent = ts.config
207 else:
208 parent = search(path_in_suite[:-1])
209
210 # Load the local configuration.
211 source_path = ts.getSourcePath(path_in_suite)
212 cfgpath = os.path.join(source_path, kLocalConfigName)
213 if litConfig.debug:
214 litConfig.note('loading local config %r' % cfgpath)
215 return TestingConfig.frompath(cfgpath, parent, litConfig,
216 mustExist = False,
217 config = parent.clone(cfgpath))
218
219 def search(path_in_suite):
220 key = (ts, path_in_suite)
221 res = cache.get(key)
222 if res is None:
223 cache[key] = res = search1(path_in_suite)
224 return res
225
226 return search(path_in_suite)
227
228def 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)
231 if ts is None:
232 litConfig.warning('unable to find test suite for %r' % path)
233 return ()
234
235 if litConfig.debug:
236 litConfig.note('resolved input %r to %r::%r' % (path, ts.name,
237 path_in_suite))
238
239 return getTestsInSuite(ts, path_in_suite, litConfig,
240 testSuiteCache, localConfigCache)
241
242def getTestsInSuite(ts, path_in_suite, litConfig,
243 testSuiteCache, localConfigCache):
244 # Check that the source path exists (errors here are reported by the
245 # caller).
246 source_path = ts.getSourcePath(path_in_suite)
247 if not os.path.exists(source_path):
248 return
249
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)
254 return
255
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)
Daniel Dunbar00a42442009-09-14 02:38:46 +0000259
260 # Search for tests.
261 for res in lc.test_format.getTestsInDirectory(ts, path_in_suite,
262 litConfig, lc):
263 yield res
264
265 # Search subdirectories.
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000266 for filename in os.listdir(source_path):
267 # FIXME: This doesn't belong here?
Daniel Dunbar705428a2009-09-28 07:32:45 +0000268 if filename in ('Output', '.svn') or filename in lc.excludes:
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000269 continue
270
Daniel Dunbarb2485c92009-09-15 20:09:17 +0000271 # Ignore non-directories.
272 file_sourcepath = os.path.join(source_path, filename)
273 if not os.path.isdir(file_sourcepath):
274 continue
Daniel Dunbaredb89972009-10-25 03:30:55 +0000275
Daniel Dunbarb2485c92009-09-15 20:09:17 +0000276 # 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 subiter = getTests(file_execpath, litConfig,
281 testSuiteCache, localConfigCache)
282 elif dirContainsTestSuite(file_sourcepath):
283 subiter = getTests(file_sourcepath, litConfig,
284 testSuiteCache, localConfigCache)
285 else:
286 # Otherwise, continue loading from inside this test suite.
287 subiter = getTestsInSuite(ts, path_in_suite + (filename,),
288 litConfig, testSuiteCache,
289 localConfigCache)
Daniel Dunbaredb89972009-10-25 03:30:55 +0000290
Daniel Dunbarb2485c92009-09-15 20:09:17 +0000291 for res in subiter:
292 yield res
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000293
294def runTests(numThreads, litConfig, provider, display):
295 # If only using one testing thread, don't use threads at all; this lets us
296 # profile, among other things.
297 if numThreads == 1:
298 t = Tester(litConfig, provider, display)
299 t.run()
300 return
301
302 # Otherwise spin up the testing threads and wait for them to finish.
303 testers = [Tester(litConfig, provider, display)
304 for i in range(numThreads)]
305 for t in testers:
306 t.start()
307 try:
308 for t in testers:
309 t.join()
310 except KeyboardInterrupt:
311 sys.exit(2)
312
313def main():
314 global options
315 from optparse import OptionParser, OptionGroup
316 parser = OptionParser("usage: %prog [options] {file-or-path}")
317
318 parser.add_option("-j", "--threads", dest="numThreads", metavar="N",
319 help="Number of testing threads",
320 type=int, action="store", default=None)
Daniel Dunbaredb89972009-10-25 03:30:55 +0000321 parser.add_option("", "--config-prefix", dest="configPrefix",
322 metavar="NAME", help="Prefix for 'lit' config files",
323 action="store", default=None)
Daniel Dunbarf6261672009-11-05 16:27:33 +0000324 parser.add_option("", "--param", dest="userParameters",
325 metavar="NAME=VAL",
326 help="Add 'NAME' = 'VAL' to the user defined parameters",
327 type=str, action="append", default=[])
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000328
329 group = OptionGroup(parser, "Output Format")
330 # FIXME: I find these names very confusing, although I like the
331 # functionality.
332 group.add_option("-q", "--quiet", dest="quiet",
333 help="Suppress no error output",
334 action="store_true", default=False)
335 group.add_option("-s", "--succinct", dest="succinct",
336 help="Reduce amount of output",
337 action="store_true", default=False)
338 group.add_option("-v", "--verbose", dest="showOutput",
339 help="Show all test output",
340 action="store_true", default=False)
341 group.add_option("", "--no-progress-bar", dest="useProgressBar",
342 help="Do not use curses based progress bar",
343 action="store_false", default=True)
344 parser.add_option_group(group)
345
346 group = OptionGroup(parser, "Test Execution")
347 group.add_option("", "--path", dest="path",
348 help="Additional paths to add to testing environment",
349 action="append", type=str, default=[])
350 group.add_option("", "--vg", dest="useValgrind",
351 help="Run tests under valgrind",
352 action="store_true", default=False)
353 group.add_option("", "--vg-arg", dest="valgrindArgs", metavar="ARG",
354 help="Specify an extra argument for valgrind",
355 type=str, action="append", default=[])
356 group.add_option("", "--time-tests", dest="timeTests",
357 help="Track elapsed wall time for each test",
358 action="store_true", default=False)
359 group.add_option("", "--no-execute", dest="noExecute",
360 help="Don't execute any tests (assume PASS)",
361 action="store_true", default=False)
362 parser.add_option_group(group)
363
364 group = OptionGroup(parser, "Test Selection")
365 group.add_option("", "--max-tests", dest="maxTests", metavar="N",
366 help="Maximum number of tests to run",
367 action="store", type=int, default=None)
368 group.add_option("", "--max-time", dest="maxTime", metavar="N",
369 help="Maximum time to spend testing (in seconds)",
370 action="store", type=float, default=None)
371 group.add_option("", "--shuffle", dest="shuffle",
372 help="Run tests in random order",
373 action="store_true", default=False)
374 parser.add_option_group(group)
375
376 group = OptionGroup(parser, "Debug and Experimental Options")
377 group.add_option("", "--debug", dest="debug",
378 help="Enable debugging (for 'lit' development)",
379 action="store_true", default=False)
380 group.add_option("", "--show-suites", dest="showSuites",
381 help="Show discovered test suites",
382 action="store_true", default=False)
383 group.add_option("", "--no-tcl-as-sh", dest="useTclAsSh",
384 help="Don't run Tcl scripts using 'sh'",
385 action="store_false", default=True)
386 parser.add_option_group(group)
387
388 (opts, args) = parser.parse_args()
389
390 if not args:
391 parser.error('No inputs specified')
392
Daniel Dunbaredb89972009-10-25 03:30:55 +0000393 if opts.configPrefix is not None:
394 global gConfigName, gSiteConfigName
395 gConfigName = '%s.cfg' % opts.configPrefix
396 gSiteConfigName = '%s.site.cfg' % opts.configPrefix
397
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000398 if opts.numThreads is None:
399 opts.numThreads = Util.detectCPUs()
400
401 inputs = args
402
Daniel Dunbarf6261672009-11-05 16:27:33 +0000403 # Create the user defined parameters.
404 userParams = {}
405 for entry in opts.userParameters:
406 if '=' not in entry:
407 name,val = entry,''
408 else:
409 name,val = entry.split('=', 1)
410 userParams[name] = val
411
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000412 # Create the global config object.
413 litConfig = LitConfig.LitConfig(progname = os.path.basename(sys.argv[0]),
414 path = opts.path,
415 quiet = opts.quiet,
416 useValgrind = opts.useValgrind,
417 valgrindArgs = opts.valgrindArgs,
418 useTclAsSh = opts.useTclAsSh,
419 noExecute = opts.noExecute,
420 debug = opts.debug,
Daniel Dunbarf6261672009-11-05 16:27:33 +0000421 isWindows = (platform.system()=='Windows'),
422 params = userParams)
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000423
424 # Load the tests from the inputs.
425 tests = []
426 testSuiteCache = {}
427 localConfigCache = {}
428 for input in inputs:
429 prev = len(tests)
430 tests.extend(getTests(input, litConfig,
431 testSuiteCache, localConfigCache))
432 if prev == len(tests):
433 litConfig.warning('input %r contained no tests' % input)
434
435 # If there were any errors during test discovery, exit now.
436 if litConfig.numErrors:
437 print >>sys.stderr, '%d errors, exiting.' % litConfig.numErrors
438 sys.exit(2)
439
440 if opts.showSuites:
441 suitesAndTests = dict([(ts,[])
Daniel Dunbaredb89972009-10-25 03:30:55 +0000442 for ts,_ in testSuiteCache.values()
443 if ts])
Daniel Dunbarbe7ada72009-09-08 05:31:18 +0000444 for t in tests:
445 suitesAndTests[t.suite].append(t)
446
447 print '-- Test Suites --'
448 suitesAndTests = suitesAndTests.items()
449 suitesAndTests.sort(key = lambda (ts,_): ts.name)
450 for ts,tests in suitesAndTests:
451 print ' %s - %d tests' %(ts.name, len(tests))
452 print ' Source Root: %s' % ts.source_root
453 print ' Exec Root : %s' % ts.exec_root
454
455 # Select and order the tests.
456 numTotalTests = len(tests)
457 if opts.shuffle:
458 random.shuffle(tests)
459 else:
460 tests.sort(key = lambda t: t.getFullName())
461 if opts.maxTests is not None:
462 tests = tests[:opts.maxTests]
463
464 extra = ''
465 if len(tests) != numTotalTests:
466 extra = ' of %d' % numTotalTests
467 header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
468 opts.numThreads)
469
470 progressBar = None
471 if not opts.quiet:
472 if opts.succinct and opts.useProgressBar:
473 try:
474 tc = ProgressBar.TerminalController()
475 progressBar = ProgressBar.ProgressBar(tc, header)
476 except ValueError:
477 print header
478 progressBar = ProgressBar.SimpleProgressBar('Testing: ')
479 else:
480 print header
481
482 # Don't create more threads than tests.
483 opts.numThreads = min(len(tests), opts.numThreads)
484
485 startTime = time.time()
486 display = TestingProgressDisplay(opts, len(tests), progressBar)
487 provider = TestProvider(tests, opts.maxTime)
488 runTests(opts.numThreads, litConfig, provider, display)
489 display.finish()
490
491 if not opts.quiet:
492 print 'Testing Time: %.2fs'%(time.time() - startTime)
493
494 # Update results for any tests which weren't run.
495 for t in tests:
496 if t.result is None:
497 t.setResult(Test.UNRESOLVED, '', 0.0)
498
499 # List test results organized by kind.
500 hasFailures = False
501 byCode = {}
502 for t in tests:
503 if t.result not in byCode:
504 byCode[t.result] = []
505 byCode[t.result].append(t)
506 if t.result.isFailure:
507 hasFailures = True
508
509 # FIXME: Show unresolved and (optionally) unsupported tests.
510 for title,code in (('Unexpected Passing Tests', Test.XPASS),
511 ('Failing Tests', Test.FAIL)):
512 elts = byCode.get(code)
513 if not elts:
514 continue
515 print '*'*20
516 print '%s (%d):' % (title, len(elts))
517 for t in elts:
518 print ' %s' % t.getFullName()
519 print
520
521 if opts.timeTests:
522 byTime = list(tests)
523 byTime.sort(key = lambda t: t.elapsed)
524 if byTime:
525 Util.printHistogram([(t.getFullName(), t.elapsed) for t in byTime],
526 title='Tests')
527
528 for name,code in (('Expected Passes ', Test.PASS),
529 ('Expected Failures ', Test.XFAIL),
530 ('Unsupported Tests ', Test.UNSUPPORTED),
531 ('Unresolved Tests ', Test.UNRESOLVED),
532 ('Unexpected Passes ', Test.XPASS),
533 ('Unexpected Failures', Test.FAIL),):
534 if opts.quiet and not code.isFailure:
535 continue
536 N = len(byCode.get(code,[]))
537 if N:
538 print ' %s: %d' % (name,N)
539
540 # If we encountered any additional errors, exit abnormally.
541 if litConfig.numErrors:
542 print >>sys.stderr, '\n%d error(s), exiting.' % litConfig.numErrors
543 sys.exit(2)
544
545 # Warn about warnings.
546 if litConfig.numWarnings:
547 print >>sys.stderr, '\n%d warning(s) in tests.' % litConfig.numWarnings
548
549 if hasFailures:
550 sys.exit(1)
551 sys.exit(0)
552
553if __name__=='__main__':
554 # Bump the GIL check interval, its more important to get any one thread to a
555 # blocking operation (hopefully exec) than to try and unblock other threads.
556 import sys
557 sys.setcheckinterval(1000)
558 main()