test-run-tests: stabilize the test (issue5735)

Summary:
Previously there is a race condition because things happen in this order:

  1. Check shouldStop
  2. If shouldStop is false, print the diff
  3. Call fail() -> set shouldStop

The check and set should really happen in a same critical section.

This patch adds a lock to address the issue.

The patch was also sent as https://phab.mercurial-scm.org/D1830

Test Plan: Run `run-tests.py -l test-run-tests.t` many times and it no longer fails.

Reviewers: durham, #mercurial

Reviewed By: durham

Subscribers: durham

Differential Revision: https://phabricator.intern.facebook.com/D6680300

Signature: 6680300:1515524998:260c3e198330e7e6c94dcb6cf155f14a055b760a
This commit is contained in:
Jun Wu 2018-01-08 17:23:24 -08:00
parent 26485b34fb
commit f446276fe1

View File

@ -670,6 +670,7 @@ class Test(unittest.TestCase):
def __init__(self, path, outputdir, tmpdir, keeptmpdir=False,
debug=False,
first=False,
timeout=None,
startport=None, extraconfigopts=None,
py3kwarnings=False, shell=None, hgcommand=None,
@ -722,6 +723,7 @@ class Test(unittest.TestCase):
self._threadtmp = tmpdir
self._keeptmpdir = keeptmpdir
self._debug = debug
self._first = first
self._timeout = timeout
self._slowtimeout = slowtimeout
self._startport = startport
@ -906,9 +908,13 @@ class Test(unittest.TestCase):
f.write(line)
# The result object handles diff calculation for us.
if self._result.addOutputMismatch(self, ret, out, self._refout):
# change was accepted, skip failing
return
with firstlock:
if self._result.addOutputMismatch(self, ret, out, self._refout):
# change was accepted, skip failing
return
if self._first:
global firsterror
firsterror = True
if ret:
msg = 'output changed and ' + describe(ret)
@ -1634,6 +1640,8 @@ class TTest(Test):
return TTest.ESCAPESUB(TTest._escapef, s)
iolock = threading.RLock()
firstlock = threading.RLock()
firsterror = False
class TestResult(unittest._TextTestResult):
"""Holds results when executing via unittest."""
@ -1719,7 +1727,7 @@ class TestResult(unittest._TextTestResult):
def addOutputMismatch(self, test, ret, got, expected):
"""Record a mismatch in test output for a particular test."""
if self.shouldStop:
if self.shouldStop or firsterror:
# don't print, some other test case already failed and
# printed, we're just stale and probably failed due to our
# temp dir getting cleaned up.
@ -2695,6 +2703,7 @@ class TestRunner(object):
t = testcls(refpath, self._outputdir, tmpdir,
keeptmpdir=self.options.keep_tmpdir,
debug=self.options.debug,
first=self.options.first,
timeout=self.options.timeout,
startport=self._getport(count),
extraconfigopts=self.options.extra_config_opt,