Mercurial > hg
comparison tests/run-tests.py @ 24330:799bc18e14d1
run-tests: avoid running the same test instance concurrently
There's a fair amount of mutable state stored on test case
instances. That causes many weird failures if you try to do something
like `run-tests.py -j16 --loop test-help.t`. The quick fix is this
slightly weird test-reloading dance, which ensures that every time a
test is executed it runs on a fresh instance of the TestCase subclass.
author | Augie Fackler <augie@google.com> |
---|---|
date | Fri, 13 Mar 2015 12:47:16 -0400 |
parents | e7ca4d4b10e1 |
children | d3bdd8c7174f |
comparison
equal
deleted
inserted
replaced
24329:e7ca4d4b10e1 | 24330:799bc18e14d1 |
---|---|
1289 class TestSuite(unittest.TestSuite): | 1289 class TestSuite(unittest.TestSuite): |
1290 """Custom unittest TestSuite that knows how to execute Mercurial tests.""" | 1290 """Custom unittest TestSuite that knows how to execute Mercurial tests.""" |
1291 | 1291 |
1292 def __init__(self, testdir, jobs=1, whitelist=None, blacklist=None, | 1292 def __init__(self, testdir, jobs=1, whitelist=None, blacklist=None, |
1293 retest=False, keywords=None, loop=False, runs_per_test=1, | 1293 retest=False, keywords=None, loop=False, runs_per_test=1, |
1294 loadtest=None, | |
1294 *args, **kwargs): | 1295 *args, **kwargs): |
1295 """Create a new instance that can run tests with a configuration. | 1296 """Create a new instance that can run tests with a configuration. |
1296 | 1297 |
1297 testdir specifies the directory where tests are executed from. This | 1298 testdir specifies the directory where tests are executed from. This |
1298 is typically the ``tests`` directory from Mercurial's source | 1299 is typically the ``tests`` directory from Mercurial's source |
1324 self._blacklist = blacklist | 1325 self._blacklist = blacklist |
1325 self._retest = retest | 1326 self._retest = retest |
1326 self._keywords = keywords | 1327 self._keywords = keywords |
1327 self._loop = loop | 1328 self._loop = loop |
1328 self._runs_per_test = runs_per_test | 1329 self._runs_per_test = runs_per_test |
1330 self._loadtest = loadtest | |
1329 | 1331 |
1330 def run(self, result): | 1332 def run(self, result): |
1331 # We have a number of filters that need to be applied. We do this | 1333 # We have a number of filters that need to be applied. We do this |
1332 # here instead of inside Test because it makes the running logic for | 1334 # here instead of inside Test because it makes the running logic for |
1333 # Test simpler. | 1335 # Test simpler. |
1334 tests = [] | 1336 tests = [] |
1337 num_tests = [0] | |
1335 for test in self._tests: | 1338 for test in self._tests: |
1339 def get(): | |
1340 num_tests[0] += 1 | |
1341 if getattr(test, 'should_reload', False): | |
1342 return self._loadtest(test.name, num_tests[0]) | |
1343 return test | |
1336 if not os.path.exists(test.path): | 1344 if not os.path.exists(test.path): |
1337 result.addSkip(test, "Doesn't exist") | 1345 result.addSkip(test, "Doesn't exist") |
1338 continue | 1346 continue |
1339 | 1347 |
1340 if not (self._whitelist and test.name in self._whitelist): | 1348 if not (self._whitelist and test.name in self._whitelist): |
1358 break | 1366 break |
1359 | 1367 |
1360 if ignored: | 1368 if ignored: |
1361 continue | 1369 continue |
1362 for _ in xrange(self._runs_per_test): | 1370 for _ in xrange(self._runs_per_test): |
1363 tests.append(test) | 1371 tests.append(get()) |
1364 | 1372 |
1365 runtests = list(tests) | 1373 runtests = list(tests) |
1366 done = queue.Queue() | 1374 done = queue.Queue() |
1367 running = 0 | 1375 running = 0 |
1368 | 1376 |
1387 continue | 1395 continue |
1388 running -= 1 | 1396 running -= 1 |
1389 if tests and not running == self._jobs: | 1397 if tests and not running == self._jobs: |
1390 test = tests.pop(0) | 1398 test = tests.pop(0) |
1391 if self._loop: | 1399 if self._loop: |
1392 tests.append(test) | 1400 if getattr(test, 'should_reload', False): |
1401 num_tests[0] += 1 | |
1402 tests.append( | |
1403 self._loadtest(test.name, num_tests[0])) | |
1404 else: | |
1405 tests.append(test) | |
1393 t = threading.Thread(target=job, name=test.name, | 1406 t = threading.Thread(target=job, name=test.name, |
1394 args=(test, result)) | 1407 args=(test, result)) |
1395 t.start() | 1408 t.start() |
1396 running += 1 | 1409 running += 1 |
1397 except KeyboardInterrupt: | 1410 except KeyboardInterrupt: |
1731 blacklist=self.options.blacklist, | 1744 blacklist=self.options.blacklist, |
1732 retest=self.options.retest, | 1745 retest=self.options.retest, |
1733 keywords=self.options.keywords, | 1746 keywords=self.options.keywords, |
1734 loop=self.options.loop, | 1747 loop=self.options.loop, |
1735 runs_per_test=self.options.runs_per_test, | 1748 runs_per_test=self.options.runs_per_test, |
1736 tests=tests) | 1749 tests=tests, loadtest=self._gettest) |
1737 verbosity = 1 | 1750 verbosity = 1 |
1738 if self.options.verbose: | 1751 if self.options.verbose: |
1739 verbosity = 2 | 1752 verbosity = 2 |
1740 runner = TextTestRunner(self, verbosity=verbosity) | 1753 runner = TextTestRunner(self, verbosity=verbosity) |
1741 result = runner.run(suite) | 1754 result = runner.run(suite) |
1771 break | 1784 break |
1772 | 1785 |
1773 refpath = os.path.join(self._testdir, test) | 1786 refpath = os.path.join(self._testdir, test) |
1774 tmpdir = os.path.join(self._hgtmp, 'child%d' % count) | 1787 tmpdir = os.path.join(self._hgtmp, 'child%d' % count) |
1775 | 1788 |
1776 return testcls(refpath, tmpdir, | 1789 t = testcls(refpath, tmpdir, |
1777 keeptmpdir=self.options.keep_tmpdir, | 1790 keeptmpdir=self.options.keep_tmpdir, |
1778 debug=self.options.debug, | 1791 debug=self.options.debug, |
1779 timeout=self.options.timeout, | 1792 timeout=self.options.timeout, |
1780 startport=self.options.port + count * 3, | 1793 startport=self.options.port + count * 3, |
1781 extraconfigopts=self.options.extra_config_opt, | 1794 extraconfigopts=self.options.extra_config_opt, |
1782 py3kwarnings=self.options.py3k_warnings, | 1795 py3kwarnings=self.options.py3k_warnings, |
1783 shell=self.options.shell) | 1796 shell=self.options.shell) |
1797 t.should_reload = True | |
1798 return t | |
1784 | 1799 |
1785 def _cleanup(self): | 1800 def _cleanup(self): |
1786 """Clean up state from this test invocation.""" | 1801 """Clean up state from this test invocation.""" |
1787 | 1802 |
1788 if self.options.keep_tmpdir: | 1803 if self.options.keep_tmpdir: |