mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 10:12:52 +00:00
[core] Resume if and while from the end of their branch instead of an embedded continuation action (#20283)
This commit is contained in:
@@ -72,6 +72,26 @@ script:
|
||||
format: "parallel-after-if-%d"
|
||||
args: ['run']
|
||||
|
||||
- id: stop_in_later_while_check
|
||||
then:
|
||||
- lambda: 'id(counter) = 0;'
|
||||
- while:
|
||||
condition:
|
||||
lambda: |-
|
||||
if (++id(counter) == 2)
|
||||
id(stop_in_later_while_check).stop();
|
||||
return true;
|
||||
then:
|
||||
- lambda: 'ESP_LOGI("test", "later-check-body-%d", id(counter));'
|
||||
- logger.log: "after-later-check-should-not-run"
|
||||
- id: empty_while
|
||||
then:
|
||||
- while:
|
||||
condition:
|
||||
lambda: 'return true;'
|
||||
then: []
|
||||
- logger.log: "after-empty-while"
|
||||
|
||||
- id: nested_if_last_in_while
|
||||
then:
|
||||
- lambda: 'id(counter) = 0;'
|
||||
@@ -206,6 +226,14 @@ api:
|
||||
ESP_LOGI("test", "stop-running=%d", id(stop_in_branch).is_running() ? 1 : 0);
|
||||
- delay: 600ms
|
||||
- logger.log: "stop-check-done"
|
||||
- action: run_stop_in_later_while_check
|
||||
then:
|
||||
- script.execute: stop_in_later_while_check
|
||||
- delay: 200ms
|
||||
- logger.log: "later-check-done"
|
||||
- action: run_empty_while
|
||||
then:
|
||||
- script.execute: empty_while
|
||||
- action: run_nested_if_last_in_while
|
||||
then:
|
||||
- script.execute: nested_if_last_in_while
|
||||
|
||||
@@ -5,7 +5,7 @@ host:
|
||||
|
||||
api:
|
||||
actions:
|
||||
# Test 1: IfAction with ContinuationAction (then/else branches)
|
||||
# Test 1: IfAction with then/else branches
|
||||
- action: test_if_action
|
||||
variables:
|
||||
condition: bool
|
||||
@@ -27,7 +27,7 @@ api:
|
||||
args: ['value']
|
||||
- logger.log: "if completed"
|
||||
|
||||
# Test 2: Nested IfAction (multiple ContinuationAction instances)
|
||||
# Test 2: Nested IfAction
|
||||
- action: test_nested_if
|
||||
variables:
|
||||
outer: bool
|
||||
@@ -51,7 +51,7 @@ api:
|
||||
- logger.log: "nested-outer-false"
|
||||
- logger.log: "nested if completed"
|
||||
|
||||
# Test 3: WhileAction with WhileLoopContinuation
|
||||
# Test 3: WhileAction
|
||||
- action: test_while_action
|
||||
variables:
|
||||
max_count: int
|
||||
@@ -74,7 +74,7 @@ api:
|
||||
value: !lambda 'return id(continuation_test_counter) + 1;'
|
||||
- logger.log: "while completed"
|
||||
|
||||
# Test 4: RepeatAction with RepeatLoopContinuation
|
||||
# Test 4: RepeatAction
|
||||
- action: test_repeat_action
|
||||
variables:
|
||||
count: int
|
||||
@@ -90,7 +90,7 @@ api:
|
||||
args: ['iteration']
|
||||
- logger.log: "repeat completed"
|
||||
|
||||
# Test 5: Combined continuations (if + while + repeat)
|
||||
# Test 5: Combined if + while + repeat
|
||||
- action: test_combined
|
||||
variables:
|
||||
do_loop: bool
|
||||
@@ -123,7 +123,7 @@ api:
|
||||
- logger.log: "combined-skipped"
|
||||
- logger.log: "combined completed"
|
||||
|
||||
# Test 6: Rapid triggers to verify memory efficiency
|
||||
# Test 6: Rapid triggers
|
||||
- action: test_rapid_if
|
||||
then:
|
||||
- logger.log: "=== Rapid if test start ==="
|
||||
@@ -153,7 +153,7 @@ globals:
|
||||
restore_value: false
|
||||
initial_value: '0'
|
||||
|
||||
# Sensor to test rapid automation triggers with if/else (ContinuationAction)
|
||||
# Sensor to test rapid automation triggers with if/else
|
||||
sensor:
|
||||
- platform: template
|
||||
id: rapid_sensor
|
||||
|
||||
@@ -73,6 +73,20 @@ async def test_branch_resume_async(
|
||||
assert count("stopped-branch-should-not-run") == 0
|
||||
assert count("stopped-after-if-should-not-run") == 0
|
||||
|
||||
# A while condition that stops the script on a later pass must not start the body again
|
||||
done = wait_for("later-check-done")
|
||||
await client.execute_service(service["run_stop_in_later_while_check"], {})
|
||||
await asyncio.wait_for(done, timeout=2.0)
|
||||
assert count("later-check-body-1") == 1
|
||||
assert count("later-check-body-2") == 0
|
||||
assert count("after-later-check-should-not-run") == 0
|
||||
|
||||
# An empty loop body cannot resume the loop, so the while finishes at once
|
||||
done = wait_for("after-empty-while")
|
||||
await client.execute_service(service["run_empty_while"], {})
|
||||
await asyncio.wait_for(done, timeout=2.0)
|
||||
assert count("after-empty-while") == 1
|
||||
|
||||
done = wait_for("after-nested-while")
|
||||
await client.execute_service(service["run_nested_if_last_in_while"], {})
|
||||
await asyncio.wait_for(done, timeout=2.0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Test continuation actions (ContinuationAction, WhileLoopContinuation, RepeatLoopContinuation)."""
|
||||
"""Test that if, while and repeat resume the actions after them once their branch finishes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -17,10 +17,7 @@ async def test_continuation_actions(
|
||||
api_client_connected: APIClientConnectedFactory,
|
||||
) -> None:
|
||||
"""
|
||||
Test that continuation actions work correctly for if/while/repeat.
|
||||
|
||||
These continuation classes replace LambdaAction with simple parent pointers,
|
||||
saving 32-36 bytes per instance and eliminating std::function overhead.
|
||||
Test that if/while/repeat resume the actions after them once their branch finishes.
|
||||
"""
|
||||
loop = asyncio.get_running_loop()
|
||||
|
||||
@@ -218,7 +215,7 @@ async def test_continuation_actions(
|
||||
)
|
||||
assert test_results["combined_complete"], "Combined did not complete"
|
||||
|
||||
# Test 6: Rapid triggers (tests memory efficiency of ContinuationAction)
|
||||
# Test 6: Rapid triggers through an if/else
|
||||
test_service = next((s for s in services if s.name == "test_rapid_if"), None)
|
||||
assert test_service is not None, "test_rapid_if service not found"
|
||||
await client.execute_service(test_service, {})
|
||||
|
||||
Reference in New Issue
Block a user