Test behavior of statistic_during_period when circular mean is undefined (#141554)

* Test behavior of statistic_during_period when circular mean is undefined

* Improve comment
This commit is contained in:
Erik Montnemery 2025-03-31 10:43:57 +02:00 committed by GitHub
parent c0e8f14745
commit f6308368b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -698,17 +698,33 @@ def _circular_mean(values: Iterable[StatisticData]) -> dict[str, float]:
}
def _circular_mean_approx(values: Iterable[StatisticData]) -> ApproxBase:
return pytest.approx(_circular_mean(values)["mean"])
def _circular_mean_approx(
values: Iterable[StatisticData], tolerance: float | None = None
) -> ApproxBase:
return pytest.approx(_circular_mean(values)["mean"], abs=tolerance)
@pytest.mark.freeze_time(datetime.datetime(2022, 10, 21, 7, 25, tzinfo=datetime.UTC))
@pytest.mark.usefixtures("recorder_mock")
@pytest.mark.parametrize("offset", [0, 1, 2])
@pytest.mark.parametrize(
("step_size", "tolerance"),
[
(123.456, 1e-4),
# In this case the angles are uniformly distributed and the mean is undefined.
# This edge case is not handled by the current implementation, but the test
# checks the behavior is consistent.
# We could consider returning None in this case, or returning also an estimate
# of the variance.
(120, 10),
],
)
async def test_statistic_during_period_circular_mean(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
offset: int,
step_size: float,
tolerance: float,
) -> None:
"""Test statistic_during_period."""
now = dt_util.utcnow()
@ -724,7 +740,7 @@ async def test_statistic_during_period_circular_mean(
imported_stats_5min: list[StatisticData] = [
{
"start": (start + timedelta(minutes=5 * i)),
"mean": (123.456 * i) % 360,
"mean": (step_size * i) % 360,
"mean_weight": 1,
}
for i in range(39)
@ -807,7 +823,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min),
"mean": _circular_mean_approx(imported_stats_5min, tolerance),
"max": None,
"min": None,
"change": None,
@ -835,7 +851,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min),
"mean": _circular_mean_approx(imported_stats_5min, tolerance),
"max": None,
"min": None,
"change": None,
@ -863,7 +879,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min),
"mean": _circular_mean_approx(imported_stats_5min, tolerance),
"max": None,
"min": None,
"change": None,
@ -887,7 +903,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min[26:]),
"mean": _circular_mean_approx(imported_stats_5min[26:], tolerance),
"max": None,
"min": None,
"change": None,
@ -910,7 +926,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min[26:]),
"mean": _circular_mean_approx(imported_stats_5min[26:], tolerance),
"max": None,
"min": None,
"change": None,
@ -934,7 +950,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min[:26]),
"mean": _circular_mean_approx(imported_stats_5min[:26], tolerance),
"max": None,
"min": None,
"change": None,
@ -964,7 +980,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min[26:32]),
"mean": _circular_mean_approx(imported_stats_5min[26:32], tolerance),
"max": None,
"min": None,
"change": None,
@ -986,7 +1002,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min[24 - offset :]),
"mean": _circular_mean_approx(imported_stats_5min[24 - offset :], tolerance),
"max": None,
"min": None,
"change": None,
@ -1005,7 +1021,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min[24 - offset :]),
"mean": _circular_mean_approx(imported_stats_5min[24 - offset :], tolerance),
"max": None,
"min": None,
"change": None,
@ -1027,7 +1043,9 @@ async def test_statistic_during_period_circular_mean(
slice_start = 24 - offset
slice_end = 36 - offset
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min[slice_start:slice_end]),
"mean": _circular_mean_approx(
imported_stats_5min[slice_start:slice_end], tolerance
),
"max": None,
"min": None,
"change": None,
@ -1044,7 +1062,7 @@ async def test_statistic_during_period_circular_mean(
response = await client.receive_json()
assert response["success"]
assert response["result"] == {
"mean": _circular_mean_approx(imported_stats_5min),
"mean": _circular_mean_approx(imported_stats_5min, tolerance),
}