"""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)