Ensure restore state is not written after the stop event (#49329)

If everything lined up, the states could be written
while Home Assistant is shutting down after the stop
event because the interval tracker was not canceled on
the stop event.
This commit is contained in:
J. Nick Koston 2021-04-16 21:03:18 -10:00 committed by GitHub
parent 41ed1f818c
commit f96a6e878f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 4 deletions

View File

@ -177,10 +177,18 @@ class RestoreStateData:
self.hass.async_create_task(_async_dump_states())
# Dump states periodically
async_track_time_interval(self.hass, _async_dump_states, STATE_DUMP_INTERVAL)
cancel_interval = async_track_time_interval(
self.hass, _async_dump_states, STATE_DUMP_INTERVAL
)
async def _async_dump_states_at_stop(*_: Any) -> None:
cancel_interval()
await self.async_dump_states()
# Dump states when stopping hass
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_dump_states)
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, _async_dump_states_at_stop
)
@callback
def async_restore_entity_added(self, entity_id: str) -> None:

View File

@ -1,8 +1,8 @@
"""The tests for the Restore component."""
from datetime import datetime
from datetime import datetime, timedelta
from unittest.mock import patch
from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import CoreState, State
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import Entity
@ -15,6 +15,8 @@ from homeassistant.helpers.restore_state import (
)
from homeassistant.util import dt as dt_util
from tests.common import async_fire_time_changed
async def test_caching_data(hass):
"""Test that we cache data."""
@ -50,6 +52,52 @@ async def test_caching_data(hass):
assert mock_write_data.called
async def test_periodic_write(hass):
"""Test that we write periodiclly but not after stop."""
data = await RestoreStateData.async_get_instance(hass)
await hass.async_block_till_done()
await data.store.async_save([])
# Emulate a fresh load
hass.data[DATA_RESTORE_STATE_TASK] = None
entity = RestoreEntity()
entity.hass = hass
entity.entity_id = "input_boolean.b1"
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
await entity.async_get_last_state()
await hass.async_block_till_done()
assert mock_write_data.called
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=15))
await hass.async_block_till_done()
assert mock_write_data.called
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
assert mock_write_data.called
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=30))
await hass.async_block_till_done()
assert not mock_write_data.called
async def test_hass_starting(hass):
"""Test that we cache data."""
hass.state = CoreState.starting