mirror of
https://github.com/esphome/esphome.git
synced 2026-08-23 06:36:23 +00:00
[ethernet][network][wifi] Arbitrate the default route from the network priority list (#17797)
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
wifi:
|
||||
ssid: "test_ssid"
|
||||
password: "test_password"
|
||||
|
||||
ethernet:
|
||||
type: W5500
|
||||
clk_pin: 19
|
||||
mosi_pin: 21
|
||||
miso_pin: 23
|
||||
cs_pin: 18
|
||||
interrupt_pin: 36
|
||||
reset_pin: 22
|
||||
clock_speed: 10Mhz
|
||||
|
||||
network:
|
||||
priority:
|
||||
- ethernet
|
||||
- wifi
|
||||
@@ -0,0 +1,24 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
rp2:
|
||||
board: rpipicow
|
||||
|
||||
wifi:
|
||||
ssid: "test_ssid"
|
||||
password: "test_password"
|
||||
|
||||
ethernet:
|
||||
type: W5500
|
||||
clk_pin: 18
|
||||
mosi_pin: 19
|
||||
miso_pin: 16
|
||||
cs_pin: 17
|
||||
interrupt_pin: 21
|
||||
reset_pin: 20
|
||||
mac_address: "02:AA:BB:CC:DD:01"
|
||||
|
||||
network:
|
||||
priority:
|
||||
- ethernet
|
||||
- wifi
|
||||
@@ -0,0 +1,15 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
wifi:
|
||||
ssid: "test_ssid"
|
||||
password: "test_password"
|
||||
|
||||
network:
|
||||
priority:
|
||||
- wifi
|
||||
@@ -16,9 +16,10 @@ from esphome.components.network import (
|
||||
_validate_priority_list,
|
||||
get_network_priority,
|
||||
)
|
||||
from esphome.const import CONF_PRIORITY
|
||||
from esphome.const import CONF_PRIORITY, PlatformFramework
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
from tests.component_tests.types import SetCoreConfigCallable
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -138,6 +139,22 @@ def test_final_validate_noop_without_priority_list() -> None:
|
||||
_final_validate({}) # must not raise
|
||||
|
||||
|
||||
def test_final_validate_rejects_unsupported_arbitration_interface(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
) -> None:
|
||||
"""The ethernet/wifi-only arbitration tripwire fails as a clean config error.
|
||||
|
||||
Unreachable through the public schema today (VALID_NETWORK_TYPES gates the
|
||||
list), so the config is hand-built to simulate a future interface type that
|
||||
was added to the schema without extending NetworkComponent::loop().
|
||||
"""
|
||||
set_core_config(PlatformFramework.ESP32_IDF)
|
||||
fv.full_config.set({"openthread": {}, "wifi": {}})
|
||||
config = {CONF_PRIORITY: [{"interface": "openthread"}, {"interface": "wifi"}]}
|
||||
with pytest.raises(Invalid, match="arbitration does not support: openthread"):
|
||||
_final_validate(config)
|
||||
|
||||
|
||||
def _cpp_setup_priority(name: str) -> float:
|
||||
"""Read a setup_priority constant straight from esphome/core/component.h."""
|
||||
header = Path(__file__).parents[3] / "esphome" / "core" / "component.h"
|
||||
@@ -199,3 +216,55 @@ def test_no_primary_interface_define_without_priority(
|
||||
assert not any(
|
||||
d.name.startswith("USE_NETWORK_PRIMARY_INTERFACE_") for d in CORE.defines
|
||||
)
|
||||
|
||||
|
||||
def _dns_per_default_netif_option() -> bool | None:
|
||||
from esphome.components.esp32.const import KEY_ESP32, KEY_SDKCONFIG_OPTIONS
|
||||
|
||||
if KEY_ESP32 not in CORE.data: # non-ESP32 configs have no sdkconfig at all
|
||||
return None
|
||||
return CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS].get(
|
||||
"CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"config_file",
|
||||
[
|
||||
"priority_wifi_first.yaml",
|
||||
"priority_ethernet_first.yaml",
|
||||
"priority_arduino.yaml",
|
||||
],
|
||||
)
|
||||
def test_multi_interface_priority_enables_default_route_arbitration(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
config_file: str,
|
||||
) -> None:
|
||||
"""More than one interface in 'priority' enables default-route arbitration."""
|
||||
generate_main(component_config_path(config_file))
|
||||
assert "USE_NETWORK_DEFAULT_ROUTE" in {d.name for d in CORE.defines}
|
||||
assert _dns_per_default_netif_option() is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"config_file",
|
||||
[
|
||||
# Single-entry priority list / no list at all.
|
||||
"priority_single.yaml",
|
||||
"wifi_only.yaml",
|
||||
# Dual-interface on rp2040: validates, but the arbitration is ESP32-only
|
||||
# (NetworkComponent::loop() is compiled under USE_ESP32) — emitting the
|
||||
# define here would be a hard build break.
|
||||
"priority_rp2040.yaml",
|
||||
],
|
||||
)
|
||||
def test_single_interface_has_no_default_route_arbitration(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
config_file: str,
|
||||
) -> None:
|
||||
"""Single-interface and non-ESP32 configs must not compile in the arbitration."""
|
||||
generate_main(component_config_path(config_file))
|
||||
assert "USE_NETWORK_DEFAULT_ROUTE" not in {d.name for d in CORE.defines}
|
||||
assert _dns_per_default_netif_option() is None
|
||||
|
||||
Reference in New Issue
Block a user