main.py revision abb9de5257375dbf10c87bdbf40ecafa777a0881
1#!/usr/bin/env python 2 3""" 4lit - LLVM Integrated Tester. 5 6See lit.pod for more information. 7""" 8 9from __future__ import absolute_import 10import math, os, platform, random, re, sys, time, threading, traceback 11 12import lit.ProgressBar 13import lit.LitConfig 14import lit.Test 15import lit.Util 16 17import lit.discovery 18 19class TestingProgressDisplay: 20 def __init__(self, opts, numTests, progressBar=None): 21 self.opts = opts 22 self.numTests = numTests 23 self.current = None 24 self.lock = threading.Lock() 25 self.progressBar = progressBar 26 self.completed = 0 27 28 def update(self, test): 29 # Avoid locking overhead in quiet mode 30 if self.opts.quiet and not test.result.isFailure: 31 self.completed += 1 32 return 33 34 # Output lock. 35 self.lock.acquire() 36 try: 37 self.handleUpdate(test) 38 finally: 39 self.lock.release() 40 41 def finish(self): 42 if self.progressBar: 43 self.progressBar.clear() 44 elif self.opts.quiet: 45 pass 46 elif self.opts.succinct: 47 sys.stdout.write('\n') 48 49 def handleUpdate(self, test): 50 self.completed += 1 51 if self.progressBar: 52 self.progressBar.update(float(self.completed)/self.numTests, 53 test.getFullName()) 54 55 if self.opts.succinct and not test.result.isFailure: 56 return 57 58 if self.progressBar: 59 self.progressBar.clear() 60 61 print('%s: %s (%d of %d)' % (test.result.name, test.getFullName(), 62 self.completed, self.numTests)) 63 64 if test.result.isFailure and self.opts.showOutput: 65 print("%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(), 66 '*'*20)) 67 print(test.output) 68 print("*" * 20) 69 70 sys.stdout.flush() 71 72class TestProvider: 73 def __init__(self, tests, maxTime): 74 self.maxTime = maxTime 75 self.iter = iter(tests) 76 self.lock = threading.Lock() 77 self.startTime = time.time() 78 self.canceled = False 79 80 def cancel(self): 81 self.lock.acquire() 82 self.canceled = True 83 self.lock.release() 84 85 def get(self): 86 # Check if we have run out of time. 87 if self.maxTime is not None: 88 if time.time() - self.startTime > self.maxTime: 89 return None 90 91 # Otherwise take the next test. 92 self.lock.acquire() 93 if self.canceled: 94 self.lock.release() 95 return None 96 for item in self.iter: 97 break 98 else: 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 = lit.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 runTests(numThreads, litConfig, provider, display): 141 # If only using one testing thread, don't use threads at all; this lets us 142 # profile, among other things. 143 if numThreads == 1: 144 t = Tester(litConfig, provider, display) 145 t.run() 146 return 147 148 # Otherwise spin up the testing threads and wait for them to finish. 149 testers = [Tester(litConfig, provider, display) 150 for i in range(numThreads)] 151 for t in testers: 152 t.start() 153 try: 154 for t in testers: 155 t.join() 156 except KeyboardInterrupt: 157 sys.exit(2) 158 159def main(builtinParameters = {}): 160 # Bump the GIL check interval, its more important to get any one thread to a 161 # blocking operation (hopefully exec) than to try and unblock other threads. 162 # 163 # FIXME: This is a hack. 164 sys.setcheckinterval(1000) 165 166 global options 167 from optparse import OptionParser, OptionGroup 168 parser = OptionParser("usage: %prog [options] {file-or-path}") 169 170 parser.add_option("-j", "--threads", dest="numThreads", metavar="N", 171 help="Number of testing threads", 172 type=int, action="store", default=None) 173 parser.add_option("", "--config-prefix", dest="configPrefix", 174 metavar="NAME", help="Prefix for 'lit' config files", 175 action="store", default=None) 176 parser.add_option("", "--param", dest="userParameters", 177 metavar="NAME=VAL", 178 help="Add 'NAME' = 'VAL' to the user defined parameters", 179 type=str, action="append", default=[]) 180 181 group = OptionGroup(parser, "Output Format") 182 # FIXME: I find these names very confusing, although I like the 183 # functionality. 184 group.add_option("-q", "--quiet", dest="quiet", 185 help="Suppress no error output", 186 action="store_true", default=False) 187 group.add_option("-s", "--succinct", dest="succinct", 188 help="Reduce amount of output", 189 action="store_true", default=False) 190 group.add_option("-v", "--verbose", dest="showOutput", 191 help="Show all test output", 192 action="store_true", default=False) 193 group.add_option("", "--no-progress-bar", dest="useProgressBar", 194 help="Do not use curses based progress bar", 195 action="store_false", default=True) 196 parser.add_option_group(group) 197 198 group = OptionGroup(parser, "Test Execution") 199 group.add_option("", "--path", dest="path", 200 help="Additional paths to add to testing environment", 201 action="append", type=str, default=[]) 202 group.add_option("", "--vg", dest="useValgrind", 203 help="Run tests under valgrind", 204 action="store_true", default=False) 205 group.add_option("", "--vg-leak", dest="valgrindLeakCheck", 206 help="Check for memory leaks under valgrind", 207 action="store_true", default=False) 208 group.add_option("", "--vg-arg", dest="valgrindArgs", metavar="ARG", 209 help="Specify an extra argument for valgrind", 210 type=str, action="append", default=[]) 211 group.add_option("", "--time-tests", dest="timeTests", 212 help="Track elapsed wall time for each test", 213 action="store_true", default=False) 214 group.add_option("", "--no-execute", dest="noExecute", 215 help="Don't execute any tests (assume PASS)", 216 action="store_true", default=False) 217 parser.add_option_group(group) 218 219 group = OptionGroup(parser, "Test Selection") 220 group.add_option("", "--max-tests", dest="maxTests", metavar="N", 221 help="Maximum number of tests to run", 222 action="store", type=int, default=None) 223 group.add_option("", "--max-time", dest="maxTime", metavar="N", 224 help="Maximum time to spend testing (in seconds)", 225 action="store", type=float, default=None) 226 group.add_option("", "--shuffle", dest="shuffle", 227 help="Run tests in random order", 228 action="store_true", default=False) 229 group.add_option("", "--filter", dest="filter", metavar="REGEX", 230 help=("Only run tests with paths matching the given " 231 "regular expression"), 232 action="store", default=None) 233 parser.add_option_group(group) 234 235 group = OptionGroup(parser, "Debug and Experimental Options") 236 group.add_option("", "--debug", dest="debug", 237 help="Enable debugging (for 'lit' development)", 238 action="store_true", default=False) 239 group.add_option("", "--show-suites", dest="showSuites", 240 help="Show discovered test suites", 241 action="store_true", default=False) 242 group.add_option("", "--show-tests", dest="showTests", 243 help="Show all discovered tests", 244 action="store_true", default=False) 245 parser.add_option_group(group) 246 247 (opts, args) = parser.parse_args() 248 249 if not args: 250 parser.error('No inputs specified') 251 252 if opts.numThreads is None: 253# Python <2.5 has a race condition causing lit to always fail with numThreads>1 254# http://bugs.python.org/issue1731717 255# I haven't seen this bug occur with 2.5.2 and later, so only enable multiple 256# threads by default there. 257 if sys.hexversion >= 0x2050200: 258 opts.numThreads = lit.Util.detectCPUs() 259 else: 260 opts.numThreads = 1 261 262 inputs = args 263 264 # Create the user defined parameters. 265 userParams = dict(builtinParameters) 266 for entry in opts.userParameters: 267 if '=' not in entry: 268 name,val = entry,'' 269 else: 270 name,val = entry.split('=', 1) 271 userParams[name] = val 272 273 # Create the global config object. 274 litConfig = lit.LitConfig.LitConfig( 275 progname = os.path.basename(sys.argv[0]), 276 path = opts.path, 277 quiet = opts.quiet, 278 useValgrind = opts.useValgrind, 279 valgrindLeakCheck = opts.valgrindLeakCheck, 280 valgrindArgs = opts.valgrindArgs, 281 noExecute = opts.noExecute, 282 debug = opts.debug, 283 isWindows = (platform.system()=='Windows'), 284 params = userParams, 285 config_prefix = opts.configPrefix) 286 287 tests = lit.discovery.find_tests_for_inputs(litConfig, inputs) 288 289 if opts.showSuites or opts.showTests: 290 # Aggregate the tests by suite. 291 suitesAndTests = {} 292 for t in tests: 293 if t.suite not in suitesAndTests: 294 suitesAndTests[t.suite] = [] 295 suitesAndTests[t.suite].append(t) 296 suitesAndTests = list(suitesAndTests.items()) 297 suitesAndTests.sort(key = lambda item: item[0].name) 298 299 # Show the suites, if requested. 300 if opts.showSuites: 301 print('-- Test Suites --') 302 for ts,ts_tests in suitesAndTests: 303 print(' %s - %d tests' %(ts.name, len(ts_tests))) 304 print(' Source Root: %s' % ts.source_root) 305 print(' Exec Root : %s' % ts.exec_root) 306 307 # Show the tests, if requested. 308 if opts.showTests: 309 print('-- Available Tests --') 310 for ts,ts_tests in suitesAndTests: 311 ts_tests.sort(key = lambda test: test.path_in_suite) 312 for test in ts_tests: 313 print(' %s' % (test.getFullName(),)) 314 315 # Exit. 316 sys.exit(0) 317 318 # Select and order the tests. 319 numTotalTests = len(tests) 320 321 # First, select based on the filter expression if given. 322 if opts.filter: 323 try: 324 rex = re.compile(opts.filter) 325 except: 326 parser.error("invalid regular expression for --filter: %r" % ( 327 opts.filter)) 328 tests = [t for t in tests 329 if rex.search(t.getFullName())] 330 331 # Then select the order. 332 if opts.shuffle: 333 random.shuffle(tests) 334 else: 335 tests.sort(key = lambda t: t.getFullName()) 336 337 # Finally limit the number of tests, if desired. 338 if opts.maxTests is not None: 339 tests = tests[:opts.maxTests] 340 341 # Don't create more threads than tests. 342 opts.numThreads = min(len(tests), opts.numThreads) 343 344 extra = '' 345 if len(tests) != numTotalTests: 346 extra = ' of %d' % numTotalTests 347 header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra, 348 opts.numThreads) 349 350 progressBar = None 351 if not opts.quiet: 352 if opts.succinct and opts.useProgressBar: 353 try: 354 tc = lit.ProgressBar.TerminalController() 355 progressBar = lit.ProgressBar.ProgressBar(tc, header) 356 except ValueError: 357 print(header) 358 progressBar = lit.ProgressBar.SimpleProgressBar('Testing: ') 359 else: 360 print(header) 361 362 startTime = time.time() 363 display = TestingProgressDisplay(opts, len(tests), progressBar) 364 provider = TestProvider(tests, opts.maxTime) 365 366 try: 367 import win32api 368 except ImportError: 369 pass 370 else: 371 def console_ctrl_handler(type): 372 provider.cancel() 373 return True 374 win32api.SetConsoleCtrlHandler(console_ctrl_handler, True) 375 376 runTests(opts.numThreads, litConfig, provider, display) 377 display.finish() 378 379 if not opts.quiet: 380 print('Testing Time: %.2fs'%(time.time() - startTime)) 381 382 # Update results for any tests which weren't run. 383 for t in tests: 384 if t.result is None: 385 t.setResult(lit.Test.UNRESOLVED, '', 0.0) 386 387 # List test results organized by kind. 388 hasFailures = False 389 byCode = {} 390 for t in tests: 391 if t.result not in byCode: 392 byCode[t.result] = [] 393 byCode[t.result].append(t) 394 if t.result.isFailure: 395 hasFailures = True 396 397 # Print each test in any of the failing groups. 398 for title,code in (('Unexpected Passing Tests', lit.Test.XPASS), 399 ('Failing Tests', lit.Test.FAIL), 400 ('Unresolved Tests', lit.Test.UNRESOLVED)): 401 elts = byCode.get(code) 402 if not elts: 403 continue 404 print('*'*20) 405 print('%s (%d):' % (title, len(elts))) 406 for t in elts: 407 print(' %s' % t.getFullName()) 408 sys.stdout.write('\n') 409 410 if opts.timeTests: 411 # Collate, in case we repeated tests. 412 times = {} 413 for t in tests: 414 key = t.getFullName() 415 times[key] = times.get(key, 0.) + t.elapsed 416 417 byTime = list(times.items()) 418 byTime.sort(key = lambda item: item[1]) 419 if byTime: 420 lit.Util.printHistogram(byTime, title='Tests') 421 422 for name,code in (('Expected Passes ', lit.Test.PASS), 423 ('Expected Failures ', lit.Test.XFAIL), 424 ('Unsupported Tests ', lit.Test.UNSUPPORTED), 425 ('Unresolved Tests ', lit.Test.UNRESOLVED), 426 ('Unexpected Passes ', lit.Test.XPASS), 427 ('Unexpected Failures', lit.Test.FAIL),): 428 if opts.quiet and not code.isFailure: 429 continue 430 N = len(byCode.get(code,[])) 431 if N: 432 print(' %s: %d' % (name,N)) 433 434 # If we encountered any additional errors, exit abnormally. 435 if litConfig.numErrors: 436 sys.stderr.write('\n%d error(s), exiting.\n' % litConfig.numErrors) 437 sys.exit(2) 438 439 # Warn about warnings. 440 if litConfig.numWarnings: 441 sys.stderr.write('\n%d warning(s) in tests.\n' % litConfig.numWarnings) 442 443 if hasFailures: 444 sys.exit(1) 445 sys.exit(0) 446 447if __name__=='__main__': 448 main() 449