"""Test discovery helpers.""" from unittest.mock import patch import pytest from homeassistant import setup from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import discovery from homeassistant.helpers.dispatcher import async_dispatcher_send from tests.common import MockModule, MockPlatform, mock_integration, mock_platform @pytest.fixture def mock_setup_component(): """Mock setup component.""" with patch("homeassistant.setup.async_setup_component", return_value=True) as mock: yield mock async def test_listen(hass: HomeAssistant, mock_setup_component) -> None: """Test discovery listen/discover combo.""" calls_single = [] @callback def callback_single(service, info): """Service discovered callback.""" calls_single.append((service, info)) discovery.async_listen(hass, "test service", callback_single) await discovery.async_discover( hass, "test service", "discovery info", "test_component", {}, ) await hass.async_block_till_done() assert mock_setup_component.called assert mock_setup_component.call_args[0] == (hass, "test_component", {}) assert len(calls_single) == 1 assert calls_single[0] == ("test service", "discovery info") async def test_platform(hass: HomeAssistant, mock_setup_component) -> None: """Test discover platform method.""" calls = [] @callback def platform_callback(platform, info): """Platform callback method.""" calls.append((platform, info)) discovery.async_listen_platform( hass, "test_component", platform_callback, ) await discovery.async_load_platform( hass, "test_component", "test_platform", "discovery info", {"test_component": {}}, ) await hass.async_block_till_done() assert mock_setup_component.called assert mock_setup_component.call_args[0] == ( hass, "test_component", {"test_component": {}}, ) await hass.async_block_till_done() await hass.async_add_executor_job( discovery.load_platform, hass, "test_component_2", "test_platform", "discovery info", {"test_component": {}}, ) await hass.async_block_till_done() assert len(calls) == 1 assert calls[0] == ("test_platform", "discovery info") async_dispatcher_send( hass, discovery.SIGNAL_PLATFORM_DISCOVERED, {"service": discovery.EVENT_LOAD_PLATFORM.format("test_component")}, ) await hass.async_block_till_done() assert len(calls) == 1 async def test_circular_import(hass: HomeAssistant) -> None: """Test we don't break doing circular import. This test will have test_component discover the switch.test_circular component while setting up. The supplied config will load test_component and will load switch.test_circular. That means that after startup, we will have test_component and switch setup. The test_circular platform has been loaded twice. """ component_calls = [] platform_calls = [] def component_setup(hass, config): """Set up mock component.""" discovery.load_platform( hass, Platform.SWITCH, "test_circular", {"key": "value"}, config ) component_calls.append(1) return True def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up mock platform.""" platform_calls.append("disc" if discovery_info else "component") mock_integration(hass, MockModule("test_component", setup=component_setup)) # dependencies are only set in component level # since we are using manifest to hold them mock_integration(hass, MockModule("test_circular", dependencies=["test_component"])) mock_platform(hass, "test_circular.switch", MockPlatform(setup_platform)) await setup.async_setup_component( hass, "test_component", {"test_component": None, "switch": [{"platform": "test_circular"}]}, ) await hass.async_block_till_done() # test_component will only be setup once assert len(component_calls) == 1 # The platform will be setup once via the config in `setup_component` # and once via the discovery inside test_component. assert len(platform_calls) == 2 assert "test_component" in hass.config.components assert "switch" in hass.config.components async def test_1st_discovers_2nd_component(hass: HomeAssistant) -> None: """Test that we don't break if one component discovers the other. If the first component fires a discovery event to set up the second component while the second component is about to be set up, it should not set up the second component twice. """ component_calls = [] async def component1_setup(hass, config): """Set up mock component.""" await discovery.async_discover( hass, "test_component2", {}, "test_component2", {} ) return True def component2_setup(hass, config): """Set up mock component.""" component_calls.append(1) return True mock_integration(hass, MockModule("test_component1", async_setup=component1_setup)) mock_integration(hass, MockModule("test_component2", setup=component2_setup)) hass.async_create_task(setup.async_setup_component(hass, "test_component1", {})) hass.async_create_task(setup.async_setup_component(hass, "test_component2", {})) await hass.async_block_till_done() # test_component will only be setup once assert len(component_calls) == 1