2016-03-09 09:25:50 +00:00
|
|
|
"""The tests for the Recorder component."""
|
2016-07-23 18:25:17 +00:00
|
|
|
# pylint: disable=protected-access
|
|
|
|
import unittest
|
2017-03-03 06:44:52 +00:00
|
|
|
from unittest.mock import patch
|
2015-04-30 06:21:31 +00:00
|
|
|
|
2017-01-09 20:53:30 +00:00
|
|
|
import pytest
|
2017-02-10 02:17:17 +00:00
|
|
|
|
2016-11-04 04:58:18 +00:00
|
|
|
from homeassistant.core import callback
|
2015-04-30 06:21:31 +00:00
|
|
|
from homeassistant.const import MATCH_ALL
|
2019-06-08 23:18:29 +00:00
|
|
|
from homeassistant.setup import async_setup_component
|
2017-03-03 06:44:52 +00:00
|
|
|
from homeassistant.components.recorder import Recorder
|
2017-02-26 22:38:06 +00:00
|
|
|
from homeassistant.components.recorder.const import DATA_INSTANCE
|
|
|
|
from homeassistant.components.recorder.util import session_scope
|
|
|
|
from homeassistant.components.recorder.models import States, Events
|
2017-03-03 06:44:52 +00:00
|
|
|
|
2017-02-21 07:40:27 +00:00
|
|
|
from tests.common import get_test_home_assistant, init_recorder_component
|
2015-04-30 06:21:31 +00:00
|
|
|
|
|
|
|
|
2017-02-26 22:38:06 +00:00
|
|
|
class TestRecorder(unittest.TestCase):
|
|
|
|
"""Test the recorder module."""
|
2015-04-30 06:21:31 +00:00
|
|
|
|
|
|
|
def setUp(self): # pylint: disable=invalid-name
|
2018-08-19 20:29:08 +00:00
|
|
|
"""Set up things to be run when tests are started."""
|
2015-04-30 06:21:31 +00:00
|
|
|
self.hass = get_test_home_assistant()
|
2017-02-21 07:40:27 +00:00
|
|
|
init_recorder_component(self.hass)
|
2015-04-30 06:21:31 +00:00
|
|
|
self.hass.start()
|
|
|
|
|
|
|
|
def tearDown(self): # pylint: disable=invalid-name
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Stop everything that was started."""
|
2015-04-30 06:21:31 +00:00
|
|
|
self.hass.stop()
|
2017-02-10 02:17:17 +00:00
|
|
|
|
2015-04-30 06:21:31 +00:00
|
|
|
def test_saving_state(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test saving and restoring a state."""
|
2015-04-30 06:21:31 +00:00
|
|
|
entity_id = 'test.recorder'
|
|
|
|
state = 'restoring_from_db'
|
|
|
|
attributes = {'test_attr': 5, 'test_attr_10': 'nice'}
|
|
|
|
|
|
|
|
self.hass.states.set(entity_id, state, attributes)
|
|
|
|
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2017-02-26 22:38:06 +00:00
|
|
|
self.hass.data[DATA_INSTANCE].block_till_done()
|
2015-04-30 06:21:31 +00:00
|
|
|
|
2017-02-26 22:38:06 +00:00
|
|
|
with session_scope(hass=self.hass) as session:
|
|
|
|
db_states = list(session.query(States))
|
|
|
|
assert len(db_states) == 1
|
2018-02-21 20:51:20 +00:00
|
|
|
assert db_states[0].event_id > 0
|
2017-02-26 22:38:06 +00:00
|
|
|
state = db_states[0].to_native()
|
2016-07-11 07:46:56 +00:00
|
|
|
|
2017-02-26 22:38:06 +00:00
|
|
|
assert state == self.hass.states.get(entity_id)
|
2015-04-30 06:21:31 +00:00
|
|
|
|
|
|
|
def test_saving_event(self):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Test saving and restoring an event."""
|
2015-04-30 06:21:31 +00:00
|
|
|
event_type = 'EVENT_TEST'
|
|
|
|
event_data = {'test_attr': 5, 'test_attr_10': 'nice'}
|
|
|
|
|
|
|
|
events = []
|
|
|
|
|
2016-11-04 04:58:18 +00:00
|
|
|
@callback
|
2015-04-30 06:21:31 +00:00
|
|
|
def event_listener(event):
|
2016-03-09 09:25:50 +00:00
|
|
|
"""Record events from eventbus."""
|
2015-04-30 06:21:31 +00:00
|
|
|
if event.event_type == event_type:
|
|
|
|
events.append(event)
|
|
|
|
|
|
|
|
self.hass.bus.listen(MATCH_ALL, event_listener)
|
|
|
|
|
|
|
|
self.hass.bus.fire(event_type, event_data)
|
|
|
|
|
2016-09-13 02:16:14 +00:00
|
|
|
self.hass.block_till_done()
|
2015-04-30 06:21:31 +00:00
|
|
|
|
2016-04-16 07:55:35 +00:00
|
|
|
assert len(events) == 1
|
|
|
|
event = events[0]
|
2017-02-26 22:38:06 +00:00
|
|
|
|
|
|
|
self.hass.data[DATA_INSTANCE].block_till_done()
|
|
|
|
|
|
|
|
with session_scope(hass=self.hass) as session:
|
|
|
|
db_events = list(session.query(Events).filter_by(
|
|
|
|
event_type=event_type))
|
|
|
|
assert len(db_events) == 1
|
|
|
|
db_event = db_events[0].to_native()
|
2016-04-16 07:55:35 +00:00
|
|
|
|
|
|
|
assert event.event_type == db_event.event_type
|
|
|
|
assert event.data == db_event.data
|
|
|
|
assert event.origin == db_event.origin
|
|
|
|
|
|
|
|
# Recorder uses SQLite and stores datetimes as integer unix timestamps
|
|
|
|
assert event.time_fired.replace(microsecond=0) == \
|
|
|
|
db_event.time_fired.replace(microsecond=0)
|
2016-04-16 02:02:17 +00:00
|
|
|
|
2017-01-09 20:53:30 +00:00
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def hass_recorder():
|
|
|
|
"""HASS fixture with in-memory recorder."""
|
|
|
|
hass = get_test_home_assistant()
|
|
|
|
|
2017-02-21 07:40:27 +00:00
|
|
|
def setup_recorder(config=None):
|
2018-08-19 20:29:08 +00:00
|
|
|
"""Set up with params."""
|
2017-02-21 07:40:27 +00:00
|
|
|
init_recorder_component(hass, config)
|
2017-01-09 20:53:30 +00:00
|
|
|
hass.start()
|
|
|
|
hass.block_till_done()
|
2017-02-26 22:38:06 +00:00
|
|
|
hass.data[DATA_INSTANCE].block_till_done()
|
2017-01-09 20:53:30 +00:00
|
|
|
return hass
|
|
|
|
|
|
|
|
yield setup_recorder
|
|
|
|
hass.stop()
|
|
|
|
|
|
|
|
|
|
|
|
def _add_entities(hass, entity_ids):
|
|
|
|
"""Add entities."""
|
|
|
|
attributes = {'test_attr': 5, 'test_attr_10': 'nice'}
|
|
|
|
for idx, entity_id in enumerate(entity_ids):
|
|
|
|
hass.states.set(entity_id, 'state{}'.format(idx), attributes)
|
|
|
|
hass.block_till_done()
|
2017-02-26 22:38:06 +00:00
|
|
|
hass.data[DATA_INSTANCE].block_till_done()
|
|
|
|
|
|
|
|
with session_scope(hass=hass) as session:
|
|
|
|
return [st.to_native() for st in session.query(States)]
|
2017-01-09 20:53:30 +00:00
|
|
|
|
|
|
|
|
2017-05-24 22:23:52 +00:00
|
|
|
def _add_events(hass, events):
|
|
|
|
with session_scope(hass=hass) as session:
|
|
|
|
session.query(Events).delete(synchronize_session=False)
|
|
|
|
for event_type in events:
|
|
|
|
hass.bus.fire(event_type)
|
|
|
|
hass.block_till_done()
|
|
|
|
hass.data[DATA_INSTANCE].block_till_done()
|
|
|
|
|
|
|
|
with session_scope(hass=hass) as session:
|
|
|
|
return [ev.to_native() for ev in session.query(Events)]
|
|
|
|
|
|
|
|
|
2017-01-09 20:53:30 +00:00
|
|
|
# pylint: disable=redefined-outer-name,invalid-name
|
|
|
|
def test_saving_state_include_domains(hass_recorder):
|
|
|
|
"""Test saving and restoring a state."""
|
|
|
|
hass = hass_recorder({'include': {'domains': 'test2'}})
|
|
|
|
states = _add_entities(hass, ['test.recorder', 'test2.recorder'])
|
|
|
|
assert len(states) == 1
|
|
|
|
assert hass.states.get('test2.recorder') == states[0]
|
|
|
|
|
|
|
|
|
|
|
|
def test_saving_state_incl_entities(hass_recorder):
|
|
|
|
"""Test saving and restoring a state."""
|
|
|
|
hass = hass_recorder({'include': {'entities': 'test2.recorder'}})
|
|
|
|
states = _add_entities(hass, ['test.recorder', 'test2.recorder'])
|
|
|
|
assert len(states) == 1
|
|
|
|
assert hass.states.get('test2.recorder') == states[0]
|
|
|
|
|
|
|
|
|
2017-05-24 22:23:52 +00:00
|
|
|
def test_saving_event_exclude_event_type(hass_recorder):
|
|
|
|
"""Test saving and restoring an event."""
|
|
|
|
hass = hass_recorder({'exclude': {'event_types': 'test'}})
|
|
|
|
events = _add_events(hass, ['test', 'test2'])
|
|
|
|
assert len(events) == 1
|
|
|
|
assert events[0].event_type == 'test2'
|
|
|
|
|
|
|
|
|
2017-01-09 20:53:30 +00:00
|
|
|
def test_saving_state_exclude_domains(hass_recorder):
|
|
|
|
"""Test saving and restoring a state."""
|
|
|
|
hass = hass_recorder({'exclude': {'domains': 'test'}})
|
|
|
|
states = _add_entities(hass, ['test.recorder', 'test2.recorder'])
|
|
|
|
assert len(states) == 1
|
|
|
|
assert hass.states.get('test2.recorder') == states[0]
|
|
|
|
|
|
|
|
|
|
|
|
def test_saving_state_exclude_entities(hass_recorder):
|
|
|
|
"""Test saving and restoring a state."""
|
|
|
|
hass = hass_recorder({'exclude': {'entities': 'test.recorder'}})
|
|
|
|
states = _add_entities(hass, ['test.recorder', 'test2.recorder'])
|
|
|
|
assert len(states) == 1
|
|
|
|
assert hass.states.get('test2.recorder') == states[0]
|
|
|
|
|
|
|
|
|
|
|
|
def test_saving_state_exclude_domain_include_entity(hass_recorder):
|
|
|
|
"""Test saving and restoring a state."""
|
|
|
|
hass = hass_recorder({
|
|
|
|
'include': {'entities': 'test.recorder'},
|
|
|
|
'exclude': {'domains': 'test'}})
|
|
|
|
states = _add_entities(hass, ['test.recorder', 'test2.recorder'])
|
|
|
|
assert len(states) == 2
|
|
|
|
|
|
|
|
|
|
|
|
def test_saving_state_include_domain_exclude_entity(hass_recorder):
|
|
|
|
"""Test saving and restoring a state."""
|
|
|
|
hass = hass_recorder({
|
|
|
|
'exclude': {'entities': 'test.recorder'},
|
|
|
|
'include': {'domains': 'test'}})
|
|
|
|
states = _add_entities(hass, ['test.recorder', 'test2.recorder',
|
|
|
|
'test.ok'])
|
|
|
|
assert len(states) == 1
|
|
|
|
assert hass.states.get('test.ok') == states[0]
|
|
|
|
assert hass.states.get('test.ok').state == 'state2'
|
2017-03-03 06:44:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_recorder_setup_failure():
|
|
|
|
"""Test some exceptions."""
|
|
|
|
hass = get_test_home_assistant()
|
|
|
|
|
|
|
|
with patch.object(Recorder, '_setup_connection') as setup, \
|
|
|
|
patch('homeassistant.components.recorder.time.sleep'):
|
|
|
|
setup.side_effect = ImportError("driver not found")
|
2017-11-03 15:28:16 +00:00
|
|
|
rec = Recorder(hass, keep_days=7, purge_interval=2,
|
|
|
|
uri='sqlite://', include={}, exclude={})
|
2017-03-03 06:44:52 +00:00
|
|
|
rec.start()
|
|
|
|
rec.join()
|
|
|
|
|
|
|
|
hass.stop()
|
2019-06-08 23:18:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_defaults_set(hass):
|
|
|
|
"""Test the config defaults are set."""
|
|
|
|
recorder_config = None
|
|
|
|
|
|
|
|
async def mock_setup(hass, config):
|
|
|
|
"""Mock setup."""
|
|
|
|
nonlocal recorder_config
|
|
|
|
recorder_config = config['recorder']
|
|
|
|
return True
|
|
|
|
|
|
|
|
with patch('homeassistant.components.recorder.async_setup',
|
|
|
|
side_effect=mock_setup):
|
|
|
|
assert await async_setup_component(hass, 'history', {})
|
|
|
|
|
|
|
|
assert recorder_config is not None
|
|
|
|
assert recorder_config['purge_keep_days'] == 10
|
|
|
|
assert recorder_config['purge_interval'] == 1
|