Switch async_track_time_interval to use async_call_later internally (#99220)

This commit is contained in:
J. Nick Koston 2023-08-28 13:56:22 -05:00 committed by GitHub
parent 821d74e904
commit 80d2309896
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 59 additions and 62 deletions

View File

@ -1514,24 +1514,19 @@ def async_track_time_interval(
"""Add a listener that fires repetitively at every timedelta interval.""" """Add a listener that fires repetitively at every timedelta interval."""
remove: CALLBACK_TYPE remove: CALLBACK_TYPE
interval_listener_job: HassJob[[datetime], None] interval_listener_job: HassJob[[datetime], None]
interval_seconds = interval.total_seconds()
job = HassJob( job = HassJob(
action, f"track time interval {interval}", cancel_on_shutdown=cancel_on_shutdown action, f"track time interval {interval}", cancel_on_shutdown=cancel_on_shutdown
) )
def next_interval() -> datetime:
"""Return the next interval."""
return dt_util.utcnow() + interval
@callback @callback
def interval_listener(now: datetime) -> None: def interval_listener(now: datetime) -> None:
"""Handle elapsed intervals.""" """Handle elapsed intervals."""
nonlocal remove nonlocal remove
nonlocal interval_listener_job nonlocal interval_listener_job
remove = async_track_point_in_utc_time( remove = async_call_later(hass, interval_seconds, interval_listener_job)
hass, interval_listener_job, next_interval()
)
hass.async_run_hass_job(job, now) hass.async_run_hass_job(job, now)
if name: if name:
@ -1542,7 +1537,7 @@ def async_track_time_interval(
interval_listener_job = HassJob( interval_listener_job = HassJob(
interval_listener, job_name, cancel_on_shutdown=cancel_on_shutdown interval_listener, job_name, cancel_on_shutdown=cancel_on_shutdown
) )
remove = async_track_point_in_utc_time(hass, interval_listener_job, next_interval()) remove = async_call_later(hass, interval_seconds, interval_listener_job)
def remove_listener() -> None: def remove_listener() -> None:
"""Remove interval listener.""" """Remove interval listener."""

View File

@ -3,7 +3,7 @@ from collections.abc import Awaitable, Callable, Generator
from typing import Any from typing import Any
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
from freezegun import freeze_time from freezegun.api import FrozenDateTimeFactory
from gardena_bluetooth.client import Client from gardena_bluetooth.client import Client
from gardena_bluetooth.const import DeviceInformation from gardena_bluetooth.const import DeviceInformation
from gardena_bluetooth.exceptions import CharacteristicNotFound from gardena_bluetooth.exceptions import CharacteristicNotFound
@ -49,19 +49,19 @@ def mock_read_char_raw():
@pytest.fixture @pytest.fixture
async def scan_step( async def scan_step(
hass: HomeAssistant, hass: HomeAssistant, freezer: FrozenDateTimeFactory
) -> Generator[None, None, Callable[[], Awaitable[None]]]: ) -> Generator[None, None, Callable[[], Awaitable[None]]]:
"""Step system time forward.""" """Step system time forward."""
with freeze_time("2023-01-01", tz_offset=1) as frozen_time: freezer.move_to("2023-01-01T01:00:00Z")
async def delay(): async def delay():
"""Trigger delay in system.""" """Trigger delay in system."""
frozen_time.tick(delta=SCAN_INTERVAL) freezer.tick(delta=SCAN_INTERVAL)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
yield delay return delay
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)

View File

@ -5,11 +5,13 @@ from datetime import timedelta
import logging import logging
from unittest import mock from unittest import mock
from freezegun.api import FrozenDateTimeFactory
from pymodbus.exceptions import ModbusException from pymodbus.exceptions import ModbusException
import pytest import pytest
from homeassistant.components.modbus.const import MODBUS_DOMAIN as DOMAIN, TCP from homeassistant.components.modbus.const import MODBUS_DOMAIN as DOMAIN, TCP
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, CONF_SLAVE, CONF_TYPE from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, CONF_SLAVE, CONF_TYPE
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
@ -140,26 +142,26 @@ async def mock_pymodbus_return_fixture(hass, register_words, mock_modbus):
@pytest.fixture(name="mock_do_cycle") @pytest.fixture(name="mock_do_cycle")
async def mock_do_cycle_fixture(hass, mock_pymodbus_exception, mock_pymodbus_return): async def mock_do_cycle_fixture(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
mock_pymodbus_exception,
mock_pymodbus_return,
) -> FrozenDateTimeFactory:
"""Trigger update call with time_changed event.""" """Trigger update call with time_changed event."""
now = dt_util.utcnow() + timedelta(seconds=90) freezer.tick(timedelta(seconds=90))
with mock.patch( async_fire_time_changed(hass)
"homeassistant.helpers.event.dt_util.utcnow", return_value=now, autospec=True await hass.async_block_till_done()
): return freezer
async_fire_time_changed(hass, now)
await hass.async_block_till_done()
return now
async def do_next_cycle(hass, now, cycle): async def do_next_cycle(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, cycle: int
) -> None:
"""Trigger update call with time_changed event.""" """Trigger update call with time_changed event."""
now += timedelta(seconds=cycle) freezer.tick(timedelta(seconds=cycle))
with mock.patch( async_fire_time_changed(hass)
"homeassistant.helpers.event.dt_util.utcnow", return_value=now, autospec=True await hass.async_block_till_done()
):
async_fire_time_changed(hass, now)
await hass.async_block_till_done()
return now
@pytest.fixture(name="mock_test_state") @pytest.fixture(name="mock_test_state")

View File

@ -1,4 +1,5 @@
"""Thetests for the Modbus sensor component.""" """Thetests for the Modbus sensor component."""
from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from homeassistant.components.binary_sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.components.binary_sensor import DOMAIN as SENSOR_DOMAIN
@ -199,14 +200,13 @@ async def test_all_binary_sensor(hass: HomeAssistant, expected, mock_do_cycle) -
], ],
) )
async def test_lazy_error_binary_sensor( async def test_lazy_error_binary_sensor(
hass: HomeAssistant, start_expect, end_expect, mock_do_cycle hass: HomeAssistant, start_expect, end_expect, mock_do_cycle: FrozenDateTimeFactory
) -> None: ) -> None:
"""Run test for given config.""" """Run test for given config."""
now = mock_do_cycle
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == end_expect assert hass.states.get(ENTITY_ID).state == end_expect

