Store: copy pending data (#59934)
This commit is contained in:
parent
0fb21af07f
commit
442597928e
8 changed files with 42 additions and 11 deletions
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
from copy import deepcopy
|
||||||
import inspect
|
import inspect
|
||||||
from json import JSONEncoder
|
from json import JSONEncoder
|
||||||
import logging
|
import logging
|
||||||
|
@ -133,6 +134,10 @@ class Store:
|
||||||
# If we didn't generate data yet, do it now.
|
# If we didn't generate data yet, do it now.
|
||||||
if "data_func" in data:
|
if "data_func" in data:
|
||||||
data["data"] = data.pop("data_func")()
|
data["data"] = data.pop("data_func")()
|
||||||
|
|
||||||
|
# We make a copy because code might assume it's safe to mutate loaded data
|
||||||
|
# and we don't want that to mess with what we're trying to store.
|
||||||
|
data = deepcopy(data)
|
||||||
else:
|
else:
|
||||||
data = await self.hass.async_add_executor_job(
|
data = await self.hass.async_add_executor_job(
|
||||||
json_util.load_json, self.path
|
json_util.load_json, self.path
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
"key": "trace.saved_traces",
|
"key": "trace.saved_traces",
|
||||||
"data": {
|
"data": {
|
||||||
"automation.sun": [
|
"automation.sun": [
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
"key": "trace.saved_traces",
|
"key": "trace.saved_traces",
|
||||||
"data": {
|
"data": {
|
||||||
"script.sun": [
|
"script.sun": [
|
||||||
|
|
|
@ -49,7 +49,7 @@ import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
DEFAULT_CONFIG_ENTRY_ID = 1
|
DEFAULT_CONFIG_ENTRY_ID = "1"
|
||||||
DEFAULT_HOST = "1.2.3.4"
|
DEFAULT_HOST = "1.2.3.4"
|
||||||
DEFAULT_SITE = "site_id"
|
DEFAULT_SITE = "site_id"
|
||||||
|
|
||||||
|
|
|
@ -1089,7 +1089,7 @@ async def test_restoring_client(hass, aioclient_mock):
|
||||||
data=ENTRY_CONFIG,
|
data=ENTRY_CONFIG,
|
||||||
source="test",
|
source="test",
|
||||||
options={},
|
options={},
|
||||||
entry_id=1,
|
entry_id="1",
|
||||||
)
|
)
|
||||||
|
|
||||||
registry = er.async_get(hass)
|
registry = er.async_get(hass)
|
||||||
|
|
|
@ -14,7 +14,7 @@ from .test_controller import (
|
||||||
setup_unifi_integration,
|
setup_unifi_integration,
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry, flush_store
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_with_no_config(hass):
|
async def test_setup_with_no_config(hass):
|
||||||
|
@ -110,6 +110,7 @@ async def test_wireless_clients(hass, hass_storage, aioclient_mock):
|
||||||
config_entry = await setup_unifi_integration(
|
config_entry = await setup_unifi_integration(
|
||||||
hass, aioclient_mock, clients_response=[client_1, client_2]
|
hass, aioclient_mock, clients_response=[client_1, client_2]
|
||||||
)
|
)
|
||||||
|
await flush_store(hass.data[unifi.UNIFI_WIRELESS_CLIENTS]._store)
|
||||||
|
|
||||||
for mac in [
|
for mac in [
|
||||||
"00:00:00:00:00:00",
|
"00:00:00:00:00:00",
|
||||||
|
|
|
@ -22,6 +22,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
|
|
||||||
from .test_controller import (
|
from .test_controller import (
|
||||||
CONTROLLER_HOST,
|
CONTROLLER_HOST,
|
||||||
|
DEFAULT_CONFIG_ENTRY_ID,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
setup_unifi_integration,
|
setup_unifi_integration,
|
||||||
|
@ -857,7 +858,7 @@ async def test_restore_client_succeed(hass, aioclient_mock):
|
||||||
data=ENTRY_CONFIG,
|
data=ENTRY_CONFIG,
|
||||||
source="test",
|
source="test",
|
||||||
options={},
|
options={},
|
||||||
entry_id=1,
|
entry_id=DEFAULT_CONFIG_ENTRY_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
registry = er.async_get(hass)
|
registry = er.async_get(hass)
|
||||||
|
@ -947,7 +948,7 @@ async def test_restore_client_no_old_state(hass, aioclient_mock):
|
||||||
data=ENTRY_CONFIG,
|
data=ENTRY_CONFIG,
|
||||||
source="test",
|
source="test",
|
||||||
options={},
|
options={},
|
||||||
entry_id=1,
|
entry_id=DEFAULT_CONFIG_ENTRY_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
registry = er.async_get(hass)
|
registry = er.async_get(hass)
|
||||||
|
|
|
@ -28,13 +28,13 @@ MOCK_DATA2 = {"goodbye": "cruel world"}
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def store(hass):
|
def store(hass):
|
||||||
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
||||||
yield storage.Store(hass, MOCK_VERSION, MOCK_KEY)
|
return storage.Store(hass, MOCK_VERSION, MOCK_KEY)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def store_v_1_1(hass):
|
def store_v_1_1(hass):
|
||||||
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
||||||
yield storage.Store(
|
return storage.Store(
|
||||||
hass, MOCK_VERSION, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_1
|
hass, MOCK_VERSION, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ def store_v_1_1(hass):
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def store_v_1_2(hass):
|
def store_v_1_2(hass):
|
||||||
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
||||||
yield storage.Store(
|
return storage.Store(
|
||||||
hass, MOCK_VERSION, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_2
|
hass, MOCK_VERSION, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ def store_v_1_2(hass):
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def store_v_2_1(hass):
|
def store_v_2_1(hass):
|
||||||
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
"""Fixture of a store that prevents writing on Home Assistant stop."""
|
||||||
yield storage.Store(
|
return storage.Store(
|
||||||
hass, MOCK_VERSION_2, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_1
|
hass, MOCK_VERSION_2, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -91,8 +91,8 @@ async def test_loading_parallel(hass, store, hass_storage, caplog):
|
||||||
|
|
||||||
results = await asyncio.gather(store.async_load(), store.async_load())
|
results = await asyncio.gather(store.async_load(), store.async_load())
|
||||||
|
|
||||||
assert results[0] is MOCK_DATA
|
assert results[0] == MOCK_DATA
|
||||||
assert results[1] is MOCK_DATA
|
assert results[0] is results[1]
|
||||||
assert caplog.text.count(f"Loading data for {store.key}")
|
assert caplog.text.count(f"Loading data for {store.key}")
|
||||||
|
|
||||||
|
|
||||||
|
@ -436,3 +436,25 @@ async def test_legacy_migration(hass, hass_storage, store_v_1_2):
|
||||||
"minor_version": 1,
|
"minor_version": 1,
|
||||||
"data": MOCK_DATA,
|
"data": MOCK_DATA,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_changing_delayed_written_data(hass, store, hass_storage):
|
||||||
|
"""Test changing data that is written with delay."""
|
||||||
|
data_to_store = {"hello": "world"}
|
||||||
|
store.async_delay_save(lambda: data_to_store, 1)
|
||||||
|
assert store.key not in hass_storage
|
||||||
|
|
||||||
|
loaded_data = await store.async_load()
|
||||||
|
assert loaded_data == data_to_store
|
||||||
|
assert loaded_data is not data_to_store
|
||||||
|
|
||||||
|
loaded_data["hello"] = "earth"
|
||||||
|
|
||||||
|
async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1))
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert hass_storage[store.key] == {
|
||||||
|
"version": MOCK_VERSION,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": MOCK_KEY,
|
||||||
|
"data": {"hello": "world"},
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue