Improve frame helper tests (#139843)

This commit is contained in:
Erik Montnemery 2025-03-05 16:13:09 +01:00 committed by GitHub
parent c69cec28fe
commit 1552aec416
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 286 additions and 43 deletions

View File

@ -0,0 +1,120 @@
# serializer version: 1
# name: test_report[core default]
list([
])
# ---
# name: test_report[core integration default]
list([
"Detected that integration 'test_core_integration' test_report_string at homeassistant/components/test_core_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_core_integration%22",
])
# ---
# name: test_report[custom integration default]
list([
"Detected that custom integration 'test_custom_integration' test_report_string at custom_components/test_custom_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_custom_integration%22",
])
# ---
# name: test_report[disable error_if_core]
list([
'Detected code that test_report_string. Please report this issue',
])
# ---
# name: test_report[error_if_integration with core integration]
list([
"Detected that integration 'test_integration_frame' test_report_string at homeassistant/components/test_integration_frame/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_integration_frame%22",
])
# ---
# name: test_report[error_if_integration with custom integration]
list([
"Detected that custom integration 'test_integration_frame' test_report_string at custom_components/test_integration_frame/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_integration_frame%22",
])
# ---
# name: test_report[log_custom_component_only with core integration]
list([
])
# ---
# name: test_report[log_custom_component_only with custom integration]
list([
"Detected that custom integration 'test_integration_frame' test_report_string at custom_components/test_integration_frame/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_integration_frame%22",
])
# ---
# name: test_report_usage[core default]
list([
])
# ---
# name: test_report_usage[core integration default]
list([
"Detected that integration 'test_core_integration' test_report_string at homeassistant/components/test_core_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_core_integration%22",
])
# ---
# name: test_report_usage[core_behavior ignore]
list([
])
# ---
# name: test_report_usage[core_behavior log]
list([
'Detected code that test_report_string. Please report this issue',
])
# ---
# name: test_report_usage[core_integration_behavior error]
list([
"Detected that integration 'test_integration_frame' test_report_string at homeassistant/components/test_integration_frame/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_integration_frame%22",
])
# ---
# name: test_report_usage[core_integration_behavior ignore]
list([
])
# ---
# name: test_report_usage[custom integration default]
list([
"Detected that custom integration 'test_custom_integration' test_report_string at custom_components/test_custom_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_custom_integration%22",
])
# ---
# name: test_report_usage[custom integration error]
list([
"Detected that custom integration 'test_custom_integration' test_report_string at custom_components/test_custom_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_custom_integration%22",
])
# ---
# name: test_report_usage[custom integration ignore]
list([
])
# ---
# name: test_report_usage_find_issue_tracker[core integration]
list([
"Detected that integration 'test_core_integration' test_report_string at homeassistant/components/test_core_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_core_integration%22",
])
# ---
# name: test_report_usage_find_issue_tracker[core]
list([
'Detected code that test_report_string. Please report this issue',
])
# ---
# name: test_report_usage_find_issue_tracker[custom integration]
list([
"Detected that custom integration 'test_custom_integration' test_report_string at custom_components/test_custom_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://blablabla.com",
])
# ---
# name: test_report_usage_find_issue_tracker[unknown custom integration]
list([
"Detected that custom integration 'unknown_custom_integration' test_report_string at custom_components/unknown_custom_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+unknown_custom_integration%22",
])
# ---
# name: test_report_usage_find_issue_tracker_other_thread[core integration]
list([
"Detected that integration 'test_core_integration' test_report_string at homeassistant/components/test_core_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_core_integration%22",
])
# ---
# name: test_report_usage_find_issue_tracker_other_thread[core]
list([
'Detected code that test_report_string. Please report this issue',
])
# ---
# name: test_report_usage_find_issue_tracker_other_thread[custom integration]
list([
"Detected that custom integration 'test_custom_integration' test_report_string at custom_components/test_custom_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+test_custom_integration%22",
])
# ---
# name: test_report_usage_find_issue_tracker_other_thread[unknown custom integration]
list([
"Detected that custom integration 'unknown_custom_integration' test_report_string at custom_components/unknown_custom_integration/light.py, line 23: self.light.is_on. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+unknown_custom_integration%22",
])
# ---

View File

@ -1,15 +1,17 @@
"""Test the frame helper.""" """Test the frame helper."""
from contextlib import AbstractContextManager, nullcontext as does_not_raise
from typing import Any from typing import Any
from unittest.mock import ANY, Mock, patch from unittest.mock import ANY, Mock, patch
import pytest import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import frame from homeassistant.helpers import frame
from homeassistant.loader import async_get_integration from homeassistant.loader import async_get_integration
from tests.common import extract_stack_to_frame from tests.common import MockModule, extract_stack_to_frame, mock_integration
async def test_extract_frame_integration( async def test_extract_frame_integration(
@ -159,68 +161,68 @@ async def test_get_integration_logger_no_integration(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("integration_frame_path", "keywords", "expected_error", "expected_log"), ("integration_frame_path", "keywords", "expected_result", "expected_log"),
[ [
pytest.param( pytest.param(
"homeassistant/test_core", "homeassistant/test_core",
{}, {},
True, pytest.raises(RuntimeError, match="test_report_string"),
0, 0,
id="core default", id="core default",
), ),
pytest.param( pytest.param(
"homeassistant/components/test_core_integration", "homeassistant/components/test_core_integration",
{}, {},
False, does_not_raise(),
1, 1,
id="core integration default", id="core integration default",
), ),
pytest.param( pytest.param(
"custom_components/test_custom_integration", "custom_components/test_custom_integration",
{}, {},
False, does_not_raise(),
1, 1,
id="custom integration default", id="custom integration default",
), ),
pytest.param( pytest.param(
"custom_components/test_custom_integration", "custom_components/test_custom_integration",
{"custom_integration_behavior": frame.ReportBehavior.IGNORE}, {"custom_integration_behavior": frame.ReportBehavior.IGNORE},
False, does_not_raise(),
0, 0,
id="custom integration ignore", id="custom integration ignore",
), ),
pytest.param( pytest.param(
"custom_components/test_custom_integration", "custom_components/test_custom_integration",
{"custom_integration_behavior": frame.ReportBehavior.ERROR}, {"custom_integration_behavior": frame.ReportBehavior.ERROR},
True, pytest.raises(RuntimeError, match="test_report_string"),
1, 1,
id="custom integration error", id="custom integration error",
), ),
pytest.param( pytest.param(
"homeassistant/components/test_integration_frame", "homeassistant/components/test_integration_frame",
{"core_integration_behavior": frame.ReportBehavior.IGNORE}, {"core_integration_behavior": frame.ReportBehavior.IGNORE},
False, does_not_raise(),
0, 0,
id="core_integration_behavior ignore", id="core_integration_behavior ignore",
), ),
pytest.param( pytest.param(
"homeassistant/components/test_integration_frame", "homeassistant/components/test_integration_frame",
{"core_integration_behavior": frame.ReportBehavior.ERROR}, {"core_integration_behavior": frame.ReportBehavior.ERROR},
True, pytest.raises(RuntimeError, match="test_report_string"),
1, 1,
id="core_integration_behavior error", id="core_integration_behavior error",
), ),
pytest.param( pytest.param(
"homeassistant/test_integration_frame", "homeassistant/test_integration_frame",
{"core_behavior": frame.ReportBehavior.IGNORE}, {"core_behavior": frame.ReportBehavior.IGNORE},
False, does_not_raise(),
0, 0,
id="core_behavior ignore", id="core_behavior ignore",
), ),
pytest.param( pytest.param(
"homeassistant/test_integration_frame", "homeassistant/test_integration_frame",
{"core_behavior": frame.ReportBehavior.LOG}, {"core_behavior": frame.ReportBehavior.LOG},
False, does_not_raise(),
1, 1,
id="core_behavior log", id="core_behavior log",
), ),
@ -229,24 +231,142 @@ async def test_get_integration_logger_no_integration(
@pytest.mark.usefixtures("mock_integration_frame") @pytest.mark.usefixtures("mock_integration_frame")
async def test_report_usage( async def test_report_usage(
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
snapshot: SnapshotAssertion,
keywords: dict[str, Any], keywords: dict[str, Any],
expected_error: bool, expected_result: AbstractContextManager,
expected_log: int, expected_log: int,
) -> None: ) -> None:
"""Test report.""" """Test report_usage.
Note: This test doesn't set up mock integrations, so it will not
find the correct issue tracker URL, and we don't check for that.
"""
what = "test_report_string" what = "test_report_string"
errored = False with patch.object(frame, "_REPORTED_INTEGRATIONS", set()), expected_result:
try:
with patch.object(frame, "_REPORTED_INTEGRATIONS", set()):
frame.report_usage(what, **keywords) frame.report_usage(what, **keywords)
except RuntimeError:
errored = True
assert errored == expected_error
assert caplog.text.count(what) == expected_log assert caplog.text.count(what) == expected_log
reports = [
rec.message for rec in caplog.records if rec.message.startswith("Detected")
]
assert reports == snapshot
@pytest.mark.parametrize(
"integration_frame_path",
[
pytest.param(
"homeassistant/test_core",
id="core",
),
pytest.param(
"homeassistant/components/test_core_integration",
id="core integration",
),
pytest.param(
"custom_components/test_custom_integration",
id="custom integration",
),
pytest.param(
"custom_components/unknown_custom_integration",
id="unknown custom integration",
),
],
)
@pytest.mark.usefixtures("mock_integration_frame")
async def test_report_usage_find_issue_tracker(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
snapshot: SnapshotAssertion,
) -> None:
"""Test report_usage finds the correct issue tracker.
Note: The issue tracker is found by loader.async_suggest_report_issue, this
test is a sanity check to ensure async_suggest_report_issue is given the
right parameters.
"""
what = "test_report_string"
mock_integration(hass, MockModule("test_core_integration"))
mock_integration(
hass,
MockModule(
"test_custom_integration",
partial_manifest={"issue_tracker": "https://blablabla.com"},
),
built_in=False,
)
with patch.object(frame, "_REPORTED_INTEGRATIONS", set()):
frame.report_usage(what, core_behavior=frame.ReportBehavior.LOG)
assert caplog.text.count(what) == 1
reports = [
rec.message for rec in caplog.records if rec.message.startswith("Detected")
]
assert reports == snapshot
@pytest.mark.parametrize(
"integration_frame_path",
[
pytest.param(
"homeassistant/test_core",
id="core",
),
pytest.param(
"homeassistant/components/test_core_integration",
id="core integration",
),
pytest.param(
"custom_components/test_custom_integration",
id="custom integration",
),
pytest.param(
"custom_components/unknown_custom_integration",
id="unknown custom integration",
),
],
)
@pytest.mark.usefixtures("mock_integration_frame")
async def test_report_usage_find_issue_tracker_other_thread(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
snapshot: SnapshotAssertion,
) -> None:
"""Test report_usage finds the correct issue tracker.
In this test, we run the report_usage in a separate thread.
Note: The issue tracker is found by loader.async_suggest_report_issue, this
test is a sanity check to ensure async_suggest_report_issue is given the
right parameters.
"""
what = "test_report_string"
mock_integration(hass, MockModule("test_core_integration"))
mock_integration(
hass,
MockModule(
"test_custom_integration",
partial_manifest={"issue_tracker": "https://blablabla.com"},
),
built_in=False,
)
def sync_job() -> None:
with patch.object(frame, "_REPORTED_INTEGRATIONS", set()):
frame.report_usage(what, core_behavior=frame.ReportBehavior.LOG)
await hass.async_add_executor_job(sync_job)
assert caplog.text.count(what) == 1
reports = [
rec.message for rec in caplog.records if rec.message.startswith("Detected")
]
assert reports == snapshot
@patch.object(frame, "_REPORTED_INTEGRATIONS", set()) @patch.object(frame, "_REPORTED_INTEGRATIONS", set())
@ -365,61 +485,61 @@ async def test_report_error_if_integration(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("integration_frame_path", "keywords", "expected_error", "expected_log"), ("integration_frame_path", "keywords", "expected_result", "expected_log"),
[ [
pytest.param( pytest.param(
"homeassistant/test_core", "homeassistant/test_core",
{}, {},
True, pytest.raises(RuntimeError, match="test_report_string"),
0, 0,
id="core default", id="core default",
), ),
pytest.param( pytest.param(
"homeassistant/components/test_core_integration", "homeassistant/components/test_core_integration",
{}, {},
False, does_not_raise(),
1, 1,
id="core integration default", id="core integration default",
), ),
pytest.param( pytest.param(
"custom_components/test_custom_integration", "custom_components/test_custom_integration",
{}, {},
False, does_not_raise(),
1, 1,
id="custom integration default", id="custom integration default",
), ),
pytest.param( pytest.param(
"custom_components/test_integration_frame", "custom_components/test_integration_frame",
{"log_custom_component_only": True}, {"log_custom_component_only": True},
False, does_not_raise(),
1, 1,
id="log_custom_component_only with custom integration", id="log_custom_component_only with custom integration",
), ),
pytest.param( pytest.param(
"homeassistant/components/test_integration_frame", "homeassistant/components/test_integration_frame",
{"log_custom_component_only": True}, {"log_custom_component_only": True},
False, does_not_raise(),
0, 0,
id="log_custom_component_only with core integration", id="log_custom_component_only with core integration",
), ),
pytest.param( pytest.param(
"homeassistant/test_integration_frame", "homeassistant/test_integration_frame",
{"error_if_core": False}, {"error_if_core": False},
False, does_not_raise(),
1, 1,
id="disable error_if_core", id="disable error_if_core",
), ),
pytest.param( pytest.param(
"custom_components/test_integration_frame", "custom_components/test_integration_frame",
{"error_if_integration": True}, {"error_if_integration": True},
True, pytest.raises(RuntimeError, match="test_report_string"),
1, 1,
id="error_if_integration with custom integration", id="error_if_integration with custom integration",
), ),
pytest.param( pytest.param(
"homeassistant/components/test_integration_frame", "homeassistant/components/test_integration_frame",
{"error_if_integration": True}, {"error_if_integration": True},
True, pytest.raises(RuntimeError, match="test_report_string"),
1, 1,
id="error_if_integration with core integration", id="error_if_integration with core integration",
), ),
@ -428,24 +548,27 @@ async def test_report_error_if_integration(
@pytest.mark.usefixtures("mock_integration_frame") @pytest.mark.usefixtures("mock_integration_frame")
async def test_report( async def test_report(
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
snapshot: SnapshotAssertion,
keywords: dict[str, Any], keywords: dict[str, Any],
expected_error: bool, expected_result: AbstractContextManager,
expected_log: int, expected_log: int,
) -> None: ) -> None:
"""Test report.""" """Test report.
Note: This test doesn't set up mock integrations, so it will not
find the correct issue tracker URL, and we don't check for that.
"""
what = "test_report_string" what = "test_report_string"
errored = False with patch.object(frame, "_REPORTED_INTEGRATIONS", set()), expected_result:
try:
with patch.object(frame, "_REPORTED_INTEGRATIONS", set()):
frame.report(what, **keywords) frame.report(what, **keywords)
except RuntimeError:
errored = True
assert errored == expected_error
assert caplog.text.count(what) == expected_log assert caplog.text.count(what) == expected_log
reports = [
rec.message for rec in caplog.records if rec.message.startswith("Detected")
]
assert reports == snapshot
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -496,7 +619,7 @@ async def test_report(
"homeassistant/components/hue", "homeassistant/components/hue",
"that integration 'hue'", "that integration 'hue'",
False, False,
id="core integration", id="core integration stack mismatch",
), ),
# Assert integration found in stack frame has priority over integration_domain # Assert integration found in stack frame has priority over integration_domain
pytest.param( pytest.param(
@ -505,7 +628,7 @@ async def test_report(
"custom_components/hue", "custom_components/hue",
"that custom integration 'hue'", "that custom integration 'hue'",
False, False,
id="custom integration", id="custom integration stack mismatch",
), ),
], ],
) )
@ -518,7 +641,7 @@ async def test_report_integration_domain(
source: str, source: str,
logs_again: bool, logs_again: bool,
) -> None: ) -> None:
"""Test report.""" """Test report_usage when integration_domain is specified."""
await async_get_integration(hass, "sensor") await async_get_integration(hass, "sensor")
await async_get_integration(hass, "test_package") await async_get_integration(hass, "test_package")