* Refactor integration startup time tracking to reduce overhead - Use monotonic time for watching integration startup time as it avoids incorrect values if time moves backwards because of ntp during startup and reduces many time conversions since we want durations in seconds and not local time - Use loop scheduling instead of a task - Moves all the dispatcher logic into the new _WatchPendingSetups * websocket as well * tweaks * simplify logic * preserve logic * preserve logic * lint * adjust
786 lines
26 KiB
Python
786 lines
26 KiB
Python
"""Test component/platform setup."""
|
|
|
|
import asyncio
|
|
import threading
|
|
from unittest.mock import AsyncMock, Mock, patch
|
|
|
|
import pytest
|
|
import voluptuous as vol
|
|
|
|
from homeassistant import config_entries, setup
|
|
from homeassistant.const import EVENT_COMPONENT_LOADED, EVENT_HOMEASSISTANT_START
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers import discovery
|
|
from homeassistant.helpers.config_validation import (
|
|
PLATFORM_SCHEMA,
|
|
PLATFORM_SCHEMA_BASE,
|
|
)
|
|
|
|
from .common import (
|
|
MockConfigEntry,
|
|
MockModule,
|
|
MockPlatform,
|
|
assert_setup_component,
|
|
mock_integration,
|
|
mock_platform,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_handlers():
|
|
"""Mock config flows."""
|
|
|
|
class MockFlowHandler(config_entries.ConfigFlow):
|
|
"""Define a mock flow handler."""
|
|
|
|
VERSION = 1
|
|
|
|
with patch.dict(config_entries.HANDLERS, {"comp": MockFlowHandler}):
|
|
yield
|
|
|
|
|
|
async def test_validate_component_config(hass: HomeAssistant) -> None:
|
|
"""Test validating component configuration."""
|
|
config_schema = vol.Schema({"comp_conf": {"hello": str}}, required=True)
|
|
mock_integration(hass, MockModule("comp_conf", config_schema=config_schema))
|
|
|
|
with assert_setup_component(0):
|
|
assert not await setup.async_setup_component(hass, "comp_conf", {})
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
|
|
with assert_setup_component(0):
|
|
assert not await setup.async_setup_component(
|
|
hass, "comp_conf", {"comp_conf": None}
|
|
)
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
|
|
with assert_setup_component(0):
|
|
assert not await setup.async_setup_component(
|
|
hass, "comp_conf", {"comp_conf": {}}
|
|
)
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
|
|
with assert_setup_component(0):
|
|
assert not await setup.async_setup_component(
|
|
hass,
|
|
"comp_conf",
|
|
{"comp_conf": {"hello": "world", "invalid": "extra"}},
|
|
)
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
|
|
with assert_setup_component(1):
|
|
assert await setup.async_setup_component(
|
|
hass, "comp_conf", {"comp_conf": {"hello": "world"}}
|
|
)
|
|
|
|
|
|
async def test_validate_platform_config(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test validating platform configuration."""
|
|
platform_schema = PLATFORM_SCHEMA.extend({"hello": str})
|
|
platform_schema_base = PLATFORM_SCHEMA_BASE.extend({})
|
|
mock_integration(
|
|
hass,
|
|
MockModule("platform_conf", platform_schema_base=platform_schema_base),
|
|
)
|
|
mock_platform(
|
|
hass,
|
|
"whatever.platform_conf",
|
|
MockPlatform(platform_schema=platform_schema),
|
|
)
|
|
|
|
with assert_setup_component(0):
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"platform_conf",
|
|
{"platform_conf": {"platform": "not_existing", "hello": "world"}},
|
|
)
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("platform_conf")
|
|
|
|
with assert_setup_component(1):
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"platform_conf",
|
|
{"platform_conf": {"platform": "whatever", "hello": "world"}},
|
|
)
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("platform_conf")
|
|
|
|
with assert_setup_component(1):
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"platform_conf",
|
|
{"platform_conf": [{"platform": "whatever", "hello": "world"}]},
|
|
)
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("platform_conf")
|
|
|
|
# Any falsey platform config will be ignored (None, {}, etc)
|
|
with assert_setup_component(0) as config:
|
|
assert await setup.async_setup_component(
|
|
hass, "platform_conf", {"platform_conf": None}
|
|
)
|
|
assert "platform_conf" in hass.config.components
|
|
assert not config["platform_conf"] # empty
|
|
|
|
assert await setup.async_setup_component(
|
|
hass, "platform_conf", {"platform_conf": {}}
|
|
)
|
|
assert "platform_conf" in hass.config.components
|
|
assert not config["platform_conf"] # empty
|
|
|
|
|
|
async def test_validate_platform_config_2(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test component PLATFORM_SCHEMA_BASE prio over PLATFORM_SCHEMA."""
|
|
platform_schema = PLATFORM_SCHEMA.extend({"hello": str})
|
|
platform_schema_base = PLATFORM_SCHEMA_BASE.extend({"hello": "world"})
|
|
mock_integration(
|
|
hass,
|
|
MockModule(
|
|
"platform_conf",
|
|
platform_schema=platform_schema,
|
|
platform_schema_base=platform_schema_base,
|
|
),
|
|
)
|
|
|
|
mock_platform(
|
|
hass,
|
|
"whatever.platform_conf",
|
|
MockPlatform("whatever", platform_schema=platform_schema),
|
|
)
|
|
|
|
with assert_setup_component(1):
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"platform_conf",
|
|
{
|
|
# pass
|
|
"platform_conf": {"platform": "whatever", "hello": "world"},
|
|
# fail: key hello violates component platform_schema_base
|
|
"platform_conf 2": {"platform": "whatever", "hello": "there"},
|
|
},
|
|
)
|
|
|
|
|
|
async def test_validate_platform_config_3(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test fallback to component PLATFORM_SCHEMA."""
|
|
component_schema = PLATFORM_SCHEMA_BASE.extend({"hello": str})
|
|
platform_schema = PLATFORM_SCHEMA.extend({"cheers": str, "hello": "world"})
|
|
mock_integration(
|
|
hass, MockModule("platform_conf", platform_schema=component_schema)
|
|
)
|
|
|
|
mock_platform(
|
|
hass,
|
|
"whatever.platform_conf",
|
|
MockPlatform("whatever", platform_schema=platform_schema),
|
|
)
|
|
|
|
with assert_setup_component(1):
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"platform_conf",
|
|
{
|
|
# pass
|
|
"platform_conf": {"platform": "whatever", "hello": "world"},
|
|
# fail: key hello violates component platform_schema
|
|
"platform_conf 2": {"platform": "whatever", "hello": "there"},
|
|
},
|
|
)
|
|
|
|
|
|
async def test_validate_platform_config_4(hass: HomeAssistant) -> None:
|
|
"""Test entity_namespace in PLATFORM_SCHEMA."""
|
|
component_schema = PLATFORM_SCHEMA_BASE
|
|
platform_schema = PLATFORM_SCHEMA
|
|
mock_integration(
|
|
hass,
|
|
MockModule("platform_conf", platform_schema_base=component_schema),
|
|
)
|
|
|
|
mock_platform(
|
|
hass,
|
|
"whatever.platform_conf",
|
|
MockPlatform(platform_schema=platform_schema),
|
|
)
|
|
|
|
with assert_setup_component(1):
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"platform_conf",
|
|
{
|
|
"platform_conf": {
|
|
# pass: entity_namespace accepted by PLATFORM_SCHEMA
|
|
"platform": "whatever",
|
|
"entity_namespace": "yummy",
|
|
}
|
|
},
|
|
)
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("platform_conf")
|
|
|
|
|
|
async def test_component_not_found(hass: HomeAssistant) -> None:
|
|
"""setup_component should not crash if component doesn't exist."""
|
|
assert await setup.async_setup_component(hass, "non_existing", {}) is False
|
|
|
|
|
|
async def test_component_not_double_initialized(hass: HomeAssistant) -> None:
|
|
"""Test we do not set up a component twice."""
|
|
mock_setup = Mock(return_value=True)
|
|
|
|
mock_integration(hass, MockModule("comp", setup=mock_setup))
|
|
|
|
assert await setup.async_setup_component(hass, "comp", {})
|
|
assert mock_setup.called
|
|
|
|
mock_setup.reset_mock()
|
|
|
|
assert await setup.async_setup_component(hass, "comp", {})
|
|
assert not mock_setup.called
|
|
|
|
|
|
async def test_component_not_installed_if_requirement_fails(
|
|
hass: HomeAssistant,
|
|
) -> None:
|
|
"""Component setup should fail if requirement can't install."""
|
|
hass.config.skip_pip = False
|
|
mock_integration(hass, MockModule("comp", requirements=["package==0.0.1"]))
|
|
|
|
with patch("homeassistant.util.package.install_package", return_value=False):
|
|
assert not await setup.async_setup_component(hass, "comp", {})
|
|
|
|
assert "comp" not in hass.config.components
|
|
|
|
|
|
async def test_component_not_setup_twice_if_loaded_during_other_setup(
|
|
hass: HomeAssistant,
|
|
) -> None:
|
|
"""Test component setup while waiting for lock is not set up twice."""
|
|
result = []
|
|
|
|
async def async_setup(hass, config):
|
|
"""Tracking Setup."""
|
|
result.append(1)
|
|
|
|
mock_integration(hass, MockModule("comp", async_setup=async_setup))
|
|
|
|
def setup_component():
|
|
"""Set up the component."""
|
|
setup.setup_component(hass, "comp", {})
|
|
|
|
thread = threading.Thread(target=setup_component)
|
|
thread.start()
|
|
await setup.async_setup_component(hass, "comp", {})
|
|
|
|
await hass.async_add_executor_job(thread.join)
|
|
|
|
assert len(result) == 1
|
|
|
|
|
|
async def test_component_not_setup_missing_dependencies(hass: HomeAssistant) -> None:
|
|
"""Test we do not set up a component if not all dependencies loaded."""
|
|
deps = ["maybe_existing"]
|
|
mock_integration(hass, MockModule("comp", dependencies=deps))
|
|
|
|
assert not await setup.async_setup_component(hass, "comp", {})
|
|
assert "comp" not in hass.config.components
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
|
|
mock_integration(hass, MockModule("comp2", dependencies=deps))
|
|
mock_integration(hass, MockModule("maybe_existing"))
|
|
|
|
assert await setup.async_setup_component(hass, "comp2", {})
|
|
|
|
|
|
async def test_component_failing_setup(hass: HomeAssistant) -> None:
|
|
"""Test component that fails setup."""
|
|
mock_integration(hass, MockModule("comp", setup=lambda hass, config: False))
|
|
|
|
assert not await setup.async_setup_component(hass, "comp", {})
|
|
assert "comp" not in hass.config.components
|
|
|
|
|
|
async def test_component_exception_setup(hass: HomeAssistant) -> None:
|
|
"""Test component that raises exception during setup."""
|
|
|
|
def exception_setup(hass, config):
|
|
"""Raise exception."""
|
|
raise Exception("fail!")
|
|
|
|
mock_integration(hass, MockModule("comp", setup=exception_setup))
|
|
|
|
assert not await setup.async_setup_component(hass, "comp", {})
|
|
assert "comp" not in hass.config.components
|
|
|
|
|
|
async def test_component_setup_with_validation_and_dependency(
|
|
hass: HomeAssistant,
|
|
) -> None:
|
|
"""Test all config is passed to dependencies."""
|
|
|
|
def config_check_setup(hass, config):
|
|
"""Test that config is passed in."""
|
|
if config.get("comp_a", {}).get("valid", False):
|
|
return True
|
|
raise Exception(f"Config not passed in: {config}")
|
|
|
|
platform = MockPlatform()
|
|
|
|
mock_integration(hass, MockModule("comp_a", setup=config_check_setup))
|
|
mock_integration(
|
|
hass,
|
|
MockModule("platform_a", setup=config_check_setup, dependencies=["comp_a"]),
|
|
)
|
|
|
|
mock_platform(hass, "platform_a.switch", platform)
|
|
|
|
await setup.async_setup_component(
|
|
hass,
|
|
"switch",
|
|
{"comp_a": {"valid": True}, "switch": {"platform": "platform_a"}},
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert "comp_a" in hass.config.components
|
|
|
|
|
|
async def test_platform_specific_config_validation(hass: HomeAssistant) -> None:
|
|
"""Test platform that specifies config."""
|
|
platform_schema = PLATFORM_SCHEMA.extend({"valid": True}, extra=vol.PREVENT_EXTRA)
|
|
|
|
mock_setup = Mock(spec_set=True)
|
|
|
|
mock_platform(
|
|
hass,
|
|
"platform_a.switch",
|
|
MockPlatform(platform_schema=platform_schema, setup_platform=mock_setup),
|
|
)
|
|
|
|
with assert_setup_component(0, "switch"), patch(
|
|
"homeassistant.setup.async_notify_setup_error"
|
|
) as mock_notify:
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"switch",
|
|
{"switch": {"platform": "platform_a", "invalid": True}},
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert mock_setup.call_count == 0
|
|
assert len(mock_notify.mock_calls) == 1
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("switch")
|
|
|
|
with assert_setup_component(0), patch(
|
|
"homeassistant.setup.async_notify_setup_error"
|
|
) as mock_notify:
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"switch",
|
|
{
|
|
"switch": {
|
|
"platform": "platform_a",
|
|
"valid": True,
|
|
"invalid_extra": True,
|
|
}
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert mock_setup.call_count == 0
|
|
assert len(mock_notify.mock_calls) == 1
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("switch")
|
|
|
|
with assert_setup_component(1, "switch"), patch(
|
|
"homeassistant.setup.async_notify_setup_error"
|
|
) as mock_notify:
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"switch",
|
|
{"switch": {"platform": "platform_a", "valid": True}},
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert mock_setup.call_count == 1
|
|
assert len(mock_notify.mock_calls) == 0
|
|
|
|
|
|
async def test_disable_component_if_invalid_return(hass: HomeAssistant) -> None:
|
|
"""Test disabling component if invalid return."""
|
|
mock_integration(
|
|
hass, MockModule("disabled_component", setup=lambda hass, config: None)
|
|
)
|
|
|
|
assert not await setup.async_setup_component(hass, "disabled_component", {})
|
|
assert "disabled_component" not in hass.config.components
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
mock_integration(
|
|
hass,
|
|
MockModule("disabled_component", setup=lambda hass, config: False),
|
|
)
|
|
|
|
assert not await setup.async_setup_component(hass, "disabled_component", {})
|
|
assert "disabled_component" not in hass.config.components
|
|
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
mock_integration(
|
|
hass, MockModule("disabled_component", setup=lambda hass, config: True)
|
|
)
|
|
|
|
assert await setup.async_setup_component(hass, "disabled_component", {})
|
|
assert "disabled_component" in hass.config.components
|
|
|
|
|
|
async def test_all_work_done_before_start(hass: HomeAssistant) -> None:
|
|
"""Test all init work done till start."""
|
|
call_order = []
|
|
|
|
async def component1_setup(hass, config):
|
|
"""Set up mock component."""
|
|
await discovery.async_discover(
|
|
hass, "test_component2", {}, "test_component2", {}
|
|
)
|
|
await discovery.async_discover(
|
|
hass, "test_component3", {}, "test_component3", {}
|
|
)
|
|
return True
|
|
|
|
def component_track_setup(hass, config):
|
|
"""Set up mock component."""
|
|
call_order.append(1)
|
|
return True
|
|
|
|
mock_integration(hass, MockModule("test_component1", async_setup=component1_setup))
|
|
|
|
mock_integration(hass, MockModule("test_component2", setup=component_track_setup))
|
|
|
|
mock_integration(hass, MockModule("test_component3", setup=component_track_setup))
|
|
|
|
@callback
|
|
def track_start(event):
|
|
"""Track start event."""
|
|
call_order.append(2)
|
|
|
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, track_start)
|
|
|
|
hass.add_job(setup.async_setup_component(hass, "test_component1", {}))
|
|
await hass.async_block_till_done()
|
|
await hass.async_start()
|
|
assert call_order == [1, 1, 2]
|
|
|
|
|
|
async def test_component_warn_slow_setup(hass: HomeAssistant) -> None:
|
|
"""Warn we log when a component setup takes a long time."""
|
|
mock_integration(hass, MockModule("test_component1"))
|
|
with patch.object(hass.loop, "call_later") as mock_call:
|
|
result = await setup.async_setup_component(hass, "test_component1", {})
|
|
assert result
|
|
assert mock_call.called
|
|
|
|
assert len(mock_call.mock_calls) == 3
|
|
timeout, logger_method = mock_call.mock_calls[0][1][:2]
|
|
|
|
assert timeout == setup.SLOW_SETUP_WARNING
|
|
assert logger_method == setup._LOGGER.warning
|
|
|
|
assert mock_call().cancel.called
|
|
|
|
|
|
async def test_platform_no_warn_slow(hass: HomeAssistant) -> None:
|
|
"""Do not warn for long entity setup time."""
|
|
mock_integration(
|
|
hass, MockModule("test_component1", platform_schema=PLATFORM_SCHEMA)
|
|
)
|
|
with patch.object(hass.loop, "call_later") as mock_call:
|
|
result = await setup.async_setup_component(hass, "test_component1", {})
|
|
assert result
|
|
assert len(mock_call.mock_calls) == 0
|
|
|
|
|
|
async def test_platform_error_slow_setup(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Don't block startup more than SLOW_SETUP_MAX_WAIT."""
|
|
|
|
with patch.object(setup, "SLOW_SETUP_MAX_WAIT", 0.1):
|
|
called = []
|
|
|
|
async def async_setup(*args):
|
|
"""Tracking Setup."""
|
|
called.append(1)
|
|
await asyncio.sleep(2)
|
|
|
|
mock_integration(hass, MockModule("test_component1", async_setup=async_setup))
|
|
result = await setup.async_setup_component(hass, "test_component1", {})
|
|
assert len(called) == 1
|
|
assert not result
|
|
assert "'test_component1' is taking longer than 0.1 seconds" in caplog.text
|
|
|
|
|
|
async def test_when_setup_already_loaded(hass: HomeAssistant) -> None:
|
|
"""Test when setup."""
|
|
calls = []
|
|
|
|
async def mock_callback(hass, component):
|
|
"""Mock callback."""
|
|
calls.append(component)
|
|
|
|
setup.async_when_setup(hass, "test", mock_callback)
|
|
await hass.async_block_till_done()
|
|
assert calls == []
|
|
|
|
hass.config.components.add("test")
|
|
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "test"})
|
|
await hass.async_block_till_done()
|
|
assert calls == ["test"]
|
|
|
|
# Event listener should be gone
|
|
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "test"})
|
|
await hass.async_block_till_done()
|
|
assert calls == ["test"]
|
|
|
|
# Should be called right away
|
|
setup.async_when_setup(hass, "test", mock_callback)
|
|
await hass.async_block_till_done()
|
|
assert calls == ["test", "test"]
|
|
|
|
|
|
async def test_async_when_setup_or_start_already_loaded(hass: HomeAssistant) -> None:
|
|
"""Test when setup or start."""
|
|
calls = []
|
|
|
|
async def mock_callback(hass, component):
|
|
"""Mock callback."""
|
|
calls.append(component)
|
|
|
|
setup.async_when_setup_or_start(hass, "test", mock_callback)
|
|
await hass.async_block_till_done()
|
|
assert calls == []
|
|
|
|
hass.config.components.add("test")
|
|
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "test"})
|
|
await hass.async_block_till_done()
|
|
assert calls == ["test"]
|
|
|
|
# Event listener should be gone
|
|
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "test"})
|
|
await hass.async_block_till_done()
|
|
assert calls == ["test"]
|
|
|
|
# Should be called right away
|
|
setup.async_when_setup_or_start(hass, "test", mock_callback)
|
|
await hass.async_block_till_done()
|
|
assert calls == ["test", "test"]
|
|
|
|
setup.async_when_setup_or_start(hass, "not_loaded", mock_callback)
|
|
await hass.async_block_till_done()
|
|
assert calls == ["test", "test"]
|
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
|
await hass.async_block_till_done()
|
|
assert calls == ["test", "test", "not_loaded"]
|
|
|
|
|
|
async def test_setup_import_blows_up(hass: HomeAssistant) -> None:
|
|
"""Test that we handle it correctly when importing integration blows up."""
|
|
with patch(
|
|
"homeassistant.loader.Integration.get_component", side_effect=ImportError
|
|
):
|
|
assert not await setup.async_setup_component(hass, "sun", {})
|
|
|
|
|
|
async def test_parallel_entry_setup(hass: HomeAssistant, mock_handlers) -> None:
|
|
"""Test config entries are set up in parallel."""
|
|
MockConfigEntry(domain="comp", data={"value": 1}).add_to_hass(hass)
|
|
MockConfigEntry(domain="comp", data={"value": 2}).add_to_hass(hass)
|
|
|
|
calls = []
|
|
|
|
async def mock_async_setup_entry(hass, entry):
|
|
"""Mock setting up an entry."""
|
|
calls.append(entry.data["value"])
|
|
await asyncio.sleep(0)
|
|
calls.append(entry.data["value"])
|
|
return True
|
|
|
|
mock_integration(
|
|
hass,
|
|
MockModule(
|
|
"comp",
|
|
async_setup_entry=mock_async_setup_entry,
|
|
),
|
|
)
|
|
mock_platform(hass, "comp.config_flow", None)
|
|
await setup.async_setup_component(hass, "comp", {})
|
|
|
|
assert calls == [1, 2, 1, 2]
|
|
|
|
|
|
async def test_integration_disabled(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test we can disable an integration."""
|
|
disabled_reason = "Dependency contains code that breaks Home Assistant"
|
|
mock_integration(
|
|
hass,
|
|
MockModule("test_component1", partial_manifest={"disabled": disabled_reason}),
|
|
)
|
|
result = await setup.async_setup_component(hass, "test_component1", {})
|
|
assert not result
|
|
assert disabled_reason in caplog.text
|
|
|
|
|
|
async def test_integration_logs_is_custom(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test we highlight it's a custom component when errors happen."""
|
|
mock_integration(
|
|
hass,
|
|
MockModule("test_component1"),
|
|
built_in=False,
|
|
)
|
|
with patch(
|
|
"homeassistant.setup.async_process_deps_reqs",
|
|
side_effect=HomeAssistantError("Boom"),
|
|
):
|
|
result = await setup.async_setup_component(hass, "test_component1", {})
|
|
assert not result
|
|
assert "Setup failed for custom integration 'test_component1': Boom" in caplog.text
|
|
|
|
|
|
async def test_async_get_loaded_integrations(hass: HomeAssistant) -> None:
|
|
"""Test we can enumerate loaded integrations."""
|
|
hass.config.components.add("notbase")
|
|
hass.config.components.add("switch")
|
|
hass.config.components.add("notbase.switch")
|
|
hass.config.components.add("myintegration")
|
|
hass.config.components.add("device_tracker")
|
|
hass.config.components.add("other.device_tracker")
|
|
hass.config.components.add("myintegration.light")
|
|
assert setup.async_get_loaded_integrations(hass) == {
|
|
"other",
|
|
"switch",
|
|
"notbase",
|
|
"myintegration",
|
|
"device_tracker",
|
|
}
|
|
|
|
|
|
async def test_integration_no_setup(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test we fail integration setup without setup functions."""
|
|
mock_integration(
|
|
hass,
|
|
MockModule("test_integration_without_setup", setup=False),
|
|
)
|
|
result = await setup.async_setup_component(
|
|
hass, "test_integration_without_setup", {}
|
|
)
|
|
assert not result
|
|
assert "No setup or config entry setup function defined" in caplog.text
|
|
|
|
|
|
async def test_integration_only_setup_entry(hass: HomeAssistant) -> None:
|
|
"""Test we have an integration with only a setup entry method."""
|
|
mock_integration(
|
|
hass,
|
|
MockModule(
|
|
"test_integration_only_entry",
|
|
setup=False,
|
|
async_setup_entry=AsyncMock(return_value=True),
|
|
),
|
|
)
|
|
assert await setup.async_setup_component(hass, "test_integration_only_entry", {})
|
|
|
|
|
|
async def test_async_start_setup(hass: HomeAssistant) -> None:
|
|
"""Test setup started context manager keeps track of setup times."""
|
|
with setup.async_start_setup(hass, ["august"]):
|
|
assert isinstance(hass.data[setup.DATA_SETUP_STARTED]["august"], float)
|
|
with setup.async_start_setup(hass, ["august"]):
|
|
assert isinstance(hass.data[setup.DATA_SETUP_STARTED]["august_2"], float)
|
|
|
|
assert "august" not in hass.data[setup.DATA_SETUP_STARTED]
|
|
assert isinstance(hass.data[setup.DATA_SETUP_TIME]["august"], float)
|
|
assert "august_2" not in hass.data[setup.DATA_SETUP_TIME]
|
|
|
|
|
|
async def test_async_start_setup_platforms(hass: HomeAssistant) -> None:
|
|
"""Test setup started context manager keeps track of setup times for platforms."""
|
|
with setup.async_start_setup(hass, ["august.sensor"]):
|
|
assert isinstance(hass.data[setup.DATA_SETUP_STARTED]["august.sensor"], float)
|
|
|
|
assert "august" not in hass.data[setup.DATA_SETUP_STARTED]
|
|
assert isinstance(hass.data[setup.DATA_SETUP_TIME]["august"], float)
|
|
assert "sensor" not in hass.data[setup.DATA_SETUP_TIME]
|
|
|
|
|
|
async def test_setup_config_entry_from_yaml(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test attempting to setup an integration which only supports config_entries."""
|
|
expected_warning = (
|
|
"The 'test_integration_only_entry' integration does not support YAML setup, "
|
|
"please remove it from your configuration"
|
|
)
|
|
|
|
mock_integration(
|
|
hass,
|
|
MockModule(
|
|
"test_integration_only_entry",
|
|
setup=False,
|
|
async_setup_entry=AsyncMock(return_value=True),
|
|
),
|
|
)
|
|
|
|
assert await setup.async_setup_component(hass, "test_integration_only_entry", {})
|
|
assert expected_warning not in caplog.text
|
|
caplog.clear()
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("test_integration_only_entry")
|
|
|
|
# There should be a warning, but setup should not fail
|
|
assert await setup.async_setup_component(
|
|
hass, "test_integration_only_entry", {"test_integration_only_entry": None}
|
|
)
|
|
assert expected_warning in caplog.text
|
|
caplog.clear()
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("test_integration_only_entry")
|
|
|
|
# There should be a warning, but setup should not fail
|
|
assert await setup.async_setup_component(
|
|
hass, "test_integration_only_entry", {"test_integration_only_entry": {}}
|
|
)
|
|
assert expected_warning in caplog.text
|
|
caplog.clear()
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("test_integration_only_entry")
|
|
|
|
# There should be a warning, but setup should not fail
|
|
assert await setup.async_setup_component(
|
|
hass,
|
|
"test_integration_only_entry",
|
|
{"test_integration_only_entry": {"hello": "world"}},
|
|
)
|
|
assert expected_warning in caplog.text
|
|
caplog.clear()
|
|
hass.data.pop(setup.DATA_SETUP)
|
|
hass.config.components.remove("test_integration_only_entry")
|