mirror of
https://github.com/esphome/esphome.git
synced 2025-10-30 06:33:51 +00:00
[core] Allow AUTO_LOAD to receive the component config to determine if it should load other components (#10961)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
@@ -101,3 +101,10 @@ def mock_get_idedata() -> Generator[Mock, None, None]:
|
||||
"""Mock get_idedata for platformio_api."""
|
||||
with patch("esphome.platformio_api.get_idedata") as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_get_component() -> Generator[Mock, None, None]:
|
||||
"""Mock get_component for config module."""
|
||||
with patch("esphome.config.get_component") as mock:
|
||||
yield mock
|
||||
|
||||
10
tests/unit_tests/fixtures/auto_load_dynamic.yaml
Normal file
10
tests/unit_tests/fixtures/auto_load_dynamic.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
esphome:
|
||||
name: test-device
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
# Test component with dynamic AUTO_LOAD
|
||||
test_component:
|
||||
enable_logger: true
|
||||
enable_api: false
|
||||
8
tests/unit_tests/fixtures/auto_load_static.yaml
Normal file
8
tests/unit_tests/fixtures/auto_load_static.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
esphome:
|
||||
name: test-device
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
# Test component with static AUTO_LOAD
|
||||
test_component:
|
||||
131
tests/unit_tests/test_config_auto_load.py
Normal file
131
tests/unit_tests/test_config_auto_load.py
Normal file
@@ -0,0 +1,131 @@
|
||||
"""Tests for AUTO_LOAD functionality including dynamic AUTO_LOAD."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import config, config_validation as cv, yaml_util
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fixtures_dir() -> Path:
|
||||
"""Get the fixtures directory."""
|
||||
return Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def default_component() -> Mock:
|
||||
"""Create a default mock component for unmocked components."""
|
||||
return Mock(
|
||||
auto_load=[],
|
||||
is_platform_component=False,
|
||||
is_platform=False,
|
||||
multi_conf=False,
|
||||
multi_conf_no_default=False,
|
||||
dependencies=[],
|
||||
conflicts_with=[],
|
||||
config_schema=cv.Schema({}, extra=cv.ALLOW_EXTRA),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def static_auto_load_component() -> Mock:
|
||||
"""Create a mock component with static AUTO_LOAD."""
|
||||
return Mock(
|
||||
auto_load=["logger"],
|
||||
is_platform_component=False,
|
||||
is_platform=False,
|
||||
multi_conf=False,
|
||||
multi_conf_no_default=False,
|
||||
dependencies=[],
|
||||
conflicts_with=[],
|
||||
config_schema=cv.Schema({}, extra=cv.ALLOW_EXTRA),
|
||||
)
|
||||
|
||||
|
||||
def test_static_auto_load_adds_components(
|
||||
mock_get_component: Mock,
|
||||
fixtures_dir: Path,
|
||||
static_auto_load_component: Mock,
|
||||
default_component: Mock,
|
||||
) -> None:
|
||||
"""Test that static AUTO_LOAD triggers loading of specified components."""
|
||||
CORE.config_path = fixtures_dir / "auto_load_static.yaml"
|
||||
|
||||
config_file = fixtures_dir / "auto_load_static.yaml"
|
||||
raw_config = yaml_util.load_yaml(config_file)
|
||||
|
||||
component_mocks = {"test_component": static_auto_load_component}
|
||||
mock_get_component.side_effect = lambda name: component_mocks.get(
|
||||
name, default_component
|
||||
)
|
||||
|
||||
result = config.validate_config(raw_config, {})
|
||||
|
||||
# Check for validation errors
|
||||
assert not result.errors, f"Validation errors: {result.errors}"
|
||||
|
||||
# Logger should have been auto-loaded by test_component
|
||||
assert "logger" in result
|
||||
assert "test_component" in result
|
||||
|
||||
|
||||
def test_dynamic_auto_load_with_config_param(
|
||||
mock_get_component: Mock,
|
||||
fixtures_dir: Path,
|
||||
default_component: Mock,
|
||||
) -> None:
|
||||
"""Test that dynamic AUTO_LOAD evaluates based on configuration."""
|
||||
CORE.config_path = fixtures_dir / "auto_load_dynamic.yaml"
|
||||
|
||||
config_file = fixtures_dir / "auto_load_dynamic.yaml"
|
||||
raw_config = yaml_util.load_yaml(config_file)
|
||||
|
||||
# Track if auto_load was called with config
|
||||
auto_load_calls = []
|
||||
|
||||
def dynamic_auto_load(conf: dict[str, Any]) -> list[str]:
|
||||
"""Dynamically load components based on config."""
|
||||
auto_load_calls.append(conf)
|
||||
component_map = {
|
||||
"enable_logger": "logger",
|
||||
"enable_api": "api",
|
||||
}
|
||||
return [comp for key, comp in component_map.items() if conf.get(key)]
|
||||
|
||||
dynamic_component = Mock(
|
||||
auto_load=dynamic_auto_load,
|
||||
is_platform_component=False,
|
||||
is_platform=False,
|
||||
multi_conf=False,
|
||||
multi_conf_no_default=False,
|
||||
dependencies=[],
|
||||
conflicts_with=[],
|
||||
config_schema=cv.Schema({}, extra=cv.ALLOW_EXTRA),
|
||||
)
|
||||
|
||||
component_mocks = {"test_component": dynamic_component}
|
||||
mock_get_component.side_effect = lambda name: component_mocks.get(
|
||||
name, default_component
|
||||
)
|
||||
|
||||
result = config.validate_config(raw_config, {})
|
||||
|
||||
# Check for validation errors
|
||||
assert not result.errors, f"Validation errors: {result.errors}"
|
||||
|
||||
# Verify auto_load was called with the validated config
|
||||
assert len(auto_load_calls) == 1, "auto_load should be called exactly once"
|
||||
assert auto_load_calls[0].get("enable_logger") is True
|
||||
assert auto_load_calls[0].get("enable_api") is False
|
||||
|
||||
# Only logger should be auto-loaded (enable_logger=true in YAML)
|
||||
assert "logger" in result, (
|
||||
f"Logger not found in result. Result keys: {list(result.keys())}"
|
||||
)
|
||||
# API should NOT be auto-loaded (enable_api=false in YAML)
|
||||
assert "api" not in result
|
||||
assert "test_component" in result
|
||||
@@ -10,13 +10,6 @@ from esphome import config, yaml_util
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_get_component() -> Generator[Mock, None, None]:
|
||||
"""Fixture for mocking get_component."""
|
||||
with patch("esphome.config.get_component") as mock_get_component:
|
||||
yield mock_get_component
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_get_platform() -> Generator[Mock, None, None]:
|
||||
"""Fixture for mocking get_platform."""
|
||||
|
||||
Reference in New Issue
Block a user