mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[core] Make config-hash independent of machine-local paths (#17523)
This commit is contained in:
@@ -1113,6 +1113,48 @@ def test_config_hash_different_for_different_configs() -> None:
|
||||
assert hash1 != hash2
|
||||
|
||||
|
||||
def test_config_hash_ignores_build_path() -> None:
|
||||
"""Test that config_hash does not depend on the build_path value.
|
||||
|
||||
build_path embeds ESPHOME_BUILD_PATH and OS path separators, so it must
|
||||
not make the hash differ between machines.
|
||||
"""
|
||||
CORE.reset()
|
||||
CORE.config = {"esphome": {"name": "test", "build_path": "build\\test"}}
|
||||
hash1 = CORE.config_hash
|
||||
|
||||
CORE.reset()
|
||||
CORE.config = {"esphome": {"name": "test", "build_path": "/build/test"}}
|
||||
hash2 = CORE.config_hash
|
||||
|
||||
assert hash1 == hash2
|
||||
|
||||
|
||||
def test_config_hash_same_for_different_config_dirs(tmp_path: Path) -> None:
|
||||
"""Test that Path values under the config dir hash the same everywhere.
|
||||
|
||||
Simulates the same project checked out at two different locations; the
|
||||
absolute paths differ but the layout relative to the config dir is the
|
||||
same, so the hashes must match.
|
||||
"""
|
||||
dir1 = tmp_path / "machine_a" / "project"
|
||||
dir2 = tmp_path / "machine_b" / "somewhere" / "else"
|
||||
dir1.mkdir(parents=True)
|
||||
dir2.mkdir(parents=True)
|
||||
|
||||
CORE.reset()
|
||||
CORE.config_path = dir1 / "device.yaml"
|
||||
CORE.config = {"esphome": {"name": "test"}, "file": dir1 / "fonts" / "arial.ttf"}
|
||||
hash1 = CORE.config_hash
|
||||
|
||||
CORE.reset()
|
||||
CORE.config_path = dir2 / "device.yaml"
|
||||
CORE.config = {"esphome": {"name": "test"}, "file": dir2 / "fonts" / "arial.ttf"}
|
||||
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(
|
||||
|
||||
@@ -167,9 +167,9 @@ def setup_core(
|
||||
CORE.data[KEY_CORE] = {KEY_TARGET_PLATFORM: platform}
|
||||
|
||||
if tmp_path is not None:
|
||||
CORE.config_path = str(tmp_path / f"{name}.yaml")
|
||||
CORE.config_path = tmp_path / f"{name}.yaml"
|
||||
CORE.name = name
|
||||
CORE.build_path = str(tmp_path / ".esphome" / "build" / name)
|
||||
CORE.build_path = tmp_path / ".esphome" / "build" / name
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -1349,6 +1349,57 @@ def test_sensitive_str__is_a_str_subclass() -> None:
|
||||
assert value == "hunter2"
|
||||
|
||||
|
||||
def test_dump_path_without_relative_to_is_unchanged() -> None:
|
||||
"""Test that Path values dump as str(path) when relative_to is not given."""
|
||||
path = Path("some") / "dir" / "file.ttf"
|
||||
output = yaml_util.dump({"file": path})
|
||||
assert output.strip() == f"file: {path}"
|
||||
|
||||
|
||||
def test_dump_path_relative_to_anchor_dir() -> None:
|
||||
"""Test that Path values under relative_to dump as relative POSIX paths."""
|
||||
anchor = Path("/config/esphome").absolute()
|
||||
data = {"file": anchor / "fonts" / "arial.ttf"}
|
||||
output = yaml_util.dump(data, relative_to=anchor)
|
||||
assert output.strip() == "file: fonts/arial.ttf"
|
||||
|
||||
|
||||
def test_dump_path_outside_anchor_dir_walks_up() -> None:
|
||||
"""Test that Path values outside relative_to walk up with ".." segments."""
|
||||
anchor = Path("/config/esphome").absolute()
|
||||
outside = Path("/config/fonts/file.ttf").absolute()
|
||||
output = yaml_util.dump({"file": outside}, relative_to=anchor)
|
||||
assert output.strip() == "file: ../fonts/file.ttf"
|
||||
|
||||
|
||||
def test_dump_path_with_dotdot_segments_is_normalized() -> None:
|
||||
"""Test that ".." segments do not defeat relativization.
|
||||
|
||||
A path like /config/other/../esphome/fonts/x.ttf is under the anchor
|
||||
once normalized, so it must dump as a plain relative path.
|
||||
"""
|
||||
anchor = Path("/config/esphome").absolute()
|
||||
path = Path("/config/other/../esphome/fonts/x.ttf").absolute()
|
||||
output = yaml_util.dump({"file": path}, relative_to=anchor)
|
||||
assert output.strip() == "file: fonts/x.ttf"
|
||||
|
||||
|
||||
def test_dump_path_dotdot_reference_outside_anchor() -> None:
|
||||
"""Test the relative_config_path("../...") shape stays relative."""
|
||||
anchor = Path("/config/esphome").absolute()
|
||||
path = anchor / ".." / "shared" / "font.ttf"
|
||||
output = yaml_util.dump({"file": path}, relative_to=anchor)
|
||||
assert output.strip() == "file: ../shared/font.ttf"
|
||||
|
||||
|
||||
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()
|
||||
path = anchor / "fonts" / "arial.ttf"
|
||||
assert "fonts/arial.ttf" in yaml_util.dump({"file": path}, relative_to=anchor)
|
||||
assert yaml_util.dump({"file": path}).strip() == f"file: {path}"
|
||||
|
||||
|
||||
def test_dump__redacts_sensitive_str_by_default() -> None:
|
||||
out = yaml_util.dump({"password": SensitiveStr("hunter2")})
|
||||
assert "\\033[8mhunter2\\033[28m" in out
|
||||
|
||||
Reference in New Issue
Block a user