View File

@ -1,4 +1,5 @@
"""The tests for the Modbus climate component.""" """The tests for the Modbus climate component."""
from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
@ -597,16 +598,15 @@ async def test_restore_state_climate(
], ],
) )
async def test_lazy_error_climate( async def test_lazy_error_climate(
hass: HomeAssistant, mock_do_cycle, start_expect, end_expect hass: HomeAssistant, mock_do_cycle: FrozenDateTimeFactory, start_expect, end_expect
) -> None: ) -> None:
"""Run test for sensor.""" """Run test for sensor."""
hass.states.async_set(ENTITY_ID, 17) hass.states.async_set(ENTITY_ID, 17)
await hass.async_block_till_done() await hass.async_block_till_done()
now = mock_do_cycle
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == end_expect assert hass.states.get(ENTITY_ID).state == end_expect

View File

@ -1,4 +1,5 @@
"""The tests for the Modbus cover component.""" """The tests for the Modbus cover component."""
from freezegun.api import FrozenDateTimeFactory
from pymodbus.exceptions import ModbusException from pymodbus.exceptions import ModbusException
import pytest import pytest
@ -142,14 +143,13 @@ async def test_coil_cover(hass: HomeAssistant, expected, mock_do_cycle) -> None:
], ],
) )
async def test_lazy_error_cover( async def test_lazy_error_cover(
hass: HomeAssistant, start_expect, end_expect, mock_do_cycle hass: HomeAssistant, start_expect, end_expect, mock_do_cycle: FrozenDateTimeFactory
) -> None: ) -> None:
"""Run test for given config.""" """Run test for given config."""
now = mock_do_cycle
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == end_expect assert hass.states.get(ENTITY_ID).state == end_expect

View File

