Fix flaky husqvarna_automower test with comprehensive race condition fix (#148911)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Franck Nijhof 2025-07-16 21:40:44 +02:00 committed by GitHub
parent 58bb2fa327
commit e8fca19335
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 21 additions and 11 deletions

View File

@ -70,6 +70,8 @@ class AutomowerCalendarEntity(AutomowerBaseEntity, CalendarEntity):
@property @property
def event(self) -> CalendarEvent | None: def event(self) -> CalendarEvent | None:
"""Return the current or next upcoming event.""" """Return the current or next upcoming event."""
if not self.available:
return None
schedule = self.mower_attributes.calendar schedule = self.mower_attributes.calendar
cursor = schedule.timeline.active_after(dt_util.now()) cursor = schedule.timeline.active_after(dt_util.now())
program_event = next(cursor, None) program_event = next(cursor, None)
@ -94,6 +96,8 @@ class AutomowerCalendarEntity(AutomowerBaseEntity, CalendarEntity):
This is only called when opening the calendar in the UI. This is only called when opening the calendar in the UI.
""" """
if not self.available:
return []
schedule = self.mower_attributes.calendar schedule = self.mower_attributes.calendar
cursor = schedule.timeline.overlapping( cursor = schedule.timeline.overlapping(
start_date, start_date,

View File

@ -114,6 +114,11 @@ class AutomowerBaseEntity(CoordinatorEntity[AutomowerDataUpdateCoordinator]):
"""Get the mower attributes of the current mower.""" """Get the mower attributes of the current mower."""
return self.coordinator.data[self.mower_id] return self.coordinator.data[self.mower_id]
@property
def available(self) -> bool:
"""Return True if the device is available."""
return super().available and self.mower_id in self.coordinator.data
class AutomowerAvailableEntity(AutomowerBaseEntity): class AutomowerAvailableEntity(AutomowerBaseEntity):
"""Replies available when the mower is connected.""" """Replies available when the mower is connected."""

View File

@ -312,8 +312,9 @@ async def test_coordinator_automatic_registry_cleanup(
dr.async_entries_for_config_entry(device_registry, entry.entry_id) dr.async_entries_for_config_entry(device_registry, entry.entry_id)
) )
# Remove mower 2 and check if it worked # Remove mower 2 and check if it worked
mower2 = values.pop("1234") values_copy = deepcopy(values)
mock_automower_client.get_status.return_value = values mower2 = values_copy.pop("1234")
mock_automower_client.get_status.return_value = values_copy
freezer.tick(SCAN_INTERVAL) freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -327,8 +328,9 @@ async def test_coordinator_automatic_registry_cleanup(
== current_devices - 1 == current_devices - 1
) )
# Add mower 2 and check if it worked # Add mower 2 and check if it worked
values["1234"] = mower2 values_copy = deepcopy(values)
mock_automower_client.get_status.return_value = values values_copy["1234"] = mower2
mock_automower_client.get_status.return_value = values_copy
freezer.tick(SCAN_INTERVAL) freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -342,8 +344,9 @@ async def test_coordinator_automatic_registry_cleanup(
) )
# Remove mower 1 and check if it worked # Remove mower 1 and check if it worked
mower1 = values.pop(TEST_MOWER_ID) values_copy = deepcopy(values)
mock_automower_client.get_status.return_value = values mower1 = values_copy.pop(TEST_MOWER_ID)
mock_automower_client.get_status.return_value = values_copy
freezer.tick(SCAN_INTERVAL) freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -357,11 +360,9 @@ async def test_coordinator_automatic_registry_cleanup(
== current_devices - 1 == current_devices - 1
) )
# Add mower 1 and check if it worked # Add mower 1 and check if it worked
values[TEST_MOWER_ID] = mower1 values_copy = deepcopy(values)
mock_automower_client.get_status.return_value = values values_copy[TEST_MOWER_ID] = mower1
freezer.tick(SCAN_INTERVAL) mock_automower_client.get_status.return_value = values_copy
async_fire_time_changed(hass)
await hass.async_block_till_done()
freezer.tick(SCAN_INTERVAL) freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()