Initial draft of statistics (#49852)
This commit is contained in:
parent
703456abea
commit
89dd3292ba
15 changed files with 774 additions and 56 deletions
|
@ -590,7 +590,7 @@ def run_tasks_at_time(hass, test_time):
|
|||
|
||||
|
||||
def test_auto_purge(hass_recorder):
|
||||
"""Test periodic purge alarm scheduling."""
|
||||
"""Test periodic purge scheduling."""
|
||||
hass = hass_recorder()
|
||||
|
||||
original_tz = dt_util.DEFAULT_TIME_ZONE
|
||||
|
@ -598,9 +598,10 @@ def test_auto_purge(hass_recorder):
|
|||
tz = dt_util.get_time_zone("Europe/Copenhagen")
|
||||
dt_util.set_default_time_zone(tz)
|
||||
|
||||
# Purging is schedule to happen at 4:12am every day. Exercise this behavior
|
||||
# by firing alarms and advancing the clock around this time. Pick an arbitrary
|
||||
# year in the future to avoid boundary conditions relative to the current date.
|
||||
# Purging is scheduled to happen at 4:12am every day. Exercise this behavior by
|
||||
# firing time changed events and advancing the clock around this time. Pick an
|
||||
# arbitrary year in the future to avoid boundary conditions relative to the current
|
||||
# date.
|
||||
#
|
||||
# The clock is started at 4:15am then advanced forward below
|
||||
now = dt_util.utcnow()
|
||||
|
@ -637,6 +638,56 @@ def test_auto_purge(hass_recorder):
|
|||
dt_util.set_default_time_zone(original_tz)
|
||||
|
||||
|
||||
def test_auto_statistics(hass_recorder):
|
||||
"""Test periodic statistics scheduling."""
|
||||
hass = hass_recorder()
|
||||
|
||||
original_tz = dt_util.DEFAULT_TIME_ZONE
|
||||
|
||||
tz = dt_util.get_time_zone("Europe/Copenhagen")
|
||||
dt_util.set_default_time_zone(tz)
|
||||
|
||||
# Statistics is scheduled to happen at *:12am every hour. Exercise this behavior by
|
||||
# firing time changed events and advancing the clock around this time. Pick an
|
||||
# arbitrary year in the future to avoid boundary conditions relative to the current
|
||||
# date.
|
||||
#
|
||||
# The clock is started at 4:15am then advanced forward below
|
||||
now = dt_util.utcnow()
|
||||
test_time = datetime(now.year + 2, 1, 1, 4, 15, 0, tzinfo=tz)
|
||||
run_tasks_at_time(hass, test_time)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.recorder.statistics.compile_statistics",
|
||||
return_value=True,
|
||||
) as compile_statistics:
|
||||
# Advance one hour, and the statistics task should run
|
||||
test_time = test_time + timedelta(hours=1)
|
||||
run_tasks_at_time(hass, test_time)
|
||||
assert len(compile_statistics.mock_calls) == 1
|
||||
|
||||
compile_statistics.reset_mock()
|
||||
|
||||
# Advance one hour, and the statistics task should run again
|
||||
test_time = test_time + timedelta(hours=1)
|
||||
run_tasks_at_time(hass, test_time)
|
||||
assert len(compile_statistics.mock_calls) == 1
|
||||
|
||||
compile_statistics.reset_mock()
|
||||
|
||||
# Advance less than one full hour. The task should not run.
|
||||
test_time = test_time + timedelta(minutes=50)
|
||||
run_tasks_at_time(hass, test_time)
|
||||
assert len(compile_statistics.mock_calls) == 0
|
||||
|
||||
# Advance to the next hour, and the statistics task should run again
|
||||
test_time = test_time + timedelta(hours=1)
|
||||
run_tasks_at_time(hass, test_time)
|
||||
assert len(compile_statistics.mock_calls) == 1
|
||||
|
||||
dt_util.set_default_time_zone(original_tz)
|
||||
|
||||
|
||||
def test_saving_sets_old_state(hass_recorder):
|
||||
"""Test saving sets old state."""
|
||||
hass = hass_recorder()
|
||||
|
|
|
@ -104,7 +104,7 @@ async def test_purge_old_states_encounters_temporary_mysql_error(
|
|||
mysql_exception.orig = MagicMock(args=(1205, "retryable"))
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.recorder.purge.time.sleep"
|
||||
"homeassistant.components.recorder.util.time.sleep"
|
||||
) as sleep_mock, patch(
|
||||
"homeassistant.components.recorder.purge._purge_old_recorder_runs",
|
||||
side_effect=[mysql_exception, None],
|
||||
|
@ -147,7 +147,7 @@ async def test_purge_old_states_encounters_operational_error(
|
|||
await async_wait_recording_done_without_instance(hass)
|
||||
|
||||
assert "retrying" not in caplog.text
|
||||
assert "Error purging history" in caplog.text
|
||||
assert "Error executing purge" in caplog.text
|
||||
|
||||
|
||||
async def test_purge_old_events(
|
||||
|
|
88
tests/components/recorder/test_statistics.py
Normal file
88
tests/components/recorder/test_statistics.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
"""The tests for sensor recorder platform."""
|
||||
# pylint: disable=protected-access,invalid-name
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch, sentinel
|
||||
|
||||
from homeassistant.components.recorder import history
|
||||
from homeassistant.components.recorder.const import DATA_INSTANCE
|
||||
from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat
|
||||
from homeassistant.components.recorder.statistics import statistics_during_period
|
||||
from homeassistant.setup import setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.components.recorder.common import wait_recording_done
|
||||
|
||||
|
||||
def test_compile_hourly_statistics(hass_recorder):
|
||||
"""Test compiling hourly statistics."""
|
||||
hass = hass_recorder()
|
||||
recorder = hass.data[DATA_INSTANCE]
|
||||
setup_component(hass, "sensor", {})
|
||||
zero, four, states = record_states(hass)
|
||||
hist = history.get_significant_states(hass, zero, four)
|
||||
assert dict(states) == dict(hist)
|
||||
|
||||
recorder.do_adhoc_statistics(period="hourly", start=zero)
|
||||
wait_recording_done(hass)
|
||||
stats = statistics_during_period(hass, zero)
|
||||
assert stats == {
|
||||
"sensor.test1": [
|
||||
{
|
||||
"statistic_id": "sensor.test1",
|
||||
"start": process_timestamp_to_utc_isoformat(zero),
|
||||
"mean": 15.0,
|
||||
"min": 10.0,
|
||||
"max": 20.0,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def record_states(hass):
|
||||
"""Record some test states.
|
||||
|
||||
We inject a bunch of state updates temperature sensors.
|
||||
"""
|
||||
mp = "media_player.test"
|
||||
sns1 = "sensor.test1"
|
||||
sns2 = "sensor.test2"
|
||||
sns3 = "sensor.test3"
|
||||
sns1_attr = {"device_class": "temperature", "state_class": "measurement"}
|
||||
sns2_attr = {"device_class": "temperature"}
|
||||
sns3_attr = {}
|
||||
|
||||
def set_state(entity_id, state, **kwargs):
|
||||
"""Set the state."""
|
||||
hass.states.set(entity_id, state, **kwargs)
|
||||
wait_recording_done(hass)
|
||||
return hass.states.get(entity_id)
|
||||
|
||||
zero = dt_util.utcnow()
|
||||
one = zero + timedelta(minutes=1)
|
||||
two = one + timedelta(minutes=15)
|
||||
three = two + timedelta(minutes=30)
|
||||
four = three + timedelta(minutes=15)
|
||||
|
||||
states = {mp: [], sns1: [], sns2: [], sns3: []}
|
||||
with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=one):
|
||||
states[mp].append(
|
||||
set_state(mp, "idle", attributes={"media_title": str(sentinel.mt1)})
|
||||
)
|
||||
states[mp].append(
|
||||
set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt2)})
|
||||
)
|
||||
states[sns1].append(set_state(sns1, "10", attributes=sns1_attr))
|
||||
states[sns2].append(set_state(sns2, "10", attributes=sns2_attr))
|
||||
states[sns3].append(set_state(sns3, "10", attributes=sns3_attr))
|
||||
|
||||
with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=two):
|
||||
states[sns1].append(set_state(sns1, "15", attributes=sns1_attr))
|
||||
states[sns2].append(set_state(sns2, "15", attributes=sns2_attr))
|
||||
states[sns3].append(set_state(sns3, "15", attributes=sns3_attr))
|
||||
|
||||
with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=three):
|
||||
states[sns1].append(set_state(sns1, "20", attributes=sns1_attr))
|
||||
states[sns2].append(set_state(sns2, "20", attributes=sns2_attr))
|
||||
states[sns3].append(set_state(sns3, "20", attributes=sns3_attr))
|
||||
|
||||
return zero, four, states
|
Loading…
Add table
Add a link
Reference in a new issue