Fix lingering timers in calendar tests (#90845)

This commit is contained in:
epenet 2023-04-06 02:46:05 +02:00 committed by GitHub
parent 4276ce96ea
commit 60692bcfdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -8,7 +8,8 @@ forward exercising the triggers.
""" """
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable, Generator from collections.abc import AsyncIterator, Callable, Generator
from contextlib import asynccontextmanager
import datetime import datetime
import logging import logging
import secrets import secrets
@ -22,6 +23,7 @@ import pytest
from homeassistant.components import calendar from homeassistant.components import calendar
import homeassistant.components.automation as automation import homeassistant.components.automation as automation
from homeassistant.components.calendar.trigger import EVENT_END, EVENT_START from homeassistant.components.calendar.trigger import EVENT_END, EVENT_START
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF
from homeassistant.core import HomeAssistant 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
@ -151,7 +153,10 @@ async def setup_calendar(hass: HomeAssistant, fake_schedule: FakeSchedule) -> No
await hass.async_block_till_done() await hass.async_block_till_done()
async def create_automation(hass: HomeAssistant, event_type: str, offset=None) -> None: @asynccontextmanager
async def create_automation(
hass: HomeAssistant, event_type: str, offset=None
) -> AsyncIterator[None]:
"""Register an automation.""" """Register an automation."""
trigger_data = { trigger_data = {
"platform": calendar.DOMAIN, "platform": calendar.DOMAIN,
@ -165,6 +170,7 @@ async def create_automation(hass: HomeAssistant, event_type: str, offset=None) -
automation.DOMAIN, automation.DOMAIN,
{ {
automation.DOMAIN: { automation.DOMAIN: {
"alias": event_type,
"trigger": trigger_data, "trigger": trigger_data,
"action": TEST_AUTOMATION_ACTION, "action": TEST_AUTOMATION_ACTION,
"mode": "queued", "mode": "queued",
@ -173,6 +179,16 @@ async def create_automation(hass: HomeAssistant, event_type: str, offset=None) -
) )
await hass.async_block_till_done() await hass.async_block_till_done()
yield
# Disable automation to cleanup lingering timers
await hass.services.async_call(
automation.DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: f"automation.{event_type}"},
blocking=True,
)
@pytest.fixture @pytest.fixture
def calls(hass: HomeAssistant) -> Callable[[], list[dict[str, Any]]]: def calls(hass: HomeAssistant) -> Callable[[], list[dict[str, Any]]]:
@ -205,12 +221,13 @@ async def test_event_start_trigger(
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
) )
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
assert len(calls()) == 0 assert len(calls()) == 0
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
)
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
)
assert calls() == [ assert calls() == [
{ {
"platform": "calendar", "platform": "calendar",
@ -239,25 +256,24 @@ async def test_event_start_trigger_with_offset(
start=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 12:30:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 12:30:00+00:00"),
) )
await create_automation(hass, EVENT_START, offset=offset_str) async with create_automation(hass, EVENT_START, offset=offset_str):
# No calls yet
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:55:00+00:00") + offset_delta,
)
assert len(calls()) == 0
# No calls yet # Event has started w/ offset
await fake_schedule.fire_until( await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:55:00+00:00") + offset_delta, datetime.datetime.fromisoformat("2022-04-19 12:05:00+00:00") + offset_delta,
) )
assert len(calls()) == 0 assert calls() == [
{
# Event has started w/ offset "platform": "calendar",
await fake_schedule.fire_until( "event": EVENT_START,
datetime.datetime.fromisoformat("2022-04-19 12:05:00+00:00") + offset_delta, "calendar_event": event_data,
) }
assert calls() == [ ]
{
"platform": "calendar",
"event": EVENT_START,
"calendar_event": event_data,
}
]
async def test_event_end_trigger( async def test_event_end_trigger(
@ -270,25 +286,24 @@ async def test_event_end_trigger(
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"),
) )
await create_automation(hass, EVENT_END) async with create_automation(hass, EVENT_END):
# Event started, nothing should fire yet
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:10:00+00:00")
)
assert len(calls()) == 0
# Event started, nothing should fire yet # Event ends
await fake_schedule.fire_until( await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:10:00+00:00") datetime.datetime.fromisoformat("2022-04-19 12:10:00+00:00")
) )
assert len(calls()) == 0 assert calls() == [
{
# Event ends "platform": "calendar",
await fake_schedule.fire_until( "event": EVENT_END,
datetime.datetime.fromisoformat("2022-04-19 12:10:00+00:00") "calendar_event": event_data,
) }
assert calls() == [ ]
{
"platform": "calendar",
"event": EVENT_END,
"calendar_event": event_data,
}
]
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -310,25 +325,24 @@ async def test_event_end_trigger_with_offset(
start=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 12:30:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 12:30:00+00:00"),
) )
await create_automation(hass, EVENT_END, offset=offset_str) async with create_automation(hass, EVENT_END, offset=offset_str):
# No calls yet
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 12:05:00+00:00") + offset_delta,
)
assert len(calls()) == 0
# No calls yet # Event has started w/ offset
await fake_schedule.fire_until( await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 12:05:00+00:00") + offset_delta, datetime.datetime.fromisoformat("2022-04-19 12:35:00+00:00") + offset_delta,
) )
assert len(calls()) == 0 assert calls() == [
{
# Event has started w/ offset "platform": "calendar",
await fake_schedule.fire_until( "event": EVENT_END,
datetime.datetime.fromisoformat("2022-04-19 12:35:00+00:00") + offset_delta, "calendar_event": event_data,
) }
assert calls() == [ ]
{
"platform": "calendar",
"event": EVENT_END,
"calendar_event": event_data,
}
]
async def test_calendar_trigger_with_no_events( async def test_calendar_trigger_with_no_events(
@ -338,13 +352,11 @@ async def test_calendar_trigger_with_no_events(
) -> None: ) -> None:
"""Test a calendar trigger setup with no events.""" """Test a calendar trigger setup with no events."""
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START), create_automation(hass, EVENT_END):
await create_automation(hass, EVENT_END) # No calls, at arbitrary times
await fake_schedule.fire_until(
# No calls, at arbitrary times datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00")
await fake_schedule.fire_until( )
datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00")
)
assert len(calls()) == 0 assert len(calls()) == 0
@ -363,11 +375,10 @@ async def test_multiple_start_events(
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
) )
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
await fake_schedule.fire_until(
await fake_schedule.fire_until( datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00")
datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00") )
)
assert calls() == [ assert calls() == [
{ {
"platform": "calendar", "platform": "calendar",
@ -397,11 +408,11 @@ async def test_multiple_end_events(
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
) )
await create_automation(hass, EVENT_END) async with create_automation(hass, EVENT_END):
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00")
)
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00")
)
assert calls() == [ assert calls() == [
{ {
"platform": "calendar", "platform": "calendar",
@ -431,11 +442,11 @@ async def test_multiple_events_sharing_start_time(
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
) )
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:35:00+00:00")
)
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:35:00+00:00")
)
assert calls() == [ assert calls() == [
{ {
"platform": "calendar", "platform": "calendar",
@ -465,11 +476,11 @@ async def test_overlap_events(
start=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:45:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 11:45:00+00:00"),
) )
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:20:00+00:00")
)
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:20:00+00:00")
)
assert calls() == [ assert calls() == [
{ {
"platform": "calendar", "platform": "calendar",
@ -537,24 +548,23 @@ async def test_update_next_event(
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
) )
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
# No calls before event start
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 10:45:00+00:00")
)
assert len(calls()) == 0
# No calls before event start # Create a new event between now and when the event fires
await fake_schedule.fire_until( event_data2 = fake_schedule.create_event(
datetime.datetime.fromisoformat("2022-04-19 10:45:00+00:00") start=datetime.datetime.fromisoformat("2022-04-19 10:55:00+00:00"),
) end=datetime.datetime.fromisoformat("2022-04-19 11:05:00+00:00"),
assert len(calls()) == 0 )
# Create a new event between now and when the event fires # Advance past the end of the events
event_data2 = fake_schedule.create_event( await fake_schedule.fire_until(
start=datetime.datetime.fromisoformat("2022-04-19 10:55:00+00:00"), datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00")
end=datetime.datetime.fromisoformat("2022-04-19 11:05:00+00:00"), )
)
# Advance past the end of the events
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00")
)
assert calls() == [ assert calls() == [
{ {
"platform": "calendar", "platform": "calendar",
@ -580,31 +590,30 @@ async def test_update_missed(
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
) )
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
# Events are refreshed at t+TEST_UPDATE_INTERVAL minutes. A new event is
# added, but the next update happens after the event is already over.
await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 10:38:00+00:00")
)
assert len(calls()) == 0
# Events are refreshed at t+TEST_UPDATE_INTERVAL minutes. A new event is fake_schedule.create_event(
# added, but the next update happens after the event is already over. start=datetime.datetime.fromisoformat("2022-04-19 10:40:00+00:00"),
await fake_schedule.fire_until( end=datetime.datetime.fromisoformat("2022-04-19 10:55:00+00:00"),
datetime.datetime.fromisoformat("2022-04-19 10:38:00+00:00") )
)
assert len(calls()) == 0
fake_schedule.create_event( # Only the first event is returned
start=datetime.datetime.fromisoformat("2022-04-19 10:40:00+00:00"), await fake_schedule.fire_until(
end=datetime.datetime.fromisoformat("2022-04-19 10:55:00+00:00"), datetime.datetime.fromisoformat("2022-04-19 11:05:00+00:00")
) )
assert calls() == [
# Only the first event is returned {
await fake_schedule.fire_until( "platform": "calendar",
datetime.datetime.fromisoformat("2022-04-19 11:05:00+00:00") "event": EVENT_START,
) "calendar_event": event_data1,
assert calls() == [ },
{ ]
"platform": "calendar",
"event": EVENT_START,
"calendar_event": event_data1,
},
]
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -670,17 +679,17 @@ async def test_event_payload(
) -> None: ) -> None:
"""Test the fields in the calendar event payload are set.""" """Test the fields in the calendar event payload are set."""
fake_schedule.create_event(**create_data) fake_schedule.create_event(**create_data)
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
assert len(calls()) == 0 assert len(calls()) == 0
await fake_schedule.fire_until(fire_time) await fake_schedule.fire_until(fire_time)
assert calls() == [ assert calls() == [
{ {
"platform": "calendar", "platform": "calendar",
"event": EVENT_START, "event": EVENT_START,
"calendar_event": payload_data, "calendar_event": payload_data,
} }
] ]
async def test_trigger_timestamp_window_edge( async def test_trigger_timestamp_window_edge(
@ -697,19 +706,19 @@ async def test_trigger_timestamp_window_edge(
start=datetime.datetime.fromisoformat("2022-04-19 11:14:00+00:00"), start=datetime.datetime.fromisoformat("2022-04-19 11:14:00+00:00"),
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"), end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
) )
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
assert len(calls()) == 0 assert len(calls()) == 0
await fake_schedule.fire_until( await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2022-04-19 11:20:00+00:00") datetime.datetime.fromisoformat("2022-04-19 11:20:00+00:00")
) )
assert calls() == [ assert calls() == [
{ {
"platform": "calendar", "platform": "calendar",
"event": EVENT_START, "event": EVENT_START,
"calendar_event": event_data, "calendar_event": event_data,
} }
] ]
async def test_event_start_trigger_dst( async def test_event_start_trigger_dst(
@ -741,26 +750,27 @@ async def test_event_start_trigger_dst(
start=datetime.datetime(2023, 3, 12, 3, 30, tzinfo=tzinfo), start=datetime.datetime(2023, 3, 12, 3, 30, tzinfo=tzinfo),
end=datetime.datetime(2023, 3, 12, 3, 45, tzinfo=tzinfo), end=datetime.datetime(2023, 3, 12, 3, 45, tzinfo=tzinfo),
) )
await create_automation(hass, EVENT_START) async with create_automation(hass, EVENT_START):
assert len(calls()) == 0 assert len(calls()) == 0
await fake_schedule.fire_until( await fake_schedule.fire_until(
datetime.datetime.fromisoformat("2023-03-12 05:00:00-08:00"), datetime.datetime.fromisoformat("2023-03-12 05:00:00-08:00"),
) )
assert calls() == [
{ assert calls() == [
"platform": "calendar", {
"event": EVENT_START, "platform": "calendar",
"calendar_event": event1_data, "event": EVENT_START,
}, "calendar_event": event1_data,
{ },
"platform": "calendar", {
"event": EVENT_START, "platform": "calendar",
"calendar_event": event2_data, "event": EVENT_START,
}, "calendar_event": event2_data,
{ },
"platform": "calendar", {
"event": EVENT_START, "platform": "calendar",
"calendar_event": event3_data, "event": EVENT_START,
}, "calendar_event": event3_data,
] },
]