Show a header for each testing phase during live logging
As suggested during review
This commit is contained in:
parent
29a7b5e064
commit
0df42b4426
|
@ -304,6 +304,8 @@ class LoggingPlugin(object):
|
||||||
"""Implements the internals of pytest_runtest_xxx() hook."""
|
"""Implements the internals of pytest_runtest_xxx() hook."""
|
||||||
with catching_logs(LogCaptureHandler(),
|
with catching_logs(LogCaptureHandler(),
|
||||||
formatter=self.formatter, level=self.log_level) as log_handler:
|
formatter=self.formatter, level=self.log_level) as log_handler:
|
||||||
|
if self.log_cli_handler:
|
||||||
|
self.log_cli_handler.reset(item, when)
|
||||||
if not hasattr(item, 'catch_log_handlers'):
|
if not hasattr(item, 'catch_log_handlers'):
|
||||||
item.catch_log_handlers = {}
|
item.catch_log_handlers = {}
|
||||||
item.catch_log_handlers[when] = log_handler
|
item.catch_log_handlers[when] = log_handler
|
||||||
|
@ -322,8 +324,6 @@ class LoggingPlugin(object):
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_setup(self, item):
|
def pytest_runtest_setup(self, item):
|
||||||
if self.log_cli_handler is not None:
|
|
||||||
self.log_cli_handler.reset()
|
|
||||||
with self._runtest_for(item, 'setup'):
|
with self._runtest_for(item, 'setup'):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
@ -387,18 +387,28 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
|
||||||
self.capture_manager = capture_manager
|
self.capture_manager = capture_manager
|
||||||
self._first_record_emitted = False
|
self._first_record_emitted = False
|
||||||
|
|
||||||
def reset(self):
|
self._section_name_shown = False
|
||||||
self._first_record_emitted = False
|
self._when = None
|
||||||
|
self._run_tests = set()
|
||||||
|
|
||||||
|
def reset(self, item, when):
|
||||||
|
self._when = when
|
||||||
|
self._section_name_shown = False
|
||||||
|
if item.name not in self._run_tests:
|
||||||
|
self._first_record_emitted = False
|
||||||
|
self._run_tests.add(item.name)
|
||||||
|
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
if self.capture_manager is not None:
|
if self.capture_manager is not None:
|
||||||
self.capture_manager.suspend_global_capture()
|
self.capture_manager.suspend_global_capture()
|
||||||
try:
|
try:
|
||||||
if not self._first_record_emitted:
|
if not self._first_record_emitted or self._when == 'teardown':
|
||||||
self.stream.write('\n')
|
self.stream.write('\n')
|
||||||
# we might consider adding a header at this point using self.stream.section('live log', sep='-')
|
|
||||||
# or something similar when we improve live logging output
|
|
||||||
self._first_record_emitted = True
|
self._first_record_emitted = True
|
||||||
|
if not self._section_name_shown:
|
||||||
|
header = 'live log' if self._when == 'call' else 'live log ' + self._when
|
||||||
|
self.stream.section(header, sep='-', bold=True)
|
||||||
|
self._section_name_shown = True
|
||||||
logging.StreamHandler.emit(self, record)
|
logging.StreamHandler.emit(self, record)
|
||||||
finally:
|
finally:
|
||||||
if self.capture_manager is not None:
|
if self.capture_manager is not None:
|
||||||
|
|
|
@ -196,17 +196,16 @@ def test_log_cli_default_level(testdir):
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_log_cli_default_level_multiple_tests(testdir):
|
def test_log_cli_default_level_multiple_tests(testdir, request):
|
||||||
"""Ensure we reset the first newline added by the live logger between tests"""
|
"""Ensure we reset the first newline added by the live logger between tests"""
|
||||||
# Default log file level
|
filename = request.node.name + '.py'
|
||||||
testdir.makepyfile('''
|
testdir.makepyfile('''
|
||||||
import pytest
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
def test_log_1(request):
|
def test_log_1():
|
||||||
logging.warning("log message from test_log_1")
|
logging.warning("log message from test_log_1")
|
||||||
|
|
||||||
def test_log_2(request):
|
def test_log_2():
|
||||||
logging.warning("log message from test_log_2")
|
logging.warning("log message from test_log_2")
|
||||||
''')
|
''')
|
||||||
testdir.makeini('''
|
testdir.makeini('''
|
||||||
|
@ -216,16 +215,63 @@ def test_log_cli_default_level_multiple_tests(testdir):
|
||||||
|
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
'test_log_cli_default_level_multiple_tests.py::test_log_1 ',
|
'{}::test_log_1 '.format(filename),
|
||||||
'*WARNING*log message from test_log_1*',
|
'*WARNING*log message from test_log_1*',
|
||||||
'PASSED *50%*',
|
'PASSED *50%*',
|
||||||
'test_log_cli_default_level_multiple_tests.py::test_log_2 ',
|
'{}::test_log_2 '.format(filename),
|
||||||
'*WARNING*log message from test_log_2*',
|
'*WARNING*log message from test_log_2*',
|
||||||
'PASSED *100%*',
|
'PASSED *100%*',
|
||||||
'=* 2 passed in *=',
|
'=* 2 passed in *=',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_log_cli_default_level_sections(testdir, request):
|
||||||
|
"""Check that with live logging enable we are printing the correct headers during setup/call/teardown."""
|
||||||
|
filename = request.node.name + '.py'
|
||||||
|
testdir.makepyfile('''
|
||||||
|
import pytest
|
||||||
|
import logging
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fix(request):
|
||||||
|
logging.warning("log message from setup of {}".format(request.node.name))
|
||||||
|
yield
|
||||||
|
logging.warning("log message from teardown of {}".format(request.node.name))
|
||||||
|
|
||||||
|
def test_log_1(fix):
|
||||||
|
logging.warning("log message from test_log_1")
|
||||||
|
|
||||||
|
def test_log_2(fix):
|
||||||
|
logging.warning("log message from test_log_2")
|
||||||
|
''')
|
||||||
|
testdir.makeini('''
|
||||||
|
[pytest]
|
||||||
|
log_cli=true
|
||||||
|
''')
|
||||||
|
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
'{}::test_log_1 '.format(filename),
|
||||||
|
'*-- live log setup --*',
|
||||||
|
'*WARNING*log message from setup of test_log_1*',
|
||||||
|
'*-- live log --*',
|
||||||
|
'*WARNING*log message from test_log_1*',
|
||||||
|
'PASSED *50%*',
|
||||||
|
'*-- live log teardown --*',
|
||||||
|
'*WARNING*log message from teardown of test_log_1*',
|
||||||
|
|
||||||
|
'{}::test_log_2 '.format(filename),
|
||||||
|
'*-- live log setup --*',
|
||||||
|
'*WARNING*log message from setup of test_log_2*',
|
||||||
|
'*-- live log --*',
|
||||||
|
'*WARNING*log message from test_log_2*',
|
||||||
|
'PASSED *100%*',
|
||||||
|
'*-- live log teardown --*',
|
||||||
|
'*WARNING*log message from teardown of test_log_2*',
|
||||||
|
'=* 2 passed in *=',
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
def test_log_cli_level(testdir):
|
def test_log_cli_level(testdir):
|
||||||
# Default log file level
|
# Default log file level
|
||||||
testdir.makepyfile('''
|
testdir.makepyfile('''
|
||||||
|
@ -473,11 +519,19 @@ def test_live_logging_suspends_capture(has_capture_manager, request):
|
||||||
assert CaptureManager.suspend_capture_item
|
assert CaptureManager.suspend_capture_item
|
||||||
assert CaptureManager.resume_global_capture
|
assert CaptureManager.resume_global_capture
|
||||||
|
|
||||||
capture_manager = MockCaptureManager() if has_capture_manager else None
|
class DummyTerminal(six.StringIO):
|
||||||
out_file = six.StringIO()
|
|
||||||
|
|
||||||
|
def section(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
out_file = DummyTerminal()
|
||||||
|
capture_manager = MockCaptureManager() if has_capture_manager else None
|
||||||
handler = _LiveLoggingStreamHandler(out_file, capture_manager)
|
handler = _LiveLoggingStreamHandler(out_file, capture_manager)
|
||||||
|
|
||||||
|
class DummyItem:
|
||||||
|
name = 'test_foo'
|
||||||
|
handler.reset(DummyItem(), 'call')
|
||||||
|
|
||||||
logger = logging.getLogger(__name__ + '.test_live_logging_suspends_capture')
|
logger = logging.getLogger(__name__ + '.test_live_logging_suspends_capture')
|
||||||
logger.addHandler(handler)
|
logger.addHandler(handler)
|
||||||
request.addfinalizer(partial(logger.removeHandler, handler))
|
request.addfinalizer(partial(logger.removeHandler, handler))
|
||||||
|
|
Loading…
Reference in New Issue