pytest-dev / pytest
Showing 2 of 3 files from the diff.

@@ -1084,3 +1084,48 @@
Loading
1084 1084
    with open(os.path.join(report_dir_base, "test_second"), "r") as rfh:
1085 1085
        content = rfh.read()
1086 1086
        assert "message from test 2" in content
1087 +
1088 +
1089 +
def test_colored_captured_log(testdir):
1090 +
    """
1091 +
    Test that the level names of captured log messages of a failing test are
1092 +
    colored.
1093 +
    """
1094 +
    testdir.makepyfile(
1095 +
        """
1096 +
        import logging
1097 +
1098 +
        logger = logging.getLogger(__name__)
1099 +
1100 +
        def test_foo():
1101 +
            logger.info('text going to logger from call')
1102 +
            assert False
1103 +
        """
1104 +
    )
1105 +
    result = testdir.runpytest("--log-level=INFO", "--color=yes")
1106 +
    assert result.ret == 1
1107 +
    result.stdout.fnmatch_lines(
1108 +
        [
1109 +
            "*-- Captured log call --*",
1110 +
            "\x1b[32mINFO    \x1b[0m*text going to logger from call",
1111 +
        ]
1112 +
    )
1113 +
1114 +
1115 +
def test_colored_ansi_esc_caplogtext(testdir):
1116 +
    """
1117 +
    Make sure that caplog.text does not contain ANSI escape sequences.
1118 +
    """
1119 +
    testdir.makepyfile(
1120 +
        """
1121 +
        import logging
1122 +
1123 +
        logger = logging.getLogger(__name__)
1124 +
1125 +
        def test_foo(caplog):
1126 +
            logger.info('text going to logger from call')
1127 +
            assert '\x1b' not in caplog.text
1128 +
        """
1129 +
    )
1130 +
    result = testdir.runpytest("--log-level=INFO", "--color=yes")
1131 +
    assert result.ret == 0

@@ -18,6 +18,11 @@
Loading
18 18
19 19
DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s"
20 20
DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S"
21 +
_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m")
22 +
23 +
24 +
def _remove_ansi_escape_sequences(text):
25 +
    return _ANSI_ESCAPE_SEQ.sub("", text)
21 26
22 27
23 28
class ColoredLevelFormatter(logging.Formatter):
@@ -257,8 +262,8 @@
Loading
257 262
258 263
    @property
259 264
    def text(self):
260 -
        """Returns the log text."""
261 -
        return self.handler.stream.getvalue()
265 +
        """Returns the formatted log text."""
266 +
        return _remove_ansi_escape_sequences(self.handler.stream.getvalue())
262 267
263 268
    @property
264 269
    def records(self):
@@ -394,7 +399,7 @@
Loading
394 399
            config.option.verbose = 1
395 400
396 401
        self.print_logs = get_option_ini(config, "log_print")
397 -
        self.formatter = logging.Formatter(
402 +
        self.formatter = self._create_formatter(
398 403
            get_option_ini(config, "log_format"),
399 404
            get_option_ini(config, "log_date_format"),
400 405
        )
@@ -428,6 +433,19 @@
Loading
428 433
        if self._log_cli_enabled():
429 434
            self._setup_cli_logging()
430 435
436 +
    def _create_formatter(self, log_format, log_date_format):
437 +
        # color option doesn't exist if terminal plugin is disabled
438 +
        color = getattr(self._config.option, "color", "no")
439 +
        if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search(
440 +
            log_format
441 +
        ):
442 +
            formatter = ColoredLevelFormatter(
443 +
                create_terminal_writer(self._config), log_format, log_date_format
444 +
            )
445 +
        else:
446 +
            formatter = logging.Formatter(log_format, log_date_format)
447 +
        return formatter
448 +
431 449
    def _setup_cli_logging(self):
432 450
        config = self._config
433 451
        terminal_reporter = config.pluginmanager.get_plugin("terminalreporter")
@@ -438,23 +456,12 @@
Loading
438 456
        capture_manager = config.pluginmanager.get_plugin("capturemanager")
439 457
        # if capturemanager plugin is disabled, live logging still works.
440 458
        log_cli_handler = _LiveLoggingStreamHandler(terminal_reporter, capture_manager)
441 -
        log_cli_format = get_option_ini(config, "log_cli_format", "log_format")
442 -
        log_cli_date_format = get_option_ini(
443 -
            config, "log_cli_date_format", "log_date_format"
459 +
460 +
        log_cli_formatter = self._create_formatter(
461 +
            get_option_ini(config, "log_cli_format", "log_format"),
462 +
            get_option_ini(config, "log_cli_date_format", "log_date_format"),
444 463
        )
445 -
        if (
446 -
            config.option.color != "no"
447 -
            and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search(log_cli_format)
448 -
        ):
449 -
            log_cli_formatter = ColoredLevelFormatter(
450 -
                create_terminal_writer(config),
451 -
                log_cli_format,
452 -
                datefmt=log_cli_date_format,
453 -
            )
454 -
        else:
455 -
            log_cli_formatter = logging.Formatter(
456 -
                log_cli_format, datefmt=log_cli_date_format
457 -
            )
464 +
458 465
        log_cli_level = get_actual_log_level(config, "log_cli_level", "log_level")
459 466
        self.log_cli_handler = log_cli_handler
460 467
        self.live_logs_context = lambda: catching_logs(
Files Coverage
src 93.63%
testing 91.43%
Project Totals (115 files) 92.38%
py27-pluggymaster-xdist
Build #None -
py27-pexpect,py27-twisted-linux
Build #9383.1 -
TRAVIS_PYTHON_VERSION=2.7
TRAVIS_OS_NAME=linux
py34-xdist
Build #None -
py35-xdist
Build #None -
py37
Build #None -
py27-lsof-nobyte-numpy
Build #None -
py27-xdist-osx
Build #9383.4 -
TRAVIS_OS_NAME=osx
linting,docs,doctesting-linux
Build #9383.3 -
TRAVIS_PYTHON_VERSION=3.7
TRAVIS_OS_NAME=linux
py37-lsof-numpy-xdist-linux
Build #9383.12 -
TRAVIS_PYTHON_VERSION=3.7
TRAVIS_OS_NAME=linux
py37-pexpect,py37-twisted-linux
Build #9383.15 -
TRAVIS_PYTHON_VERSION=3.7
TRAVIS_OS_NAME=linux

No yaml found.

Create your codecov.yml to customize your Codecov experience

Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading