Files
esphome/tests/unit_tests/test_pins.py
T

111 lines
3.9 KiB
Python

"""Unit tests for esphome/pins.py."""
import pytest
from esphome import config_validation as cv, pins
import esphome.codegen as cg
from esphome.const import CONF_ADDRESS
from esphome.core import ID
from esphome.schema_extractors import SCHEMA_EXTRACT
SomeHub = cg.esphome_ns.class_("SomeHub")
def test_use_id_or_address_with_explicit_id() -> None:
"""An explicit id string still resolves exactly like cv.use_id."""
validator = pins.use_id_or_address(SomeHub)
result = validator("my_hub")
assert isinstance(result, ID)
assert result.id == "my_hub"
assert result.type is SomeHub
assert result.is_declaration is False
assert result.match_config is None
def test_use_id_or_address_with_omitted_id() -> None:
"""Omitting the id (None) still falls back to the single-instance auto-pick."""
validator = pins.use_id_or_address(SomeHub)
result = validator(None)
assert isinstance(result, ID)
assert result.id is None
assert result.type is SomeHub
assert result.match_config is None
def test_use_id_or_address_with_address_mapping() -> None:
"""A {address: ...} mapping produces an unnamed ID with match_config set."""
validator = pins.use_id_or_address(SomeHub)
result = validator({CONF_ADDRESS: 0x21})
assert isinstance(result, ID)
assert result.id is None
assert result.type is SomeHub
assert result.is_declaration is False
assert result.match_config == {CONF_ADDRESS: 0x21}
def test_use_id_or_address_with_string_address() -> None:
"""A hex string address is normalized the same way cv.i2c_address does."""
validator = pins.use_id_or_address(SomeHub)
result = validator({CONF_ADDRESS: "0x21"})
assert result.match_config == {CONF_ADDRESS: 0x21}
def test_use_id_or_address_mapping_requires_address_key() -> None:
"""A mapping without the address key is rejected, not silently ignored."""
validator = pins.use_id_or_address(SomeHub)
with pytest.raises(cv.Invalid):
validator({})
def test_use_id_or_address_mapping_rejects_invalid_address() -> None:
"""An out-of-range address is still validated by cv.i2c_address."""
validator = pins.use_id_or_address(SomeHub)
with pytest.raises(cv.Invalid):
validator({CONF_ADDRESS: 0x1FF})
def test_use_id_or_address_custom_address_key() -> None:
"""A custom address_key is honored both in the mapping and match_config."""
validator = pins.use_id_or_address(SomeHub, address_key="i2c_address")
result = validator({"i2c_address": 0x10})
assert result.match_config == {"i2c_address": 0x10}
def test_use_id_or_address_marks_address_match_as_manual() -> None:
"""The resolved id must survive strip_default_ids(), unlike a plain
omitted-id auto-pick, since the user explicitly gave selection criteria."""
validator = pins.use_id_or_address(SomeHub)
result = validator({CONF_ADDRESS: 0x21})
assert result.is_manual is True
def test_use_id_or_address_schema_extract_passthrough() -> None:
"""Called with the SCHEMA_EXTRACT sentinel (as build_language_schema.py
does), the wrapper must fall through to cv.use_id's own handling and
return the hub type, not try to treat it as an address mapping."""
validator = pins.use_id_or_address(SomeHub)
assert validator(SCHEMA_EXTRACT) is SomeHub
def test_use_id_or_address_registers_as_use_id_schema(monkeypatch) -> None:
"""The wrapper must be discoverable by build_language_schema.py as a
`use_id` schema (like a bare cv.use_id), or the generated language schema
silently drops hub-id completion for every pin schema using it."""
from esphome import schema_extractors
monkeypatch.setattr(schema_extractors, "EnableSchemaExtraction", True)
monkeypatch.setattr(schema_extractors, "hidden_schemas", {})
validator = pins.use_id_or_address(SomeHub)
assert schema_extractors.hidden_schemas.get(repr(validator)) == "use_id"