tests/run-tests.py
changeset 25046 40b4308d5653
parent 25045 8a425c2eef5d
child 25047 8c7e938c8146
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