blob: 62ebf51a71d52610c5fb38b33b2eb6431cf5e221 [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
19# FIXME: Rename to 'config.lit', 'site.lit', and 'local.lit' ?
20kConfigName = 'lit.cfg'
21kSiteConfigName = 'lit.site.cfg'
22kLocalConfigName = 'lit.local.cfg'
23
24class TestingProgressDisplay:
25 def __init__(self, opts, numTests, progressBar=None):
26 self.opts = opts
27 self.numTests = numTests
28 self.current = None
29 self.lock = threading.Lock()
30 self.progressBar = progressBar
31 self.completed = 0
32
33 def update(self, test):
34 # Avoid locking overhead in quiet mode
35 if self.opts.quiet and not test.result.isFailure:
36 return
37
38 # Output lock.
39 self.lock.acquire()
40 try:
41 self.handleUpdate(test)
42 finally:
43 self.lock.release()
44
45 def finish(self):
46 if self.progressBar:
47 self.progressBar.clear()
48 elif self.opts.quiet:
49 pass
50 elif self.opts.succinct:
51 sys.stdout.write('\n')
52
53 def handleUpdate(self, test):
54 self.completed += 1
55 if self.progressBar:
56 self.progressBar.update(float(self.completed)/self.numTests,
57 test.getFullName())
58
59 if self.opts.succinct and not test.result.isFailure:
60 return
61
62 if self.progressBar:
63 self.progressBar.clear()
64
65 print '%s: %s (%d of %d)' % (test.result.name, test.getFullName(),
66 self.completed, self.numTests)
67
68 if test.result.isFailure and self.opts.showOutput:
69 print "%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
70 '*'*20)
71 print test.output
72 print "*" * 20
73
74 sys.stdout.flush()
75
76class TestProvider:
77 def __init__(self, tests, maxTime):
78 self.maxTime = maxTime
79 self.iter = iter(tests)
80 self.lock = threading.Lock()
81 self.startTime = time.time()
82
83 def get(self):
84 # Check if we have run out of time.
85 if self.maxTime is not None:
86 if time.time() - self.startTime > self.maxTime:
87 return None
88
89 # Otherwise take the next test.
90 self.lock.acquire()
91 try:
92 item = self.iter.next()
93 except StopIteration:
94 item = None
95 self.lock.release()
96 return item
97
98class Tester(threading.Thread):
99 def __init__(self, litConfig, provider, display):
100 threading.Thread.__init__(self)
101 self.litConfig = litConfig
102 self.provider = provider
103 self.display = display
104
105 def run(self):
106 while 1:
107 item = self.provider.get()
108 if item is None:
109 break
110 self.runTest(item)
111
112 def runTest(self, test):
113 result = None
114 startTime = time.time()
115 try:
116 result, output = test.config.test_format.execute(test,
117 self.litConfig)
118 except KeyboardInterrupt:
119 # This is a sad hack. Unfortunately subprocess goes
120 # bonkers with ctrl-c and we start forking merrily.
121 print '\nCtrl-C detected, goodbye.'
122 os.kill(0,9)
123 except:
124 if self.litConfig.debug:
125 raise
126 result = Test.UNRESOLVED
127 output = 'Exception during script execution:\n'
128 output += traceback.format_exc()
129 output += '\n'
130 elapsed = time.time() - startTime
131
132 test.setResult(result, output, elapsed)
133 self.display.update(test)
134
135def dirContainsTestSuite(path):
136 cfgpath = os.path.join(path, kSiteConfigName)
137 if os.path.exists(cfgpath):
138 return cfgpath
139 cfgpath = os.path.join(path, kConfigName)
140 if os.path.exists(cfgpath):
141 return cfgpath
142
143def getTestSuite(item, litConfig, cache):
144 """getTestSuite(item, litConfig, cache) -> (suite, relative_path)
145
146 Find the test suite containing @arg item.
147
148 @retval (None, ...) - Indicates no test suite contains @arg item.
149 @retval (suite, relative_path) - The suite that @arg item is in, and its
150 relative path inside that suite.
151 """
152 def search1(path):
153 # Check for a site config or a lit config.
154 cfgpath = dirContainsTestSuite(path)
155
156 # If we didn't find a config file, keep looking.
157 if not cfgpath:
158 parent,base = os.path.split(path)
159 if parent == item:
160 return (None, ())
161
162 ts, relative = search(parent)
163 return (ts, relative + (base,))
164
165 # We found a config file, load it.
166 if litConfig.debug:
167 litConfig.note('loading suite config %r' % cfgpath)
168
169 cfg = TestingConfig.frompath(cfgpath, None, litConfig, mustExist = True)
170 source_root = os.path.realpath(cfg.test_source_root or path)
171 exec_root = os.path.realpath(cfg.test_exec_root or path)
172 return Test.TestSuite(cfg.name, source_root, exec_root, cfg), ()
173
174 def search(path):
175 # Check for an already instantiated test suite.
176 res = cache.get(path)
177 if res is None:
178 cache[path] = res = search1(path)
179 return res
180
181 # Canonicalize the path.
182 item = os.path.realpath(item)
183
184 # Skip files and virtual components.
185 components = []
186 while not os.path.isdir(item):
187 parent,base = os.path.split(item)
188 if parent == item:
189 return (None, ())
190 components.append(base)
191 item = parent
192 components.reverse()
193
194 ts, relative = search(item)
195 return ts, tuple(relative + tuple(components))
196
197def getLocalConfig(ts, path_in_suite, litConfig, cache):
198 def search1(path_in_suite):
199 # Get the parent config.
200 if not path_in_suite:
201 parent = ts.config
202 else:
203 parent = search(path_in_suite[:-1])
204
205 # Load the local configuration.
206 source_path = ts.getSourcePath(path_in_suite)
207 cfgpath = os.path.join(source_path, kLocalConfigName)
208 if litConfig.debug:
209 litConfig.note('loading local config %r' % cfgpath)
210 return TestingConfig.frompath(cfgpath, parent, litConfig,
211 mustExist = False,
212 config = parent.clone(cfgpath))
213
214 def search(path_in_suite):
215 key = (ts, path_in_suite)
216 res = cache.get(key)
217 if res is None:
218 cache[key] = res = search1(path_in_suite)
219 return res
220
221 return search(path_in_suite)
222
223def getTests(path, litConfig, testSuiteCache, localConfigCache):
224 # Find the test suite for this input and its relative path.
225 ts,path_in_suite = getTestSuite(path, litConfig, testSuiteCache)
226 if ts is None:
227 litConfig.warning('unable to find test suite for %r' % path)
228 return ()
229
230 if litConfig.debug:
231 litConfig.note('resolved input %r to %r::%r' % (path, ts.name,
232 path_in_suite))
233
234 return getTestsInSuite(ts, path_in_suite, litConfig,
235 testSuiteCache, localConfigCache)
236
237def getTestsInSuite(ts, path_in_suite, litConfig,
238 testSuiteCache, localConfigCache):
239 # Check that the source path exists (errors here are reported by the
240 # caller).
241 source_path = ts.getSourcePath(path_in_suite)
242 if not os.path.exists(source_path):
243 return
244
245 # Check if the user named a test directly.
246 if not os.path.isdir(source_path):
247 lc = getLocalConfig(ts, path_in_suite[:-1], litConfig, localConfigCache)
248 yield Test.Test(ts, path_in_suite, lc)
249 return
250
251 # Otherwise we have a directory to search for tests, start by getting the
252 # local configuration.
253 lc = getLocalConfig(ts, path_in_suite, litConfig, localConfigCache)
254 for filename in os.listdir(source_path):
255 # FIXME: This doesn't belong here?
256 if filename == 'Output' or filename in lc.excludes:
257 continue
258
259 filepath = os.path.join(source_path, filename)
260 if os.path.isdir(filepath):
261 # If this directory contains a test suite, reload it.
262 if dirContainsTestSuite(filepath):
263 for res in getTests(filepath, litConfig,
264 testSuiteCache, localConfigCache):
265 yield res
266 else:
267 # Otherwise, continue loading from inside this test suite.
268 for res in getTestsInSuite(ts, path_in_suite + (filename,),
269 litConfig, testSuiteCache,
270 localConfigCache):
271 yield res
272 else:
273 # Otherwise add tests for matching suffixes.
274 base,ext = os.path.splitext(filename)
275 if ext in lc.suffixes:
276 yield Test.Test(ts, path_in_suite + (filename,), lc)
277
278def runTests(numThreads, litConfig, provider, display):
279 # If only using one testing thread, don't use threads at all; this lets us
280 # profile, among other things.
281 if numThreads == 1:
282 t = Tester(litConfig, provider, display)
283 t.run()
284 return
285
286 # Otherwise spin up the testing threads and wait for them to finish.
287 testers = [Tester(litConfig, provider, display)
288 for i in range(numThreads)]
289 for t in testers:
290 t.start()
291 try:
292 for t in testers:
293 t.join()
294 except KeyboardInterrupt:
295 sys.exit(2)
296
297def main():
298 global options
299 from optparse import OptionParser, OptionGroup
300 parser = OptionParser("usage: %prog [options] {file-or-path}")
301
302 parser.add_option("-j", "--threads", dest="numThreads", metavar="N",
303 help="Number of testing threads",
304 type=int, action="store", default=None)
305
306 group = OptionGroup(parser, "Output Format")
307 # FIXME: I find these names very confusing, although I like the
308 # functionality.
309 group.add_option("-q", "--quiet", dest="quiet",
310 help="Suppress no error output",
311 action="store_true", default=False)
312 group.add_option("-s", "--succinct", dest="succinct",
313 help="Reduce amount of output",
314 action="store_true", default=False)
315 group.add_option("-v", "--verbose", dest="showOutput",
316 help="Show all test output",
317 action="store_true", default=False)
318 group.add_option("", "--no-progress-bar", dest="useProgressBar",
319 help="Do not use curses based progress bar",
320 action="store_false", default=True)
321 parser.add_option_group(group)
322
323 group = OptionGroup(parser, "Test Execution")
324 group.add_option("", "--path", dest="path",
325 help="Additional paths to add to testing environment",
326 action="append", type=str, default=[])
327 group.add_option("", "--vg", dest="useValgrind",
328 help="Run tests under valgrind",
329 action="store_true", default=False)
330 group.add_option("", "--vg-arg", dest="valgrindArgs", metavar="ARG",
331 help="Specify an extra argument for valgrind",
332 type=str, action="append", default=[])
333 group.add_option("", "--time-tests", dest="timeTests",
334 help="Track elapsed wall time for each test",
335 action="store_true", default=False)
336 group.add_option("", "--no-execute", dest="noExecute",
337 help="Don't execute any tests (assume PASS)",
338 action="store_true", default=False)
339 parser.add_option_group(group)
340
341 group = OptionGroup(parser, "Test Selection")
342 group.add_option("", "--max-tests", dest="maxTests", metavar="N",
343 help="Maximum number of tests to run",
344 action="store", type=int, default=None)
345 group.add_option("", "--max-time", dest="maxTime", metavar="N",
346 help="Maximum time to spend testing (in seconds)",
347 action="store", type=float, default=None)
348 group.add_option("", "--shuffle", dest="shuffle",
349 help="Run tests in random order",
350 action="store_true", default=False)
351 parser.add_option_group(group)
352
353 group = OptionGroup(parser, "Debug and Experimental Options")
354 group.add_option("", "--debug", dest="debug",
355 help="Enable debugging (for 'lit' development)",
356 action="store_true", default=False)
357 group.add_option("", "--show-suites", dest="showSuites",
358 help="Show discovered test suites",
359 action="store_true", default=False)
360 group.add_option("", "--no-tcl-as-sh", dest="useTclAsSh",
361 help="Don't run Tcl scripts using 'sh'",
362 action="store_false", default=True)
363 parser.add_option_group(group)
364
365 (opts, args) = parser.parse_args()
366
367 if not args:
368 parser.error('No inputs specified')
369
370 if opts.numThreads is None:
371 opts.numThreads = Util.detectCPUs()
372
373 inputs = args
374
375 # Create the global config object.
376 litConfig = LitConfig.LitConfig(progname = os.path.basename(sys.argv[0]),
377 path = opts.path,
378 quiet = opts.quiet,
379 useValgrind = opts.useValgrind,
380 valgrindArgs = opts.valgrindArgs,
381 useTclAsSh = opts.useTclAsSh,
382 noExecute = opts.noExecute,
383 debug = opts.debug,
384 isWindows = (platform.system()=='Windows'))
385
386 # Load the tests from the inputs.
387 tests = []
388 testSuiteCache = {}
389 localConfigCache = {}
390 for input in inputs:
391 prev = len(tests)
392 tests.extend(getTests(input, litConfig,
393 testSuiteCache, localConfigCache))
394 if prev == len(tests):
395 litConfig.warning('input %r contained no tests' % input)
396
397 # If there were any errors during test discovery, exit now.
398 if litConfig.numErrors:
399 print >>sys.stderr, '%d errors, exiting.' % litConfig.numErrors
400 sys.exit(2)
401
402 if opts.showSuites:
403 suitesAndTests = dict([(ts,[])
404 for ts,_ in testSuiteCache.values()])
405 for t in tests:
406 suitesAndTests[t.suite].append(t)
407
408 print '-- Test Suites --'
409 suitesAndTests = suitesAndTests.items()
410 suitesAndTests.sort(key = lambda (ts,_): ts.name)
411 for ts,tests in suitesAndTests:
412 print ' %s - %d tests' %(ts.name, len(tests))
413 print ' Source Root: %s' % ts.source_root
414 print ' Exec Root : %s' % ts.exec_root
415
416 # Select and order the tests.
417 numTotalTests = len(tests)
418 if opts.shuffle:
419 random.shuffle(tests)
420 else:
421 tests.sort(key = lambda t: t.getFullName())
422 if opts.maxTests is not None:
423 tests = tests[:opts.maxTests]
424
425 extra = ''
426 if len(tests) != numTotalTests:
427 extra = ' of %d' % numTotalTests
428 header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
429 opts.numThreads)
430
431 progressBar = None
432 if not opts.quiet:
433 if opts.succinct and opts.useProgressBar:
434 try:
435 tc = ProgressBar.TerminalController()
436 progressBar = ProgressBar.ProgressBar(tc, header)
437 except ValueError:
438 print header
439 progressBar = ProgressBar.SimpleProgressBar('Testing: ')
440 else:
441 print header
442
443 # Don't create more threads than tests.
444 opts.numThreads = min(len(tests), opts.numThreads)
445
446 startTime = time.time()
447 display = TestingProgressDisplay(opts, len(tests), progressBar)
448 provider = TestProvider(tests, opts.maxTime)
449 runTests(opts.numThreads, litConfig, provider, display)
450 display.finish()
451
452 if not opts.quiet:
453 print 'Testing Time: %.2fs'%(time.time() - startTime)
454
455 # Update results for any tests which weren't run.
456 for t in tests:
457 if t.result is None:
458 t.setResult(Test.UNRESOLVED, '', 0.0)
459
460 # List test results organized by kind.
461 hasFailures = False
462 byCode = {}
463 for t in tests:
464 if t.result not in byCode:
465 byCode[t.result] = []
466 byCode[t.result].append(t)
467 if t.result.isFailure:
468 hasFailures = True
469
470 # FIXME: Show unresolved and (optionally) unsupported tests.
471 for title,code in (('Unexpected Passing Tests', Test.XPASS),
472 ('Failing Tests', Test.FAIL)):
473 elts = byCode.get(code)
474 if not elts:
475 continue
476 print '*'*20
477 print '%s (%d):' % (title, len(elts))
478 for t in elts:
479 print ' %s' % t.getFullName()
480 print
481
482 if opts.timeTests:
483 byTime = list(tests)
484 byTime.sort(key = lambda t: t.elapsed)
485 if byTime:
486 Util.printHistogram([(t.getFullName(), t.elapsed) for t in byTime],
487 title='Tests')
488
489 for name,code in (('Expected Passes ', Test.PASS),
490 ('Expected Failures ', Test.XFAIL),
491 ('Unsupported Tests ', Test.UNSUPPORTED),
492 ('Unresolved Tests ', Test.UNRESOLVED),
493 ('Unexpected Passes ', Test.XPASS),
494 ('Unexpected Failures', Test.FAIL),):
495 if opts.quiet and not code.isFailure:
496 continue
497 N = len(byCode.get(code,[]))
498 if N:
499 print ' %s: %d' % (name,N)
500
501 # If we encountered any additional errors, exit abnormally.
502 if litConfig.numErrors:
503 print >>sys.stderr, '\n%d error(s), exiting.' % litConfig.numErrors
504 sys.exit(2)
505
506 # Warn about warnings.
507 if litConfig.numWarnings:
508 print >>sys.stderr, '\n%d warning(s) in tests.' % litConfig.numWarnings
509
510 if hasFailures:
511 sys.exit(1)
512 sys.exit(0)
513
514if __name__=='__main__':
515 # Bump the GIL check interval, its more important to get any one thread to a
516 # blocking operation (hopefully exec) than to try and unblock other threads.
517 import sys
518 sys.setcheckinterval(1000)
519 main()