Refactor integration startup time to show wall clock time (#113707)
* Refactor setup time tracking to exclude time waiting on other operations We now exclude the import time and th time waiting on base platforms to setup from the setup times * tweak * tweak * tweak * tweak * adjust * fixes * fixes * preen * preen * tweak * tweak * adjust * tweak * reduce * do not count integrtion platforms against their parent integration * handle legacy tts platforms * stt as well * one more wait * use the same pattern in all the legacy * fix tts and stt legacy * fix * fix * reduce * preen * entity comp does not wait for platforms * scene blocks as well * fix test * test fixes * coverage * coverage * coverage * fix test * Update tests/test_setup.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update tests/test_setup.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/setup.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * strip * strip WAIT_PLATFORM_INTEGRATION * strip WAIT_PLATFORM_INTEGRATION * strip WAIT_PLATFORM_INTEGRATION * strip WAIT_PLATFORM_INTEGRATION * remove complexity * Apply suggestions from code review * no longer works that way * fixes * fixes * fixes --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
9be5f3531f
commit
c615b52840
20 changed files with 598 additions and 176 deletions
|
@ -2,14 +2,14 @@
|
|||
|
||||
import asyncio
|
||||
import threading
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
from unittest.mock import ANY, AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries, loader, setup
|
||||
from homeassistant.const import EVENT_COMPONENT_LOADED, EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import CoreState, HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import discovery, translation
|
||||
from homeassistant.helpers.config_validation import (
|
||||
|
@ -727,26 +727,244 @@ async def test_integration_only_setup_entry(hass: HomeAssistant) -> None:
|
|||
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)
|
||||
async def test_async_start_setup_running(hass: HomeAssistant) -> None:
|
||||
"""Test setup started context manager does nothing when running."""
|
||||
assert hass.state is CoreState.running
|
||||
setup_started: dict[tuple[str, str | None], float]
|
||||
setup_started = hass.data.setdefault(setup.DATA_SETUP_STARTED, {})
|
||||
|
||||
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]
|
||||
with setup.async_start_setup(
|
||||
hass, integration="august", phase=setup.SetupPhases.SETUP
|
||||
):
|
||||
assert not setup_started
|
||||
|
||||
|
||||
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)
|
||||
async def test_async_start_setup_config_entry(hass: HomeAssistant) -> None:
|
||||
"""Test setup started keeps track of setup times with a config entry."""
|
||||
hass.set_state(CoreState.not_running)
|
||||
setup_started: dict[tuple[str, str | None], float]
|
||||
setup_started = hass.data.setdefault(setup.DATA_SETUP_STARTED, {})
|
||||
setup_time = setup._setup_times(hass)
|
||||
|
||||
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]
|
||||
with setup.async_start_setup(
|
||||
hass, integration="august", phase=setup.SetupPhases.SETUP
|
||||
):
|
||||
assert isinstance(setup_started[("august", None)], float)
|
||||
|
||||
with setup.async_start_setup(
|
||||
hass,
|
||||
integration="august",
|
||||
group="entry_id",
|
||||
phase=setup.SetupPhases.CONFIG_ENTRY_SETUP,
|
||||
):
|
||||
assert isinstance(setup_started[("august", "entry_id")], float)
|
||||
with setup.async_start_setup(
|
||||
hass,
|
||||
integration="august",
|
||||
group="entry_id",
|
||||
phase=setup.SetupPhases.CONFIG_ENTRY_PLATFORM_SETUP,
|
||||
):
|
||||
assert isinstance(setup_started[("august", "entry_id")], float)
|
||||
|
||||
# CONFIG_ENTRY_PLATFORM_SETUP inside of CONFIG_ENTRY_SETUP should not be tracked
|
||||
assert setup_time["august"] == {
|
||||
None: {setup.SetupPhases.SETUP: ANY},
|
||||
"entry_id": {setup.SetupPhases.CONFIG_ENTRY_SETUP: ANY},
|
||||
}
|
||||
with setup.async_start_setup(
|
||||
hass,
|
||||
integration="august",
|
||||
group="entry_id",
|
||||
phase=setup.SetupPhases.CONFIG_ENTRY_PLATFORM_SETUP,
|
||||
):
|
||||
assert isinstance(setup_started[("august", "entry_id")], float)
|
||||
# Platforms outside of CONFIG_ENTRY_SETUP should be tracked
|
||||
# This simulates a late platform forward
|
||||
assert setup_time["august"] == {
|
||||
None: {setup.SetupPhases.SETUP: ANY},
|
||||
"entry_id": {
|
||||
setup.SetupPhases.CONFIG_ENTRY_SETUP: ANY,
|
||||
setup.SetupPhases.CONFIG_ENTRY_PLATFORM_SETUP: ANY,
|
||||
},
|
||||
}
|
||||
|
||||
with setup.async_start_setup(
|
||||
hass,
|
||||
integration="august",
|
||||
group="entry_id2",
|
||||
phase=setup.SetupPhases.CONFIG_ENTRY_SETUP,
|
||||
):
|
||||
assert isinstance(setup_started[("august", "entry_id2")], float)
|
||||
# We wrap places where we wait for other components
|
||||
# or the import of a module with async_freeze_setup
|
||||
# so we can subtract the time waited from the total setup time
|
||||
with setup.async_pause_setup(hass, setup.SetupPhases.WAIT_BASE_PLATFORM_SETUP):
|
||||
await asyncio.sleep(0)
|
||||
|
||||
# Wait time should be added if freeze_setup is used
|
||||
assert setup_time["august"] == {
|
||||
None: {setup.SetupPhases.SETUP: ANY},
|
||||
"entry_id": {
|
||||
setup.SetupPhases.CONFIG_ENTRY_SETUP: ANY,
|
||||
setup.SetupPhases.CONFIG_ENTRY_PLATFORM_SETUP: ANY,
|
||||
},
|
||||
"entry_id2": {
|
||||
setup.SetupPhases.CONFIG_ENTRY_SETUP: ANY,
|
||||
setup.SetupPhases.WAIT_BASE_PLATFORM_SETUP: ANY,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def test_async_start_setup_top_level_yaml(hass: HomeAssistant) -> None:
|
||||
"""Test setup started context manager keeps track of setup times with modern yaml."""
|
||||
hass.set_state(CoreState.not_running)
|
||||
setup_started: dict[tuple[str, str | None], float]
|
||||
setup_started = hass.data.setdefault(setup.DATA_SETUP_STARTED, {})
|
||||
setup_time = setup._setup_times(hass)
|
||||
|
||||
with setup.async_start_setup(
|
||||
hass, integration="command_line", phase=setup.SetupPhases.SETUP
|
||||
):
|
||||
assert isinstance(setup_started[("command_line", None)], float)
|
||||
|
||||
assert setup_time["command_line"] == {
|
||||
None: {setup.SetupPhases.SETUP: ANY},
|
||||
}
|
||||
|
||||
|
||||
async def test_async_start_setup_platform_integration(hass: HomeAssistant) -> None:
|
||||
"""Test setup started keeps track of setup times a platform integration."""
|
||||
hass.set_state(CoreState.not_running)
|
||||
setup_started: dict[tuple[str, str | None], float]
|
||||
setup_started = hass.data.setdefault(setup.DATA_SETUP_STARTED, {})
|
||||
setup_time = setup._setup_times(hass)
|
||||
|
||||
with setup.async_start_setup(
|
||||
hass, integration="sensor", phase=setup.SetupPhases.SETUP
|
||||
):
|
||||
assert isinstance(setup_started[("sensor", None)], float)
|
||||
|
||||
# Platform integration setups happen in another task
|
||||
with setup.async_start_setup(
|
||||
hass,
|
||||
integration="filter",
|
||||
group="123456",
|
||||
phase=setup.SetupPhases.PLATFORM_SETUP,
|
||||
):
|
||||
assert isinstance(setup_started[("filter", "123456")], float)
|
||||
|
||||
assert setup_time["sensor"] == {
|
||||
None: {
|
||||
setup.SetupPhases.SETUP: ANY,
|
||||
},
|
||||
}
|
||||
assert setup_time["filter"] == {
|
||||
"123456": {
|
||||
setup.SetupPhases.PLATFORM_SETUP: ANY,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def test_async_start_setup_legacy_platform_integration(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test setup started keeps track of setup times for a legacy platform integration."""
|
||||
hass.set_state(CoreState.not_running)
|
||||
setup_started: dict[tuple[str, str | None], float]
|
||||
setup_started = hass.data.setdefault(setup.DATA_SETUP_STARTED, {})
|
||||
setup_time = setup._setup_times(hass)
|
||||
|
||||
with setup.async_start_setup(
|
||||
hass, integration="notify", phase=setup.SetupPhases.SETUP
|
||||
):
|
||||
assert isinstance(setup_started[("notify", None)], float)
|
||||
|
||||
with setup.async_start_setup(
|
||||
hass,
|
||||
integration="legacy_notify_integration",
|
||||
group="123456",
|
||||
phase=setup.SetupPhases.PLATFORM_SETUP,
|
||||
):
|
||||
assert isinstance(setup_started[("legacy_notify_integration", "123456")], float)
|
||||
|
||||
assert setup_time["notify"] == {
|
||||
None: {
|
||||
setup.SetupPhases.SETUP: ANY,
|
||||
},
|
||||
}
|
||||
assert setup_time["legacy_notify_integration"] == {
|
||||
"123456": {
|
||||
setup.SetupPhases.PLATFORM_SETUP: ANY,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def test_async_start_setup_simple_integration_end_to_end(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test end to end timings for a simple integration with no platforms."""
|
||||
hass.set_state(CoreState.not_running)
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"test_integration_no_platforms",
|
||||
setup=False,
|
||||
async_setup_entry=AsyncMock(return_value=True),
|
||||
),
|
||||
)
|
||||
assert await setup.async_setup_component(hass, "test_integration_no_platforms", {})
|
||||
await hass.async_block_till_done()
|
||||
assert setup.async_get_setup_timings(hass) == {
|
||||
"test_integration_no_platforms": ANY,
|
||||
}
|
||||
|
||||
|
||||
async def test_async_get_setup_timings(hass) -> None:
|
||||
"""Test we can get the setup timings from the setup time data."""
|
||||
setup_time = setup._setup_times(hass)
|
||||
# Mock setup time data
|
||||
setup_time.update(
|
||||
{
|
||||
"august": {
|
||||
None: {setup.SetupPhases.SETUP: 1},
|
||||
"entry_id": {
|
||||
setup.SetupPhases.CONFIG_ENTRY_SETUP: 1,
|
||||
setup.SetupPhases.CONFIG_ENTRY_PLATFORM_SETUP: 4,
|
||||
},
|
||||
"entry_id2": {
|
||||
setup.SetupPhases.CONFIG_ENTRY_SETUP: 7,
|
||||
setup.SetupPhases.WAIT_BASE_PLATFORM_SETUP: -5,
|
||||
},
|
||||
},
|
||||
"notify": {
|
||||
None: {
|
||||
setup.SetupPhases.SETUP: 2,
|
||||
},
|
||||
},
|
||||
"legacy_notify_integration": {
|
||||
"123456": {
|
||||
setup.SetupPhases.PLATFORM_SETUP: 3,
|
||||
},
|
||||
},
|
||||
"sensor": {
|
||||
None: {
|
||||
setup.SetupPhases.SETUP: 1,
|
||||
},
|
||||
},
|
||||
"filter": {
|
||||
"123456": {
|
||||
setup.SetupPhases.PLATFORM_SETUP: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
assert setup.async_get_setup_timings(hass) == {
|
||||
"august": 6,
|
||||
"notify": 2,
|
||||
"legacy_notify_integration": 3,
|
||||
"sensor": 1,
|
||||
"filter": 2,
|
||||
}
|
||||
|
||||
|
||||
async def test_setup_config_entry_from_yaml(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue