[core] Emit filter, action and condition lists one item at a time (#20116)

This commit is contained in:
J. Nick Koston
2026-10-11 15:12:59 -10:00
committed by GitHub
parent 5d64b65d72
commit ed6ab4565d
11 changed files with 151 additions and 26 deletions
@@ -15,7 +15,7 @@ def test_single_condition_groups_are_unwrapped(
assert "IfAction<false>(lambdacondition_id);" in main_cpp
assert "IfAction<false>(lambdacondition_id_2);" in main_cpp
group = re.search(
r"new\((\w+)\) AndCondition<2>\(\{lambdacondition_id_3, lambdacondition_id_4\}\);",
r"new\((\w+)\) AndCondition<2>\(lambdacondition_id_3, lambdacondition_id_4\);",
main_cpp,
)
assert group is not None
@@ -15,10 +15,10 @@ def test_timeout_throttle_pair_is_merged(
len(re.findall(r"new\(\w+\) sensor::TimeoutThrottleFilter\(1000\);", main_cpp))
== 1
)
merged = re.search(r"merged->set_filters\(\{(\w+)\}\);", main_cpp)
assert merged is not None
assert f"new({merged.group(1)}) sensor::TimeoutThrottleFilter(1000);" in main_cpp
merged = re.findall(r"merged->add_filter\((\w+)\);", main_cpp)
assert len(merged) == 1
assert f"new({merged[0]}) sensor::TimeoutThrottleFilter(1000);" in main_cpp
# Every other shape keeps its two filters
for sensor_id in ("other_periods", "other_order", "value_list", "configured_value"):
call = re.search(rf"{sensor_id}->set_filters\(\{{([^}}]*)\}}\);", main_cpp)
assert call is not None and len(call.group(1).split(",")) == 2, sensor_id
calls = re.findall(rf"{sensor_id}->add_filter\(\w+\);", main_cpp)
assert len(calls) == 2, sensor_id
@@ -0,0 +1,62 @@
esphome:
name: test
esp32:
board: esp32dev
logger:
globals:
- id: counter
type: int
initial_value: "0"
sensor:
- platform: template
id: temp
lambda: return 1.0;
filters:
- multiply: 2.0
- offset: 1.0
- or:
- throttle: 2s
- delta: 1.0
binary_sensor:
- platform: template
id: button
lambda: return true;
filters:
- invert:
- delayed_on: 10ms
text_sensor:
- platform: template
id: label
lambda: return {"x"};
filters:
- to_upper:
- append: "!"
interval:
- interval: 10s
then:
- if:
condition:
and:
- lambda: return id(counter) == 0;
- binary_sensor.is_on: button
then:
- lambda: id(counter) += 1;
- logger.log: even
else:
- logger.log: odd
- repeat:
count: 2
then:
- logger.log: again
- while:
condition:
lambda: return id(counter) < 3;
then:
- lambda: id(counter) += 1;
@@ -0,0 +1,30 @@
"""Filters and action lists are added one item at a time, so no pointer list lands in .rodata."""
from collections.abc import Callable
from pathlib import Path
import re
def test_filters_and_actions_are_added_per_item(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
main_cpp = generate_main(component_config_path("per_item_lists.yaml"))
for list_call in (
"add_filters(",
"set_filters(",
"add_actions(",
"add_then(",
"add_else(",
):
assert list_call not in main_cpp
assert len(re.findall(r"temp->add_filter\(", main_cpp)) == 3
assert len(re.findall(r"button->add_filter\(", main_cpp)) == 2
assert len(re.findall(r"label->add_filter\(", main_cpp)) == 2
assert main_cpp.count("->add_then_action(") == 4
assert main_cpp.count("->add_else_action(") == 1
assert main_cpp.count("->finish_then();") == 3
assert main_cpp.count("->finish_else();") == 1
# and/or conditions and the sensor `or` filter take their items as separate arguments
assert not re.search(r"(Condition|OrFilter)<[^>]*>\(\{", main_cpp)