Merge branch 'esp8266-native-library-backend' into esp8266-native-build-spec

This commit is contained in:
J. Nick Koston
2026-08-22 14:52:40 -05:00
5 changed files with 103 additions and 64 deletions
+24 -2
View File
@@ -188,14 +188,16 @@ def test_library_info_declared_filter_matches_nothing_warns(
assert "declares srcFilter/srcDir but no source files matched" in caplog.text
def test_library_info_header_only_does_not_warn(
def test_library_info_empty_tree_warns(
tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
"""No sources and no headers is an empty archive waiting to fail at
link; warn by name even without a declared filter."""
read_path = tmp_path / "lib"
(read_path / "src").mkdir(parents=True)
lib = component._library_info("x", read_path, {})
assert not lib.sources
assert "no source files matched" not in caplog.text
assert "has no sources or headers" in caplog.text
def test_library_info_no_src_dir(tmp_path: Path) -> None:
@@ -458,6 +460,26 @@ def test_nonplatform_rejection_warns_once_through_real_converter(
assert caplog.text.count("manifest is corrupt") == 1
def test_short_name_collision_with_bundled_name_warns(
tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
"""Suppressing a genuinely bundled name on a short-name match warns;
an accidental collision would otherwise surface at link."""
framework = _make_framework(tmp_path)
_add_library("Someone/Wire", "1.0.0")
converted = _converted(
"someone__Wire",
tmp_path / "conv",
{"build": {}, "dependencies": [{"name": "Wire"}]},
)
(tmp_path / "conv" / "src").mkdir(parents=True)
with _emitting_converter(converted):
libs = _resolve(framework)
assert "Wire" not in [lib.name for lib in libs]
assert "assumed satisfied by a requested external library" in caplog.text
assert any(r.levelname == "WARNING" for r in caplog.records)
def test_missing_libraries_dir_is_a_broken_install(tmp_path: Path) -> None:
"""A framework tree without libraries/ must fail by name, not silently
reroute every bundled name to the registry."""
+8 -28
View File
@@ -11,24 +11,28 @@ from pathlib import Path
import re
import sys
import time
from types import SimpleNamespace
from typing import Any, Self
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from pytest import CaptureFixture
import serial
from zeroconf import ServiceStateChange
from esphome import __main__ as main
from esphome import __main__ as main, yaml_util
from esphome.__main__ import (
Purpose,
_get_configured_xtal_freq,
_make_crystal_freq_callback,
_redact_with_legacy_fallback,
_resolve_network_devices,
_should_subscribe_states,
_split_network_devices,
_unresolved_default_error,
_validate_bootloader_binary,
_validate_partition_table_binary,
_wrap_to_code,
check_permissions,
choose_upload_log_host,
command_analyze_memory,
@@ -67,19 +71,21 @@ from esphome.__main__ import (
)
from esphome.address_cache import AddressCache
from esphome.bundle import BUNDLE_EXTENSION, BundleFile, BundleResult
from esphome.components import esp32, esp8266
from esphome.components import esp32, esp8266, mqtt
from esphome.components.esp32 import (
KEY_ESP32,
KEY_VARIANT,
VARIANT_ESP32,
get_esp32_variant,
)
from esphome.config import Config
from esphome.const import (
CONF_API,
CONF_AUTH,
CONF_BAUD_RATE,
CONF_BROKER,
CONF_DISABLED,
CONF_DISCOVER_IP,
CONF_ESPHOME,
CONF_LEVEL,
CONF_LOG,
@@ -565,8 +571,6 @@ def test_command_config__no_defaults_dumps_user_snapshot(
) -> None:
"""``--no-defaults`` dumps ``config.user_config`` instead of the
validated config, so schema defaults don't leak into the output."""
from esphome.config import Config
setup_core(tmp_path=tmp_path, config={"esphome": {"name": "test"}})
args = MockArgs()
args.show_secrets = True
@@ -619,8 +623,6 @@ def test_command_config__no_defaults_skips_strip_default_ids(
) -> None:
"""When ``--no-defaults`` is set, ``strip_default_ids`` isn't run --
the user snapshot is already free of schema-injected IDs."""
from esphome.config import Config
setup_core(tmp_path=tmp_path, config={"esphome": {"name": "test"}})
args = MockArgs()
args.show_secrets = True
@@ -3438,9 +3440,6 @@ def test_get_port_type() -> None:
def test_mqtt_reexports_discover_ip() -> None:
"""The old import path must keep working for external code."""
from esphome.components import mqtt
from esphome.const import CONF_DISCOVER_IP
assert mqtt.CONF_DISCOVER_IP is CONF_DISCOVER_IP
@@ -5907,8 +5906,6 @@ class MockSerial:
chunk = self.chunks[self.chunk_index]
if chunk is MOCK_SERIAL_END:
# Sentinel means we're done - simulate port closed
import serial
raise serial.SerialException("Port closed")
# Respect the requested size and keep any remaining bytes
if size <= 0:
@@ -5922,8 +5919,6 @@ class MockSerial:
# Entire chunk consumed; advance to the next one
self.chunk_index += 1
return data # type: ignore[return-value]
import serial
raise serial.SerialException("Port closed")
@@ -6782,8 +6777,6 @@ def test_parse_args_argcomplete_only_runs_when_completing() -> None:
def test_should_subscribe_states_default() -> None:
"""Test that states are shown by default when nothing is set."""
from esphome.__main__ import _should_subscribe_states
args = parse_args(["esphome", "logs", "device.yaml"])
with patch.dict(os.environ, {}, clear=False):
os.environ.pop("ESPHOME_LOG_STATES", None)
@@ -6792,8 +6785,6 @@ def test_should_subscribe_states_default() -> None:
def test_should_subscribe_states_env_suppresses() -> None:
"""Test that ESPHOME_LOG_STATES=false suppresses states by default."""
from esphome.__main__ import _should_subscribe_states
args = parse_args(["esphome", "logs", "device.yaml"])
with patch.dict(os.environ, {"ESPHOME_LOG_STATES": "false"}):
assert _should_subscribe_states(args) is False
@@ -6801,8 +6792,6 @@ def test_should_subscribe_states_env_suppresses() -> None:
def test_should_subscribe_states_env_enables() -> None:
"""Test that ESPHOME_LOG_STATES=true enables states by default."""
from esphome.__main__ import _should_subscribe_states
args = parse_args(["esphome", "logs", "device.yaml"])
with patch.dict(os.environ, {"ESPHOME_LOG_STATES": "true"}):
assert _should_subscribe_states(args) is True
@@ -6810,8 +6799,6 @@ def test_should_subscribe_states_env_enables() -> None:
def test_should_subscribe_states_flag_overrides_env() -> None:
"""Test that --states overrides ESPHOME_LOG_STATES=false."""
from esphome.__main__ import _should_subscribe_states
args = parse_args(["esphome", "logs", "--states", "device.yaml"])
with patch.dict(os.environ, {"ESPHOME_LOG_STATES": "false"}):
assert _should_subscribe_states(args) is True
@@ -6819,8 +6806,6 @@ def test_should_subscribe_states_flag_overrides_env() -> None:
def test_should_subscribe_states_no_flag_overrides_env() -> None:
"""Test that --no-states overrides ESPHOME_LOG_STATES=true."""
from esphome.__main__ import _should_subscribe_states
args = parse_args(["esphome", "logs", "--no-states", "device.yaml"])
with patch.dict(os.environ, {"ESPHOME_LOG_STATES": "true"}):
assert _should_subscribe_states(args) is False
@@ -7250,11 +7235,6 @@ async def test_wrap_to_code_comment_is_insertion_order_independent() -> None:
"""The config comment dumps with sorted keys: voluptuous fills schema
defaults in set-iteration order, so an unsorted dump would churn
main.cpp and relink the firmware on every run."""
from types import SimpleNamespace
from esphome import yaml_util
from esphome.__main__ import _wrap_to_code
comments: list[str] = []
async def to_code(conf):
+29 -4
View File
@@ -814,6 +814,34 @@ def test_versionless_dependency_without_provider_warns(
)
def test_url_version_dependency_is_not_substituted_by_provides(
tmp_path, monkeypatch, caplog: pytest.LogCaptureFixture
) -> None:
"""A URL-valued version names one specific source; the backend-provided
skip must not replace it with the bundled copy."""
_patch_download_with_manifests(
monkeypatch,
tmp_path,
{
"esphome/A": {
"name": "A",
"dependencies": [
{"name": "Hash", "version": "https://github.com/o/Hash.git"}
],
},
"o/Hash": {"name": "Hash"},
},
)
emitted: list[str] = []
convert_libraries(
[Library("esphome/A", "1.0.0", None)],
_backend(emit=lambda c: emitted.append(c.name), provides=lambda name: True),
)
assert "Skip backend-provided" not in caplog.text
assert "using the library bundled" not in caplog.text
assert any("o/hash" in n.lower() for n in emitted)
def test_versionless_owner_qualified_dependency_warns_despite_provides(
tmp_path, monkeypatch, caplog: pytest.LogCaptureFixture
) -> None:
@@ -899,10 +927,7 @@ def test_versionless_dependency_matching_resolved_manifest_name_stays_quiet(
monkeypatch,
tmp_path,
{
"esphome/A": {
"name": "A",
"dependencies": [{"name": "B"}, {"version": "1.0"}],
},
"esphome/A": {"name": "A", "dependencies": [{"name": "B"}]},
"esphome/B": {"name": "B"},
},
)