Skip to content

Commit b305afe

Browse files
committed
Add test and fix #641: total coverage is not saved from the last ran reporter, instead a simpler reporter is used.
1 parent aa6873f commit b305afe

3 files changed

Lines changed: 38 additions & 12 deletions

File tree

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
Changelog
33
=========
44

5+
6+
* Fixed total coverage computation to always be consistent, regardless of reporting settings.
7+
Previously some reports could produce different total counts, and consequently can make --cov-fail-under behave different depending on
8+
reporting options.
9+
See `#641 <https://github.com/pytest-dev/pytest-cov/issues/641>`_.
10+
511
* Improve handling of ResourceWarning from sqlite3.
612

713
The plugin adds warning filter for sqlite3 ``ResourceWarning`` unclosed database (since 6.2.0).

src/pytest_cov/engine.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,9 @@ def sep(self, stream, s, txt):
150150
@_ensure_topdir
151151
def summary(self, stream):
152152
"""Produce coverage reports."""
153-
total = None
154153

155-
if not self.cov_report:
156-
with _backup(self.cov, 'config'):
157-
return self.cov.report(show_missing=True, ignore_errors=True, file=_NullFile)
154+
with _backup(self.cov, 'config'):
155+
total = self.cov.report(ignore_errors=True, output_format='total', file=_NullFile)
158156

159157
# Output coverage section header.
160158
if len(self.node_descs) == 1:
@@ -182,7 +180,7 @@ def summary(self, stream):
182180
skip_covered = isinstance(self.cov_report, dict) and 'skip-covered' in self.cov_report.values()
183181
options.update({'skip_covered': skip_covered or None})
184182
with _backup(self.cov, 'config'):
185-
total = self.cov.report(**options)
183+
self.cov.report(**options)
186184

187185
# Produce annotated source code report if wanted.
188186
if 'annotate' in self.cov_report:
@@ -194,7 +192,7 @@ def summary(self, stream):
194192
# Coverage.annotate don't return any total and we need it for --cov-fail-under.
195193

196194
with _backup(self.cov, 'config'):
197-
total = self.cov.report(ignore_errors=True, file=_NullFile)
195+
self.cov.report(ignore_errors=True, file=_NullFile)
198196
if annotate_dir:
199197
stream.write(f'Coverage annotated source written to dir {annotate_dir}\n')
200198
else:
@@ -204,37 +202,37 @@ def summary(self, stream):
204202
if 'html' in self.cov_report:
205203
output = self.cov_report['html']
206204
with _backup(self.cov, 'config'):
207-
total = self.cov.html_report(ignore_errors=True, directory=output)
205+
self.cov.html_report(ignore_errors=True, directory=output)
208206
stream.write(f'Coverage HTML written to dir {self.cov.config.html_dir if output is None else output}\n')
209207

210208
# Produce xml report if wanted.
211209
if 'xml' in self.cov_report:
212210
output = self.cov_report['xml']
213211
with _backup(self.cov, 'config'):
214-
total = self.cov.xml_report(ignore_errors=True, outfile=output)
212+
self.cov.xml_report(ignore_errors=True, outfile=output)
215213
stream.write(f'Coverage XML written to file {self.cov.config.xml_output if output is None else output}\n')
216214

217215
# Produce json report if wanted
218216
if 'json' in self.cov_report:
219217
output = self.cov_report['json']
220218
with _backup(self.cov, 'config'):
221-
total = self.cov.json_report(ignore_errors=True, outfile=output)
219+
self.cov.json_report(ignore_errors=True, outfile=output)
222220
stream.write('Coverage JSON written to file %s\n' % (self.cov.config.json_output if output is None else output))
223221

224222
# Produce Markdown report if wanted.
225223
if 'markdown' in self.cov_report:
226224
output = self.cov_report['markdown']
227225
with _backup(self.cov, 'config'):
228226
with Path(output).open('w') as output_file:
229-
total = self.cov.report(ignore_errors=True, file=output_file, output_format='markdown')
227+
self.cov.report(ignore_errors=True, file=output_file, output_format='markdown')
230228
stream.write(f'Coverage Markdown information written to file {output}\n')
231229

232230
# Produce Markdown report if wanted, appending to output file
233231
if 'markdown-append' in self.cov_report:
234232
output = self.cov_report['markdown-append']
235233
with _backup(self.cov, 'config'):
236234
with Path(output).open('a') as output_file:
237-
total = self.cov.report(ignore_errors=True, file=output_file, output_format='markdown')
235+
self.cov.report(ignore_errors=True, file=output_file, output_format='markdown')
238236
stream.write(f'Coverage Markdown information appended to file {output}\n')
239237

240238
# Produce lcov report if wanted.
@@ -245,7 +243,7 @@ def summary(self, stream):
245243

246244
# We need to call Coverage.report here, just to get the total
247245
# Coverage.lcov_report doesn't return any total and we need it for --cov-fail-under.
248-
total = self.cov.report(ignore_errors=True, file=_NullFile)
246+
self.cov.report(ignore_errors=True, file=_NullFile)
249247

250248
stream.write(f'Coverage LCOV written to file {self.cov.config.lcov_output if output is None else output}\n')
251249

tests/test_pytest_cov.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,28 @@ def test_cov_min_50(testdir):
494494
result.stdout.fnmatch_lines(['Required test coverage of 50% reached. Total coverage: *%'])
495495

496496

497+
def test_cov_reporting_bug_641(testdir, monkeypatch):
498+
testdir.tmpdir.mkdir('src').join('foo.py').write('')
499+
testdir.tmpdir.mkdir('tests').join('test_foo.py').write("""
500+
import foo
501+
502+
def test_foo():
503+
pass
504+
""")
505+
testdir.tmpdir.join('.coveragerc').write("""
506+
[run]
507+
relative_files = True
508+
source = src, tests
509+
""")
510+
monkeypatch.setitem(os.environ, 'PYTHONPATH', os.pathsep.join([os.environ.get('PYTHONPATH', ''), 'src']))
511+
result = testdir.runpytest('-v', '--cov=src', '--cov-report=xml', '--cov-fail-under=100')
512+
assert result.ret == 0
513+
result.stdout.fnmatch_lines(['Required test coverage of 100% reached. Total coverage: *%'])
514+
result = testdir.runpytest('-v', '--cov=src', '--cov-report=', '--cov-fail-under=100')
515+
assert result.ret == 0
516+
result.stdout.fnmatch_lines(['Required test coverage of 100% reached. Total coverage: *%'])
517+
518+
497519
def test_cov_min_float_value(testdir):
498520
script = testdir.makepyfile(SCRIPT)
499521

0 commit comments

Comments
 (0)