mirror of
https://github.com/esphome/esphome.git
synced 2026-08-29 09:13:28 +00:00
Pin the lx106 layout, reject non-3.x cores, pin installer args, delegate ccache policy tests
This commit is contained in:
@@ -63,8 +63,15 @@ def framework_package_version(ver: Version) -> str:
|
||||
|
||||
Same encoding as the PlatformIO package registry uses for core 3.x
|
||||
releases (3.1.2 -> 3.30102.0). The native toolchain only supports core
|
||||
>= MIN_FRAMEWORK_VERSION, so the 1.x/2.x encodings never apply here.
|
||||
3.x: 1.x/2.x fall below MIN_FRAMEWORK_VERSION, and a future major bump
|
||||
needs its own encoding and toolchain pin rather than a registry lookup
|
||||
for a package that cannot exist.
|
||||
"""
|
||||
if ver.major != 3:
|
||||
raise EsphomeError(
|
||||
f"The native toolchain does not support Arduino core {ver} "
|
||||
"(only 3.x); use 'toolchain: platformio'"
|
||||
)
|
||||
return f"3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
|
||||
|
||||
|
||||
@@ -113,7 +120,8 @@ def check_and_install(framework_version: Version) -> InstalledPaths:
|
||||
toolchain_path,
|
||||
ESPHOME_ARDUINO8266_TOOLCHAIN_MIRRORS,
|
||||
downloads_dir,
|
||||
expect=("bin",),
|
||||
# xtensa-lx106-elf pins the target: every gcc package has a bin/
|
||||
expect=("bin", "xtensa-lx106-elf"),
|
||||
)
|
||||
return InstalledPaths(
|
||||
framework=framework_path, toolchain=toolchain_path, ninja=ninja_path
|
||||
|
||||
@@ -4,7 +4,6 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
@@ -23,6 +22,9 @@ def _clear_caches(tmp_path: Path) -> None:
|
||||
def test_framework_package_version() -> None:
|
||||
assert framework.framework_package_version(cv.Version(3, 1, 2)) == "3.30102.0"
|
||||
assert framework.framework_package_version(cv.Version(3, 2, 0)) == "3.30200.0"
|
||||
# A future major bump needs its own encoding, not a doomed registry lookup
|
||||
with pytest.raises(EsphomeError, match="only 3.x"):
|
||||
framework.framework_package_version(cv.Version(4, 0, 0))
|
||||
|
||||
|
||||
def test_tools_path_default_and_prefix(tmp_path: Path) -> None:
|
||||
@@ -46,11 +48,25 @@ def test_check_and_install_returns_paths(tmp_path: Path) -> None:
|
||||
assert paths.toolchain == tmp_path / "toolchains" / framework.TOOLCHAIN_VERSION
|
||||
assert paths.ninja == tmp_path / "ninja"
|
||||
assert mock_install.call_count == 2
|
||||
# The layout checks cover the directories write_project needs, including
|
||||
# the bundled libraries/ tree
|
||||
fw_expect = mock_install.call_args_list[0].kwargs["expect"]
|
||||
assert fw_expect == ("cores/esp8266", "tools/sdk", "libraries")
|
||||
assert mock_install.call_args_list[1].kwargs["expect"] == ("bin",)
|
||||
# Full argument pinning: a copy-paste swap between the two near-identical
|
||||
# calls (mirrors, destination) must not stay green
|
||||
fw_call, tc_call = mock_install.call_args_list
|
||||
assert fw_call.args == (
|
||||
framework.FRAMEWORK_PACKAGE,
|
||||
"3.30102.0",
|
||||
tmp_path / "frameworks" / "3.30102.0",
|
||||
framework.ESPHOME_ARDUINO8266_FRAMEWORK_MIRRORS,
|
||||
tmp_path / "downloads",
|
||||
)
|
||||
assert fw_call.kwargs["expect"] == ("cores/esp8266", "tools/sdk", "libraries")
|
||||
assert tc_call.args == (
|
||||
framework.TOOLCHAIN_PACKAGE,
|
||||
framework.TOOLCHAIN_VERSION,
|
||||
tmp_path / "toolchains" / framework.TOOLCHAIN_VERSION,
|
||||
framework.ESPHOME_ARDUINO8266_TOOLCHAIN_MIRRORS,
|
||||
tmp_path / "downloads",
|
||||
)
|
||||
assert tc_call.kwargs["expect"] == ("bin", "xtensa-lx106-elf")
|
||||
|
||||
|
||||
def test_get_build_env_prepends_toolchain_bin(tmp_path: Path) -> None:
|
||||
@@ -60,52 +76,18 @@ def test_get_build_env_prepends_toolchain_bin(tmp_path: Path) -> None:
|
||||
assert env["CCACHE_DIR"] == "x"
|
||||
|
||||
|
||||
def test_ccache_path_disabled_by_env() -> None:
|
||||
with patch.dict(os.environ, {"ESPHOME_CCACHE_ENABLE": "0"}):
|
||||
assert framework.ccache_path() is None
|
||||
|
||||
|
||||
def test_ccache_path_no_binary(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("ESPHOME_CCACHE_ENABLE", raising=False)
|
||||
with patch("shutil.which", return_value=None):
|
||||
assert framework.ccache_path() is None
|
||||
|
||||
|
||||
def test_ccache_path_probe_failure(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("ESPHOME_CCACHE_ENABLE", raising=False)
|
||||
with (
|
||||
patch("shutil.which", return_value="/usr/bin/ccache"),
|
||||
patch("subprocess.run", side_effect=subprocess.SubprocessError),
|
||||
):
|
||||
assert framework.ccache_path() is None
|
||||
|
||||
|
||||
def test_ccache_path_ok(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("ESPHOME_CCACHE_ENABLE", raising=False)
|
||||
with (
|
||||
patch("shutil.which", return_value="/usr/bin/ccache"),
|
||||
patch("subprocess.run"),
|
||||
):
|
||||
assert framework.ccache_path() == "/usr/bin/ccache"
|
||||
|
||||
|
||||
def test_ccache_path_explicit_missing_binary_warns(
|
||||
monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
|
||||
def test_ccache_path_delegates_and_caches(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setenv("ESPHOME_CCACHE_ENABLE", "1")
|
||||
with patch("shutil.which", return_value=None):
|
||||
assert framework.ccache_path() is None
|
||||
assert "no ccache binary is on PATH" in caplog.text
|
||||
|
||||
|
||||
def test_ccache_path_explicit_skips_probe(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""An explicit opt-in trusts the binary without the runnability probe."""
|
||||
monkeypatch.setenv("ESPHOME_CCACHE_ENABLE", "1")
|
||||
with (
|
||||
patch("shutil.which", return_value="/usr/bin/ccache"),
|
||||
patch("esphome.build_helpers.ccache._ccache_runs", side_effect=AssertionError),
|
||||
):
|
||||
"""The wrapper delegates to the shared policy (covered in
|
||||
build_helpers/test_ccache.py) and caches the result."""
|
||||
monkeypatch.delenv("ESPHOME_CCACHE_ENABLE", raising=False)
|
||||
with patch.object(
|
||||
framework, "resolve_ccache_path", return_value="/usr/bin/ccache"
|
||||
) as mock_resolve:
|
||||
assert framework.ccache_path() == "/usr/bin/ccache"
|
||||
assert framework.ccache_path() == "/usr/bin/ccache"
|
||||
mock_resolve.assert_called_once()
|
||||
|
||||
|
||||
def test_ccache_env(tmp_path: Path) -> None:
|
||||
@@ -123,16 +105,6 @@ def test_ccache_env(tmp_path: Path) -> None:
|
||||
assert env["CCACHE_DIR"].endswith("ccache")
|
||||
|
||||
|
||||
def test_ccache_env_requires_build_path() -> None:
|
||||
"""Building the env before preload set build_path fails loudly."""
|
||||
CORE.build_path = None
|
||||
with (
|
||||
patch.object(framework, "ccache_path", return_value="/cc/ccache"),
|
||||
pytest.raises(ValueError, match="build_path"),
|
||||
):
|
||||
framework.ccache_env()
|
||||
|
||||
|
||||
def test_check_and_install_rejects_old_core(tmp_path: Path) -> None:
|
||||
"""Calling the installer below the floor fails before any download."""
|
||||
with pytest.raises(EsphomeError, match=">= 3.1.1"):
|
||||
|
||||
Reference in New Issue
Block a user