Fail tests which leak hass instances

This commit is contained in:
Erik
2025-04-30 10:31:20 +02:00
parent d0ed8b67c4
commit aa19dfacfc
2 changed files with 25 additions and 2 deletions

View File

@@ -41,6 +41,7 @@ from typing import (
final, final,
overload, overload,
) )
import weakref
from propcache.api import cached_property, under_cached_property from propcache.api import cached_property, under_cached_property
import voluptuous as vol import voluptuous as vol
@@ -409,6 +410,9 @@ class CoreState(enum.Enum):
return self.value return self.value
hass_instances: list[weakref.ref[HomeAssistant]] = []
class HomeAssistant: class HomeAssistant:
"""Root object of the Home Assistant home automation.""" """Root object of the Home Assistant home automation."""
@@ -419,6 +423,7 @@ class HomeAssistant:
def __new__(cls, config_dir: str) -> Self: def __new__(cls, config_dir: str) -> Self:
"""Set the _hass thread local data.""" """Set the _hass thread local data."""
hass = super().__new__(cls) hass = super().__new__(cls)
hass_instances.append(weakref.ref(hass))
_hass.hass = hass _hass.hass = hass
return hass return hass

View File

@@ -288,8 +288,25 @@ def garbage_collection() -> None:
handles the most common cases and let each module override handles the most common cases and let each module override
to run per test case if needed. to run per test case if needed.
""" """
start_live_hass_instances = len(
[hass() for hass in ha.hass_instances if hass() is not None]
)
yield
gc.collect() gc.collect()
gc.freeze() end_live_hass_instances = len(
[hass() for hass in ha.hass_instances if hass() is not None]
)
if abs(start_live_hass_instances - end_live_hass_instances) > 1:
_LOGGER.error(
"Garbage collection did not clean up all Home Assistant instances. "
"Start: %s, End: %s",
start_live_hass_instances,
end_live_hass_instances,
)
pytest.fail(
f"Garbage collection did not clean up all Home Assistant instances. "
f"Start: {start_live_hass_instances}, End: {end_live_hass_instances}"
)
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
@@ -1325,7 +1342,8 @@ def disable_translations_once(
translations_once.start() translations_once.start()
@pytest_asyncio.fixture(autouse=True, scope="session", loop_scope="session") # @pytest_asyncio.fixture(autouse=True, scope="session", loop_scope="session")
@pytest_asyncio.fixture(autouse=True)
async def mock_zeroconf_resolver() -> AsyncGenerator[_patch]: async def mock_zeroconf_resolver() -> AsyncGenerator[_patch]:
"""Mock out the zeroconf resolver.""" """Mock out the zeroconf resolver."""
resolver = AsyncResolver() resolver = AsyncResolver()