Skip to content

Commit 6b2fb10

Browse files
committed
Fixed color inconsistency in verbose mode where test status showed green instead of yellow for passed tests with warnings.
1 parent c97a401 commit 6b2fb10

File tree

5 files changed

+99
-5
lines changed

5 files changed

+99
-5
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ Tomer Keren
463463
Tony Narlock
464464
Tor Colvin
465465
Trevor Bekolay
466+
Trey Shaffer
466467
Tushar Sadhwani
467468
Tyler Goodlet
468469
Tyler Smart

changelog/13201.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed color inconsistency in verbose mode where test status showed green instead of yellow for passed tests with warnings.

src/_pytest/terminal.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -635,10 +635,16 @@ def pytest_runtest_logreport(self, report: TestReport) -> None:
635635
return
636636
if markup is None:
637637
was_xfail = hasattr(report, "wasxfail")
638-
if rep.passed and not was_xfail:
639-
markup = {"green": True}
640-
elif rep.passed and was_xfail:
641-
markup = {"yellow": True}
638+
has_warnings = any(
639+
name == "has_warnings" and value
640+
for name, value in getattr(report, "user_properties", [])
641+
)
642+
643+
if rep.passed:
644+
if was_xfail or has_warnings:
645+
markup = {"yellow": True}
646+
else:
647+
markup = {"green": True}
642648
elif rep.failed:
643649
markup = {"red": True}
644650
elif rep.skipped:

src/_pytest/warnings.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,28 @@
66
from contextlib import ExitStack
77
import sys
88
from typing import Literal
9+
from typing import TYPE_CHECKING
910
import warnings
1011

1112
from _pytest.config import apply_warning_filters
1213
from _pytest.config import Config
1314
from _pytest.config import parse_warning_filter
1415
from _pytest.main import Session
1516
from _pytest.nodes import Item
17+
from _pytest.stash import StashKey
1618
from _pytest.terminal import TerminalReporter
1719
from _pytest.tracemalloc import tracemalloc_message
1820
import pytest
1921

2022

23+
if TYPE_CHECKING:
24+
from _pytest.reports import TestReport
25+
from _pytest.runner import CallInfo
26+
27+
# StashKey for storing warning log on items
28+
warning_captured_log_key = StashKey[list[warnings.WarningMessage]]()
29+
30+
2131
@contextmanager
2232
def catch_warnings_for_item(
2333
config: Config,
@@ -51,6 +61,9 @@ def catch_warnings_for_item(
5161
for mark in item.iter_markers(name="filterwarnings"):
5262
for arg in mark.args:
5363
warnings.filterwarnings(*parse_warning_filter(arg, escape=False))
64+
# Store the warning log on the item so it can be accessed during reporting
65+
if record and log is not None:
66+
item.stash[warning_captured_log_key] = log
5467

5568
try:
5669
yield
@@ -89,6 +102,20 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]:
89102
return (yield)
90103

91104

105+
@pytest.hookimpl(hookwrapper=True)
106+
def pytest_runtest_makereport(
107+
item: Item, call: CallInfo[None]
108+
) -> Generator[None, TestReport, None]:
109+
"""Attach warning information to test reports for terminal coloring."""
110+
outcome = yield
111+
report: TestReport = outcome.get_result()
112+
113+
if report.passed and report.when == "call":
114+
warning_log = item.stash.get(warning_captured_log_key, None)
115+
if warning_log:
116+
report.user_properties.append(("has_warnings", True))
117+
118+
92119
@pytest.hookimpl(wrapper=True, tryfirst=True)
93120
def pytest_collection(session: Session) -> Generator[None, object, object]:
94121
config = session.config

testing/test_terminal.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2191,7 +2191,7 @@ def test_foobar(i): raise ValueError()
21912191
[
21922192
r"test_axfail.py {yellow}x{reset}{green} \s+ \[ 4%\]{reset}",
21932193
r"test_bar.py ({green}\.{reset}){{10}}{green} \s+ \[ 52%\]{reset}",
2194-
r"test_foo.py ({green}\.{reset}){{5}}{yellow} \s+ \[ 76%\]{reset}",
2194+
r"test_foo.py ({yellow}\.{reset}){{5}}{yellow} \s+ \[ 76%\]{reset}",
21952195
r"test_foobar.py ({red}F{reset}){{5}}{red} \s+ \[100%\]{reset}",
21962196
]
21972197
)
@@ -2208,6 +2208,65 @@ def test_foobar(i): raise ValueError()
22082208
)
22092209
)
22102210

2211+
def test_verbose_colored_warnings(
2212+
self, pytester: Pytester, monkeypatch, color_mapping
2213+
) -> None:
2214+
"""Test that verbose mode shows yellow PASSED for tests with warnings."""
2215+
monkeypatch.setenv("PY_COLORS", "1")
2216+
pytester.makepyfile(
2217+
test_warning="""
2218+
import warnings
2219+
def test_with_warning():
2220+
warnings.warn("test warning", DeprecationWarning)
2221+
pass
2222+
2223+
def test_without_warning():
2224+
pass
2225+
"""
2226+
)
2227+
result = pytester.runpytest("-v")
2228+
result.stdout.re_match_lines(
2229+
color_mapping.format_for_rematch(
2230+
[
2231+
r"test_warning.py::test_with_warning {yellow}PASSED{reset}{green} \s+ \[ 50%\]{reset}",
2232+
r"test_warning.py::test_without_warning {green}PASSED{reset}{yellow} \s+ \[100%\]{reset}",
2233+
]
2234+
)
2235+
)
2236+
2237+
def test_verbose_colored_warnings_xdist(
2238+
self, pytester: Pytester, monkeypatch, color_mapping
2239+
) -> None:
2240+
"""Test that warning coloring works correctly with pytest-xdist parallel execution."""
2241+
pytest.importorskip("xdist")
2242+
monkeypatch.setenv("PY_COLORS", "1")
2243+
pytester.makepyfile(
2244+
test_warning_xdist="""
2245+
import warnings
2246+
def test_with_warning_1():
2247+
warnings.warn("warning in test 1", DeprecationWarning)
2248+
pass
2249+
2250+
def test_with_warning_2():
2251+
warnings.warn("warning in test 2", DeprecationWarning)
2252+
pass
2253+
2254+
def test_without_warning():
2255+
pass
2256+
"""
2257+
)
2258+
2259+
output = pytester.runpytest("-v", "-n2")
2260+
output.stdout.re_match_lines(
2261+
color_mapping.format_for_rematch(
2262+
[
2263+
r"test_warning_xdist.py::test_with_warning_1 {yellow}PASSED{reset}",
2264+
r"test_warning_xdist.py::test_with_warning_2 {yellow}PASSED{reset}",
2265+
r"test_warning_xdist.py::test_without_warning {green}PASSED{reset}",
2266+
]
2267+
)
2268+
)
2269+
22112270
def test_count(self, many_tests_files, pytester: Pytester) -> None:
22122271
pytester.makeini(
22132272
"""

0 commit comments

Comments
 (0)