mirror of
https://github.com/esphome/esphome.git
synced 2026-10-01 00:40:21 +00:00
Merge branch 'esp8266-native-ninja-emission' into esp8266-arduino-toolchain
This commit is contained in:
@@ -1,14 +1,8 @@
|
||||
"""Drift tests for the native ESP8266 Arduino build generator.
|
||||
|
||||
These pin the ESPHome side of the transliteration (the knob-define
|
||||
precedence, the define/flag sets, and the linker-script generation) against
|
||||
literals audited from the PlatformIO builder
|
||||
(framework-arduinoespressif8266/tools/platformio-build.py and
|
||||
platform-espressif8266/builder/main.py): the knob-define precedence, the
|
||||
define/flag sets, the link line, and the core source exclusions. They catch
|
||||
an accidental edit on this side; an upstream change in a new framework
|
||||
release is caught by the A/B byte-identical build check on a version bump,
|
||||
not by these tests.
|
||||
Pin the transliterated flag/define/link sets against literals audited from
|
||||
the PlatformIO builder. Upstream drift is caught by the A/B build check on
|
||||
version bumps, not here.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -17,10 +11,12 @@ from collections.abc import Generator
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.arduino.library import ArduinoLibrary
|
||||
from esphome.arduino8266.framework import InstalledPaths, toolchain_tool
|
||||
from esphome.build_gen import arduino8266
|
||||
from esphome.build_gen.arduino8266 import (
|
||||
@@ -30,7 +26,7 @@ from esphome.build_gen.arduino8266 import (
|
||||
_resolve_build_config,
|
||||
get_flash_ld_path,
|
||||
)
|
||||
from esphome.components.esp8266.boards import ESP8266_BOARD_BUILD
|
||||
from esphome.components.esp8266.boards import BOARDS, ESP8266_BOARD_BUILD
|
||||
from esphome.components.esp8266.build_surgery import RATETABLE_RULE
|
||||
from esphome.components.esp8266.const import (
|
||||
KEY_BOARD,
|
||||
@@ -255,9 +251,7 @@ def test_write_project_link_line_and_exclusions(tmp_path: Path) -> None:
|
||||
assert "-T eagle.flash.4m.ld" in content
|
||||
# scanf float disabled: the forced-link flag must not appear
|
||||
assert "_scanf_float" not in content
|
||||
# $in/$out must stay UNQUOTED: ninja shell-escapes its built-in path
|
||||
# variables itself, so added quotes would wrap ninja's quoting and break
|
||||
# space-containing paths.
|
||||
# $in/$out must stay unquoted; ninja escapes its own path variables
|
||||
assert "-c $in -o $out" in content
|
||||
assert "--app $in --flash_mode" in content
|
||||
assert '"$in"' not in content
|
||||
@@ -492,7 +486,6 @@ def test_generate_ld_scripts_testing_mode(tmp_path: Path) -> None:
|
||||
def test_write_project_libraries_and_variant(
|
||||
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
from esphome.arduino.library import ArduinoLibrary
|
||||
|
||||
paths = _make_framework(tmp_path)
|
||||
variant_src = paths.framework / "variants" / "nodemcu" / "variant.cpp"
|
||||
@@ -572,7 +565,6 @@ def test_write_project_testing_mode(tmp_path: Path) -> None:
|
||||
|
||||
def test_write_project_missing_framework_dir_raises(tmp_path: Path) -> None:
|
||||
"""An incomplete framework install fails naming the missing path."""
|
||||
import shutil
|
||||
|
||||
paths = _make_framework(tmp_path)
|
||||
shutil.rmtree(paths.framework / "tools" / "sdk" / "lwip2")
|
||||
@@ -781,10 +773,7 @@ def test_write_project_missing_src_dir_raises(tmp_path: Path) -> None:
|
||||
|
||||
|
||||
def test_build_config_custom_mmu_without_knob_raises() -> None:
|
||||
"""Custom MMU sizes without the CUSTOM knob would compile against a
|
||||
layout the linker script does not implement; refuse instead of warning
|
||||
(PlatformIO warns, but its defaults win the compile line; ours would
|
||||
not)."""
|
||||
"""Custom MMU sizes without the CUSTOM knob are refused."""
|
||||
with pytest.raises(EsphomeError, match="PIO_FRAMEWORK_ARDUINO_MMU_CUSTOM"):
|
||||
_resolve("-DMMU_IRAM_SIZE=0xC000")
|
||||
|
||||
@@ -833,8 +822,7 @@ def test_flag_tables_match_platformio_builder() -> None:
|
||||
"-free",
|
||||
"-fipa-pta",
|
||||
]
|
||||
# The -u block is where the deliberate -u _scanf_float omission lives;
|
||||
# pinned in full so "restoring" it fails here first
|
||||
# Pins the deliberate -u _scanf_float omission
|
||||
assert arduino8266._LINKFLAGS == [
|
||||
"-Os",
|
||||
"-nostdlib",
|
||||
@@ -895,7 +883,6 @@ def test_write_project_asm_keeps_quoted_defines(tmp_path: Path) -> None:
|
||||
|
||||
def test_write_project_unarchived_library_links_objects(tmp_path: Path) -> None:
|
||||
"""A libArchive:false library's objects reach the link directly."""
|
||||
from esphome.arduino.library import ArduinoLibrary
|
||||
|
||||
paths = _make_framework(tmp_path)
|
||||
lib_src = tmp_path / "gdb" / "src"
|
||||
@@ -927,8 +914,7 @@ def test_write_project_unknown_board_fails_by_name(tmp_path: Path) -> None:
|
||||
|
||||
|
||||
def test_unflag_tokens_join_spaced_entries() -> None:
|
||||
"""A spaced build_unflags entry ("-D FOO") removes -DFOO, and no bare half leaks into the
|
||||
unflag set to collaterally drop unrelated tokens."""
|
||||
"""Spaced build_unflags entries ("-D FOO") match the joined token."""
|
||||
CORE.build_unflags = {"-D FOO", "-l bar"}
|
||||
tokens = arduino8266._unflag_tokens()
|
||||
assert tokens == {"-DFOO", "-lbar"}
|
||||
@@ -950,8 +936,7 @@ def test_flag_defines_respects_unflags() -> None:
|
||||
|
||||
|
||||
def test_vtables_unknown_raises() -> None:
|
||||
"""A typo'd knob would win the sorted pick and die in the SDK header's
|
||||
#error; fail by name at generation instead."""
|
||||
"""An unknown VTABLES_IN_* knob fails by name."""
|
||||
with pytest.raises(EsphomeError, match="Unknown VTABLES_IN_.*BANANA"):
|
||||
_resolve("-DVTABLES_IN_BANANA")
|
||||
|
||||
@@ -1044,9 +1029,7 @@ def test_lexed_build_flags_shared_between_consumers(
|
||||
"tok", ["-Tcustom.ld", "-Xlinker", "-u", "-e", "-s", "-static", "-nostartfiles"]
|
||||
)
|
||||
def test_project_flags_rejects_plain_linker_forms(tok: str) -> None:
|
||||
"""A plain-form linker flag would land on the -c compile line where it
|
||||
is inert; refuse naming the -Wl, form instead of shipping firmware that
|
||||
silently lacks the requested link behavior."""
|
||||
"""Plain-form linker flags are refused, naming the -Wl, form."""
|
||||
_set_flags(tok)
|
||||
with pytest.raises(EsphomeError, match="use the -Wl, form"):
|
||||
arduino8266._project_flags(set(), arduino8266._lexed_build_flags())
|
||||
@@ -1227,11 +1210,7 @@ def test_generate_ld_scripts_unreadable_header_forces_regeneration(
|
||||
|
||||
|
||||
def test_board_tables_are_equal() -> None:
|
||||
"""write_project rejects a board missing from either table, so the two
|
||||
must stay exactly in sync (the build-surgery test only checks the
|
||||
subset direction, which is how d1_wroom_02 went missing)."""
|
||||
from esphome.components.esp8266.boards import BOARDS
|
||||
|
||||
"""BOARDS and ESP8266_BOARD_BUILD must stay exactly in sync."""
|
||||
assert set(BOARDS) == set(ESP8266_BOARD_BUILD)
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -50,8 +52,6 @@ def test_unknown_mode(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
|
||||
def test_runs_as_script(tmp_path: Path) -> None:
|
||||
"""The ninja rules invoke the file as a plain script."""
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
src = tmp_path / "a.bin"
|
||||
src.write_text("x")
|
||||
|
||||
@@ -429,8 +429,7 @@ def test_load_or_build_idedata_corrupted_cache_is_logged(
|
||||
|
||||
|
||||
def test_load_or_build_idedata_never_caches_a_launcher(tmp_path: Path) -> None:
|
||||
"""A compile DB naming a launcher as the compiler is rejected by name,
|
||||
before the toolchain probe could fail opaquely, and never cached."""
|
||||
"""A compile DB naming a launcher as the compiler is rejected, never cached."""
|
||||
compile_commands = tmp_path / "compile_commands.json"
|
||||
compile_commands.write_text(
|
||||
json.dumps(
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -84,7 +85,6 @@ def test_shell_token_quotes_shell_metacharacters() -> None:
|
||||
def test_shell_token_posix_roundtrips_through_sh() -> None:
|
||||
"""Backslash runs, $, backticks, and quotes must reach the compiler
|
||||
exactly as lexed once ninja un-doubles $$ and /bin/sh strips quotes."""
|
||||
import subprocess
|
||||
|
||||
if sys.platform == "win32":
|
||||
pytest.skip("POSIX sh quoting")
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.esp8266 import build_surgery
|
||||
from esphome.components.esp8266.boards import BOARDS, ESP8266_BOARD_BUILD
|
||||
from esphome.components.esp8266.build_surgery import (
|
||||
RATETABLE_RULE,
|
||||
@@ -110,11 +115,6 @@ def test_board_build_covers_every_board() -> None:
|
||||
def test_surgery_fingerprint_is_stable_and_sensitive(tmp_path) -> None:
|
||||
"""The properties the linker-script cache depends on: the fingerprint is
|
||||
stable across calls and changes when the module's source changes."""
|
||||
import importlib.util
|
||||
from pathlib import Path as _Path
|
||||
import sys
|
||||
|
||||
from esphome.components.esp8266 import build_surgery
|
||||
|
||||
first = build_surgery.surgery_fingerprint()
|
||||
assert first == build_surgery.surgery_fingerprint()
|
||||
@@ -124,7 +124,7 @@ def test_surgery_fingerprint_is_stable_and_sensitive(tmp_path) -> None:
|
||||
# A modified copy of the module must fingerprint differently
|
||||
copy = tmp_path / "build_surgery_variant.py"
|
||||
copy.write_text(
|
||||
_Path(build_surgery.__file__).read_text(encoding="utf-8")
|
||||
Path(build_surgery.__file__).read_text(encoding="utf-8")
|
||||
+ "\nEXTRA_BEHAVIORAL_INPUT = 1\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
@@ -26,9 +26,8 @@ def test_framework_package_version() -> None:
|
||||
# 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
|
||||
# The boundary matches the PlatformIO era guard; a 2.6.2 pre-release
|
||||
# keeps this 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"
|
||||
@@ -102,9 +101,8 @@ def test_get_build_env_prepends_toolchain_bin(tmp_path: Path) -> None:
|
||||
def test_ccache_path_delegates_uncached(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""The wrapper delegates to the shared policy (covered in
|
||||
build_helpers/test_ccache.py) on every call: the env/PATH decision
|
||||
must not freeze for the process lifetime in a long-lived host."""
|
||||
"""Delegates on every call; the env/PATH decision must not freeze for
|
||||
the process lifetime."""
|
||||
monkeypatch.delenv("ESPHOME_CCACHE_ENABLE", raising=False)
|
||||
with patch.object(
|
||||
framework, "resolve_ccache_path", return_value="/usr/bin/ccache"
|
||||
|
||||
@@ -12,7 +12,13 @@ import pytest
|
||||
from esphome.arduino import library as component
|
||||
from esphome.const import KEY_CORE, KEY_TARGET_PLATFORM, PLATFORM_ESP8266
|
||||
from esphome.core import CORE, EsphomeError, Library
|
||||
from esphome.platformio.library import ConvertedLibrary, LibraryBackend
|
||||
import esphome.platformio.library as pio_library
|
||||
from esphome.platformio.library import (
|
||||
ConvertedLibrary,
|
||||
IncompatiblePlatform,
|
||||
InvalidLibrary,
|
||||
LibraryBackend,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -212,9 +218,7 @@ def test_resolve_libraries_bundled(tmp_path: Path) -> None:
|
||||
def test_resolve_libraries_registry_name_is_external(
|
||||
tmp_path: Path, version: str | None
|
||||
) -> None:
|
||||
"""A name that is not bundled reaches the converter: bare resolves from
|
||||
the registry at the latest version (matching PlatformIO and the
|
||||
documented libraries: key) and a version pin is a registry package."""
|
||||
"""A name that is not bundled reaches the converter, bare or pinned."""
|
||||
framework = _make_framework(tmp_path)
|
||||
_add_library("pngle", version)
|
||||
with patch.object(component, "convert_libraries", return_value=[]) as mock_convert:
|
||||
@@ -410,12 +414,8 @@ def test_bundled_dependency_nonplatform_rejection_warns(
|
||||
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""An InvalidLibrary whose cause is not the platform filter is visible."""
|
||||
from esphome.platformio.library import InvalidLibrary
|
||||
|
||||
framework = _make_framework(tmp_path)
|
||||
converted = _webserver(tmp_path, {"build": {}, "dependencies": [{"name": "Wire"}]})
|
||||
import esphome.platformio.library as pio_library
|
||||
|
||||
with (
|
||||
_emitting_converter(converted),
|
||||
patch.object(
|
||||
@@ -466,9 +466,7 @@ def test_library_info_lib_archive_parse(
|
||||
|
||||
|
||||
def test_bundled_dependency_dict_shorthand_prefers_bundled(tmp_path: Path) -> None:
|
||||
"""The {"Wire": "*"} dict shorthand (version="*", no owner) must resolve
|
||||
to the bundled library, matching PIO's process_dependencies, instead of
|
||||
being routed to the registry."""
|
||||
"""The {"Wire": "*"} dict shorthand resolves to the bundled library."""
|
||||
framework = _make_framework(tmp_path)
|
||||
converted = _webserver(tmp_path, {"build": {}, "dependencies": {"Wire": "*"}})
|
||||
with _emitting_converter(converted):
|
||||
@@ -481,12 +479,8 @@ def test_bundled_dependency_platform_rejection_is_debug(
|
||||
) -> None:
|
||||
"""The typed IncompatiblePlatform (the routine cross-platform skip)
|
||||
stays at debug regardless of message wording."""
|
||||
from esphome.platformio.library import IncompatiblePlatform
|
||||
|
||||
framework = _make_framework(tmp_path)
|
||||
converted = _webserver(tmp_path, {"build": {}, "dependencies": [{"name": "Wire"}]})
|
||||
import esphome.platformio.library as pio_library
|
||||
|
||||
with (
|
||||
_emitting_converter(converted),
|
||||
patch.object(
|
||||
@@ -626,11 +620,8 @@ def test_bundled_library_non_dict_manifest_skips_probes_and_raises(
|
||||
def test_dict_shorthand_dependency_skips_registry_through_real_converter(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""{"Wire": "*"} in a real manifest must never reach the registry: the
|
||||
graph walk skips backend-provided names and the bundled copy is added
|
||||
after emit (no converter mock; a registry touch fails the test)."""
|
||||
import esphome.platformio.library as pio_library
|
||||
|
||||
"""{"Wire": "*"} resolves to the bundled copy without touching the
|
||||
registry (real converter)."""
|
||||
framework = _make_framework(tmp_path)
|
||||
_local_lib(tmp_path, {"Wire": "*"})
|
||||
# Pin the component cache to tmp_path (data_dir honors an ambient
|
||||
@@ -702,8 +693,6 @@ def test_pinned_bundled_dependency_substitution_warns(
|
||||
) -> None:
|
||||
"""A non-* version pin on a backend-provided dependency is discarded
|
||||
for the bundled copy; the substitution must be visible."""
|
||||
import esphome.platformio.library as pio_library
|
||||
|
||||
framework = _make_framework(tmp_path)
|
||||
_local_lib(tmp_path, {"Wire": "^2.0.0"})
|
||||
monkeypatch.setenv("ESPHOME_DATA_DIR", str(tmp_path / ".esphome"))
|
||||
@@ -720,9 +709,7 @@ def test_pinned_bundled_dependency_substitution_warns(
|
||||
def test_transitively_resolved_dependency_does_not_warn(
|
||||
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""A version-less dependency the walk resolved as another library's
|
||||
registry dependency is present in the build; the skipping warning must
|
||||
stay quiet for it."""
|
||||
"""A dependency the walk already resolved does not warn."""
|
||||
framework = _make_framework(tmp_path)
|
||||
_add_library("ESP32Async/ESPAsyncWebServer", "3.9.6")
|
||||
ws, tcp = _ws_tcp_pair(tmp_path)
|
||||
@@ -754,9 +741,8 @@ def test_external_short_name(spec: str, expected: str) -> None:
|
||||
def test_converted_manifest_name_suppresses_bundled_dependency(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""A dependency name a converted library's manifest provides is not
|
||||
also added from the framework tree (a duplicate archive would surface
|
||||
as duplicate-symbol link errors), even when the provider emits later."""
|
||||
"""A name a converted library's manifest provides is not also added
|
||||
from the framework tree, even when the provider emits later."""
|
||||
framework = _make_framework(tmp_path)
|
||||
_add_library("ESP32Async/ESPAsyncWebServer", "3.9.6")
|
||||
# Requested under a different short name; only the manifest says "Wire"
|
||||
@@ -814,8 +800,6 @@ def test_versionless_dependency_with_provider_stays_quiet(
|
||||
) -> None:
|
||||
"""With a provides backend the version-less skip is routine (debug) and
|
||||
the bundled copy is picked up after emit."""
|
||||
import esphome.platformio.library as pio_library
|
||||
|
||||
framework = _make_framework(tmp_path)
|
||||
_local_lib(tmp_path, [{"name": "Wire"}])
|
||||
monkeypatch.setenv("ESPHOME_DATA_DIR", str(tmp_path / ".esphome"))
|
||||
|
||||
@@ -643,8 +643,7 @@ def test_save_compiled_config_and_sidecar_toolchain_mismatch(
|
||||
tmp_path: Path, sidecar_toolchain: str | None, saved: bool
|
||||
) -> None:
|
||||
"""A config validated under a different toolchain than the compile's
|
||||
must not overwrite the cache: platforms normalize toolchain-sensitive
|
||||
keys differently and the sidecar keeps the compile's toolchain."""
|
||||
must not overwrite the cache."""
|
||||
yaml_path = _bare_yaml(tmp_path)
|
||||
_prime_core(tmp_path)
|
||||
CORE.config = {CONF_ESPHOME: {CONF_NAME: "lite_test"}}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
@@ -48,6 +49,7 @@ from esphome.const import (
|
||||
TYPE_GIT,
|
||||
TYPE_LOCAL,
|
||||
Framework,
|
||||
Toolchain,
|
||||
)
|
||||
from esphome.core import (
|
||||
CORE,
|
||||
@@ -3169,9 +3171,6 @@ def test_file__remapped_path_is_directory_raises(setup_core: Path) -> None:
|
||||
|
||||
def test_require_platformio_toolchain() -> None:
|
||||
"""Platforms with only the PlatformIO backend reject other toolchains."""
|
||||
from esphome.const import Toolchain
|
||||
from esphome.core import CORE
|
||||
|
||||
validator = cv.require_platformio_toolchain("RP2")
|
||||
CORE.toolchain = None
|
||||
config: dict = {}
|
||||
@@ -3186,9 +3185,6 @@ def test_require_platformio_toolchain() -> None:
|
||||
def test_check_supported_toolchain_unresolved_is_an_ordering_bug() -> None:
|
||||
"""Calling the check before resolution fails naming the ordering bug,
|
||||
not a user-facing unsupported-toolchain error."""
|
||||
from esphome.const import Toolchain
|
||||
from esphome.core import CORE
|
||||
|
||||
CORE.toolchain = None
|
||||
with pytest.raises(Invalid, match="not resolved before RP2 validation"):
|
||||
cv._check_supported_toolchain("RP2", (Toolchain.PLATFORMIO,))
|
||||
@@ -3209,14 +3205,7 @@ def test_check_supported_toolchain_unresolved_is_an_ordering_bug() -> None:
|
||||
def test_every_platformio_only_platform_rejects_arduino_toolchain(
|
||||
platform: str, minimal_config: dict
|
||||
) -> None:
|
||||
"""The invariant every native-toolchain gate relies on: a platform that
|
||||
cannot serve a CLI toolchain rejects it at validation (esp32, esp8266,
|
||||
and nrf52 pin this in their own suites)."""
|
||||
import importlib
|
||||
|
||||
from esphome.const import Toolchain
|
||||
from esphome.core import CORE
|
||||
|
||||
"""A platform that cannot serve a CLI toolchain rejects it at validation."""
|
||||
module = importlib.import_module(f"esphome.components.{platform}")
|
||||
CORE.toolchain = Toolchain.ARDUINO
|
||||
with pytest.raises(Invalid, match="Unsupported toolchain 'arduino'"):
|
||||
|
||||
@@ -6,6 +6,7 @@ from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components import esp32 as esp32_module
|
||||
from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
@@ -15,6 +16,7 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.core import CORE, Library
|
||||
from esphome.espidf.component import (
|
||||
_emit_idf_component,
|
||||
generate_cmakelists_txt,
|
||||
generate_idf_component_yml,
|
||||
generate_idf_components,
|
||||
@@ -1072,8 +1074,6 @@ def test_idf_component_download_passes_salt() -> None:
|
||||
def test_emit_idf_component_wires_esp32_target(tmp_path, monkeypatch):
|
||||
"""Emitting a component resolves the esp32 variant into the shared
|
||||
extraScript helper."""
|
||||
from esphome.components import esp32 as esp32_module
|
||||
from esphome.espidf.component import _emit_idf_component
|
||||
|
||||
monkeypatch.setattr(esp32_module, "get_esp32_variant", lambda: "ESP32")
|
||||
(tmp_path / "src").mkdir()
|
||||
|
||||
@@ -1395,8 +1395,6 @@ def test_get_framework_env_without_python_env_uses_os_path(tmp_path: Path) -> No
|
||||
|
||||
def _ccache_patches(tmp_path: Path, which: str | None, build_path: Path | None):
|
||||
return (
|
||||
# The gate defers to the shared resolver (which carries the PATH
|
||||
# lookup, ESPHOME_CCACHE_ENABLE parse, and runnability probe)
|
||||
patch("esphome.espidf.framework.resolve_ccache_path", return_value=which),
|
||||
patch(
|
||||
"esphome.espidf.framework.get_idf_tools_path",
|
||||
|
||||
@@ -101,6 +101,7 @@ from esphome.const import (
|
||||
CONF_WEB_SERVER,
|
||||
CONF_WIFI,
|
||||
KEY_CORE,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
@@ -7268,7 +7269,7 @@ def test_command_idedata_incompatible_toolchain(tmp_path: Path) -> None:
|
||||
RuntimeError("Could not query builtin include dirs"),
|
||||
ValueError("no C++ translation unit found"),
|
||||
KeyError("command"),
|
||||
None, # replaced with EsphomeError inside (import is function-local)
|
||||
None, # replaced with EsphomeError inside
|
||||
],
|
||||
)
|
||||
def test_compile_program_espidf_idedata_failure_does_not_fail_build(
|
||||
@@ -7276,14 +7277,6 @@ def test_compile_program_espidf_idedata_failure_does_not_fail_build(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""A post-compile idedata error is a warning: the firmware already built."""
|
||||
from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
Toolchain,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError
|
||||
|
||||
if error is None:
|
||||
error = EsphomeError("compile database is unusable")
|
||||
CORE.toolchain = Toolchain.ESP_IDF
|
||||
@@ -7307,14 +7300,6 @@ def test_compile_program_espidf_idedata_success_is_silent(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""The healthy path: idedata generated, nothing to warn about."""
|
||||
from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
Toolchain,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
CORE.toolchain = Toolchain.ESP_IDF
|
||||
CORE.data[KEY_CORE] = {
|
||||
KEY_TARGET_PLATFORM: "esp32",
|
||||
@@ -7336,14 +7321,6 @@ def test_compile_program_espidf_idedata_none_warns(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""A silent None from the post-compile idedata refresh is made visible."""
|
||||
from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
Toolchain,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
CORE.toolchain = Toolchain.ESP_IDF
|
||||
CORE.data[KEY_CORE] = {
|
||||
KEY_TARGET_PLATFORM: "esp32",
|
||||
@@ -7394,8 +7371,6 @@ def test_command_analyze_memory_unsupported_toolchain(
|
||||
def test_cli_toolchain_skips_the_validated_config_cache(tmp_path: Path) -> None:
|
||||
"""An explicit --toolchain must run the per-platform validators, so the
|
||||
upload/logs fast path becomes a cache miss."""
|
||||
from esphome.__main__ import run_esphome
|
||||
|
||||
conf = tmp_path / "device.yaml"
|
||||
conf.write_text("esphome:\n name: t\n")
|
||||
argv = ["esphome", "--toolchain", "arduino", "logs", str(conf)]
|
||||
@@ -7437,8 +7412,6 @@ def test_cli_toolchain_still_refreshes_the_validated_config_cache(
|
||||
"""An explicit --toolchain gates only the cache read; the freshly
|
||||
validated config is still saved so a later plain run keeps the fast
|
||||
path (an existing compile-written sidecar keeps its toolchain)."""
|
||||
from esphome.__main__ import run_esphome
|
||||
|
||||
conf = tmp_path / "device.yaml"
|
||||
conf.write_text("esphome:\n name: t\n")
|
||||
argv = ["esphome", "--toolchain", "platformio", "logs", str(conf)]
|
||||
|
||||
@@ -7,8 +7,10 @@ import sys
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import platformdirs
|
||||
import pytest
|
||||
|
||||
from esphome.components.nrf52 import _resolve_toolchain
|
||||
from esphome.components.nrf52.framework import (
|
||||
_PLATFORMIO_PENV_REQUIREMENTS,
|
||||
_REQUIREMENTS,
|
||||
@@ -22,8 +24,9 @@ from esphome.components.nrf52.framework import (
|
||||
get_sdk_nrf_tools_path,
|
||||
setup_platformio_python_env,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.config_validation import Version
|
||||
from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION
|
||||
from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION, Toolchain
|
||||
from esphome.core import CORE, EsphomeError
|
||||
from esphome.framework_helpers import get_python_env_executable_path
|
||||
|
||||
@@ -558,7 +561,6 @@ def testget_tools_path_blank_env_falls_back_to_default(
|
||||
Path("") would resolve to the working directory, which clean-all could
|
||||
then delete by accident.
|
||||
"""
|
||||
import platformdirs
|
||||
|
||||
monkeypatch.setenv("ESPHOME_SDK_NRF_PREFIX", value)
|
||||
expected = (
|
||||
@@ -570,7 +572,6 @@ def testget_tools_path_blank_env_falls_back_to_default(
|
||||
def testget_tools_path_default_is_global_cache(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
import platformdirs
|
||||
|
||||
monkeypatch.delenv("ESPHOME_SDK_NRF_PREFIX", raising=False)
|
||||
expected = (
|
||||
@@ -623,9 +624,6 @@ def test_needs_venv_rebuild_on_dangling_interpreter_symlink(tmp_path: Path) -> N
|
||||
|
||||
def test_resolve_toolchain_rejects_unsupported() -> None:
|
||||
"""A --toolchain nRF52 cannot serve fails instead of degrading silently."""
|
||||
from esphome.components.nrf52 import _resolve_toolchain
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import Toolchain
|
||||
|
||||
CORE.toolchain = Toolchain.ARDUINO
|
||||
with pytest.raises(cv.Invalid, match="Unsupported toolchain 'arduino'"):
|
||||
|
||||
@@ -2,19 +2,25 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.core import EsphomeError
|
||||
from esphome.platformio.extra_script import (
|
||||
ExtraScriptResult,
|
||||
_FakeSConsEnv,
|
||||
apply_extra_script,
|
||||
captured_as_build_flags,
|
||||
run_extra_script,
|
||||
)
|
||||
from esphome.platformio.library import ConvertedLibrary as IDFComponent, URLSource
|
||||
|
||||
|
||||
def test_extra_script_captures_libpath_libs_and_defines(tmp_path):
|
||||
from esphome.platformio.extra_script import (
|
||||
captured_as_build_flags,
|
||||
run_extra_script,
|
||||
)
|
||||
|
||||
(tmp_path / "src" / "esp32").mkdir(parents=True)
|
||||
script = tmp_path / "extra_script.py"
|
||||
@@ -58,10 +64,6 @@ def test_extra_script_libpath_relative_resolves_against_library_dir(
|
||||
"""Relative LIBPATH entries must resolve against ``library_dir``, not the
|
||||
caller's CWD (the shim restores CWD before ``captured_as_build_flags``
|
||||
runs)."""
|
||||
from esphome.platformio.extra_script import (
|
||||
ExtraScriptResult,
|
||||
captured_as_build_flags,
|
||||
)
|
||||
|
||||
(tmp_path / "lib" / "esp32").mkdir(parents=True)
|
||||
elsewhere = tmp_path.parent / "not_the_library_dir"
|
||||
@@ -76,10 +78,6 @@ def test_extra_script_libpath_relative_resolves_against_library_dir(
|
||||
|
||||
|
||||
def test_extra_script_libpath_absolute_outside_library_dir(tmp_path):
|
||||
from esphome.platformio.extra_script import (
|
||||
ExtraScriptResult,
|
||||
captured_as_build_flags,
|
||||
)
|
||||
|
||||
outside = tmp_path.parent / "system_lib"
|
||||
outside.mkdir(exist_ok=True)
|
||||
@@ -90,7 +88,6 @@ def test_extra_script_libpath_absolute_outside_library_dir(tmp_path):
|
||||
|
||||
|
||||
def test_extra_script_failure_returns_empty_result(tmp_path, caplog):
|
||||
from esphome.platformio.extra_script import run_extra_script
|
||||
|
||||
script = tmp_path / "broken.py"
|
||||
script.write_text("raise RuntimeError('boom')\n")
|
||||
@@ -106,7 +103,6 @@ def test_extra_script_failure_returns_empty_result(tmp_path, caplog):
|
||||
|
||||
|
||||
def test_apply_extra_script_path_traversal_is_rejected(tmp_path):
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
library_dir = tmp_path / "lib"
|
||||
library_dir.mkdir()
|
||||
@@ -117,8 +113,6 @@ def test_apply_extra_script_path_traversal_is_rejected(tmp_path):
|
||||
c.path = library_dir
|
||||
c.data = {"build": {"extraScript": "../evil.py"}}
|
||||
|
||||
from esphome.core import EsphomeError
|
||||
|
||||
with pytest.raises(EsphomeError, match="escapes the library directory"):
|
||||
apply_extra_script(c, board_mcu=lambda: "esp32", pio_platform="espressif32")
|
||||
# Nothing was folded into flags: the traversal was rejected before
|
||||
@@ -127,7 +121,6 @@ def test_apply_extra_script_path_traversal_is_rejected(tmp_path):
|
||||
|
||||
|
||||
def test_apply_extra_script_merges_into_existing_flags(tmp_path):
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
(tmp_path / "src").mkdir()
|
||||
script = tmp_path / "extra.py"
|
||||
@@ -146,8 +139,6 @@ def test_apply_extra_script_merges_into_existing_flags(tmp_path):
|
||||
def test_apply_extra_script_malformed_flags_raises(tmp_path) -> None:
|
||||
"""A null/dict build.flags fails naming the library instead of injecting
|
||||
a non-string into the compiler command line."""
|
||||
from esphome.core import EsphomeError
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
(tmp_path / "src").mkdir()
|
||||
script = tmp_path / "extra.py"
|
||||
@@ -164,7 +155,6 @@ def test_apply_extra_script_malformed_flags_raises(tmp_path) -> None:
|
||||
def test_apply_extra_script_callable_target_and_str_flags(tmp_path) -> None:
|
||||
"""The shared helper resolves the board_mcu callable lazily and normalizes
|
||||
a string ``build.flags`` value into a list before extending it."""
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
(tmp_path / "src").mkdir()
|
||||
script = tmp_path / "extra.py"
|
||||
@@ -180,7 +170,6 @@ def test_apply_extra_script_callable_target_and_str_flags(tmp_path) -> None:
|
||||
|
||||
|
||||
def test_apply_extra_script_no_script_and_no_flags(tmp_path) -> None:
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
# No extraScript declared: nothing happens, the target is never resolved
|
||||
c = IDFComponent("owner/name", "1.0", source=URLSource("http://dummy"))
|
||||
@@ -203,9 +192,6 @@ def test_apply_extra_script_no_script_and_no_flags(tmp_path) -> None:
|
||||
def test_apply_extra_script_ignores_uncaptured_env_calls(tmp_path, caplog) -> None:
|
||||
"""Un-captured env vars and unsupported env methods are skipped but
|
||||
diagnosable from the build log."""
|
||||
import logging
|
||||
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
caplog.set_level(logging.DEBUG)
|
||||
script = tmp_path / "extra.py"
|
||||
@@ -223,7 +209,6 @@ def test_apply_extra_script_ignores_uncaptured_env_calls(tmp_path, caplog) -> No
|
||||
|
||||
def test_apply_extra_script_swallows_script_errors(tmp_path, caplog) -> None:
|
||||
"""A raising extra-script is best-effort: logged and skipped."""
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
script = tmp_path / "extra.py"
|
||||
script.write_text("raise RuntimeError('boom')\n")
|
||||
@@ -237,7 +222,6 @@ def test_apply_extra_script_swallows_script_errors(tmp_path, caplog) -> None:
|
||||
|
||||
def test_apply_extra_script_pio_platform(tmp_path) -> None:
|
||||
"""The backend's platform token is exposed to the script as PIOPLATFORM."""
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
script = tmp_path / "extra.py"
|
||||
script.write_text("env.Append(LIBS=[env.get('PIOPLATFORM')])\n")
|
||||
@@ -251,8 +235,6 @@ def test_apply_extra_script_pio_platform(tmp_path) -> None:
|
||||
def test_apply_extra_script_missing_script_raises(tmp_path) -> None:
|
||||
"""A declared but absent extraScript is a broken package and fails by
|
||||
name, as it would under PlatformIO."""
|
||||
from esphome.core import EsphomeError
|
||||
from esphome.platformio.extra_script import apply_extra_script
|
||||
|
||||
c = IDFComponent("owner/name", "1.0", source=URLSource("http://dummy"))
|
||||
c.path = tmp_path
|
||||
@@ -264,7 +246,6 @@ def test_apply_extra_script_missing_script_raises(tmp_path) -> None:
|
||||
def test_run_extra_script_failure_discards_partial_capture(tmp_path, caplog) -> None:
|
||||
"""A crashed script yields an empty result: half-applied flags could
|
||||
build wrong-output firmware that links cleanly."""
|
||||
from esphome.platformio.extra_script import run_extra_script
|
||||
|
||||
script = tmp_path / "extra.py"
|
||||
script.write_text("env.Append(LIBS=['algobsec'])\nraise RuntimeError('boom')\n")
|
||||
@@ -278,7 +259,6 @@ def test_run_extra_script_failure_discards_partial_capture(tmp_path, caplog) ->
|
||||
def test_run_extra_script_syntax_error_is_best_effort(tmp_path, caplog) -> None:
|
||||
"""A vendored script that does not even compile warns and skips instead
|
||||
of aborting the build."""
|
||||
from esphome.platformio.extra_script import run_extra_script
|
||||
|
||||
script = tmp_path / "extra.py"
|
||||
script.write_text("def broken(:\n")
|
||||
@@ -291,7 +271,6 @@ def test_run_extra_script_syntax_error_is_best_effort(tmp_path, caplog) -> None:
|
||||
|
||||
def test_unsupported_env_method_warns_once(caplog) -> None:
|
||||
"""Repeated calls to the same unsupported method warn only once."""
|
||||
from esphome.platformio.extra_script import _FakeSConsEnv
|
||||
|
||||
env = _FakeSConsEnv(
|
||||
board_mcu="esp8266", pio_env="esphome_esp8266", pio_platform="espressif8266"
|
||||
@@ -304,7 +283,6 @@ def test_unsupported_env_method_warns_once(caplog) -> None:
|
||||
def test_run_extra_script_sys_exit_is_best_effort(tmp_path, caplog) -> None:
|
||||
"""A nonzero sys.exit() in a vendored script must not kill the esphome
|
||||
run, and its output is discarded."""
|
||||
from esphome.platformio.extra_script import run_extra_script
|
||||
|
||||
script = tmp_path / "extra.py"
|
||||
script.write_text("import sys\nenv.Append(LIBS=['x'])\nsys.exit(3)\n")
|
||||
@@ -317,7 +295,6 @@ def test_run_extra_script_sys_exit_is_best_effort(tmp_path, caplog) -> None:
|
||||
|
||||
def test_run_extra_script_sys_exit_zero_is_success(tmp_path, caplog) -> None:
|
||||
"""sys.exit(0) is a normal PlatformIO script ending: the capture is kept."""
|
||||
from esphome.platformio.extra_script import run_extra_script
|
||||
|
||||
script = tmp_path / "extra.py"
|
||||
script.write_text("import sys\nenv.Append(LIBS=['algobsec'])\nsys.exit(0)\n")
|
||||
@@ -330,10 +307,6 @@ def test_run_extra_script_sys_exit_zero_is_success(tmp_path, caplog) -> None:
|
||||
|
||||
def test_run_extra_script_unreadable_raises(tmp_path) -> None:
|
||||
"""An unreadable declared script is a broken package, like a missing one."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from esphome.core import EsphomeError
|
||||
from esphome.platformio.extra_script import run_extra_script
|
||||
|
||||
script = tmp_path / "extra.py"
|
||||
script.write_text("")
|
||||
@@ -348,7 +321,6 @@ def test_run_extra_script_unreadable_raises(tmp_path) -> None:
|
||||
|
||||
def test_run_extra_script_bad_encoding_is_best_effort(tmp_path, caplog) -> None:
|
||||
"""Undecodable content warns and skips, like a SyntaxError."""
|
||||
from esphome.platformio.extra_script import run_extra_script
|
||||
|
||||
script = tmp_path / "extra.py"
|
||||
script.write_bytes(b"\xff\xfe\x00bad")
|
||||
@@ -361,7 +333,6 @@ def test_run_extra_script_bad_encoding_is_best_effort(tmp_path, caplog) -> None:
|
||||
|
||||
def test_uncaptured_append_key_warns_once(caplog) -> None:
|
||||
"""A loop of Appends to the same uncaptured key warns once."""
|
||||
from esphome.platformio.extra_script import _FakeSConsEnv
|
||||
|
||||
env = _FakeSConsEnv(
|
||||
board_mcu="esp8266", pio_env="esphome_esp8266", pio_platform="espressif8266"
|
||||
|
||||
@@ -13,6 +13,7 @@ import pytest
|
||||
from esphome.core import EsphomeError, Library
|
||||
import esphome.platformio.library as lib
|
||||
from esphome.platformio.library import (
|
||||
SOURCE_KIND_FOR_SUFFIX,
|
||||
ConvertedLibrary,
|
||||
GitSource,
|
||||
InvalidLibrary,
|
||||
@@ -23,6 +24,8 @@ from esphome.platformio.library import (
|
||||
_resolve_registry_version,
|
||||
check_library_data,
|
||||
convert_libraries,
|
||||
join_flag_args,
|
||||
split_flag_entry,
|
||||
)
|
||||
|
||||
|
||||
@@ -539,7 +542,6 @@ def test_convert_libraries_skips_incompatible_dependency(tmp_path, monkeypatch):
|
||||
|
||||
def test_split_flag_entry_unbalanced_quote_is_clean() -> None:
|
||||
"""A malformed flags entry raises EsphomeError, not a raw ValueError."""
|
||||
from esphome.platformio.library import split_flag_entry
|
||||
|
||||
assert split_flag_entry('-DX="a b"', "library x") == ["-DX=a b"]
|
||||
with pytest.raises(EsphomeError, match=r"Malformed build flag.*library x"):
|
||||
@@ -548,7 +550,6 @@ def test_split_flag_entry_unbalanced_quote_is_clean() -> None:
|
||||
|
||||
def test_join_flag_args_reglues_spaced_define() -> None:
|
||||
"""A spaced -D re-glues to its argument, as ParseFlags does."""
|
||||
from esphome.platformio.library import join_flag_args
|
||||
|
||||
assert join_flag_args(["-D", "FOO=1", "-Os"], "x") == ["-DFOO=1", "-Os"]
|
||||
|
||||
@@ -556,7 +557,6 @@ def test_join_flag_args_reglues_spaced_define() -> None:
|
||||
def test_join_flag_args_trailing_bare_flag_warns(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
from esphome.platformio.library import join_flag_args
|
||||
|
||||
assert join_flag_args(["-Os", "-l"], "library x") == ["-Os"]
|
||||
assert "Ignoring trailing '-l'" in caplog.text
|
||||
@@ -576,7 +576,6 @@ def test_lex_build_flags_dangling_flag_does_not_cross_entries(
|
||||
def test_split_flag_entry_non_string_is_clean() -> None:
|
||||
"""A dict or number from a third-party manifest fails naming the entry,
|
||||
not with an opaque shlex traceback."""
|
||||
from esphome.platformio.library import split_flag_entry
|
||||
|
||||
with pytest.raises(EsphomeError, match="Malformed build flag"):
|
||||
split_flag_entry({"esp32": ["-DX"]}, "lib x")
|
||||
@@ -587,7 +586,6 @@ def test_split_flag_entry_non_string_is_clean() -> None:
|
||||
def test_source_kind_map_shape() -> None:
|
||||
"""The kind values the native compile rules key on, and the deliberate
|
||||
AS/ASPP merge (.s and .S both map to asm)."""
|
||||
from esphome.platformio.library import SOURCE_KIND_FOR_SUFFIX
|
||||
|
||||
assert set(SOURCE_KIND_FOR_SUFFIX.values()) == {"c", "cxx", "asm"}
|
||||
assert SOURCE_KIND_FOR_SUFFIX[".s"] == "asm"
|
||||
@@ -681,9 +679,8 @@ def test_walk_warns_for_nonplatform_invalid_library(
|
||||
def test_versionless_owner_qualified_dependency_warns_despite_provides(
|
||||
tmp_path, monkeypatch, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""The backend's provides() only covers owner-less names (the walk's
|
||||
backend-provided skip has the same guard), so an owner-qualified
|
||||
version-less dependency that nobody adds must still warn."""
|
||||
"""An owner-qualified version-less dependency is not satisfied by
|
||||
provides(); it must still warn."""
|
||||
_patch_download_with_manifests(
|
||||
monkeypatch,
|
||||
tmp_path,
|
||||
@@ -758,9 +755,8 @@ def test_versionless_url_ish_dependency_name_warns_cleanly(
|
||||
def test_versionless_dependency_matching_resolved_manifest_name_stays_quiet(
|
||||
tmp_path, monkeypatch, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""A bare dependency name satisfied by a component requested under an
|
||||
owner-qualified spec (manifest names match) is not a drop; a nameless
|
||||
entry is skipped without a reconciliation warning."""
|
||||
"""A bare name satisfied by an owner-qualified component's manifest
|
||||
name is not a drop."""
|
||||
_patch_download_with_manifests(
|
||||
monkeypatch,
|
||||
tmp_path,
|
||||
|
||||
@@ -1051,8 +1051,6 @@ def test_clean_all_removes_global_arduino8266_install(
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
|
||||
from esphome.writer import clean_all
|
||||
|
||||
with caplog.at_level("INFO"):
|
||||
clean_all([str(config_dir)])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user