Files
2026-08-05 14:14:32 +12:00

129 lines
4.8 KiB
Python

"""Tests for the ``lvgl.widget.set_z_index`` action: schema validation and
code generation.
"""
from __future__ import annotations
from pathlib import Path
import pytest
from esphome.components.lvgl.automation import SET_Z_INDEX_SCHEMA
from esphome.config_validation import Invalid
# ---------------------------------------------------------------------------
# Validation
# ---------------------------------------------------------------------------
class TestSetZIndexSchemaValidation:
"""Test that SET_Z_INDEX_SCHEMA accepts the documented forms and rejects
everything else.
"""
@pytest.mark.parametrize("position", ["top", "bottom", "up", "down"])
def test_keyword_position_accepted(self, position: str) -> None:
config = SET_Z_INDEX_SCHEMA({"id": "my_widget", "position": position})
assert config["position"] == position.upper()
@pytest.mark.parametrize("position", ["Top", "BOTTOM", "Up", "dOwN"])
def test_keyword_position_case_insensitive(self, position: str) -> None:
config = SET_Z_INDEX_SCHEMA({"id": "my_widget", "position": position})
assert config["position"] == position.upper()
@pytest.mark.parametrize("position", [0, 1, 5, -1, -5])
def test_integer_position_accepted(self, position: int) -> None:
config = SET_Z_INDEX_SCHEMA({"id": "my_widget", "position": position})
assert config["position"] == position
def test_unknown_keyword_rejected(self) -> None:
with pytest.raises(Invalid):
SET_Z_INDEX_SCHEMA({"id": "my_widget", "position": "sideways"})
def test_float_position_rejected(self) -> None:
with pytest.raises(Invalid):
SET_Z_INDEX_SCHEMA({"id": "my_widget", "position": 1.5})
def test_missing_id_rejected(self) -> None:
with pytest.raises(Invalid):
SET_Z_INDEX_SCHEMA({"position": "top"})
def test_missing_position_rejected(self) -> None:
with pytest.raises(Invalid):
SET_Z_INDEX_SCHEMA({"id": "my_widget"})
def test_single_id_is_wrapped_in_list(self) -> None:
config = SET_Z_INDEX_SCHEMA({"id": "my_widget", "position": "top"})
assert len(config["id"]) == 1
assert config["id"][0]["id"].id == "my_widget"
def test_list_of_ids_accepted(self) -> None:
config = SET_Z_INDEX_SCHEMA({"id": ["widget_a", "widget_b"], "position": "top"})
assert [entry["id"].id for entry in config["id"]] == ["widget_a", "widget_b"]
# ---------------------------------------------------------------------------
# Code generation
# ---------------------------------------------------------------------------
@pytest.fixture(scope="module")
def main_cpp(request: pytest.FixtureRequest) -> str:
"""Generate the C++ output for the shared set_z_index YAML config once
per module. See ``test_widget_state.py`` for why this is module-scoped
and self-contained rather than using the function-scoped ``generate_main``
fixture from ``conftest.py``.
"""
from esphome.__main__ import generate_cpp_contents
from esphome.config import read_config
from esphome.core import CORE
config_path = Path(request.fspath).parent / "config" / "set_z_index_test.yaml"
original_path = CORE.config_path
try:
CORE.config_path = config_path
CORE.config = read_config({})
generate_cpp_contents(CORE.config)
return CORE.cpp_global_section + CORE.cpp_main_section
finally:
CORE.config_path = original_path
CORE.reset()
def test_top_emits_move_foreground(main_cpp: str) -> None:
assert "lv_obj_move_foreground(label_a);" in main_cpp
def test_bottom_emits_move_background(main_cpp: str) -> None:
assert "lv_obj_move_background(label_a);" in main_cpp
def test_up_emits_unguarded_index_increment(main_cpp: str) -> None:
assert "lv_obj_move_to_index(label_a, lv_obj_get_index(label_a) + 1);" in main_cpp
def test_down_emits_guarded_index_decrement(main_cpp: str) -> None:
"""``down`` must be guarded so that a widget already at index 0 isn't
reinterpreted by LVGL as "move to the top" (LVGL treats a negative
index as "count from the back").
"""
assert "if (lv_obj_get_index(label_a) > 0) {" in main_cpp
assert "lv_obj_move_to_index(label_a, lv_obj_get_index(label_a) - 1);" in main_cpp
def test_positive_integer_emits_direct_index(main_cpp: str) -> None:
assert "lv_obj_move_to_index(label_a, 3);" in main_cpp
def test_negative_integer_emits_direct_index(main_cpp: str) -> None:
assert "lv_obj_move_to_index(label_a, -2);" in main_cpp
def test_list_of_ids_applies_to_each_widget(main_cpp: str) -> None:
"""``id: [label_a, label_b]`` must emit the move call once per widget."""
assert (
main_cpp.count("lv_obj_move_to_index(label_a, lv_obj_get_index(label_a) + 1);")
== 2
)
assert "lv_obj_move_to_index(label_b, lv_obj_get_index(label_b) + 1);" in main_cpp