mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
<= 2.6.2 instead of < 2.6.3: a 2.6.2 pre-release sorts above 2.6.2 and belongs to the package-major-3 encoding, so the helper now describes the same set as _format_framework_arduino_version and a 2.6.2-b1 pin formats as it does on dev. The module never logged, so its _LOGGER and logging import go.
155 lines
6.6 KiB
Python
155 lines
6.6 KiB
Python
"""Tests for esphome.arduino8266.framework (downloads and environment)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from esphome.arduino8266 import framework
|
|
import esphome.config_validation as cv
|
|
from esphome.core import CORE, EsphomeError
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_caches(tmp_path: Path) -> None:
|
|
framework.ccache_path.cache_clear()
|
|
CORE.build_path = tmp_path
|
|
|
|
|
|
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"
|
|
# 2.6.3+ cores use the same package-major-3 encoding (PlatformIO path)
|
|
assert framework.framework_package_version(cv.Version(2, 7, 4)) == "3.20704.0"
|
|
# A future major bump needs its own encoding, not a doomed registry lookup
|
|
with pytest.raises(EsphomeError, match="not supported yet"):
|
|
framework.framework_package_version(cv.Version(4, 0, 0))
|
|
# Cores up to 2.6.2 use other encodings; the helper is total, not wrong,
|
|
# and its boundary matches the PlatformIO era guard: a 2.6.2 pre-release
|
|
# sorts above 2.6.2 and keeps the package-major-3 encoding
|
|
with pytest.raises(EsphomeError, match="older package encoding"):
|
|
framework.framework_package_version(cv.Version(2, 6, 2))
|
|
assert framework.framework_package_version(cv.Version(2, 6, 2, "b1")) == "3.20602.0"
|
|
assert framework.framework_package_version(cv.Version(2, 6, 3)) == "3.20603.0"
|
|
|
|
|
|
def test_format_framework_arduino_version_pins_all_series() -> None:
|
|
"""The esp8266 component's PIO source formatter across every encoding
|
|
era, including the 4.x rejection it now shares with the installer."""
|
|
from esphome.components.esp8266 import _format_framework_arduino_version as fmt
|
|
|
|
assert fmt(cv.Version(2, 4, 1)) == "~1.20401.0"
|
|
assert fmt(cv.Version(2, 6, 2)) == "~2.20602.0"
|
|
assert fmt(cv.Version(2, 7, 4)) == "~3.20704.0"
|
|
assert fmt(cv.Version(3, 1, 2)) == "~3.30102.0"
|
|
# Anchored to the framework version line, not a bare EsphomeError
|
|
with pytest.raises(cv.Invalid, match="not supported yet") as excinfo:
|
|
fmt(cv.Version(4, 0, 0))
|
|
assert excinfo.value.path == ["version"]
|
|
|
|
|
|
def test_tools_path_default_and_prefix(tmp_path: Path) -> None:
|
|
with patch.dict(os.environ, {"ESPHOME_ARDUINO8266_PREFIX": str(tmp_path)}):
|
|
assert framework.get_arduino8266_tools_path() == tmp_path.resolve()
|
|
# A blank prefix must be treated as unset, not as the CWD
|
|
with patch.dict(os.environ, {"ESPHOME_ARDUINO8266_PREFIX": " "}):
|
|
path = framework.get_arduino8266_tools_path()
|
|
assert path.name == "arduino8266"
|
|
assert path != Path.cwd()
|
|
|
|
|
|
def test_check_and_install_returns_paths(tmp_path: Path) -> None:
|
|
with (
|
|
patch.dict(os.environ, {"ESPHOME_ARDUINO8266_PREFIX": str(tmp_path)}),
|
|
patch.object(framework, "install_package") as mock_install,
|
|
patch.object(framework, "find_ninja", return_value=tmp_path / "ninja"),
|
|
):
|
|
paths = framework.check_and_install(cv.Version(3, 1, 2))
|
|
assert paths.framework == tmp_path / "frameworks" / "3.30102.0"
|
|
assert paths.toolchain == tmp_path / "toolchains" / framework.TOOLCHAIN_VERSION
|
|
assert paths.ninja == tmp_path / "ninja"
|
|
assert mock_install.call_count == 2
|
|
# 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:
|
|
with patch.object(framework, "ccache_env", return_value={"CCACHE_DIR": "x"}):
|
|
env = framework.get_build_env(tmp_path)
|
|
assert env["PATH"].startswith(str(tmp_path / "bin") + os.pathsep)
|
|
assert env["CCACHE_DIR"] == "x"
|
|
|
|
|
|
def test_ccache_path_delegates_and_caches(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""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:
|
|
with patch.object(framework, "ccache_path", return_value=None):
|
|
assert framework.ccache_env() == {}
|
|
with (
|
|
patch.object(framework, "ccache_path", return_value="/usr/bin/ccache"),
|
|
patch.dict(os.environ, {"CCACHE_NOHASHDIR": "false"}, clear=True),
|
|
):
|
|
env = framework.ccache_env()
|
|
# User-set values are respected; the rest get defaults
|
|
assert "CCACHE_NOHASHDIR" not in env
|
|
assert env["CCACHE_DEPEND"] == "1"
|
|
assert env["CCACHE_BASEDIR"] == str(Path(CORE.build_path).resolve())
|
|
assert env["CCACHE_DIR"].endswith("ccache")
|
|
|
|
|
|
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"):
|
|
framework.check_and_install(cv.Version(3, 0, 2))
|
|
|
|
|
|
def test_get_build_env_without_path_has_no_empty_entry(tmp_path: Path) -> None:
|
|
"""An absent PATH must not leave a trailing separator (an empty entry
|
|
means the current directory to the shell)."""
|
|
with (
|
|
patch.dict(os.environ, {}, clear=True),
|
|
patch.object(framework, "ccache_env", return_value={}),
|
|
):
|
|
env = framework.get_build_env(tmp_path)
|
|
assert env["PATH"] == str(tmp_path / "bin")
|
|
with (
|
|
patch.dict(
|
|
os.environ, {"PATH": f"/usr/bin{os.pathsep}{os.pathsep}/bin"}, clear=True
|
|
),
|
|
patch.object(framework, "ccache_env", return_value={}),
|
|
):
|
|
env = framework.get_build_env(tmp_path)
|
|
assert env["PATH"].split(os.pathsep) == [str(tmp_path / "bin"), "/usr/bin", "/bin"]
|