201 lines
6.6 KiB
Python
201 lines
6.6 KiB
Python
"""Tests for async util methods from Python source."""
|
|
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
|
|
from homeassistant.util import loop as haloop
|
|
|
|
from tests.common import extract_stack_to_frame
|
|
|
|
|
|
def banned_function():
|
|
"""Mock banned function."""
|
|
|
|
|
|
async def test_check_loop_async() -> None:
|
|
"""Test check_loop detects when called from event loop without integration context."""
|
|
with pytest.raises(RuntimeError):
|
|
haloop.check_loop(banned_function)
|
|
|
|
|
|
async def test_check_loop_async_non_strict_core(
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test non_strict_core check_loop detects from event loop without integration context."""
|
|
haloop.check_loop(banned_function, strict_core=False)
|
|
assert "Detected blocking call to banned_function" in caplog.text
|
|
|
|
|
|
async def test_check_loop_async_integration(caplog: pytest.LogCaptureFixture) -> None:
|
|
"""Test check_loop detects and raises when called from event loop from integration context."""
|
|
frames = extract_stack_to_frame(
|
|
[
|
|
Mock(
|
|
filename="/home/paulus/homeassistant/core.py",
|
|
lineno="23",
|
|
line="do_something()",
|
|
),
|
|
Mock(
|
|
filename="/home/paulus/homeassistant/components/hue/light.py",
|
|
lineno="23",
|
|
line="self.light.is_on",
|
|
),
|
|
Mock(
|
|
filename="/home/paulus/aiohue/lights.py",
|
|
lineno="2",
|
|
line="something()",
|
|
),
|
|
]
|
|
)
|
|
with (
|
|
pytest.raises(RuntimeError),
|
|
patch(
|
|
"homeassistant.helpers.frame.linecache.getline",
|
|
return_value="self.light.is_on",
|
|
),
|
|
patch(
|
|
"homeassistant.util.loop._get_line_from_cache",
|
|
return_value="mock_line",
|
|
),
|
|
patch(
|
|
"homeassistant.util.loop.get_current_frame",
|
|
return_value=frames,
|
|
),
|
|
patch(
|
|
"homeassistant.helpers.frame.get_current_frame",
|
|
return_value=frames,
|
|
),
|
|
):
|
|
haloop.check_loop(banned_function)
|
|
assert (
|
|
"Detected blocking call to banned_function inside the event loop by integration"
|
|
" 'hue' at homeassistant/components/hue/light.py, line 23: self.light.is_on "
|
|
"(offender: /home/paulus/aiohue/lights.py, line 2: mock_line), please create "
|
|
"a bug report at https://github.com/home-assistant/core/issues?"
|
|
"q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+hue%22" in caplog.text
|
|
)
|
|
|
|
|
|
async def test_check_loop_async_integration_non_strict(
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test check_loop detects when called from event loop from integration context."""
|
|
frames = extract_stack_to_frame(
|
|
[
|
|
Mock(
|
|
filename="/home/paulus/homeassistant/core.py",
|
|
lineno="23",
|
|
line="do_something()",
|
|
),
|
|
Mock(
|
|
filename="/home/paulus/homeassistant/components/hue/light.py",
|
|
lineno="23",
|
|
line="self.light.is_on",
|
|
),
|
|
Mock(
|
|
filename="/home/paulus/aiohue/lights.py",
|
|
lineno="2",
|
|
line="something()",
|
|
),
|
|
]
|
|
)
|
|
with (
|
|
patch(
|
|
"homeassistant.helpers.frame.linecache.getline",
|
|
return_value="self.light.is_on",
|
|
),
|
|
patch(
|
|
"homeassistant.util.loop._get_line_from_cache",
|
|
return_value="mock_line",
|
|
),
|
|
patch(
|
|
"homeassistant.util.loop.get_current_frame",
|
|
return_value=frames,
|
|
),
|
|
patch(
|
|
"homeassistant.helpers.frame.get_current_frame",
|
|
return_value=frames,
|
|
),
|
|
):
|
|
haloop.check_loop(banned_function, strict=False)
|
|
assert (
|
|
"Detected blocking call to banned_function inside the event loop by integration"
|
|
" 'hue' at homeassistant/components/hue/light.py, line 23: self.light.is_on "
|
|
"(offender: /home/paulus/aiohue/lights.py, line 2: mock_line), "
|
|
"please create a bug report at https://github.com/home-assistant/core/issues?"
|
|
"q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+hue%22" in caplog.text
|
|
)
|
|
|
|
|
|
async def test_check_loop_async_custom(caplog: pytest.LogCaptureFixture) -> None:
|
|
"""Test check_loop detects when called from event loop with custom component context."""
|
|
frames = extract_stack_to_frame(
|
|
[
|
|
Mock(
|
|
filename="/home/paulus/homeassistant/core.py",
|
|
lineno="23",
|
|
line="do_something()",
|
|
),
|
|
Mock(
|
|
filename="/home/paulus/config/custom_components/hue/light.py",
|
|
lineno="23",
|
|
line="self.light.is_on",
|
|
),
|
|
Mock(
|
|
filename="/home/paulus/aiohue/lights.py",
|
|
lineno="2",
|
|
line="something()",
|
|
),
|
|
]
|
|
)
|
|
with (
|
|
pytest.raises(RuntimeError),
|
|
patch(
|
|
"homeassistant.helpers.frame.linecache.getline",
|
|
return_value="self.light.is_on",
|
|
),
|
|
patch(
|
|
"homeassistant.util.loop._get_line_from_cache",
|
|
return_value="mock_line",
|
|
),
|
|
patch(
|
|
"homeassistant.util.loop.get_current_frame",
|
|
return_value=frames,
|
|
),
|
|
patch(
|
|
"homeassistant.helpers.frame.get_current_frame",
|
|
return_value=frames,
|
|
),
|
|
):
|
|
haloop.check_loop(banned_function)
|
|
assert (
|
|
"Detected blocking call to banned_function inside the event loop by custom "
|
|
"integration 'hue' at custom_components/hue/light.py, line 23: self.light.is_on"
|
|
" (offender: /home/paulus/aiohue/lights.py, line 2: mock_line), "
|
|
"please create a bug report at https://github.com/home-assistant/core/issues?"
|
|
"q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+hue%22"
|
|
) in caplog.text
|
|
|
|
|
|
def test_check_loop_sync(caplog: pytest.LogCaptureFixture) -> None:
|
|
"""Test check_loop does nothing when called from thread."""
|
|
haloop.check_loop(banned_function)
|
|
assert "Detected blocking call inside the event loop" not in caplog.text
|
|
|
|
|
|
def test_protect_loop_sync() -> None:
|
|
"""Test protect_loop calls check_loop."""
|
|
func = Mock()
|
|
with patch("homeassistant.util.loop.check_loop") as mock_check_loop:
|
|
haloop.protect_loop(func)(1, test=2)
|
|
mock_check_loop.assert_called_once_with(
|
|
func,
|
|
strict=True,
|
|
args=(1,),
|
|
check_allowed=None,
|
|
kwargs={"test": 2},
|
|
strict_core=True,
|
|
)
|
|
func.assert_called_once_with(1, test=2)
|