Improve statistics tests (#148937)

This commit is contained in:
Erik Montnemery 2025-07-17 11:42:25 +02:00 committed by GitHub
parent 0e6a1e3242
commit d72fb021c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -54,6 +54,9 @@ VALUES_BINARY = ["on", "off", "on", "off", "on", "off", "on", "off", "on"]
VALUES_NUMERIC = [17, 20, 15.2, 5, 3.8, 9.2, 6.7, 14, 6] VALUES_NUMERIC = [17, 20, 15.2, 5, 3.8, 9.2, 6.7, 14, 6]
VALUES_NUMERIC_LINEAR = [1, 2, 3, 4, 5, 6, 7, 8, 9] VALUES_NUMERIC_LINEAR = [1, 2, 3, 4, 5, 6, 7, 8, 9]
A1 = {"attr": "value1"}
A2 = {"attr": "value2"}
async def test_unique_id( async def test_unique_id(
hass: HomeAssistant, entity_registry: er.EntityRegistry hass: HomeAssistant, entity_registry: er.EntityRegistry
@ -249,7 +252,22 @@ async def test_sensor_defaults_binary(hass: HomeAssistant) -> None:
assert "age_coverage_ratio" not in state.attributes assert "age_coverage_ratio" not in state.attributes
async def test_sensor_state_reported(hass: HomeAssistant) -> None: @pytest.mark.parametrize("force_update", [True, False])
@pytest.mark.parametrize(
("values", "attributes"),
[
# Fires last reported events
([18, 1, 1, 1, 1, 1, 1, 1, 9], [A1, A1, A1, A1, A1, A1, A1, A1, A1]),
# Fires state change events
([18, 1, 1, 1, 1, 1, 1, 1, 9], [A1, A2, A1, A2, A1, A2, A1, A2, A1]),
],
)
async def test_sensor_state_updated_reported(
hass: HomeAssistant,
values: list[float],
attributes: list[dict[str, Any]],
force_update: bool,
) -> None:
"""Test the behavior of the sensor with a sequence of identical values. """Test the behavior of the sensor with a sequence of identical values.
Forced updates no longer make a difference, since the statistics are now reacting not Forced updates no longer make a difference, since the statistics are now reacting not
@ -258,7 +276,6 @@ async def test_sensor_state_reported(hass: HomeAssistant) -> None:
This fixes problems with time based averages and some other functions that behave This fixes problems with time based averages and some other functions that behave
differently when repeating values are reported. differently when repeating values are reported.
""" """
repeating_values = [18, 0, 0, 0, 0, 0, 0, 0, 9]
assert await async_setup_component( assert await async_setup_component(
hass, hass,
"sensor", "sensor",
@ -267,14 +284,7 @@ async def test_sensor_state_reported(hass: HomeAssistant) -> None:
{ {
"platform": "statistics", "platform": "statistics",
"name": "test_normal", "name": "test_normal",
"entity_id": "sensor.test_monitored_normal", "entity_id": "sensor.test_monitored",
"state_characteristic": "mean",
"sampling_size": 20,
},
{
"platform": "statistics",
"name": "test_force",
"entity_id": "sensor.test_monitored_force",
"state_characteristic": "mean", "state_characteristic": "mean",
"sampling_size": 20, "sampling_size": 20,
}, },
@ -283,27 +293,19 @@ async def test_sensor_state_reported(hass: HomeAssistant) -> None:
) )
await hass.async_block_till_done() await hass.async_block_till_done()
for value in repeating_values: for value, attribute in zip(values, attributes, strict=True):
hass.states.async_set( hass.states.async_set(
"sensor.test_monitored_normal", "sensor.test_monitored",
str(value), str(value),
{ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS}, {ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS} | attribute,
) force_update=force_update,
hass.states.async_set(
"sensor.test_monitored_force",
str(value),
{ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS},
force_update=True,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
state_normal = hass.states.get("sensor.test_normal") state = hass.states.get("sensor.test_normal")
state_force = hass.states.get("sensor.test_force") assert state
assert state_normal and state_force assert state.state == str(round(sum(values) / 9, 2))
assert state_normal.state == str(round(sum(repeating_values) / 9, 2)) assert state.attributes.get("buffer_usage_ratio") == round(9 / 20, 2)
assert state_force.state == str(round(sum(repeating_values) / 9, 2))
assert state_normal.attributes.get("buffer_usage_ratio") == round(9 / 20, 2)
assert state_force.attributes.get("buffer_usage_ratio") == round(9 / 20, 2)
async def test_sampling_boundaries_given(hass: HomeAssistant) -> None: async def test_sampling_boundaries_given(hass: HomeAssistant) -> None:
@ -1785,12 +1787,40 @@ async def test_update_before_load(recorder_mock: Recorder, hass: HomeAssistant)
assert float(hass.states.get("sensor.test").state) == pytest.approx(4.5) assert float(hass.states.get("sensor.test").state) == pytest.approx(4.5)
async def test_average_linear_unevenly_timed(hass: HomeAssistant) -> None: @pytest.mark.parametrize("force_update", [True, False])
@pytest.mark.parametrize(
("values_attributes_and_times", "expected_state"),
[
(
# Fires last reported events
[(5.0, A1, 2), (10.0, A1, 1), (10.0, A1, 1), (10.0, A1, 2), (5.0, A1, 1)],
"8.33",
),
( # Fires state change events
[(5.0, A1, 2), (10.0, A2, 1), (10.0, A1, 1), (10.0, A2, 2), (5.0, A1, 1)],
"8.33",
),
(
# Fires last reported events
[(10.0, A1, 2), (10.0, A1, 1), (10.0, A1, 1), (10.0, A1, 2), (10.0, A1, 1)],
"10.0",
),
( # Fires state change events
[(10.0, A1, 2), (10.0, A2, 1), (10.0, A1, 1), (10.0, A2, 2), (10.0, A1, 1)],
"10.0",
),
],
)
async def test_average_linear_unevenly_timed(
hass: HomeAssistant,
force_update: bool,
values_attributes_and_times: list[tuple[float, dict[str, Any], float]],
expected_state: str,
) -> None:
"""Test the average_linear state characteristic with unevenly distributed values. """Test the average_linear state characteristic with unevenly distributed values.
This also implicitly tests the correct timing of repeating values. This also implicitly tests the correct timing of repeating values.
""" """
values_and_times = [[5.0, 2], [10.0, 1], [10.0, 1], [10.0, 2], [5.0, 1]]
current_time = dt_util.utcnow() current_time = dt_util.utcnow()
@ -1814,22 +1844,23 @@ async def test_average_linear_unevenly_timed(hass: HomeAssistant) -> None:
) )
await hass.async_block_till_done() await hass.async_block_till_done()
for value_and_time in values_and_times: for value, extra_attributes, time in values_attributes_and_times:
hass.states.async_set( hass.states.async_set(
"sensor.test_monitored", "sensor.test_monitored",
str(value_and_time[0]), str(value),
{ATTR_UNIT_OF_MEASUREMENT: DEGREE}, {ATTR_UNIT_OF_MEASUREMENT: DEGREE} | extra_attributes,
force_update=force_update,
) )
current_time += timedelta(seconds=value_and_time[1]) current_time += timedelta(seconds=time)
freezer.move_to(current_time) freezer.move_to(current_time)
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get("sensor.test_sensor_average_linear") state = hass.states.get("sensor.test_sensor_average_linear")
assert state is not None assert state is not None
assert state.state == "8.33", ( assert state.state == expected_state, (
"value mismatch for characteristic 'sensor/average_linear' - " "value mismatch for characteristic 'sensor/average_linear' - "
f"assert {state.state} == 8.33" f"assert {state.state} == {expected_state}"
) )