Restore input_select and test helper proposal (#6148)

* Restore input_select and test helper proposal

* DB still active
This commit is contained in:
Johann Kellerman 2017-02-22 10:15:48 +02:00 committed by Paulus Schoutsen
parent 8983b826c4
commit aee8758fc1
5 changed files with 135 additions and 22 deletions

View File

@ -13,6 +13,7 @@ from homeassistant.const import ATTR_ENTITY_ID, CONF_ICON, CONF_NAME
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import async_get_last_state
DOMAIN = 'input_select' DOMAIN = 'input_select'
@ -194,6 +195,16 @@ class InputSelect(Entity):
self._options = options self._options = options
self._icon = icon self._icon = icon
@asyncio.coroutine
def async_added_to_hass(self):
"""Called when entity about to be added to hass."""
state = yield from async_get_last_state(self.hass, self.entity_id)
if not state:
return
if state.state not in self._options:
return
self._current_option = state.state
@property @property
def should_poll(self): def should_poll(self):
"""If entity should be polled.""" """If entity should be polled."""

View File

@ -297,6 +297,9 @@ class Recorder(threading.Thread):
"""Tell the recorder to shut down.""" """Tell the recorder to shut down."""
global _INSTANCE # pylint: disable=global-statement global _INSTANCE # pylint: disable=global-statement
self.queue.put(None) self.queue.put(None)
if not self.start_recording.is_set():
_LOGGER.warning("Recorder never started correctly")
self.start_recording.set()
self.join() self.join()
_INSTANCE = None _INSTANCE = None

View File

@ -15,6 +15,7 @@ from homeassistant import core as ha, loader
from homeassistant.bootstrap import ( from homeassistant.bootstrap import (
setup_component, async_prepare_setup_component) setup_component, async_prepare_setup_component)
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
from homeassistant.util.unit_system import METRIC_SYSTEM from homeassistant.util.unit_system import METRIC_SYSTEM
import homeassistant.util.dt as date_util import homeassistant.util.dt as date_util
import homeassistant.util.yaml as yaml import homeassistant.util.yaml as yaml
@ -88,6 +89,7 @@ def async_test_home_assistant(loop):
hass = ha.HomeAssistant(loop) hass = ha.HomeAssistant(loop)
def async_add_job(target, *args): def async_add_job(target, *args):
"""Add a magic mock."""
if isinstance(target, MagicMock): if isinstance(target, MagicMock):
return return
hass._async_add_job_tracking(target, *args) hass._async_add_job_tracking(target, *args)
@ -459,24 +461,19 @@ def init_recorder_component(hass, add_config=None, db_ready_callback=None):
config = dict(add_config) if add_config else {} config = dict(add_config) if add_config else {}
config[recorder.CONF_DB_URL] = 'sqlite://' # In memory DB config[recorder.CONF_DB_URL] = 'sqlite://' # In memory DB
saved_recorder = recorder.Recorder assert setup_component(hass, recorder.DOMAIN,
{recorder.DOMAIN: config})
class Recorder2(saved_recorder):
"""Recorder with a callback after db_ready."""
def _setup_connection(self):
"""Setup the connection and run the callback."""
super(Recorder2, self)._setup_connection()
if db_ready_callback:
_LOGGER.debug('db_ready_callback start (db_ready not set,'
'never use get_instance in the callback)')
db_ready_callback()
_LOGGER.debug('db_ready_callback completed')
with patch('homeassistant.components.recorder.Recorder',
side_effect=Recorder2):
assert setup_component(hass, recorder.DOMAIN,
{recorder.DOMAIN: config})
assert recorder.DOMAIN in hass.config.components assert recorder.DOMAIN in hass.config.components
recorder.get_instance().block_till_db_ready() recorder.get_instance().block_till_db_ready()
_LOGGER.info("In-memory recorder successfully started") _LOGGER.info("In-memory recorder successfully started")
def mock_restore_cache(hass, states):
"""Mock the DATA_RESTORE_CACHE."""
hass.data[DATA_RESTORE_CACHE] = {
state.entity_id: state for state in states}
_LOGGER.debug('Restore cache: %s', hass.data[DATA_RESTORE_CACHE])
assert len(hass.data[DATA_RESTORE_CACHE]) == len(states), \
"Duplicate entity_id? {}".format(states)
hass.state = ha.CoreState.starting
hass.config.components.add(recorder.DOMAIN)

View File

@ -1,10 +1,12 @@
"""The tests for the Input select component.""" """The tests for the Input select component."""
# pylint: disable=protected-access # pylint: disable=protected-access
import asyncio
import unittest import unittest
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant, mock_restore_cache
from homeassistant.bootstrap import setup_component from homeassistant.core import State
from homeassistant.bootstrap import setup_component, async_setup_component
from homeassistant.components.input_select import ( from homeassistant.components.input_select import (
ATTR_OPTIONS, DOMAIN, SERVICE_SET_OPTIONS, ATTR_OPTIONS, DOMAIN, SERVICE_SET_OPTIONS,
select_option, select_next, select_previous) select_option, select_next, select_previous)
@ -211,3 +213,35 @@ class TestInputSelect(unittest.TestCase):
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)
self.assertEqual('test2', state.state) self.assertEqual('test2', state.state)
@asyncio.coroutine
def test_restore_state(hass):
"""Ensure states are restored on startup."""
mock_restore_cache(hass, (
State('input_select.s1', 'last option'),
State('input_select.s2', 'bad option'),
))
options = {
'options': [
'first option',
'middle option',
'last option',
],
'initial': 'middle option',
}
yield from async_setup_component(hass, DOMAIN, {
DOMAIN: {
's1': options,
's2': options,
}})
state = hass.states.get('input_select.s1')
assert state
assert state.state == 'last option'
state = hass.states.get('input_select.s2')
assert state
assert state.state == 'middle option'

