diff --git a/_pytest/logging.py b/_pytest/logging.py index 1568d3f08..f7b4dc383 100644 --- a/_pytest/logging.py +++ b/_pytest/logging.py @@ -2,7 +2,6 @@ from __future__ import absolute_import, division, print_function import logging from contextlib import closing, contextmanager -import sys import six import pytest @@ -270,30 +269,13 @@ class LoggingPlugin(object): The formatter can be safely shared across all handlers so create a single one for the entire test session here. """ + self._config = config self.print_logs = get_option_ini(config, 'log_print') self.formatter = logging.Formatter( get_option_ini(config, 'log_format'), get_option_ini(config, 'log_date_format')) self.log_level = get_actual_log_level(config, 'log_level') - if config.getini('log_cli'): - log_cli_handler = logging.StreamHandler(sys.stderr) - log_cli_format = get_option_ini( - config, 'log_cli_format', 'log_format') - log_cli_date_format = get_option_ini( - config, 'log_cli_date_format', 'log_date_format') - log_cli_formatter = logging.Formatter( - log_cli_format, - datefmt=log_cli_date_format) - log_cli_level = get_actual_log_level(config, 'log_cli_level', 'log_level') - self.log_cli_handler = log_cli_handler # needed for a single unittest - self.live_logs_context = catching_logs(log_cli_handler, - formatter=log_cli_formatter, - level=log_cli_level) - else: - self.log_cli_handler = None - self.live_logs_context = _dummy_context_manager() - log_file = get_option_ini(config, 'log_file') if log_file: self.log_file_level = get_actual_log_level(config, 'log_file_level') @@ -352,6 +334,7 @@ class LoggingPlugin(object): @pytest.hookimpl(hookwrapper=True) def pytest_runtestloop(self, session): """Runs all collected test items.""" + self._setup_cli_logging() with self.live_logs_context: if self.log_file_handler is not None: with closing(self.log_file_handler): @@ -360,3 +343,27 @@ class LoggingPlugin(object): yield # run all the tests else: yield # run all the tests + + def _setup_cli_logging(self): + """Setups the handler and logger for the Live Logs feature, if enabled. + + This must be done right before starting the loop so we can access the terminal reporter plugin. + """ + terminal_reporter = self._config.pluginmanager.get_plugin('terminalreporter') + if self._config.getini('log_cli') and terminal_reporter is not None: + log_cli_handler = logging.StreamHandler(terminal_reporter._tw) + log_cli_format = get_option_ini( + self._config, 'log_cli_format', 'log_format') + log_cli_date_format = get_option_ini( + self._config, 'log_cli_date_format', 'log_date_format') + log_cli_formatter = logging.Formatter( + log_cli_format, + datefmt=log_cli_date_format) + log_cli_level = get_actual_log_level(self._config, 'log_cli_level', 'log_level') + self.log_cli_handler = log_cli_handler # needed for a single unittest + self.live_logs_context = catching_logs(log_cli_handler, + formatter=log_cli_formatter, + level=log_cli_level) + else: + self.log_cli_handler = None + self.live_logs_context = _dummy_context_manager() diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index 1a896514a..24f015d09 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -156,9 +156,9 @@ def test_log_cli_enabled_disabled(testdir, enabled): ''') result = testdir.runpytest('-s') if enabled: - assert msg in result.stderr.str() + assert msg in result.stdout.str() else: - assert msg not in result.stderr.str() + assert msg not in result.stdout.str() def test_log_cli_default_level(testdir): @@ -182,12 +182,13 @@ def test_log_cli_default_level(testdir): # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines([ - 'test_log_cli_default_level.py PASSED', + 'test_log_cli_default_level.py*', + 'PASSED', # 'PASSED' on its own line because the log message prints a new line ]) - result.stderr.fnmatch_lines([ + result.stdout.fnmatch_lines([ '*WARNING message will be shown*', ]) - assert "INFO message won't be shown" not in result.stderr.str() + assert "INFO message won't be shown" not in result.stdout.str() # make sure that that we get a '0' exit code for the testsuite assert result.ret == 0 @@ -213,12 +214,13 @@ def test_log_cli_level(testdir): # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines([ - 'test_log_cli_level.py PASSED', + 'test_log_cli_level.py*', + 'PASSED', # 'PASSED' on its own line because the log message prints a new line ]) - result.stderr.fnmatch_lines([ + result.stdout.fnmatch_lines([ "* This log message will be shown" ]) - for line in result.errlines: + for line in result.outlines: try: assert "This log message won't be shown" in line pytest.fail("A log message was shown and it shouldn't have been") @@ -232,12 +234,13 @@ def test_log_cli_level(testdir): # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines([ - 'test_log_cli_level.py PASSED', + 'test_log_cli_level.py*', + 'PASSED', # 'PASSED' on its own line because the log message prints a new line ]) - result.stderr.fnmatch_lines([ + result.stdout.fnmatch_lines([ "* This log message will be shown" ]) - for line in result.errlines: + for line in result.outlines: try: assert "This log message won't be shown" in line pytest.fail("A log message was shown and it shouldn't have been") @@ -270,12 +273,13 @@ def test_log_cli_ini_level(testdir): # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines([ - 'test_log_cli_ini_level.py PASSED', + 'test_log_cli_ini_level.py*', + 'PASSED', # 'PASSED' on its own line because the log message prints a new line ]) - result.stderr.fnmatch_lines([ + result.stdout.fnmatch_lines([ "* This log message will be shown" ]) - for line in result.errlines: + for line in result.outlines: try: assert "This log message won't be shown" in line pytest.fail("A log message was shown and it shouldn't have been")