Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.4.x] Ensure logging tests always cleanup after themselves #11541

Merged
merged 1 commit into from Oct 23, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
56 changes: 33 additions & 23 deletions testing/logging/test_fixture.py
@@ -1,5 +1,7 @@
# mypy: disable-error-code="attr-defined"
# mypy: disallow-untyped-defs
import logging
from typing import Iterator

import pytest
from _pytest.logging import caplog_records_key
Expand All @@ -9,8 +11,8 @@
sublogger = logging.getLogger(__name__ + ".baz")


@pytest.fixture
def cleanup_disabled_logging():
@pytest.fixture(autouse=True)
def cleanup_disabled_logging() -> Iterator[None]:
"""Simple fixture that ensures that a test doesn't disable logging.

This is necessary because ``logging.disable()`` is global, so a test disabling logging
Expand All @@ -27,7 +29,7 @@ def test_fixture_help(pytester: Pytester) -> None:
result.stdout.fnmatch_lines(["*caplog*"])


def test_change_level(caplog):
def test_change_level(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.INFO)
logger.debug("handler DEBUG level")
logger.info("handler INFO level")
Expand All @@ -42,7 +44,7 @@ def test_change_level(caplog):
assert "CRITICAL" in caplog.text


def test_change_level_logging_disabled(caplog, cleanup_disabled_logging):
def test_change_level_logging_disabled(caplog: pytest.LogCaptureFixture) -> None:
logging.disable(logging.CRITICAL)
assert logging.root.manager.disable == logging.CRITICAL
caplog.set_level(logging.WARNING)
Expand Down Expand Up @@ -85,9 +87,7 @@ def test2(caplog):
result.stdout.no_fnmatch_line("*log from test2*")


def test_change_disabled_level_undo(
pytester: Pytester, cleanup_disabled_logging
) -> None:
def test_change_disabled_level_undo(pytester: Pytester) -> None:
"""Ensure that '_force_enable_logging' in 'set_level' is undone after the end of the test.

Tests the logging output themselves (affected by disabled logging level).
Expand Down Expand Up @@ -144,7 +144,7 @@ def test3(caplog):
result.assert_outcomes(passed=3)


def test_with_statement(caplog):
def test_with_statement(caplog: pytest.LogCaptureFixture) -> None:
with caplog.at_level(logging.INFO):
logger.debug("handler DEBUG level")
logger.info("handler INFO level")
Expand All @@ -159,7 +159,7 @@ def test_with_statement(caplog):
assert "CRITICAL" in caplog.text


def test_with_statement_logging_disabled(caplog, cleanup_disabled_logging):
def test_with_statement_logging_disabled(caplog: pytest.LogCaptureFixture) -> None:
logging.disable(logging.CRITICAL)
assert logging.root.manager.disable == logging.CRITICAL
with caplog.at_level(logging.WARNING):
Expand Down Expand Up @@ -198,8 +198,8 @@ def test_with_statement_logging_disabled(caplog, cleanup_disabled_logging):
],
)
def test_force_enable_logging_level_string(
caplog, cleanup_disabled_logging, level_str, expected_disable_level
):
caplog: pytest.LogCaptureFixture, level_str: str, expected_disable_level: int
) -> None:
"""Test _force_enable_logging using a level string.

``expected_disable_level`` is one level below ``level_str`` because the disabled log level
Expand All @@ -218,15 +218,15 @@ def test_force_enable_logging_level_string(
assert test_logger.manager.disable == expected_disable_level


def test_log_access(caplog):
def test_log_access(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.INFO)
logger.info("boo %s", "arg")
assert caplog.records[0].levelname == "INFO"
assert caplog.records[0].msg == "boo %s"
assert "boo arg" in caplog.text


def test_messages(caplog):
def test_messages(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.INFO)
logger.info("boo %s", "arg")
logger.info("bar %s\nbaz %s", "arg1", "arg2")
Expand All @@ -247,22 +247,22 @@ def test_messages(caplog):
assert "Exception" not in caplog.messages[-1]


def test_record_tuples(caplog):
def test_record_tuples(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.INFO)
logger.info("boo %s", "arg")

assert caplog.record_tuples == [(__name__, logging.INFO, "boo arg")]


def test_unicode(caplog):
def test_unicode(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.INFO)
logger.info("bū")
assert caplog.records[0].levelname == "INFO"
assert caplog.records[0].msg == "bū"
assert "bū" in caplog.text


def test_clear(caplog):
def test_clear(caplog: pytest.LogCaptureFixture) -> None:
caplog.set_level(logging.INFO)
logger.info("bū")
assert len(caplog.records)
Expand All @@ -273,15 +273,19 @@ def test_clear(caplog):


@pytest.fixture
def logging_during_setup_and_teardown(caplog):
def logging_during_setup_and_teardown(
caplog: pytest.LogCaptureFixture,
) -> Iterator[None]:
caplog.set_level("INFO")
logger.info("a_setup_log")
yield
logger.info("a_teardown_log")
assert [x.message for x in caplog.get_records("teardown")] == ["a_teardown_log"]


def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardown):
def test_caplog_captures_for_all_stages(
caplog: pytest.LogCaptureFixture, logging_during_setup_and_teardown: None
) -> None:
assert not caplog.records
assert not caplog.get_records("call")
logger.info("a_call_log")
Expand All @@ -290,25 +294,31 @@ def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardow
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]

# This reaches into private API, don't use this type of thing in real tests!
assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"}
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}


def test_clear_for_call_stage(caplog, logging_during_setup_and_teardown):
def test_clear_for_call_stage(
caplog: pytest.LogCaptureFixture, logging_during_setup_and_teardown: None
) -> None:
logger.info("a_call_log")
assert [x.message for x in caplog.get_records("call")] == ["a_call_log"]
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"}
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}

caplog.clear()

assert caplog.get_records("call") == []
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"}
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}

logging.info("a_call_log_after_clear")
assert [x.message for x in caplog.get_records("call")] == ["a_call_log_after_clear"]
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"}
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}


def test_ini_controls_global_log_level(pytester: Pytester) -> None:
Expand Down