Mercurial > hg
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 |