"""Tests for the ethernet final-validation coexistence gate and schema bounds.""" import pytest from voluptuous import Invalid from esphome import config_validation as cv from esphome.components.esp32 import ( KEY_BOARD, KEY_IDF_VERSION, KEY_VARIANT, VARIANT_ESP32, VARIANT_ESP32S3, ) from esphome.components.ethernet import CONF_CLOCK_SPEED, CONFIG_SCHEMA, _final_validate from esphome.components.network import _validate_priority_list from esphome.const import CONF_PRIORITY, PlatformFramework from esphome.core import CORE import esphome.final_validate as fv from ..types import SetCoreConfigCallable _CH390_CONFIG = { "type": "CH390", "clk_pin": 47, "mosi_pin": 48, "miso_pin": 14, "cs_pin": 21, } def test_rejects_wifi_and_ethernet_without_priority() -> None: """Wi-Fi + ethernet without a network: priority: list must be rejected.""" fv.full_config.set({"wifi": {}, "ethernet": {}}) with pytest.raises(Invalid, match="cannot be used together with component wifi"): _final_validate({}) def test_rejects_wifi_and_ethernet_with_incomplete_priority() -> None: """A priority list missing an interface is rejected and names what's missing.""" fv.full_config.set( { "wifi": {}, "ethernet": {}, "network": {CONF_PRIORITY: _validate_priority_list(["ethernet"])}, } ) with pytest.raises(Invalid, match=r"must.*list both interfaces; missing: wifi"): _final_validate({}) @pytest.mark.parametrize("clock_speed", ["26.67MHz", "72MHz"]) def test_ch390_accepts_clock_speed_up_to_the_datasheet_maximum( set_core_config: SetCoreConfigCallable, clock_speed: str ) -> None: """CH390 SCK is rated to 72MHz, so the schema must accept the whole range.""" set_core_config( PlatformFramework.ESP32_IDF, platform_data={ KEY_BOARD: "esp32-s3-devkitc-1", KEY_VARIANT: VARIANT_ESP32S3, KEY_IDF_VERSION: cv.Version(5, 3, 2), }, ) # _validate derives use_address from the node name, which has no default here. CORE.name = "ch390-test" config = CONFIG_SCHEMA({**_CH390_CONFIG, CONF_CLOCK_SPEED: clock_speed}) assert config[CONF_CLOCK_SPEED] == cv.frequency(clock_speed) def test_ch390_rejects_clock_speed_above_the_datasheet_maximum( set_core_config: SetCoreConfigCallable, ) -> None: """The shared 80MHz ceiling is out of spec for this part.""" set_core_config( PlatformFramework.ESP32_IDF, platform_data={ KEY_BOARD: "esp32-s3-devkitc-1", KEY_VARIANT: VARIANT_ESP32S3, KEY_IDF_VERSION: cv.Version(5, 3, 2), }, ) # _validate derives use_address from the node name, which has no default here. CORE.name = "ch390-test" with pytest.raises(Invalid, match="value must be at most 72000000"): CONFIG_SCHEMA({**_CH390_CONFIG, CONF_CLOCK_SPEED: "80MHz"}) _LAN8720_CONFIG = {"type": "LAN8720", "mdc_pin": 23, "mdio_pin": 18} def _set_esp32_core(set_core_config: SetCoreConfigCallable) -> None: set_core_config( PlatformFramework.ESP32_IDF, platform_data={ KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32, KEY_IDF_VERSION: cv.Version(5, 3, 2), }, ) CORE.name = "rmii-test" def test_rmii_rejects_removed_clk_mode( set_core_config: SetCoreConfigCallable, ) -> None: """The removed clk_mode option points at the clk: replacement.""" _set_esp32_core(set_core_config) with pytest.raises(Invalid, match="GPIO17_OUT -> mode: CLK_OUT, pin: GPIO17"): CONFIG_SCHEMA( { **_LAN8720_CONFIG, "clk_mode": "GPIO17_OUT", "clk": {"mode": "CLK_OUT", "pin": 17}, } ) def test_rmii_requires_clk(set_core_config: SetCoreConfigCallable) -> None: """An RMII PHY has no default clock, so clk must be given.""" _set_esp32_core(set_core_config) with pytest.raises(Invalid, match=r"required key not provided @ data\['clk'\]"): CONFIG_SCHEMA(_LAN8720_CONFIG)