Reduce registry overhead in tests (#110955)

* Avoid scheduling registry loads as tasks in tests

Since we patch out async_load in Store, these will not yield
to the event loop so it makes sense to await them instead
of creating tasks

This reduced my local test run times ~2.5% on average

* mock out save as well so we do not schedule tasks to save empty data

* tweaks

* fix lingering files

* another one

* too much for one PR, reduce

* fix targets
This commit is contained in:
J. Nick Koston 2024-02-20 20:01:50 -06:00 committed by GitHub
parent a542b36997
commit 98d5f2fc01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 19 deletions

View File

@ -17,7 +17,7 @@ import pathlib
import threading import threading
import time import time
from types import ModuleType from types import ModuleType
from typing import Any, NoReturn from typing import Any, NoReturn, TypeVar
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
from aiohttp.test_utils import unused_port as get_test_instance_port # noqa: F401 from aiohttp.test_utils import unused_port as get_test_instance_port # noqa: F401
@ -194,13 +194,26 @@ def get_test_home_assistant() -> Generator[HomeAssistant, None, None]:
loop.close() loop.close()
_T = TypeVar("_T", bound=Mapping[str, Any] | Sequence[Any])
class StoreWithoutWriteLoad(storage.Store[_T]):
"""Fake store that does not write or load. Used for testing."""
async def async_save(self, *args: Any, **kwargs: Any) -> None:
"""Save the data."""
@asynccontextmanager @asynccontextmanager
async def async_test_home_assistant( async def async_test_home_assistant(
event_loop: asyncio.AbstractEventLoop | None = None, event_loop: asyncio.AbstractEventLoop | None = None,
load_registries: bool = True, load_registries: bool = True,
storage_dir: str | None = None,
) -> AsyncGenerator[HomeAssistant, None]: ) -> AsyncGenerator[HomeAssistant, None]:
"""Return a Home Assistant object pointing at test config dir.""" """Return a Home Assistant object pointing at test config dir."""
hass = HomeAssistant(get_test_config_dir()) hass = HomeAssistant(get_test_config_dir())
if storage_dir:
hass.config.config_dir = storage_dir
store = auth_store.AuthStore(hass) store = auth_store.AuthStore(hass)
hass.auth = auth.AuthManager(hass, store, {}, {}) hass.auth = auth.AuthManager(hass, store, {}, {})
ensure_auth_manager_loaded(hass.auth) ensure_auth_manager_loaded(hass.auth)
@ -284,23 +297,36 @@ async def async_test_home_assistant(
hass hass
) )
if load_registries: if load_registries:
with patch( with patch.object(
"homeassistant.helpers.storage.Store.async_load", return_value=None StoreWithoutWriteLoad, "async_load", return_value=None
), patch(
"homeassistant.helpers.area_registry.AreaRegistryStore",
StoreWithoutWriteLoad,
), patch(
"homeassistant.helpers.device_registry.DeviceRegistryStore",
StoreWithoutWriteLoad,
), patch(
"homeassistant.helpers.entity_registry.EntityRegistryStore",
StoreWithoutWriteLoad,
), patch(
"homeassistant.helpers.storage.Store", # Floor & label registry are different
StoreWithoutWriteLoad,
), patch(
"homeassistant.helpers.issue_registry.IssueRegistryStore",
StoreWithoutWriteLoad,
), patch( ), patch(
"homeassistant.helpers.restore_state.RestoreStateData.async_setup_dump", "homeassistant.helpers.restore_state.RestoreStateData.async_setup_dump",
return_value=None, return_value=None,
), patch( ), patch(
"homeassistant.helpers.restore_state.start.async_at_start", "homeassistant.helpers.restore_state.start.async_at_start",
): ):
await asyncio.gather( await ar.async_load(hass)
ar.async_load(hass), await dr.async_load(hass)
dr.async_load(hass), await er.async_load(hass)
er.async_load(hass), await fr.async_load(hass)
fr.async_load(hass), await ir.async_load(hass)
ir.async_load(hass), await lr.async_load(hass)
lr.async_load(hass), await rs.async_load(hass)
rs.async_load(hass),
)
hass.data[bootstrap.DATA_REGISTRIES_LOADED] = None hass.data[bootstrap.DATA_REGISTRIES_LOADED] = None
hass.set_state(CoreState.running) hass.set_state(CoreState.running)

View File

@ -560,10 +560,10 @@ async def test_loading_corrupt_core_file(
tmpdir: py.path.local, caplog: pytest.LogCaptureFixture tmpdir: py.path.local, caplog: pytest.LogCaptureFixture
) -> None: ) -> None:
"""Test we handle unrecoverable corruption in a core file.""" """Test we handle unrecoverable corruption in a core file."""
async with async_test_home_assistant() as hass: loop = asyncio.get_running_loop()
tmp_storage = await hass.async_add_executor_job(tmpdir.mkdir, "temp_storage") tmp_storage = await loop.run_in_executor(None, tmpdir.mkdir, "temp_storage")
hass.config.config_dir = tmp_storage
async with async_test_home_assistant(storage_dir=tmp_storage) as hass:
storage_key = "core.anything" storage_key = "core.anything"
store = storage.Store( store = storage.Store(
hass, MOCK_VERSION_2, storage_key, minor_version=MOCK_MINOR_VERSION_1 hass, MOCK_VERSION_2, storage_key, minor_version=MOCK_MINOR_VERSION_1
@ -618,13 +618,14 @@ async def test_loading_corrupt_file_known_domain(
tmpdir: py.path.local, caplog: pytest.LogCaptureFixture tmpdir: py.path.local, caplog: pytest.LogCaptureFixture
) -> None: ) -> None:
"""Test we handle unrecoverable corruption for a known domain.""" """Test we handle unrecoverable corruption for a known domain."""
async with async_test_home_assistant() as hass:
loop = asyncio.get_running_loop()
tmp_storage = await loop.run_in_executor(None, tmpdir.mkdir, "temp_storage")
async with async_test_home_assistant(storage_dir=tmp_storage) as hass:
hass.config.components.add("testdomain") hass.config.components.add("testdomain")
storage_key = "testdomain.testkey" storage_key = "testdomain.testkey"
tmp_storage = await hass.async_add_executor_job(tmpdir.mkdir, "temp_storage")
hass.config.config_dir = tmp_storage
store = storage.Store( store = storage.Store(
hass, MOCK_VERSION_2, storage_key, minor_version=MOCK_MINOR_VERSION_1 hass, MOCK_VERSION_2, storage_key, minor_version=MOCK_MINOR_VERSION_1
) )