@ -1,4 +1,5 @@
"""The tests for the Modbus sensor component.""" """The tests for the Modbus sensor component."""
from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from homeassistant.components.modbus.const import ( from homeassistant.components.modbus.const import (
@ -928,16 +929,15 @@ async def test_wrong_unpack(hass: HomeAssistant, mock_do_cycle) -> None:
], ],
) )
async def test_lazy_error_sensor( async def test_lazy_error_sensor(
hass: HomeAssistant, mock_do_cycle, start_expect, end_expect hass: HomeAssistant, mock_do_cycle: FrozenDateTimeFactory, start_expect, end_expect
) -> None: ) -> None:
"""Run test for sensor.""" """Run test for sensor."""
hass.states.async_set(ENTITY_ID, 17) hass.states.async_set(ENTITY_ID, 17)
await hass.async_block_till_done() await hass.async_block_till_done()
now = mock_do_cycle
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == end_expect assert hass.states.get(ENTITY_ID).state == end_expect

View File

@ -2,6 +2,7 @@
from datetime import timedelta from datetime import timedelta
from unittest import mock from unittest import mock
from freezegun.api import FrozenDateTimeFactory
from pymodbus.exceptions import ModbusException from pymodbus.exceptions import ModbusException
import pytest import pytest
@ -237,14 +238,13 @@ async def test_all_switch(hass: HomeAssistant, mock_do_cycle, expected) -> None:
], ],
) )
async def test_lazy_error_switch( async def test_lazy_error_switch(
hass: HomeAssistant, start_expect, end_expect, mock_do_cycle hass: HomeAssistant, start_expect, end_expect, mock_do_cycle: FrozenDateTimeFactory
) -> None: ) -> None:
"""Run test for given config.""" """Run test for given config."""
now = mock_do_cycle
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == start_expect assert hass.states.get(ENTITY_ID).state == start_expect
now = await do_next_cycle(hass, now, 11) await do_next_cycle(hass, mock_do_cycle, 11)
assert hass.states.get(ENTITY_ID).state == end_expect assert hass.states.get(ENTITY_ID).state == end_expect

View File

@ -4,6 +4,7 @@ from unittest.mock import patch
from aiounifi.models.message import MessageKey from aiounifi.models.message import MessageKey
from aiounifi.websocket import WebsocketState from aiounifi.websocket import WebsocketState
from freezegun.api import FrozenDateTimeFactory
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.device_tracker import DOMAIN as TRACKER_DOMAIN from homeassistant.components.device_tracker import DOMAIN as TRACKER_DOMAIN
@ -169,6 +170,7 @@ async def test_tracked_clients(
async def test_tracked_wireless_clients_event_source( async def test_tracked_wireless_clients_event_source(
hass: HomeAssistant, hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker, aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
mock_unifi_websocket, mock_unifi_websocket,
mock_device_registry, mock_device_registry,
) -> None: ) -> None:
@ -234,10 +236,9 @@ async def test_tracked_wireless_clients_event_source(
assert hass.states.get("device_tracker.client").state == STATE_HOME assert hass.states.get("device_tracker.client").state == STATE_HOME
# Change time to mark client as away # Change time to mark client as away
new_time = dt_util.utcnow() + controller.option_detection_time freezer.tick(controller.option_detection_time + timedelta(seconds=1))
with patch("homeassistant.util.dt.utcnow", return_value=new_time): async_fire_time_changed(hass)
async_fire_time_changed(hass, new_time) await hass.async_block_till_done()
await hass.async_block_till_done()
assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME
@ -274,12 +275,11 @@ async def test_tracked_wireless_clients_event_source(
assert hass.states.get("device_tracker.client").state == STATE_HOME assert hass.states.get("device_tracker.client").state == STATE_HOME
# Change time to mark client as away # Change time to mark client as away
new_time = dt_util.utcnow() + controller.option_detection_time freezer.tick(controller.option_detection_time + timedelta(seconds=1))
with patch("homeassistant.util.dt.utcnow", return_value=new_time): async_fire_time_changed(hass)
async_fire_time_changed(hass, new_time) await hass.async_block_till_done()
await hass.async_block_till_done()
assert hass.states.get("device_tracker.client").state == STATE_HOME assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME
async def test_tracked_devices( async def test_tracked_devices(