Merge pull request #3110 from nicoddemus/progress-teardown-3088

Fix progress report when tests fail during teardown
This commit is contained in:
Ronny Pfannschmidt 2018-01-12 11:09:01 +01:00 committed by GitHub
commit 01e37fe892
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 27 deletions

View File

@ -152,9 +152,18 @@ class TerminalReporter:
self.reportchars = getreportopt(config)
self.hasmarkup = self._tw.hasmarkup
self.isatty = file.isatty()
self._progress_items_reported = 0
self._show_progress_info = (self.config.getoption('capture') != 'no' and
self.config.getini('console_output_style') == 'progress')
self._progress_nodeids_reported = set()
self._show_progress_info = self._determine_show_progress_info()
def _determine_show_progress_info(self):
"""Return True if we should display progress information based on the current config"""
# do not show progress if we are not capturing output (#3038)
if self.config.getoption('capture') == 'no':
return False
# do not show progress if we are showing fixture setup/teardown
if self.config.getoption('setupshow'):
return False
return self.config.getini('console_output_style') == 'progress'
def hasopt(self, char):
char = {'xfailed': 'x', 'skipped': 's'}.get(char, char)
@ -179,7 +188,6 @@ class TerminalReporter:
if extra:
self._tw.write(extra, **kwargs)
self.currentfspath = -2
self._write_progress_information_filling_space()
def ensure_newline(self):
if self.currentfspath:
@ -269,14 +277,13 @@ class TerminalReporter:
# probably passed setup/teardown
return
running_xdist = hasattr(rep, 'node')
self._progress_items_reported += 1
if self.verbosity <= 0:
if not running_xdist and self.showfspath:
self.write_fspath_result(rep.nodeid, letter)
else:
self._tw.write(letter)
self._write_progress_if_past_edge()
else:
self._progress_nodeids_reported.add(rep.nodeid)
if markup is None:
if rep.passed:
markup = {'green': True}
@ -289,6 +296,8 @@ class TerminalReporter:
line = self._locationline(rep.nodeid, *rep.location)
if not running_xdist:
self.write_ensure_prefix(line, word, **markup)
if self._show_progress_info:
self._write_progress_information_filling_space()
else:
self.ensure_newline()
self._tw.write("[%s]" % rep.node.gateway.id)
@ -300,31 +309,28 @@ class TerminalReporter:
self._tw.write(" " + line)
self.currentfspath = -2
def _write_progress_if_past_edge(self):
if not self._show_progress_info:
return
last_item = self._progress_items_reported == self._session.testscollected
if last_item:
self._write_progress_information_filling_space()
return
past_edge = self._tw.chars_on_current_line + self._PROGRESS_LENGTH + 1 >= self._screen_width
if past_edge:
msg = self._get_progress_information_message()
self._tw.write(msg + '\n', cyan=True)
def pytest_runtest_logfinish(self, nodeid):
if self.verbosity <= 0 and self._show_progress_info:
self._progress_nodeids_reported.add(nodeid)
last_item = len(self._progress_nodeids_reported) == self._session.testscollected
if last_item:
self._write_progress_information_filling_space()
else:
past_edge = self._tw.chars_on_current_line + self._PROGRESS_LENGTH + 1 >= self._screen_width
if past_edge:
msg = self._get_progress_information_message()
self._tw.write(msg + '\n', cyan=True)
_PROGRESS_LENGTH = len(' [100%]')
def _get_progress_information_message(self):
collected = self._session.testscollected
if collected:
progress = self._progress_items_reported * 100 // collected
progress = len(self._progress_nodeids_reported) * 100 // collected
return ' [{:3d}%]'.format(progress)
return ' [100%]'
def _write_progress_information_filling_space(self):
if not self._show_progress_info:
return
msg = self._get_progress_information_message()
fill = ' ' * (self._tw.fullwidth - self._tw.chars_on_current_line - len(msg) - 1)
self.write(fill + msg, cyan=True)

1
changelog/3088.bugfix Normal file
View File

@ -0,0 +1 @@
Fix progress percentage reported when tests fail during teardown.

View File

