Merge remote-tracking branch 'upstream/dev' into integration

This commit is contained in:
J. Nick Koston
2026-04-07 08:53:34 -10:00
3 changed files with 67 additions and 1 deletions
+1
View File
@@ -1,5 +1,6 @@
sensor:
- platform: hdc2080
i2c_id: i2c_bus
temperature:
name: Temperature
humidity:
@@ -25,6 +25,13 @@ globals:
- id: callback_timeout_fired
type: bool
initial_value: "false"
# Track set_interval replacing itself from within callback
- id: replace_count
type: int
initial_value: "0"
- id: replaced_count
type: int
initial_value: "0"
interval:
# Fast interval: 50ms
@@ -81,3 +88,26 @@ interval:
// Stop this interval after scheduling the timeout
id(timeout_creator)->stop_poller();
}
# Interval that calls set_interval with the same name from within its callback,
# replacing itself. Tests that the old item (marked removed) is not rescheduled
# via push_heap, and the new item goes through to_add_ correctly.
- interval: 60ms
id: replace_test
then:
- lambda: |-
id(replace_count) += 1;
if (id(replace_count) == 1) {
ESP_LOGI("test", "REPLACE_ORIGINAL_FIRE");
// Replace the polling interval with a named interval on the same component
id(replace_test)->set_interval("replaced_interval", 80, []() {
id(replaced_count) += 1;
ESP_LOGI("test", "REPLACED_FIRE count=%d", id(replaced_count));
if (id(replaced_count) >= 3) {
id(replace_test)->cancel_interval("replaced_interval");
ESP_LOGI("test", "REPLACED_STOPPED");
}
});
// Stop the original polling interval
id(replace_test)->stop_poller();
}
@@ -9,6 +9,7 @@ Key scenarios tested:
2. Heap ordering is preserved — faster intervals fire proportionally more often
3. An interval that cancels itself mid-callback is not rescheduled
4. A timeout scheduled from within an interval callback (to_add_ path) still works
5. An interval that replaces itself via set_interval from within its callback
"""
from __future__ import annotations
@@ -36,11 +37,14 @@ async def test_scheduler_interval_reschedule(
slow_3_future: asyncio.Future[tuple[int, int]] = loop.create_future()
self_cancel_stopped_future: asyncio.Future[None] = loop.create_future()
callback_timeout_future: asyncio.Future[None] = loop.create_future()
replace_original_future: asyncio.Future[None] = loop.create_future()
replaced_stopped_future: asyncio.Future[None] = loop.create_future()
self_cancel_fire_count = 0
replaced_fire_count = 0
def on_log_line(line: str) -> None:
nonlocal self_cancel_fire_count
nonlocal self_cancel_fire_count, replaced_fire_count
if "FAST_10_REACHED" in line and not fast_10_future.done():
fast_10_future.set_result(None)
@@ -63,6 +67,16 @@ async def test_scheduler_interval_reschedule(
if "CALLBACK_TIMEOUT_FIRED" in line and not callback_timeout_future.done():
callback_timeout_future.set_result(None)
if "REPLACE_ORIGINAL_FIRE" in line and not replace_original_future.done():
replace_original_future.set_result(None)
match = re.search(r"REPLACED_FIRE count=(\d+)", line)
if match:
replaced_fire_count = int(match.group(1))
if "REPLACED_STOPPED" in line and not replaced_stopped_future.done():
replaced_stopped_future.set_result(None)
async with (
run_compiled(yaml_config, line_callback=on_log_line),
api_client_connected() as client,
@@ -128,3 +142,24 @@ async def test_scheduler_interval_reschedule(
await asyncio.wait_for(callback_timeout_future, timeout=5.0)
except TimeoutError:
pytest.fail("Timeout scheduled from interval callback did not fire")
# 6. Interval that replaces itself via set_interval from within callback
# The original fires once, sets up a new named interval, then stops itself.
# The replacement interval should fire 3 times then cancel itself.
try:
await asyncio.wait_for(replace_original_future, timeout=5.0)
except TimeoutError:
pytest.fail("Replace-test original interval did not fire")
try:
await asyncio.wait_for(replaced_stopped_future, timeout=5.0)
except TimeoutError:
pytest.fail(
f"Replaced interval did not stop. Fired {replaced_fire_count} times"
)
# Wait to ensure replacement doesn't fire again after cancellation
await asyncio.sleep(0.3)
assert replaced_fire_count == 3, (
f"Replaced interval fired {replaced_fire_count} times, expected exactly 3"
)