mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 12:33:10 +00:00
[mipi_spi] Cache code generation across display tests
The native.yaml and lvgl.yaml fixtures are each compiled twice; once by the codegen assertions in test_init.py and once by the metadata assertions in test_display_metadata.py. Add a session scoped cache so each fixture is generated once, with the resulting main.cpp text and a snapshot of the display metadata reused across tests. CI hits the duplicated tests in well under 10ms instead of paying the full generation cost a second time.
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
"""Tests for mpip_spi configuration validation."""
|
"""Tests for mpip_spi configuration validation."""
|
||||||
|
|
||||||
from collections.abc import Callable, Generator
|
from collections.abc import Callable, Generator
|
||||||
|
from pathlib import Path
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from esphome import config_validation as cv
|
from esphome import config_validation as cv
|
||||||
|
from esphome.components.display import DisplayMetaData, get_all_display_metadata
|
||||||
from esphome.components.esp32 import KEY_ESP32, KEY_VARIANT, VARIANTS
|
from esphome.components.esp32 import KEY_ESP32, KEY_VARIANT, VARIANTS
|
||||||
from esphome.components.esp32.gpio import validate_gpio_pin
|
from esphome.components.esp32.gpio import validate_gpio_pin
|
||||||
from esphome.const import CONF_INPUT, CONF_OUTPUT
|
from esphome.const import CONF_INPUT, CONF_OUTPUT
|
||||||
@@ -23,6 +25,40 @@ def mock_spi_final_validate():
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def _generation_cache() -> dict[Path, tuple[str, dict[str, DisplayMetaData]]]:
|
||||||
|
"""Session-wide cache of generation output keyed by fixture yaml path.
|
||||||
|
|
||||||
|
Stores ``(main_cpp_text, display_metadata_snapshot)`` so multiple tests
|
||||||
|
that exercise the same fixture only pay for code generation once.
|
||||||
|
"""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def cached_generate_main(
|
||||||
|
generate_main: Callable[[str | Path], str],
|
||||||
|
_generation_cache: dict[Path, tuple[str, dict[str, DisplayMetaData]]],
|
||||||
|
) -> Callable[[Path], tuple[str, dict[str, DisplayMetaData]]]:
|
||||||
|
"""Like ``generate_main`` but caches results across the test session.
|
||||||
|
|
||||||
|
Returns ``(main_cpp_text, display_metadata_snapshot)``; the snapshot is
|
||||||
|
captured at generation time so callers see the same data after CORE has
|
||||||
|
been reset for the next test.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _get(path: Path) -> tuple[str, dict[str, DisplayMetaData]]:
|
||||||
|
path = Path(path)
|
||||||
|
cached = _generation_cache.get(path)
|
||||||
|
if cached is None:
|
||||||
|
main_cpp = generate_main(path)
|
||||||
|
cached = (main_cpp, dict(get_all_display_metadata()))
|
||||||
|
_generation_cache[path] = cached
|
||||||
|
return cached
|
||||||
|
|
||||||
|
return _get
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def choose_variant_with_pins() -> Generator[Callable[[list], None]]:
|
def choose_variant_with_pins() -> Generator[Callable[[list], None]]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -173,12 +173,11 @@ def test_metadata_multiple_displays_independent(
|
|||||||
|
|
||||||
|
|
||||||
def test_metadata_via_code_generation_native(
|
def test_metadata_via_code_generation_native(
|
||||||
generate_main: Callable[[str | Path], str],
|
cached_generate_main: Callable[[Path], tuple[str, dict[str, DisplayMetaData]]],
|
||||||
component_fixture_path: Callable[[str], Path],
|
component_fixture_path: Callable[[str], Path],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Full code generation for native.yaml should produce correct metadata."""
|
"""Full code generation for native.yaml should produce correct metadata."""
|
||||||
generate_main(component_fixture_path("native.yaml"))
|
_, all_meta = cached_generate_main(component_fixture_path("native.yaml"))
|
||||||
all_meta = get_all_display_metadata()
|
|
||||||
# native.yaml: model JC3636W518 -> 360x360, no writer, full hardware rotation
|
# native.yaml: model JC3636W518 -> 360x360, no writer, full hardware rotation
|
||||||
assert len(all_meta) == 1
|
assert len(all_meta) == 1
|
||||||
meta = next(iter(all_meta.values()))
|
meta = next(iter(all_meta.values()))
|
||||||
@@ -188,12 +187,11 @@ def test_metadata_via_code_generation_native(
|
|||||||
|
|
||||||
|
|
||||||
def test_metadata_via_code_generation_lvgl(
|
def test_metadata_via_code_generation_lvgl(
|
||||||
generate_main: Callable[[str | Path], str],
|
cached_generate_main: Callable[[Path], tuple[str, dict[str, DisplayMetaData]]],
|
||||||
component_fixture_path: Callable[[str], Path],
|
component_fixture_path: Callable[[str], Path],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Full code generation for lvgl.yaml should produce correct metadata."""
|
"""Full code generation for lvgl.yaml should produce correct metadata."""
|
||||||
generate_main(component_fixture_path("lvgl.yaml"))
|
_, all_meta = cached_generate_main(component_fixture_path("lvgl.yaml"))
|
||||||
all_meta = get_all_display_metadata()
|
|
||||||
# lvgl.yaml: model ST7735 -> 128x160, no writer (lvgl draws directly), full hw rotation
|
# lvgl.yaml: model ST7735 -> 128x160, no writer (lvgl draws directly), full hw rotation
|
||||||
assert len(all_meta) == 1
|
assert len(all_meta) == 1
|
||||||
meta = next(iter(all_meta.values()))
|
meta = next(iter(all_meta.values()))
|
||||||
|
|||||||
@@ -307,12 +307,12 @@ def test_all_predefined_models(
|
|||||||
|
|
||||||
|
|
||||||
def test_native_generation(
|
def test_native_generation(
|
||||||
generate_main: Callable[[str | Path], str],
|
cached_generate_main: Callable[[Path], tuple[str, dict[str, Any]]],
|
||||||
component_fixture_path: Callable[[str], Path],
|
component_fixture_path: Callable[[str], Path],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test code generation for display."""
|
"""Test code generation for display."""
|
||||||
|
|
||||||
main_cpp = generate_main(component_fixture_path("native.yaml"))
|
main_cpp, _ = cached_generate_main(component_fixture_path("native.yaml"))
|
||||||
assert (
|
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, 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, true, 1, 1>()"
|
||||||
in main_cpp
|
in main_cpp
|
||||||
@@ -323,12 +323,12 @@ def test_native_generation(
|
|||||||
|
|
||||||
|
|
||||||
def test_lvgl_generation(
|
def test_lvgl_generation(
|
||||||
generate_main: Callable[[str | Path], str],
|
cached_generate_main: Callable[[Path], tuple[str, dict[str, Any]]],
|
||||||
component_fixture_path: Callable[[str], Path],
|
component_fixture_path: Callable[[str], Path],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test LVGL generation configuration."""
|
"""Test LVGL generation configuration."""
|
||||||
|
|
||||||
main_cpp = generate_main(component_fixture_path("lvgl.yaml"))
|
main_cpp, _ = cached_generate_main(component_fixture_path("lvgl.yaml"))
|
||||||
assert (
|
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, 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, true>();"
|
||||||
in main_cpp
|
in main_cpp
|
||||||
|
|||||||
Reference in New Issue
Block a user