mirror of
https://github.com/home-assistant/core.git
synced 2026-09-28 22:18:13 +01:00
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Robert Resch <robert@resch.dev> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
140 lines
4.4 KiB
Python
140 lines
4.4 KiB
Python
"""CodSpeed benchmarks for the state machine and entity write path.
|
|
|
|
Every state update in Home Assistant flows through ``StateMachine.async_set``,
|
|
and every entity that pushes an update lands in ``Entity._async_write_ha_state``.
|
|
These are among the busiest call sites in the whole process.
|
|
|
|
Run locally with: ``pytest benchmarks --codspeed``.
|
|
"""
|
|
|
|
from collections.abc import Callable
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from pytest_codspeed import BenchmarkFixture
|
|
|
|
from homeassistant.core import HomeAssistant, State
|
|
from homeassistant.helpers.entity import Entity
|
|
|
|
_ATTRS = {
|
|
"friendly_name": "Benchmark light",
|
|
"brightness": 255,
|
|
"color_temp_kelvin": 4000,
|
|
"supported_color_modes": ["color_temp", "rgb"],
|
|
}
|
|
|
|
|
|
def test_state_set_create(benchmark: BenchmarkFixture, hass: HomeAssistant) -> None:
|
|
"""Write a brand new entity into the state machine (the create branch).
|
|
|
|
``pedantic`` with a teardown that removes the entity keeps every measured
|
|
call on the create path; without it only the first call creates and the rest
|
|
measure the update path.
|
|
"""
|
|
benchmark.pedantic(
|
|
lambda: hass.states.async_set("light.benchmark", "on", _ATTRS),
|
|
teardown=lambda: hass.states.async_remove("light.benchmark"),
|
|
rounds=1000,
|
|
)
|
|
|
|
|
|
def test_state_set_update(benchmark: BenchmarkFixture, hass: HomeAssistant) -> None:
|
|
"""Write a changed state for an existing entity (the update branch)."""
|
|
counter = 0
|
|
|
|
def _set() -> None:
|
|
nonlocal counter
|
|
counter += 1
|
|
hass.states.async_set("light.benchmark", str(counter), _ATTRS)
|
|
|
|
benchmark(_set)
|
|
|
|
assert hass.states.get("light.benchmark") is not None
|
|
|
|
|
|
def test_state_set_report(benchmark: BenchmarkFixture, hass: HomeAssistant) -> None:
|
|
"""Re-set an unchanged state (the EVENT_STATE_REPORTED fast path).
|
|
|
|
Polling integrations hammer this branch: same value, same attributes, over
|
|
and over. It fires a lightweight reported event instead of a state change.
|
|
"""
|
|
hass.states.async_set("sensor.benchmark", "21.5", _ATTRS)
|
|
|
|
benchmark(lambda: hass.states.async_set("sensor.benchmark", "21.5", _ATTRS))
|
|
|
|
|
|
def test_state_get(benchmark: BenchmarkFixture, hass: HomeAssistant) -> None:
|
|
"""Look a single state up by entity_id."""
|
|
hass.states.async_set("sensor.benchmark", "21.5", _ATTRS)
|
|
|
|
state: State | None = benchmark(lambda: hass.states.get("sensor.benchmark"))
|
|
|
|
assert state is not None
|
|
assert state.state == "21.5"
|
|
|
|
|
|
@pytest.mark.parametrize("count", [10, 100, 1000])
|
|
def test_state_all(
|
|
benchmark: BenchmarkFixture,
|
|
populate_states: Callable[[int], None],
|
|
hass: HomeAssistant,
|
|
count: int,
|
|
) -> None:
|
|
"""Read the full state list out of a populated machine, at several sizes."""
|
|
populate_states(count)
|
|
|
|
states: list[State] = benchmark(hass.states.async_all)
|
|
|
|
assert len(states) == count
|
|
|
|
|
|
@pytest.mark.parametrize("count", [10, 100, 1000])
|
|
def test_state_entity_ids(
|
|
benchmark: BenchmarkFixture,
|
|
populate_states: Callable[[int], None],
|
|
hass: HomeAssistant,
|
|
count: int,
|
|
) -> None:
|
|
"""List entity ids out of a populated machine, at several sizes."""
|
|
populate_states(count)
|
|
|
|
entity_ids = benchmark(hass.states.async_entity_ids)
|
|
|
|
assert len(entity_ids) == count
|
|
|
|
|
|
class _BenchmarkEntity(Entity):
|
|
"""A minimal entity carrying capability and extra state attributes."""
|
|
|
|
_attr_should_poll = False
|
|
_attr_name = "Benchmark"
|
|
_attr_supported_features = 3
|
|
|
|
def __init__(self, state: str) -> None:
|
|
"""Initialize the benchmark entity."""
|
|
self._attr_state = state
|
|
self._attr_extra_state_attributes: dict[str, Any] = {
|
|
"brightness": 255,
|
|
"color_temp_kelvin": 4000,
|
|
}
|
|
|
|
@property
|
|
def capability_attributes(self) -> dict[str, Any]:
|
|
"""Return capability attributes, assembled on every write."""
|
|
return {"supported_color_modes": ["color_temp", "rgb"]}
|
|
|
|
|
|
def test_entity_write(benchmark: BenchmarkFixture, hass: HomeAssistant) -> None:
|
|
"""Write an entity's state through the entity layer.
|
|
|
|
This measures ``__async_calculate_state`` (assembling state and attributes
|
|
from the entity's properties) plus the ``async_set`` it lands in.
|
|
"""
|
|
entity = _BenchmarkEntity("on")
|
|
entity.hass = hass
|
|
entity.entity_id = "light.benchmark"
|
|
|
|
benchmark(entity._async_write_ha_state) # noqa: SLF001
|
|
|
|
assert hass.states.get("light.benchmark") is not None
|