Mercurial > public > mercurial-scm > hg
comparison tests/run-tests.py @ 24505:031947baf4d0
run-tests: collect aggregate code coverage
Before this patch, every Python process during a code coverage run was
writing coverage data to the same file. I'm not sure if the coverage
package even tries to obtain a lock on the file. But what I do know is
there was some last write wins leading to loss of code coverage data, at
least with -j > 1.
This patch changes the code coverage mechanism to be multiple process
safe. The mechanism for initializing code coverage via sitecustomize.py
has been tweaked so each Python process will produce a separate coverage
data file on disk. Unless two processes generate the same random value,
there are no race conditions writing to the same file. At the end of the
test run, we combine all written files into an aggregate report.
On my machine, running the full test suite produces a little over
20,000 coverage files consuming ~350 MB. As you can imagine, it takes
several seconds to load and merge these coverage files. But when it is
done, you have an accurate picture of the aggregate code coverage for the
entire test suite, which is ~60% line coverage.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Sat, 28 Mar 2015 00:47:58 -0700 |
parents | 7046ecabd9a8 |
children | 60bbb4079c28 |
comparison
equal
deleted
inserted
replaced
24504:7046ecabd9a8 | 24505:031947baf4d0 |
---|---|
1949 vlog('# Installing coverage trigger to %s' % target) | 1949 vlog('# Installing coverage trigger to %s' % target) |
1950 shutil.copyfile(custom, target) | 1950 shutil.copyfile(custom, target) |
1951 rc = os.path.join(self._testdir, '.coveragerc') | 1951 rc = os.path.join(self._testdir, '.coveragerc') |
1952 vlog('# Installing coverage rc to %s' % rc) | 1952 vlog('# Installing coverage rc to %s' % rc) |
1953 os.environ['COVERAGE_PROCESS_START'] = rc | 1953 os.environ['COVERAGE_PROCESS_START'] = rc |
1954 fn = os.path.join(self._installdir, '..', '.coverage') | 1954 covdir = os.path.join(self._installdir, '..', 'coverage') |
1955 os.environ['COVERAGE_FILE'] = fn | 1955 try: |
1956 os.mkdir(covdir) | |
1957 except OSError, e: | |
1958 if e.errno != errno.EEXIST: | |
1959 raise | |
1960 | |
1961 os.environ['COVERAGE_DIR'] = covdir | |
1956 | 1962 |
1957 def _checkhglib(self, verb): | 1963 def _checkhglib(self, verb): |
1958 """Ensure that the 'mercurial' package imported by python is | 1964 """Ensure that the 'mercurial' package imported by python is |
1959 the one we expect it to be. If not, print a warning to stderr.""" | 1965 the one we expect it to be. If not, print a warning to stderr.""" |
1960 if ((self._bindir == self._pythondir) and | 1966 if ((self._bindir == self._pythondir) and |
1989 | 1995 |
1990 vlog('# Producing coverage report') | 1996 vlog('# Producing coverage report') |
1991 # chdir is the easiest way to get short, relative paths in the | 1997 # chdir is the easiest way to get short, relative paths in the |
1992 # output. | 1998 # output. |
1993 os.chdir(self._pythondir) | 1999 os.chdir(self._pythondir) |
1994 covdir = os.path.join(self._installdir, '..') | 2000 covdir = os.path.join(self._installdir, '..', 'coverage') |
1995 cov = coverage(data_file=os.path.join(covdir, '.coverage')) | 2001 cov = coverage(data_file=os.path.join(covdir, 'cov')) |
1996 cov.load() | 2002 cov.combine() |
1997 | 2003 |
1998 omit = [os.path.join(x, '*') for x in [self._bindir, self._testdir]] | 2004 omit = [os.path.join(x, '*') for x in [self._bindir, self._testdir]] |
1999 cov.report(ignore_errors=True, omit=omit) | 2005 cov.report(ignore_errors=True, omit=omit) |
2000 | 2006 |
2001 if self.options.htmlcov: | 2007 if self.options.htmlcov: |