Fix boundary case in calednar trigger (#70467)

Update calendar trigger scan logic to add a one second boundary due to the
exclusive search. Add a test that reproduced the issue.
This commit is contained in:
Allen Porter 2022-04-24 12:52:17 -07:00 committed by GitHub
parent 965665213f
commit dc7e3a6df6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 4 deletions

View File

@ -81,7 +81,8 @@ class CalendarEventListener:
async def _fetch_events(self, last_endtime: datetime.datetime) -> None:
"""Update the set of eligible events."""
end_time = last_endtime + UPDATE_INTERVAL
# Event time ranges are exclusive so the end time is expanded by 1sec
end_time = last_endtime + UPDATE_INTERVAL + datetime.timedelta(seconds=1)
_LOGGER.debug("Fetching events between %s, %s", last_endtime, end_time)
events = await self._entity.async_get_events(self._hass, last_endtime, end_time)
@ -125,8 +126,12 @@ class CalendarEventListener:
async def _handle_calendar_event(self, now: datetime.datetime) -> None:
"""Handle calendar event."""
_LOGGER.debug("Calendar event @ %s", now)
self._dispatch_events(now)
self._clear_event_listener()
self._listen_next_calendar_event()
# Consume all events that are eligible to fire
def _dispatch_events(self, now: datetime.datetime) -> None:
"""Dispatch all events that are eligible to fire."""
while self._events and self._events[0][0] <= now:
(_fire_time, event) = self._events.pop(0)
_LOGGER.debug("Event: %s", event)
@ -134,12 +139,13 @@ class CalendarEventListener:
self._job,
{"trigger": {**self._trigger_data, "calendar_event": event.as_dict()}},
)
self._clear_event_listener()
self._listen_next_calendar_event()
async def _handle_refresh(self, now: datetime.datetime) -> None:
"""Handle core config update."""
_LOGGER.debug("Refresh events @ %s", now)
# Dispatch any eligible events in the boundary case where refresh
# fires before the calendar event.
self._dispatch_events(now)
self._clear_event_listener()
await self._fetch_events(now)
self._listen_next_calendar_event()

View File

@ -481,3 +481,27 @@ async def test_event_payload(hass, calls, fake_schedule):
"calendar_event": event_data,
}
]
async def test_trigger_timestamp_window_edge(hass, calls, fake_schedule, freezer):
"""Test that events in the edge of a scan are included."""
freezer.move_to("2022-04-19 11:00:00+00:00")
# Exactly at a TEST_UPDATE_INTERVAL boundary the start time,
# making this excluded from the first window.
event_data = fake_schedule.create_event(
start=datetime.datetime.fromisoformat("2022-04-19 11:14:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
)
await create_automation(hass, EVENT_START)
assert len(calls()) == 0
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:20:00+00:00")
)
assert calls() == [
{
"platform": "calendar",
"event": EVENT_START,
"calendar_event": event_data,
}
]