comparison tests/run-tests.py @ 25046:40b4308d5653

run-tests: switch all uses of iolock.acquire() to a context manager
author Augie Fackler <augie@google.com>
date Mon, 13 Apr 2015 15:55:48 -0400
parents 8a425c2eef5d
children 8c7e938c8146
comparison
equal deleted inserted replaced
25045:8a425c2eef5d 25046:40b4308d5653
369 def log(*msg): 369 def log(*msg):
370 """Log something to stdout. 370 """Log something to stdout.
371 371
372 Arguments are strings to print. 372 Arguments are strings to print.
373 """ 373 """
374 iolock.acquire() 374 with iolock:
375 if verbose: 375 if verbose:
376 print(verbose, end=' ') 376 print(verbose, end=' ')
377 for m in msg: 377 for m in msg:
378 print(m, end=' ') 378 print(m, end=' ')
379 print() 379 print()
380 sys.stdout.flush() 380 sys.stdout.flush()
381 iolock.release()
382 381
383 def terminate(proc): 382 def terminate(proc):
384 """Terminate subprocess (with fallback for Python versions < 2.6)""" 383 """Terminate subprocess (with fallback for Python versions < 2.6)"""
385 vlog('# Terminating process %d' % proc.pid) 384 vlog('# Terminating process %d' % proc.pid)
386 try: 385 try:
1207 self.failures.append((test, reason)) 1206 self.failures.append((test, reason))
1208 1207
1209 if self._options.first: 1208 if self._options.first:
1210 self.stop() 1209 self.stop()
1211 else: 1210 else:
1212 iolock.acquire() 1211 with iolock:
1213 if not self._options.nodiff: 1212 if not self._options.nodiff:
1214 self.stream.write('\nERROR: %s output changed\n' % test) 1213 self.stream.write('\nERROR: %s output changed\n' % test)
1215 1214
1216 self.stream.write('!') 1215 self.stream.write('!')
1217 self.stream.flush() 1216 self.stream.flush()
1218 iolock.release()
1219 1217
1220 def addSuccess(self, test): 1218 def addSuccess(self, test):
1221 iolock.acquire() 1219 with iolock:
1222 super(TestResult, self).addSuccess(test) 1220 super(TestResult, self).addSuccess(test)
1223 iolock.release()
1224 self.successes.append(test) 1221 self.successes.append(test)
1225 1222
1226 def addError(self, test, err): 1223 def addError(self, test, err):
1227 super(TestResult, self).addError(test, err) 1224 super(TestResult, self).addError(test, err)
1228 if self._options.first: 1225 if self._options.first:
1229 self.stop() 1226 self.stop()
1230 1227
1231 # Polyfill. 1228 # Polyfill.
1232 def addSkip(self, test, reason): 1229 def addSkip(self, test, reason):
1233 self.skipped.append((test, reason)) 1230 self.skipped.append((test, reason))
1234 iolock.acquire() 1231 with iolock:
1235 if self.showAll: 1232 if self.showAll:
1236 self.stream.writeln('skipped %s' % reason) 1233 self.stream.writeln('skipped %s' % reason)
1237 else: 1234 else:
1238 self.stream.write('s') 1235 self.stream.write('s')
1239 self.stream.flush() 1236 self.stream.flush()
1240 iolock.release()
1241 1237
1242 def addIgnore(self, test, reason): 1238 def addIgnore(self, test, reason):
1243 self.ignored.append((test, reason)) 1239 self.ignored.append((test, reason))
1244 iolock.acquire() 1240 with iolock:
1245 if self.showAll: 1241 if self.showAll:
1246 self.stream.writeln('ignored %s' % reason) 1242 self.stream.writeln('ignored %s' % reason)
1247 else:
1248 if reason != 'not retesting' and reason != "doesn't match keyword":
1249 self.stream.write('i')
1250 else: 1243 else:
1251 self.testsRun += 1 1244 if reason not in ('not retesting', "doesn't match keyword"):
1252 self.stream.flush() 1245 self.stream.write('i')
1253 iolock.release() 1246 else:
1247 self.testsRun += 1
1248 self.stream.flush()
1254 1249
1255 def addWarn(self, test, reason): 1250 def addWarn(self, test, reason):
1256 self.warned.append((test, reason)) 1251 self.warned.append((test, reason))
1257 1252
1258 if self._options.first: 1253 if self._options.first:
1259 self.stop() 1254 self.stop()
1260 1255
1261 iolock.acquire() 1256 with iolock:
1262 if self.showAll: 1257 if self.showAll:
1263 self.stream.writeln('warned %s' % reason) 1258 self.stream.writeln('warned %s' % reason)
1264 else: 1259 else:
1265 self.stream.write('~') 1260 self.stream.write('~')
1266 self.stream.flush() 1261 self.stream.flush()
1267 iolock.release()
1268 1262
1269 def addOutputMismatch(self, test, ret, got, expected): 1263 def addOutputMismatch(self, test, ret, got, expected):
1270 """Record a mismatch in test output for a particular test.""" 1264 """Record a mismatch in test output for a particular test."""
1271 if self.shouldStop: 1265 if self.shouldStop:
1272 # don't print, some other test case already failed and 1266 # don't print, some other test case already failed and
1276 1270
1277 accepted = False 1271 accepted = False
1278 failed = False 1272 failed = False
1279 lines = [] 1273 lines = []
1280 1274
1281 iolock.acquire() 1275 with iolock:
1282 if self._options.nodiff: 1276 if self._options.nodiff:
1283 pass 1277 pass
1284 elif self._options.view: 1278 elif self._options.view:
1285 os.system("%s %s %s" % 1279 os.system("%s %s %s" %
1286 (self._options.view, test.refpath, test.errpath)) 1280 (self._options.view, test.refpath, test.errpath))
1287 else:
1288 servefail, lines = getdiff(expected, got,
1289 test.refpath, test.errpath)
1290 if servefail:
1291 self.addFailure(
1292 test,
1293 'server failed to start (HGPORT=%s)' % test._startport)
1294 else: 1281 else:
1295 self.stream.write('\n') 1282 servefail, lines = getdiff(expected, got,
1296 for line in lines: 1283 test.refpath, test.errpath)
1297 self.stream.write(line) 1284 if servefail:
1298 self.stream.flush() 1285 self.addFailure(
1299 1286 test,
1300 # handle interactive prompt without releasing iolock 1287 'server failed to start (HGPORT=%s)' % test._startport)
1301 if self._options.interactive:
1302 self.stream.write('Accept this change? [n] ')
1303 answer = sys.stdin.readline().strip()
1304 if answer.lower() in ('y', 'yes'):
1305 if test.name.endswith('.t'):
1306 rename(test.errpath, test.path)
1307 else: 1288 else:
1308 rename(test.errpath, '%s.out' % test.path) 1289 self.stream.write('\n')
1309 accepted = True 1290 for line in lines:
1310 if not accepted and not failed: 1291 self.stream.write(line)
1311 self.faildata[test.name] = ''.join(lines) 1292 self.stream.flush()
1312 iolock.release() 1293
1294 # handle interactive prompt without releasing iolock
1295 if self._options.interactive:
1296 self.stream.write('Accept this change? [n] ')
1297 answer = sys.stdin.readline().strip()
1298 if answer.lower() in ('y', 'yes'):
1299 if test.name.endswith('.t'):
1300 rename(test.errpath, test.path)
1301 else:
1302 rename(test.errpath, '%s.out' % test.path)
1303 accepted = True
1304 if not accepted and not failed:
1305 self.faildata[test.name] = ''.join(lines)
1313 1306
1314 return accepted 1307 return accepted
1315 1308
1316 def startTest(self, test): 1309 def startTest(self, test):
1317 super(TestResult, self).startTest(test) 1310 super(TestResult, self).startTest(test)
1334 endtime[3] - starttime[3], # sys space CPU time 1327 endtime[3] - starttime[3], # sys space CPU time
1335 endtime[4] - starttime[4], # real time 1328 endtime[4] - starttime[4], # real time
1336 )) 1329 ))
1337 1330
1338 if interrupted: 1331 if interrupted:
1339 iolock.acquire() 1332 with iolock:
1340 self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % ( 1333 self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % (
1341 test.name, self.times[-1][3])) 1334 test.name, self.times[-1][3]))
1342 iolock.release()
1343 1335
1344 class TestSuite(unittest.TestSuite): 1336 class TestSuite(unittest.TestSuite):
1345 """Custom unittest TestSuite that knows how to execute Mercurial tests.""" 1337 """Custom unittest TestSuite that knows how to execute Mercurial tests."""
1346 1338
1347 def __init__(self, testdir, jobs=1, whitelist=None, blacklist=None, 1339 def __init__(self, testdir, jobs=1, whitelist=None, blacklist=None,
1500 failed = len(result.failures) 1492 failed = len(result.failures)
1501 warned = len(result.warned) 1493 warned = len(result.warned)
1502 skipped = len(result.skipped) 1494 skipped = len(result.skipped)
1503 ignored = len(result.ignored) 1495 ignored = len(result.ignored)
1504 1496
1505 iolock.acquire() 1497 with iolock:
1506 self.stream.writeln('') 1498 self.stream.writeln('')
1507 1499
1508 if not self._runner.options.noskips: 1500 if not self._runner.options.noskips:
1509 for test, msg in result.skipped: 1501 for test, msg in result.skipped:
1510 self.stream.writeln('Skipped %s: %s' % (test.name, msg)) 1502 self.stream.writeln('Skipped %s: %s' % (test.name, msg))
1511 for test, msg in result.warned: 1503 for test, msg in result.warned:
1512 self.stream.writeln('Warned %s: %s' % (test.name, msg)) 1504 self.stream.writeln('Warned %s: %s' % (test.name, msg))
1513 for test, msg in result.failures: 1505 for test, msg in result.failures:
1514 self.stream.writeln('Failed %s: %s' % (test.name, msg)) 1506 self.stream.writeln('Failed %s: %s' % (test.name, msg))
1515 for test, msg in result.errors: 1507 for test, msg in result.errors:
1516 self.stream.writeln('Errored %s: %s' % (test.name, msg)) 1508 self.stream.writeln('Errored %s: %s' % (test.name, msg))
1517 1509
1518 if self._runner.options.xunit: 1510 if self._runner.options.xunit:
1519 xuf = open(self._runner.options.xunit, 'wb') 1511 xuf = open(self._runner.options.xunit, 'wb')
1520 try: 1512 try:
1521 timesd = dict((t[0], t[3]) for t in result.times) 1513 timesd = dict((t[0], t[3]) for t in result.times)
1522 doc = minidom.Document() 1514 doc = minidom.Document()
1523 s = doc.createElement('testsuite') 1515 s = doc.createElement('testsuite')
1524 s.setAttribute('name', 'run-tests') 1516 s.setAttribute('name', 'run-tests')
1525 s.setAttribute('tests', str(result.testsRun)) 1517 s.setAttribute('tests', str(result.testsRun))
1526 s.setAttribute('errors', "0") # TODO 1518 s.setAttribute('errors', "0") # TODO
1527 s.setAttribute('failures', str(failed)) 1519 s.setAttribute('failures', str(failed))
1528 s.setAttribute('skipped', str(skipped + ignored)) 1520 s.setAttribute('skipped', str(skipped + ignored))
1529 doc.appendChild(s) 1521 doc.appendChild(s)
1530 for tc in result.successes: 1522 for tc in result.successes:
1531 t = doc.createElement('testcase') 1523 t = doc.createElement('testcase')
1532 t.setAttribute('name', tc.name) 1524 t.setAttribute('name', tc.name)
1533 t.setAttribute('time', '%.3f' % timesd[tc.name]) 1525 t.setAttribute('time', '%.3f' % timesd[tc.name])
1534 s.appendChild(t) 1526 s.appendChild(t)
1535 for tc, err in sorted(result.faildata.iteritems()): 1527 for tc, err in sorted(result.faildata.iteritems()):
1536 t = doc.createElement('testcase') 1528 t = doc.createElement('testcase')
1537 t.setAttribute('name', tc) 1529 t.setAttribute('name', tc)
1538 t.setAttribute('time', '%.3f' % timesd[tc]) 1530 t.setAttribute('time', '%.3f' % timesd[tc])
1539 # createCDATASection expects a unicode or it will convert 1531 # createCDATASection expects a unicode or it will
1540 # using default conversion rules, which will fail if 1532 # convert using default conversion rules, which will
1541 # string isn't ASCII. 1533 # fail if string isn't ASCII.
1542 err = cdatasafe(err).decode('utf-8', 'replace') 1534 err = cdatasafe(err).decode('utf-8', 'replace')
1543 cd = doc.createCDATASection(err) 1535 cd = doc.createCDATASection(err)
1544 t.appendChild(cd) 1536 t.appendChild(cd)
1545 s.appendChild(t) 1537 s.appendChild(t)
1546 xuf.write(doc.toprettyxml(indent=' ', encoding='utf-8')) 1538 xuf.write(doc.toprettyxml(indent=' ', encoding='utf-8'))
1547 finally: 1539 finally:
1548 xuf.close() 1540 xuf.close()
1549 1541
1550 if self._runner.options.json: 1542 if self._runner.options.json:
1551 if json is None: 1543 if json is None:
1552 raise ImportError("json module not installed") 1544 raise ImportError("json module not installed")
1553 jsonpath = os.path.join(self._runner._testdir, 'report.json') 1545 jsonpath = os.path.join(self._runner._testdir, 'report.json')
1554 fp = open(jsonpath, 'w') 1546 fp = open(jsonpath, 'w')
1555 try: 1547 try:
1556 timesd = {} 1548 timesd = {}
1557 for tdata in result.times: 1549 for tdata in result.times:
1558 test = tdata[0] 1550 test = tdata[0]
1559 timesd[test] = tdata[1:] 1551 timesd[test] = tdata[1:]
1560 1552
1561 outcome = {} 1553 outcome = {}
1562 groups = [('success', ((tc, None) for tc in result.successes)), 1554 groups = [('success', ((tc, None)
1563 ('failure', result.failures), 1555 for tc in result.successes)),
1564 ('skip', result.skipped)] 1556 ('failure', result.failures),
1565 for res, testcases in groups: 1557 ('skip', result.skipped)]
1566 for tc, __ in testcases: 1558 for res, testcases in groups:
1567 testresult = {'result': res, 1559 for tc, __ in testcases:
1568 'time': ('%0.3f' % timesd[tc.name][2]), 1560 tres = {'result': res,
1569 'cuser': ('%0.3f' % timesd[tc.name][0]), 1561 'time': ('%0.3f' % timesd[tc.name][2]),
1570 'csys': ('%0.3f' % timesd[tc.name][1])} 1562 'cuser': ('%0.3f' % timesd[tc.name][0]),
1571 outcome[tc.name] = testresult 1563 'csys': ('%0.3f' % timesd[tc.name][1])}
1572 1564 outcome[tc.name] = tres
1573 jsonout = json.dumps(outcome, sort_keys=True, indent=4) 1565
1574 fp.writelines(("testreport =", jsonout)) 1566 jsonout = json.dumps(outcome, sort_keys=True, indent=4)
1575 finally: 1567 fp.writelines(("testreport =", jsonout))
1576 fp.close() 1568 finally:
1577 1569 fp.close()
1578 self._runner._checkhglib('Tested') 1570
1579 1571 self._runner._checkhglib('Tested')
1580 self.stream.writeln('# Ran %d tests, %d skipped, %d warned, %d failed.' 1572
1581 % (result.testsRun, 1573 self.stream.writeln(
1582 skipped + ignored, warned, failed)) 1574 '# Ran %d tests, %d skipped, %d warned, %d failed.'
1583 if failed: 1575 % (result.testsRun,
1584 self.stream.writeln('python hash seed: %s' % 1576 skipped + ignored, warned, failed))
1585 os.environ['PYTHONHASHSEED']) 1577 if failed:
1586 if self._runner.options.time: 1578 self.stream.writeln('python hash seed: %s' %
1587 self.printtimes(result.times) 1579 os.environ['PYTHONHASHSEED'])
1588 1580 if self._runner.options.time:
1589 iolock.release() 1581 self.printtimes(result.times)
1590 1582
1591 return result 1583 return result
1592 1584
1593 def printtimes(self, times): 1585 def printtimes(self, times):
1594 # iolock held by run 1586 # iolock held by run