lit: Add --param NAME=VALUE option, for test suite specific use (to communicate
[oota-llvm.git] / utils / lit / lit.py
1 #!/usr/bin/env python
2
3 """
4 lit - LLVM Integrated Tester.
5
6 See lit.pod for more information.
7 """
8
9 import math, os, platform, random, re, sys, time, threading, traceback
10
11 import ProgressBar
12 import TestRunner
13 import Util
14
15 from TestingConfig import TestingConfig
16 import LitConfig
17 import Test
18
19 # Configuration files to look for when discovering test suites. These can be
20 # overridden with --config-prefix.
21 #
22 # FIXME: Rename to 'config.lit', 'site.lit', and 'local.lit' ?
23 gConfigName = 'lit.cfg'
24 gSiteConfigName = 'lit.site.cfg'
25
26 kLocalConfigName = 'lit.local.cfg'
27
28 class 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:
40             self.completed += 1
41             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
81 class 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
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
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
140 def dirContainsTestSuite(path):
141     cfgpath = os.path.join(path, gSiteConfigName)
142     if os.path.exists(cfgpath):
143         return cfgpath
144     cfgpath = os.path.join(path, gConfigName)
145     if os.path.exists(cfgpath):
146         return cfgpath
147
148 def 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)
164             if parent == path:
165                 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
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:
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
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)
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
242 def 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)
259
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.
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:
269             continue
270
271         # Ignore non-directories.
272         file_sourcepath = os.path.join(source_path, filename)
273         if not os.path.isdir(file_sourcepath):
274             continue
275
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             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)
290
291         for res in subiter:
292             yield res
293
294 def 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
313 def 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)
321     parser.add_option("", "--config-prefix", dest="configPrefix",
322                       metavar="NAME", help="Prefix for 'lit' config files",
323                       action="store", default=None)
324     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=[])
328
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
393     if opts.configPrefix is not None:
394         global gConfigName, gSiteConfigName
395         gConfigName = '%s.cfg' % opts.configPrefix
396         gSiteConfigName = '%s.site.cfg' % opts.configPrefix
397
398     if opts.numThreads is None:
399         opts.numThreads = Util.detectCPUs()
400
401     inputs = args
402
403     # 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
412     # 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,
421                                     isWindows = (platform.system()=='Windows'),
422                                     params = userParams)
423
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,[])
442                                for ts,_ in testSuiteCache.values()
443                                if ts])
444         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
553 if __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()