mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-15 00:04:05 +02:00
Fix CI: rewrite tests to directly test _off_to_on_state_event_is_from_turn_on
The original test failed because HA light groups are expanded - the group itself is removed from self.lights and replaced with member lights. This means on_to_off_event is never stored for the group. Rewrote tests to directly test _off_to_on_state_event_is_from_turn_on(): - test_off_to_on_state_event_is_from_turn_on_detects_group_members - test_off_to_on_state_event_is_from_turn_on_respects_timing - test_just_turned_off_polling_artifact_still_detected (negative case)
This commit is contained in:
parent
b7ad2cdd35
commit
bf0478fe0c
1 changed files with 98 additions and 35 deletions
|
|
@ -2916,8 +2916,8 @@ async def test_adapt_only_on_bare_turn_on_respects_pause_changed_mode(hass, inte
|
|||
)
|
||||
|
||||
|
||||
async def test_just_turned_off_context_reuse_with_light_groups(hass):
|
||||
"""Test that just_turned_off handles light group context reuse correctly.
|
||||
async def test_off_to_on_state_event_is_from_turn_on_detects_group_members(hass):
|
||||
"""Test that _off_to_on_state_event_is_from_turn_on detects member turn_on events.
|
||||
|
||||
Regression test for https://github.com/basnijholt/adaptive-lighting/issues/1378
|
||||
|
||||
|
|
@ -2927,6 +2927,9 @@ async def test_just_turned_off_context_reuse_with_light_groups(hass):
|
|||
|
||||
The fix uses causality-based detection: if any member light has a turn_on_event
|
||||
that happened after the group's on→off event, the group's turn-on is valid.
|
||||
|
||||
This test directly tests _off_to_on_state_event_is_from_turn_on() with mocked
|
||||
events to verify the causality detection works for light groups.
|
||||
"""
|
||||
from homeassistant.core import Event
|
||||
|
||||
|
|
@ -2954,30 +2957,7 @@ async def test_just_turned_off_context_reuse_with_light_groups(hass):
|
|||
# Verify the group is expanded and members are tracked
|
||||
assert member_light in switch.lights, "Group members should be in switch.lights"
|
||||
|
||||
# 1. Turn on the group first (so it can be turned off)
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: light_group},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(light_group).state == STATE_ON
|
||||
|
||||
# 2. Turn off the group (stores on_to_off_event with context A)
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: light_group},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(light_group).state == STATE_OFF
|
||||
|
||||
on_to_off_event = manager.on_to_off_event.get(light_group)
|
||||
assert on_to_off_event is not None, "on_to_off_event should be stored for group"
|
||||
|
||||
# 3. Simulate a member light being turned on by a motion sensor
|
||||
# 1. Simulate a member light being turned on by a motion sensor
|
||||
# This stores turn_on_event for the member
|
||||
member_turn_on_context = Context(id="motion_sensor_turn_on")
|
||||
await hass.services.async_call(
|
||||
|
|
@ -2994,8 +2974,9 @@ async def test_just_turned_off_context_reuse_with_light_groups(hass):
|
|||
manager.turn_on_event.get(member_light) is not None
|
||||
), "turn_on_event should be stored for member light"
|
||||
|
||||
# 4. Create an off→on event for the GROUP with the SAME context as turn_off
|
||||
# This simulates what HA does when children turning on causes parent to turn on
|
||||
# 2. Create an off→on event for the GROUP
|
||||
# This simulates the group turning on because a member was turned on
|
||||
group_off_to_on_context = Context(id="group_state_change")
|
||||
off_to_on_event = Event(
|
||||
event_type=EVENT_STATE_CHANGED,
|
||||
data={
|
||||
|
|
@ -3003,23 +2984,105 @@ async def test_just_turned_off_context_reuse_with_light_groups(hass):
|
|||
"old_state": State(light_group, STATE_OFF),
|
||||
"new_state": State(light_group, STATE_ON),
|
||||
},
|
||||
context=on_to_off_event.context, # SAME context as turn_off (HA reuses it)
|
||||
context=group_off_to_on_context,
|
||||
)
|
||||
manager.off_to_on_event[light_group] = off_to_on_event
|
||||
|
||||
# 5. Call just_turned_off - it should return False (allow adaptation)
|
||||
# because a member light has a turn_on_event that explains the group turning on
|
||||
result = await manager.just_turned_off(light_group)
|
||||
# 3. Call _off_to_on_state_event_is_from_turn_on for the GROUP
|
||||
# It should return True because member light was turned on
|
||||
result = manager._off_to_on_state_event_is_from_turn_on(
|
||||
light_group,
|
||||
off_to_on_event,
|
||||
)
|
||||
|
||||
# Before the fix: this would return True (cancel adaptation) due to matching context IDs
|
||||
# After the fix: should return False because member light was turned on
|
||||
assert result is False, (
|
||||
"just_turned_off should return False when a group member was turned on, "
|
||||
"even if the group's off→on context matches the on→off context. "
|
||||
# Before the fix: this would return False (no turn_on for group itself)
|
||||
# After the fix: should return True because member light was turned on
|
||||
assert result is True, (
|
||||
"_off_to_on_state_event_is_from_turn_on should return True when a group member "
|
||||
"was turned on, even if no turn_on event exists for the group itself. "
|
||||
"The member's turn_on explains why the group turned on."
|
||||
)
|
||||
|
||||
|
||||
async def test_off_to_on_state_event_is_from_turn_on_respects_timing(hass):
|
||||
"""Test that member turn_on must happen after group's on→off to count.
|
||||
|
||||
This ensures we don't incorrectly attribute a group's turn-on to an old
|
||||
member turn_on event that happened before the group was turned off.
|
||||
"""
|
||||
from homeassistant.core import Event
|
||||
|
||||
# Setup lights with a group
|
||||
lights = await setup_lights(hass, with_group=True)
|
||||
entity_ids = [light.entity_id for light in lights[:3]]
|
||||
entity_ids.append("light.light_group")
|
||||
|
||||
_, switch = await setup_switch(
|
||||
hass,
|
||||
{
|
||||
CONF_LIGHTS: entity_ids,
|
||||
CONF_SUNRISE_TIME: datetime.time(SUNRISE.hour),
|
||||
CONF_SUNSET_TIME: datetime.time(SUNSET.hour),
|
||||
CONF_INITIAL_TRANSITION: 0,
|
||||
CONF_TRANSITION: 0,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
manager = switch.manager
|
||||
|
||||
light_group = "light.light_group"
|
||||
member_light = "light.light_4"
|
||||
|
||||
# 1. Turn on member first (stores turn_on_event with early timestamp)
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: member_light},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
member_turn_on_event = manager.turn_on_event.get(member_light)
|
||||
assert member_turn_on_event is not None
|
||||
|
||||
# 2. Simulate the group being turned off AFTER the member turn_on
|
||||
# Create a fake on_to_off_event with a timestamp after the member turn_on
|
||||
await asyncio.sleep(0.01) # Small delay to ensure different timestamps
|
||||
group_turn_off_context = Context(id="group_turn_off")
|
||||
group_on_to_off_event = Event(
|
||||
event_type=EVENT_STATE_CHANGED,
|
||||
data={
|
||||
ATTR_ENTITY_ID: light_group,
|
||||
"old_state": State(light_group, STATE_ON),
|
||||
"new_state": State(light_group, STATE_OFF),
|
||||
},
|
||||
context=group_turn_off_context,
|
||||
)
|
||||
manager.on_to_off_event[light_group] = group_on_to_off_event
|
||||
|
||||
# 3. Create an off→on event for the group
|
||||
off_to_on_event = Event(
|
||||
event_type=EVENT_STATE_CHANGED,
|
||||
data={
|
||||
ATTR_ENTITY_ID: light_group,
|
||||
"old_state": State(light_group, STATE_OFF),
|
||||
"new_state": State(light_group, STATE_ON),
|
||||
},
|
||||
context=Context(id="group_turn_on"),
|
||||
)
|
||||
|
||||
# 4. The member's turn_on happened BEFORE the group's on→off,
|
||||
# so it should NOT explain the group's turn-on
|
||||
result = manager._off_to_on_state_event_is_from_turn_on(
|
||||
light_group,
|
||||
off_to_on_event,
|
||||
)
|
||||
|
||||
assert result is False, (
|
||||
"_off_to_on_state_event_is_from_turn_on should return False when the member's "
|
||||
"turn_on happened BEFORE the group's on→off event. The old turn_on event "
|
||||
"cannot explain a new turn-on after the group was turned off."
|
||||
)
|
||||
|
||||
|
||||
async def test_just_turned_off_polling_artifact_still_detected(hass):
|
||||
"""Test that polling artifacts are still correctly detected as false positives.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue