[core] cpp tests: Allow customizing code generation during tests (#14681)

Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Javier Peletier
2026-03-18 00:16:01 +00:00
committed by GitHub
co-authored by J. Nick Koston J. Nick Koston
parent 342020e1d3
commit 0c5f055d45
22 changed files with 667 additions and 96 deletions
+1 -1
View File
@@ -1846,7 +1846,7 @@ def test_should_run_benchmarks_benchmark_infra_change() -> None:
"""Test benchmarks trigger on benchmark infrastructure changes."""
for infra_file in [
"script/cpp_benchmark.py",
"script/test_helpers.py",
"script/build_helpers.py",
"script/setup_codspeed_lib.py",
]:
with patch.object(determine_jobs, "changed_files", return_value=[infra_file]):
-22
View File
@@ -1073,28 +1073,6 @@ def test_get_all_dependencies_platform_component_with_dependencies() -> None:
assert result == {"sensor.bthome", "sensor"}
def test_get_all_dependencies_cpp_testing_flag() -> None:
"""cpp_testing=True propagates to CORE.cpp_testing during resolution."""
from esphome.core import CORE
with (
patch("esphome.loader.get_component") as mock_get_component,
patch("esphome.loader.get_platform"),
):
observed: list[bool] = []
def capturing_get_component(name: str):
observed.append(CORE.cpp_testing)
mock_get_component.side_effect = capturing_get_component
helpers.get_all_dependencies({"some_comp"}, cpp_testing=True)
assert observed and all(observed), (
"CORE.cpp_testing should be True during resolution"
)
def test_get_components_from_integration_fixtures() -> None:
"""Test extraction of components from fixture YAML files."""
yaml_content = {
+260
View File
@@ -0,0 +1,260 @@
"""Unit tests for script/build_helpers.py manifest override and build helpers."""
import os
from pathlib import Path
import sys
import textwrap
from unittest.mock import MagicMock, patch
import pytest
# Add the script directory to Python path so we can import build_helpers
sys.path.insert(
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "script"))
)
import build_helpers # noqa: E402
from esphome.loader import ComponentManifest # noqa: E402
from tests.testing_helpers import ComponentManifestOverride # noqa: E402
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _make_component_manifest(*, to_code=None, dependencies=None) -> ComponentManifest:
mod = MagicMock()
mod.to_code = to_code
mod.DEPENDENCIES = dependencies or []
return ComponentManifest(mod)
# ---------------------------------------------------------------------------
# filter_components_with_files
# ---------------------------------------------------------------------------
def test_filter_keeps_components_with_cpp_files(tmp_path: Path) -> None:
comp_dir = tmp_path / "mycomp"
comp_dir.mkdir()
(comp_dir / "mycomp_test.cpp").write_text("")
result = build_helpers.filter_components_with_files(["mycomp"], tmp_path)
assert result == ["mycomp"]
def test_filter_keeps_components_with_h_files(tmp_path: Path) -> None:
comp_dir = tmp_path / "mycomp"
comp_dir.mkdir()
(comp_dir / "helpers.h").write_text("")
result = build_helpers.filter_components_with_files(["mycomp"], tmp_path)
assert result == ["mycomp"]
def test_filter_drops_components_without_test_dir(tmp_path: Path) -> None:
result = build_helpers.filter_components_with_files(["nodir"], tmp_path)
assert result == []
def test_filter_drops_components_with_no_cpp_or_h(tmp_path: Path) -> None:
comp_dir = tmp_path / "mycomp"
comp_dir.mkdir()
(comp_dir / "README.md").write_text("")
result = build_helpers.filter_components_with_files(["mycomp"], tmp_path)
assert result == []
# ---------------------------------------------------------------------------
# get_platform_components
# ---------------------------------------------------------------------------
def test_get_platform_components_discovers_subdirectory(tmp_path: Path) -> None:
(tmp_path / "bthome" / "sensor").mkdir(parents=True)
sensor_mod = MagicMock()
sensor_mod.IS_PLATFORM_COMPONENT = True
with patch(
"build_helpers.get_component", return_value=ComponentManifest(sensor_mod)
):
result = build_helpers.get_platform_components(["bthome"], tmp_path)
assert result == ["sensor.bthome"]
def test_get_platform_components_skips_pycache(tmp_path: Path) -> None:
(tmp_path / "bthome" / "__pycache__").mkdir(parents=True)
result = build_helpers.get_platform_components(["bthome"], tmp_path)
assert result == []
def test_get_platform_components_raises_for_invalid_domain(tmp_path: Path) -> None:
(tmp_path / "bthome" / "notadomain").mkdir(parents=True)
with (
patch("build_helpers.get_component", return_value=None),
pytest.raises(ValueError, match="notadomain"),
):
build_helpers.get_platform_components(["bthome"], tmp_path)
# ---------------------------------------------------------------------------
# load_test_manifest_overrides
# ---------------------------------------------------------------------------
def test_load_suppresses_to_code(tmp_path: Path) -> None:
"""to_code is always set to None before the override is called."""
async def real_to_code(config):
pass
inner = _make_component_manifest(to_code=real_to_code)
with (
patch("build_helpers.get_component", return_value=inner),
patch("build_helpers.set_testing_manifest") as mock_set,
):
build_helpers.load_test_manifest_overrides(["mycomp"], tmp_path)
installed: ComponentManifestOverride = mock_set.call_args[0][1]
assert installed.to_code is None
def test_load_calls_override_fn(tmp_path: Path) -> None:
"""override_manifest() in test_init is called with the ComponentManifestOverride."""
comp_dir = tmp_path / "mycomp"
comp_dir.mkdir()
init_py = comp_dir / "__init__.py"
init_py.write_text(
textwrap.dedent("""\
def override_manifest(manifest):
manifest.dependencies = ["injected"]
""")
)
inner = _make_component_manifest()
override = ComponentManifestOverride(inner)
override.to_code = None
with (
patch("build_helpers.get_component", return_value=inner),
patch("build_helpers.set_testing_manifest") as mock_set,
):
build_helpers.load_test_manifest_overrides(["mycomp"], tmp_path)
installed: ComponentManifestOverride = mock_set.call_args[0][1]
assert installed.dependencies == ["injected"]
def test_load_enable_codegen_in_override(tmp_path: Path) -> None:
"""An override_manifest that calls enable_codegen() restores to_code."""
async def real_to_code(config):
pass
comp_dir = tmp_path / "mycomp"
comp_dir.mkdir()
init_py = comp_dir / "__init__.py"
init_py.write_text(
textwrap.dedent("""\
def override_manifest(manifest):
manifest.enable_codegen()
""")
)
inner = _make_component_manifest(to_code=real_to_code)
with (
patch("build_helpers.get_component", return_value=inner),
patch("build_helpers.set_testing_manifest") as mock_set,
):
build_helpers.load_test_manifest_overrides(["mycomp"], tmp_path)
installed: ComponentManifestOverride = mock_set.call_args[0][1]
assert installed.to_code is real_to_code
def test_load_no_override_file(tmp_path: Path) -> None:
"""No override file: manifest is wrapped and to_code suppressed, nothing else."""
inner = _make_component_manifest()
with (
patch("build_helpers.get_component", return_value=inner),
patch("build_helpers.set_testing_manifest") as mock_set,
):
build_helpers.load_test_manifest_overrides(["mycomp"], tmp_path)
mock_set.assert_called_once()
key, installed = mock_set.call_args[0]
assert key == "mycomp"
assert isinstance(installed, ComponentManifestOverride)
def test_load_skips_already_wrapped(tmp_path: Path) -> None:
"""Components already wrapped as ComponentManifestOverride are not double-wrapped."""
inner = _make_component_manifest()
already_wrapped = ComponentManifestOverride(inner)
with (
patch("build_helpers.get_component", return_value=already_wrapped),
patch("build_helpers.set_testing_manifest") as mock_set,
):
build_helpers.load_test_manifest_overrides(["mycomp"], tmp_path)
mock_set.assert_not_called()
def test_load_skips_platform_component_already_wrapped(tmp_path: Path) -> None:
inner = _make_component_manifest()
already_wrapped = ComponentManifestOverride(inner)
with (
patch("build_helpers.get_platform", return_value=already_wrapped),
patch("build_helpers.set_testing_manifest") as mock_set,
):
build_helpers.load_test_manifest_overrides(["sensor.bthome"], tmp_path)
mock_set.assert_not_called()
def test_load_wraps_top_level_component(tmp_path: Path) -> None:
inner = _make_component_manifest()
with (
patch("build_helpers.get_component", return_value=inner),
patch("build_helpers.set_testing_manifest") as mock_set,
):
build_helpers.load_test_manifest_overrides(["mycomp"], tmp_path)
mock_set.assert_called_once()
key, installed = mock_set.call_args[0]
assert key == "mycomp"
assert isinstance(installed, ComponentManifestOverride)
assert installed.to_code is None
def test_load_wraps_platform_component(tmp_path: Path) -> None:
inner = _make_component_manifest()
with (
patch("build_helpers.get_platform", return_value=inner),
patch("build_helpers.set_testing_manifest") as mock_set,
):
build_helpers.load_test_manifest_overrides(["sensor.bthome"], tmp_path)
mock_set.assert_called_once()
key, installed = mock_set.call_args[0]
assert key == "bthome.sensor"
assert isinstance(installed, ComponentManifestOverride)
assert installed.to_code is None