@ -969,7 +969,7 @@ def test_no_trailing_whitespace_after_inifile_word(testdir):
class TestProgress:
@pytest.fixture
def many_tests_file(self, testdir):
def many_tests_files(self, testdir):
testdir.makepyfile(
test_bar="""
import pytest
@ -1006,7 +1006,7 @@ class TestProgress:
'=* 2 passed in *=',
])
def test_normal(self, many_tests_file, testdir):
def test_normal(self, many_tests_files, testdir):
output = testdir.runpytest()
output.stdout.re_match_lines([
r'test_bar.py \.{10} \s+ \[ 50%\]',
@ -1014,7 +1014,7 @@ class TestProgress:
r'test_foobar.py \.{5} \s+ \[100%\]',
])
def test_verbose(self, many_tests_file, testdir):
def test_verbose(self, many_tests_files, testdir):
output = testdir.runpytest('-v')
output.stdout.re_match_lines([
r'test_bar.py::test_bar\[0\] PASSED \s+ \[ 5%\]',
@ -1022,14 +1022,14 @@ class TestProgress:
r'test_foobar.py::test_foobar\[4\] PASSED \s+ \[100%\]',
])
def test_xdist_normal(self, many_tests_file, testdir):
def test_xdist_normal(self, many_tests_files, testdir):
pytest.importorskip('xdist')
output = testdir.runpytest('-n2')
output.stdout.re_match_lines([
r'\.{20} \s+ \[100%\]',
])
def test_xdist_verbose(self, many_tests_file, testdir):
def test_xdist_verbose(self, many_tests_files, testdir):
pytest.importorskip('xdist')
output = testdir.runpytest('-n2', '-v')
output.stdout.re_match_lines_random([
@ -1038,10 +1038,85 @@ class TestProgress:
r'\[gw\d\] \[\s*\d+%\] PASSED test_foobar.py::test_foobar\[1\]',
])
def test_capture_no(self, many_tests_file, testdir):
def test_capture_no(self, many_tests_files, testdir):
output = testdir.runpytest('-s')
output.stdout.re_match_lines([
r'test_bar.py \.{10}',
r'test_foo.py \.{5}',
r'test_foobar.py \.{5}',
])
class TestProgressWithTeardown:
"""Ensure we show the correct percentages for tests that fail during teardown (#3088)"""
@pytest.fixture
def contest_with_teardown_fixture(self, testdir):
testdir.makeconftest('''
import pytest
@pytest.fixture
def fail_teardown():
yield
assert False
''')
@pytest.fixture
def many_files(self, testdir, contest_with_teardown_fixture):
testdir.makepyfile(
test_bar='''
import pytest
@pytest.mark.parametrize('i', range(5))
def test_bar(fail_teardown, i):
pass
''',
test_foo='''
import pytest
@pytest.mark.parametrize('i', range(15))
def test_foo(fail_teardown, i):
pass
''',
)
def test_teardown_simple(self, testdir, contest_with_teardown_fixture):
testdir.makepyfile('''
def test_foo(fail_teardown):
pass
''')
output = testdir.runpytest()
output.stdout.re_match_lines([
r'test_teardown_simple.py \.E\s+\[100%\]',
])
def test_teardown_with_test_also_failing(self, testdir, contest_with_teardown_fixture):
testdir.makepyfile('''
def test_foo(fail_teardown):
assert False
''')
output = testdir.runpytest()
output.stdout.re_match_lines([
r'test_teardown_with_test_also_failing.py FE\s+\[100%\]',
])
def test_teardown_many(self, testdir, many_files):
output = testdir.runpytest()
output.stdout.re_match_lines([
r'test_bar.py (\.E){5}\s+\[ 25%\]',
r'test_foo.py (\.E){15}\s+\[100%\]',
])
def test_teardown_many_verbose(self, testdir, many_files):
output = testdir.runpytest('-v')
output.stdout.re_match_lines([
r'test_bar.py::test_bar\[0\] PASSED\s+\[ 5%\]',
r'test_bar.py::test_bar\[0\] ERROR\s+\[ 5%\]',
r'test_bar.py::test_bar\[4\] PASSED\s+\[ 25%\]',
r'test_bar.py::test_bar\[4\] ERROR\s+\[ 25%\]',
])
def test_xdist_normal(self, many_files, testdir):
pytest.importorskip('xdist')
output = testdir.runpytest('-n2')
output.stdout.re_match_lines([
r'[\.E]{40} \s+ \[100%\]',
])