View File

@ -1,14 +1,18 @@
"""The tests for the Restore component.""" """The tests for the Restore component."""
import asyncio import asyncio
from datetime import timedelta
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from homeassistant.bootstrap import setup_component
from homeassistant.const import EVENT_HOMEASSISTANT_START from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.core import CoreState, State from homeassistant.core import CoreState, split_entity_id, State
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.components import input_boolean, recorder
from homeassistant.helpers.restore_state import ( from homeassistant.helpers.restore_state import (
async_get_last_state, DATA_RESTORE_CACHE) async_get_last_state, DATA_RESTORE_CACHE)
from tests.common import get_test_home_assistant, init_recorder_component
@asyncio.coroutine @asyncio.coroutine
def test_caching_data(hass): def test_caching_data(hass):
@ -40,3 +44,67 @@ def test_caching_data(hass):
yield from hass.async_block_till_done() yield from hass.async_block_till_done()
assert DATA_RESTORE_CACHE not in hass.data assert DATA_RESTORE_CACHE not in hass.data
def _add_data_in_last_run(entities):
"""Add test data in the last recorder_run."""
# pylint: disable=protected-access
t_now = dt_util.utcnow() - timedelta(minutes=10)
t_min_1 = t_now - timedelta(minutes=20)
t_min_2 = t_now - timedelta(minutes=30)
recorder_runs = recorder.get_model('RecorderRuns')
states = recorder.get_model('States')
with recorder.session_scope() as session:
run = recorder_runs(
start=t_min_2,
end=t_now,
created=t_min_2
)
recorder._INSTANCE._commit(session, run)
for entity_id, state in entities.items():
dbstate = states(
entity_id=entity_id,
domain=split_entity_id(entity_id)[0],
state=state,
attributes='{}',
last_changed=t_min_1,
last_updated=t_min_1,
created=t_min_1)
recorder._INSTANCE._commit(session, dbstate)
def test_filling_the_cache():
"""Test filling the cache from the DB."""
test_entity_id1 = 'input_boolean.b1'
test_entity_id2 = 'input_boolean.b2'
hass = get_test_home_assistant()
hass.state = CoreState.starting
init_recorder_component(hass)
_add_data_in_last_run({
test_entity_id1: 'on',
test_entity_id2: 'off',
})
hass.block_till_done()
setup_component(hass, input_boolean.DOMAIN, {
input_boolean.DOMAIN: {
'b1': None,
'b2': None,
}})
hass.start()
state = hass.states.get('input_boolean.b1')
assert state
assert state.state == 'on'
state = hass.states.get('input_boolean.b2')
assert state
assert state.state == 'off'
hass.stop()