mirror of
https://github.com/esphome/esphome.git
synced 2025-09-03 11:52:20 +01:00
Merge branch 'clean_comp_removed' into integration
This commit is contained in:
@@ -294,6 +294,7 @@ async def to_code(config):
|
||||
|
||||
if config[CONF_ADVERTISING]:
|
||||
cg.add_define("USE_ESP32_BLE_ADVERTISING")
|
||||
cg.add_define("USE_ESP32_BLE_UUID")
|
||||
|
||||
|
||||
@automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({}))
|
||||
|
@@ -212,7 +212,7 @@ def validate_use_legacy(value):
|
||||
f"All i2s_audio components must set {CONF_USE_LEGACY} to the same value."
|
||||
)
|
||||
if (not value[CONF_USE_LEGACY]) and (CORE.using_arduino):
|
||||
raise cv.Invalid("Arduino supports only the legacy i2s driver.")
|
||||
raise cv.Invalid("Arduino supports only the legacy i2s driver")
|
||||
_use_legacy_driver = value[CONF_USE_LEGACY]
|
||||
return value
|
||||
|
||||
|
@@ -92,7 +92,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
def _final_validate(_):
|
||||
if not use_legacy():
|
||||
raise cv.Invalid("I2S media player is only compatible with legacy i2s driver.")
|
||||
raise cv.Invalid("I2S media player is only compatible with legacy i2s driver")
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
@@ -122,7 +122,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
def _final_validate(config):
|
||||
if not use_legacy() and config[CONF_ADC_TYPE] == "internal":
|
||||
raise cv.Invalid("Internal ADC is only compatible with legacy i2s driver.")
|
||||
raise cv.Invalid("Internal ADC is only compatible with legacy i2s driver")
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
@@ -163,7 +163,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
def _final_validate(config):
|
||||
if not use_legacy():
|
||||
if config[CONF_DAC_TYPE] == "internal":
|
||||
raise cv.Invalid("Internal DAC is only compatible with legacy i2s driver.")
|
||||
raise cv.Invalid("Internal DAC is only compatible with legacy i2s driver")
|
||||
if config[CONF_I2S_COMM_FMT] == "stand_max":
|
||||
raise cv.Invalid(
|
||||
"I2S standard max format only implemented with legacy i2s driver."
|
||||
|
@@ -201,7 +201,7 @@ def _validate_manifest_version(manifest_data):
|
||||
else:
|
||||
raise cv.Invalid("Invalid manifest version")
|
||||
else:
|
||||
raise cv.Invalid("Invalid manifest file, missing 'version' key.")
|
||||
raise cv.Invalid("Invalid manifest file, missing 'version' key")
|
||||
|
||||
|
||||
def _process_http_source(config):
|
||||
@@ -421,7 +421,7 @@ def _feature_step_size_validate(config):
|
||||
if features_step_size is None:
|
||||
features_step_size = model_step_size
|
||||
elif features_step_size != model_step_size:
|
||||
raise cv.Invalid("Cannot load models with different features step sizes.")
|
||||
raise cv.Invalid("Cannot load models with different features step sizes")
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
|
@@ -147,7 +147,7 @@ def _read_audio_file_and_type(file_config):
|
||||
elif file_source == TYPE_WEB:
|
||||
path = _compute_local_file_path(conf_file)
|
||||
else:
|
||||
raise cv.Invalid("Unsupported file source.")
|
||||
raise cv.Invalid("Unsupported file source")
|
||||
|
||||
with open(path, "rb") as f:
|
||||
data = f.read()
|
||||
@@ -219,7 +219,7 @@ def _validate_supported_local_file(config):
|
||||
for file_config in config.get(CONF_FILES, []):
|
||||
_, media_file_type = _read_audio_file_and_type(file_config)
|
||||
if str(media_file_type) == str(audio.AUDIO_FILE_TYPE_ENUM["NONE"]):
|
||||
raise cv.Invalid("Unsupported local media file.")
|
||||
raise cv.Invalid("Unsupported local media file")
|
||||
if not config[CONF_CODEC_SUPPORT_ENABLED] and str(media_file_type) != str(
|
||||
audio.AUDIO_FILE_TYPE_ENUM["WAV"]
|
||||
):
|
||||
|
@@ -86,7 +86,10 @@ def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool:
|
||||
|
||||
if old.src_version != new.src_version:
|
||||
return True
|
||||
return old.build_path != new.build_path
|
||||
if old.build_path != new.build_path:
|
||||
return True
|
||||
# Check if any components have been removed
|
||||
return bool(old.loaded_integrations - new.loaded_integrations)
|
||||
|
||||
|
||||
def storage_should_update_cmake_cache(old: StorageJSON, new: StorageJSON) -> bool:
|
||||
@@ -108,7 +111,14 @@ def update_storage_json():
|
||||
return
|
||||
|
||||
if storage_should_clean(old, new):
|
||||
_LOGGER.info("Core config, version changed, cleaning build files...")
|
||||
if old and old.loaded_integrations - new.loaded_integrations:
|
||||
removed = old.loaded_integrations - new.loaded_integrations
|
||||
_LOGGER.info(
|
||||
"Components removed (%s), cleaning build files...",
|
||||
", ".join(sorted(removed)),
|
||||
)
|
||||
else:
|
||||
_LOGGER.info("Core config or version changed, cleaning build files...")
|
||||
clean_build()
|
||||
elif storage_should_update_cmake_cache(old, new):
|
||||
_LOGGER.info("Integrations changed, cleaning cmake cache...")
|
||||
|
139
tests/unit_tests/test_writer.py
Normal file
139
tests/unit_tests/test_writer.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""Test writer module functionality."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.storage_json import StorageJSON
|
||||
from esphome.writer import storage_should_clean
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_storage() -> Callable[..., StorageJSON]:
|
||||
"""Factory fixture to create StorageJSON instances."""
|
||||
|
||||
def _create(
|
||||
loaded_integrations: list[str] | None = None, **kwargs: Any
|
||||
) -> StorageJSON:
|
||||
return StorageJSON(
|
||||
storage_version=kwargs.get("storage_version", 1),
|
||||
name=kwargs.get("name", "test"),
|
||||
friendly_name=kwargs.get("friendly_name", "Test Device"),
|
||||
comment=kwargs.get("comment"),
|
||||
esphome_version=kwargs.get("esphome_version", "2025.1.0"),
|
||||
src_version=kwargs.get("src_version", 1),
|
||||
address=kwargs.get("address", "test.local"),
|
||||
web_port=kwargs.get("web_port", 80),
|
||||
target_platform=kwargs.get("target_platform", "ESP32"),
|
||||
build_path=kwargs.get("build_path", "/build"),
|
||||
firmware_bin_path=kwargs.get("firmware_bin_path", "/firmware.bin"),
|
||||
loaded_integrations=set(loaded_integrations or []),
|
||||
loaded_platforms=kwargs.get("loaded_platforms", set()),
|
||||
no_mdns=kwargs.get("no_mdns", False),
|
||||
framework=kwargs.get("framework", "arduino"),
|
||||
core_platform=kwargs.get("core_platform", "esp32"),
|
||||
)
|
||||
|
||||
return _create
|
||||
|
||||
|
||||
def test_storage_should_clean_when_old_is_none(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test that clean is triggered when old storage is None."""
|
||||
new = create_storage(loaded_integrations=["api", "wifi"])
|
||||
assert storage_should_clean(None, new) is True
|
||||
|
||||
|
||||
def test_storage_should_clean_when_src_version_changes(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test that clean is triggered when src_version changes."""
|
||||
old = create_storage(loaded_integrations=["api", "wifi"], src_version=1)
|
||||
new = create_storage(loaded_integrations=["api", "wifi"], src_version=2)
|
||||
assert storage_should_clean(old, new) is True
|
||||
|
||||
|
||||
def test_storage_should_clean_when_build_path_changes(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test that clean is triggered when build_path changes."""
|
||||
old = create_storage(loaded_integrations=["api", "wifi"], build_path="/build1")
|
||||
new = create_storage(loaded_integrations=["api", "wifi"], build_path="/build2")
|
||||
assert storage_should_clean(old, new) is True
|
||||
|
||||
|
||||
def test_storage_should_clean_when_component_removed(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test that clean is triggered when a component is removed."""
|
||||
old = create_storage(
|
||||
loaded_integrations=["api", "wifi", "bluetooth_proxy", "esp32_ble_tracker"]
|
||||
)
|
||||
new = create_storage(loaded_integrations=["api", "wifi", "esp32_ble_tracker"])
|
||||
assert storage_should_clean(old, new) is True
|
||||
|
||||
|
||||
def test_storage_should_clean_when_multiple_components_removed(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test that clean is triggered when multiple components are removed."""
|
||||
old = create_storage(
|
||||
loaded_integrations=["api", "wifi", "ota", "web_server", "logger"]
|
||||
)
|
||||
new = create_storage(loaded_integrations=["api", "wifi", "logger"])
|
||||
assert storage_should_clean(old, new) is True
|
||||
|
||||
|
||||
def test_storage_should_not_clean_when_nothing_changes(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test that clean is not triggered when nothing changes."""
|
||||
old = create_storage(loaded_integrations=["api", "wifi", "logger"])
|
||||
new = create_storage(loaded_integrations=["api", "wifi", "logger"])
|
||||
assert storage_should_clean(old, new) is False
|
||||
|
||||
|
||||
def test_storage_should_not_clean_when_component_added(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test that clean is not triggered when a component is only added."""
|
||||
old = create_storage(loaded_integrations=["api", "wifi"])
|
||||
new = create_storage(loaded_integrations=["api", "wifi", "ota"])
|
||||
assert storage_should_clean(old, new) is False
|
||||
|
||||
|
||||
def test_storage_should_not_clean_when_other_fields_change(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test that clean is not triggered when non-relevant fields change."""
|
||||
old = create_storage(
|
||||
loaded_integrations=["api", "wifi"],
|
||||
friendly_name="Old Name",
|
||||
esphome_version="2024.12.0",
|
||||
)
|
||||
new = create_storage(
|
||||
loaded_integrations=["api", "wifi"],
|
||||
friendly_name="New Name",
|
||||
esphome_version="2025.1.0",
|
||||
)
|
||||
assert storage_should_clean(old, new) is False
|
||||
|
||||
|
||||
def test_storage_edge_case_empty_integrations(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test edge case when old has integrations but new has none."""
|
||||
old = create_storage(loaded_integrations=["api", "wifi"])
|
||||
new = create_storage(loaded_integrations=[])
|
||||
assert storage_should_clean(old, new) is True
|
||||
|
||||
|
||||
def test_storage_edge_case_from_empty_integrations(
|
||||
create_storage: Callable[..., StorageJSON],
|
||||
) -> None:
|
||||
"""Test edge case when old has no integrations but new has some."""
|
||||
old = create_storage(loaded_integrations=[])
|
||||
new = create_storage(loaded_integrations=["api", "wifi"])
|
||||
assert storage_should_clean(old, new) is False
|
Reference in New Issue
Block a user