Compare commits

...
11 changed files with 42 additions and 14 deletions
+4 -1
View File
@@ -642,7 +642,10 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj":
MockObj(f"reinterpret_cast<{pointer_type} *>({storage_name})"),
)
)
placement_new = CallExpression(f"new({id_.id}) {actual_type}", *call_expr.args)
# No parens without args: `T()` value-initializes, zero-filling .bss that is already zero.
placement_new = RawExpression(f"new({id_.id}) {actual_type}")
if call_expr.args.args:
placement_new = CallExpression(placement_new, *call_expr.args)
CORE.add(ExpressionStatement(placement_new))
else:
decl = VariableDeclarationExpression(id_.type, "*", id_, static=True)
+1 -1
View File
@@ -14,7 +14,7 @@ def test_button_is_setup(generate_main):
# Then
assert "static wake_on_lan::WakeOnLanButton *const" in main_cpp
assert ") wake_on_lan::WakeOnLanButton();" in main_cpp
assert ") wake_on_lan::WakeOnLanButton;" in main_cpp
assert "App.register_button" in main_cpp
assert "App.register_component" in main_cpp
@@ -19,7 +19,7 @@ def test_deep_sleep_setup(generate_main):
"static deep_sleep::DeepSleepComponent *const deepsleep = reinterpret_cast<deep_sleep::DeepSleepComponent *>(deep_sleep__deepsleep__pstorage);"
in main_cpp
)
assert "new(deepsleep) deep_sleep::DeepSleepComponent();" in main_cpp
assert "new(deepsleep) deep_sleep::DeepSleepComponent;" in main_cpp
assert "App.register_component_(deepsleep, " in main_cpp
@@ -49,7 +49,7 @@ def test_deep_sleep_on_wake_trigger(generate_main):
"""
main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep3.yaml")
assert "deep_sleep::WakeTrigger();" in main_cpp
assert "deep_sleep::WakeTrigger;" in main_cpp
assert "Automation<deep_sleep::WakeupCause>" in main_cpp
@@ -22,7 +22,7 @@ def test_gpio_binary_sensor_basic_setup(
main_cpp = generate_main("tests/component_tests/gpio/test_gpio_binary_sensor.yaml")
assert "static gpio::GPIOBinarySensor *const" in main_cpp
assert ") gpio::GPIOBinarySensor();" in main_cpp
assert ") gpio::GPIOBinarySensor;" in main_cpp
assert "App.register_binary_sensor" in main_cpp
# set_use_interrupt(true) should NOT be generated (uses C++ default)
assert "bs_gpio->set_use_interrupt(true);" not in main_cpp
@@ -26,6 +26,6 @@ def test_ili9xxx_placement_new_uses_model_subclass(
# Pointer is declared as the base type for polymorphism.
assert "static ili9xxx::ILI9XXXDisplay *const tft_display" in main_cpp
# Placement new runs the subclass constructor — this is the actual regression fix.
assert "new(tft_display) ili9xxx::ILI9XXXST7789V()" in main_cpp
assert "new(tft_display) ili9xxx::ILI9XXXST7789V;" in main_cpp
# Base-class default constructor must NOT be used.
assert "new(tft_display) ili9xxx::ILI9XXXDisplay()" not in main_cpp
assert "new(tft_display) ili9xxx::ILI9XXXDisplay;" not in main_cpp
+2 -2
View File
@@ -358,7 +358,7 @@ def test_native_generation(
main_cpp = generate_main(component_fixture_path("native.yaml"))
assert (
"mipi_spi::MipiSpiBuffer<uint16_t, mipi_spi::PIXEL_MODE_16, true, mipi_spi::PIXEL_MODE_16, mipi_spi::BUS_TYPE_QUAD, 360, 360, 0, 1, 0, 0, 0, true, 1, 1>()"
"mipi_spi::MipiSpiBuffer<uint16_t, mipi_spi::PIXEL_MODE_16, true, mipi_spi::PIXEL_MODE_16, mipi_spi::BUS_TYPE_QUAD, 360, 360, 0, 1, 0, 0, 0, true, 1, 1>;"
in main_cpp
)
# A 10ms post-reset delay ({10, 255}) is prepended ahead of the model commands.
@@ -375,7 +375,7 @@ def test_lvgl_generation(
main_cpp = generate_main(component_fixture_path("lvgl.yaml"))
assert (
"mipi_spi::MipiSpi<uint16_t, mipi_spi::PIXEL_MODE_16, true, mipi_spi::PIXEL_MODE_16, mipi_spi::BUS_TYPE_SINGLE, 128, 160, 0, 0, 0, 0, 0, true>();"
"mipi_spi::MipiSpi<uint16_t, mipi_spi::PIXEL_MODE_16, true, mipi_spi::PIXEL_MODE_16, mipi_spi::BUS_TYPE_SINGLE, 128, 160, 0, 0, 0, 0, 0, true>;"
in main_cpp
)
# A 10ms post-reset delay ({10, 255}) is prepended ahead of the model commands.
@@ -314,7 +314,7 @@ class TestTemplateParameterPassing:
# FRACTION, ROUNDING)
# The instantiation should include padding values (0, 0 for default)
assert (
"mipi_spi::MipiSpiBuffer<uint16_t, mipi_spi::PIXEL_MODE_16, true, mipi_spi::PIXEL_MODE_16, mipi_spi::BUS_TYPE_QUAD, 360, 360, 0, 1, 0, 0, 0, true, 1, 1>()"
"mipi_spi::MipiSpiBuffer<uint16_t, mipi_spi::PIXEL_MODE_16, true, mipi_spi::PIXEL_MODE_16, mipi_spi::BUS_TYPE_QUAD, 360, 360, 0, 1, 0, 0, 0, true, 1, 1>;"
in main_cpp
), (
"Padding parameters (0, 0) should be in the MipiSpiBuffer template instantiation"
@@ -30,10 +30,10 @@ def test_template_text_saver_uses_placement_new_with_templated_subclass(
in main_cpp
)
# Placement new runs the templated subclass constructor.
assert "new(test_text_restore_value_saver) template_::TextSaver<10>()" in main_cpp
assert "new(test_text_restore_value_saver) template_::TextSaver<10>;" in main_cpp
# Base-class default ctor must NOT be used.
assert (
"new(test_text_restore_value_saver) template_::TemplateTextSaverBase()"
"new(test_text_restore_value_saver) template_::TemplateTextSaverBase;"
not in main_cpp
)
# No heap `new TextSaver<...>()` left over — the pre-fix pattern.
+1 -1
View File
@@ -14,7 +14,7 @@ def test_text_is_setup(generate_main):
# Then
assert "static template_::TemplateText *const" in main_cpp
assert ") template_::TemplateText();" in main_cpp
assert ") template_::TemplateText;" in main_cpp
assert "App.register_text" in main_cpp
@@ -14,7 +14,7 @@ def test_text_sensor_is_setup(generate_main):
# Then
assert "static template_::TemplateTextSensor *const" in main_cpp
assert ") template_::TemplateTextSensor();" in main_cpp
assert ") template_::TemplateTextSensor;" in main_cpp
assert "App.register_text_sensor" in main_cpp
+25
View File
@@ -4,6 +4,7 @@ import math
import pytest
from esphome import cpp_generator as cg, cpp_types as ct
from esphome.core import CORE
class TestExpressions:
@@ -790,3 +791,27 @@ async def test_templatable__lambda_with_std_string() -> None:
result = await cg.templatable(lambda_obj, [], ct.std_string)
assert isinstance(result, cg.LambdaExpression)
class TestPvariablePlacementNew:
def _placement_new(self) -> str:
return next(
str(stmt) for stmt in CORE.main_statements if str(stmt).startswith("new(")
)
def test_no_args_uses_default_initialization(self) -> None:
"""`new(p) T` rather than `new(p) T()`: value initialization would zero-fill
storage that is already zero, costing a loop of flash at every site."""
id_ = cg.ID("my_id", is_declaration=True, type=ct.esphome_ns.class_("Foo"))
cg.new_Pvariable(id_)
assert self._placement_new() == "new(my_id) Foo;"
def test_with_args_calls_constructor(self) -> None:
id_ = cg.ID("my_id", is_declaration=True, type=ct.esphome_ns.class_("Foo"))
cg.new_Pvariable(id_, 1, 2)
assert self._placement_new() == "new(my_id) Foo(1, 2);"
def test_template_args_without_ctor_args(self) -> None:
id_ = cg.ID("my_id", is_declaration=True, type=ct.esphome_ns.class_("Foo"))
cg.new_Pvariable(id_, cg.TemplateArguments(ct.uint8))
assert self._placement_new() == "new(my_id) Foo<uint8_t>;"