mirror of
https://github.com/esphome/esphome.git
synced 2026-09-26 14:30:23 +00:00
Merge branch 'esp8266-native-pch' into esp32-idf-pch
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""Tests for the esp32 sdkconfig write and its toolchain-gated clean."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.esp32 import _write_sdkconfig
|
||||
from esphome.components.esp32.const import KEY_SDKCONFIG_OPTIONS
|
||||
from esphome.const import KEY_CORE, KEY_ESP32, KEY_FRAMEWORK_VERSION, Toolchain
|
||||
from esphome.core import CORE
|
||||
from esphome.espidf.toolchain import has_outdated_files
|
||||
|
||||
|
||||
def _setup_core(tmp_path: Path, toolchain: Toolchain | None) -> None:
|
||||
CORE.config_path = tmp_path / "test.yaml"
|
||||
CORE.build_path = tmp_path
|
||||
CORE.toolchain = toolchain
|
||||
CORE.data[KEY_ESP32] = {KEY_SDKCONFIG_OPTIONS: {"CONFIG_X": "y"}}
|
||||
CORE.data[KEY_CORE] = {KEY_FRAMEWORK_VERSION: "5.5.5"}
|
||||
|
||||
|
||||
def _seed_configured_build(tmp_path: Path) -> None:
|
||||
"""A settled native build: configure outputs predate what comes next."""
|
||||
build = tmp_path / "build"
|
||||
(build / "config").mkdir(parents=True)
|
||||
(build / "config" / "sdkconfig.h").write_text("")
|
||||
(build / "CMakeCache.txt").write_text("")
|
||||
(build / "build.ninja").write_text("")
|
||||
# Explicitly older than what the test writes next: has_outdated_files()
|
||||
# compares st_mtime with a strict >, so same-tick writes would pass
|
||||
past = time.time() - 60
|
||||
for f in build.rglob("*"):
|
||||
os.utime(f, (past, past))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("toolchain", "clean_expected"),
|
||||
[(Toolchain.ESP_IDF, False), (Toolchain.PLATFORMIO, True), (None, True)],
|
||||
)
|
||||
def test_write_sdkconfig_cleans_only_on_platformio(
|
||||
tmp_path: Path, toolchain: Toolchain | None, clean_expected: bool
|
||||
) -> None:
|
||||
"""A changed sdkconfig forces a full clean only under PlatformIO; the
|
||||
esp-idf toolchain reconfigures via has_outdated_files() instead; an
|
||||
unresolved toolchain fails safe onto the clean."""
|
||||
_setup_core(tmp_path, toolchain)
|
||||
_seed_configured_build(tmp_path)
|
||||
with (
|
||||
patch.object(CORE, "name", "test"),
|
||||
patch("esphome.components.esp32.clean_build") as clean,
|
||||
):
|
||||
_write_sdkconfig()
|
||||
assert "CONFIG_X" in CORE.relative_build_path("sdkconfig.test").read_text()
|
||||
assert clean.called is clean_expected
|
||||
if clean_expected:
|
||||
clean.assert_called_once_with(clear_pio_cache=False)
|
||||
# The change must still trigger a reconfigure: the internal
|
||||
# sdkconfig snapshot is now newer than build/CMakeCache.txt
|
||||
assert has_outdated_files() is True
|
||||
clean.reset_mock()
|
||||
# A settled configure restamps the cache; an unchanged rewrite
|
||||
# must then neither clean nor mark the build stale
|
||||
future = time.time() + 60
|
||||
os.utime(CORE.relative_build_path("build/CMakeCache.txt"), (future, future))
|
||||
_write_sdkconfig()
|
||||
clean.assert_not_called()
|
||||
assert has_outdated_files() is False
|
||||
@@ -1127,6 +1127,34 @@ def test_config_hash_same_for_different_config_dirs(tmp_path: Path) -> None:
|
||||
assert hash1 == hash2
|
||||
|
||||
|
||||
def test_config_hash_same_for_different_data_dirs(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test that downloaded file paths hash the same wherever data_dir lives."""
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
|
||||
CORE.reset()
|
||||
CORE.config_path = config_dir / "device.yaml"
|
||||
CORE.config = {
|
||||
"esphome": {"name": "test"},
|
||||
"file": config_dir / ".esphome" / "image" / "c44630d6",
|
||||
}
|
||||
hash1 = CORE.config_hash
|
||||
|
||||
other_data_dir = tmp_path / "data"
|
||||
CORE.reset()
|
||||
monkeypatch.setenv("ESPHOME_DATA_DIR", str(other_data_dir))
|
||||
CORE.config_path = config_dir / "device.yaml"
|
||||
CORE.config = {
|
||||
"esphome": {"name": "test"},
|
||||
"file": other_data_dir / "image" / "c44630d6",
|
||||
}
|
||||
hash2 = CORE.config_hash
|
||||
|
||||
assert hash1 == hash2
|
||||
|
||||
|
||||
def test_make_app_name_cpp_no_mac_simple() -> None:
|
||||
"""Test simple name without MAC suffix returns string literal."""
|
||||
cpp_expr, global_decl, byte_len = make_app_name_cpp(
|
||||
|
||||
@@ -1706,6 +1706,53 @@ def test_dump_path_dotdot_reference_outside_anchor() -> None:
|
||||
assert output.strip() == "file: ../shared/font.ttf"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_dir",
|
||||
[
|
||||
pytest.param(Path("/config/.esphome"), id="cli"),
|
||||
pytest.param(Path("/data"), id="addon"),
|
||||
],
|
||||
)
|
||||
def test_dump_path_under_data_dir_uses_default_location(data_dir: Path) -> None:
|
||||
"""Test that Path values under data_dir dump as .esphome/<rest> for any layout."""
|
||||
anchor = Path("/config").absolute()
|
||||
path = data_dir.absolute() / "image" / "c44630d6"
|
||||
output = yaml_util.dump(
|
||||
{"file": path}, relative_to=anchor, data_dir=data_dir.absolute()
|
||||
)
|
||||
assert output.strip() == "file: .esphome/image/c44630d6"
|
||||
|
||||
|
||||
def test_dump_path_equal_to_data_dir() -> None:
|
||||
"""Test that the data dir itself dumps as .esphome, matching the default layout."""
|
||||
anchor = Path("/config").absolute()
|
||||
data_dir = Path("/data").absolute()
|
||||
output = yaml_util.dump({"dir": data_dir}, relative_to=anchor, data_dir=data_dir)
|
||||
assert output.strip() == "dir: .esphome"
|
||||
default = yaml_util.dump(
|
||||
{"dir": anchor / ".esphome"}, relative_to=anchor, data_dir=anchor / ".esphome"
|
||||
)
|
||||
assert default == output
|
||||
|
||||
|
||||
def test_dump_path_outside_data_dir_still_relative_to_anchor() -> None:
|
||||
"""Test that data_dir does not affect paths that are not under it."""
|
||||
anchor = Path("/config").absolute()
|
||||
path = anchor / "fonts" / "arial.ttf"
|
||||
output = yaml_util.dump(
|
||||
{"file": path}, relative_to=anchor, data_dir=Path("/data").absolute()
|
||||
)
|
||||
assert output.strip() == "file: fonts/arial.ttf"
|
||||
|
||||
|
||||
def test_dump_path_data_dir_without_relative_to_is_unchanged() -> None:
|
||||
"""Test that data_dir alone does not change the output."""
|
||||
data_dir = Path("/data").absolute()
|
||||
path = data_dir / "image" / "c44630d6"
|
||||
output = yaml_util.dump({"file": path}, data_dir=data_dir)
|
||||
assert output.strip() == f"file: {path}"
|
||||
|
||||
|
||||
def test_dump_relative_to_does_not_leak_between_calls() -> None:
|
||||
"""Test that the relative_to flag is scoped to a single dump call."""
|
||||
anchor = Path("/config/esphome").absolute()
|
||||
|
||||
Reference in New Issue
Block a user