mirror of
https://github.com/esphome/esphome.git
synced 2026-10-02 09:20:22 +00:00
Pin that the native upload path imports no platform component package
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""Run the native-toolchain serial-upload path and report heavy modules.
|
||||
|
||||
Executed as a subprocess by test_lazy_imports.py: heavy module names come
|
||||
in on argv, the ones found in sys.modules afterwards go out on stdout.
|
||||
``upload_using_esptool`` dispatches native toolchains through a
|
||||
toolchain-keyed table; if someone routes it back through the platform
|
||||
component packages (esp32 or esp8266), this reports the leak.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
|
||||
from _leak_report import print_leaked_modules
|
||||
|
||||
from esphome.__main__ import upload_using_esptool
|
||||
from esphome.const import (
|
||||
CONF_ESPHOME,
|
||||
KEY_CORE,
|
||||
KEY_ESP32,
|
||||
KEY_TARGET_PLATFORM,
|
||||
KEY_VARIANT,
|
||||
Toolchain,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
# An ambient ESPHOME_USE_SUBPROCESS would route past the patched
|
||||
# run_external_command into run_external_process and confuse the checks.
|
||||
os.environ.pop("ESPHOME_USE_SUBPROCESS", None)
|
||||
|
||||
config = {CONF_ESPHOME: {"platformio_options": {}}}
|
||||
|
||||
with tempfile.TemporaryDirectory() as build_dir:
|
||||
CORE.name = "leaktest"
|
||||
CORE.build_path = build_dir
|
||||
|
||||
for platform, toolchain, backend in (
|
||||
("esp8266", Toolchain.ARDUINO, "esphome.arduino8266.toolchain"),
|
||||
("esp32", Toolchain.ESP_IDF, "esphome.espidf.toolchain"),
|
||||
):
|
||||
CORE.data[KEY_CORE] = {KEY_TARGET_PLATFORM: platform}
|
||||
if platform == "esp32":
|
||||
CORE.data[KEY_ESP32] = {KEY_VARIANT: "ESP32S3"}
|
||||
CORE.toolchain = toolchain
|
||||
|
||||
import importlib
|
||||
|
||||
image = importlib.import_module(backend).get_factory_firmware_path()
|
||||
image.parent.mkdir(parents=True, exist_ok=True)
|
||||
image.write_bytes(b"\x00")
|
||||
|
||||
with patch("esphome.__main__.run_external_command", return_value=0) as mock_run:
|
||||
rc = upload_using_esptool(config, "/dev/ttyUSB0", None, None)
|
||||
|
||||
# Fail loudly if the upload path stopped doing its work; otherwise
|
||||
# an empty leak list could just mean nothing ran.
|
||||
if rc != 0:
|
||||
sys.exit(f"upload_using_esptool({platform}) returned {rc}")
|
||||
cmd = list(mock_run.call_args[0][1:])
|
||||
if str(image) not in [str(Path(c)) for c in cmd]:
|
||||
sys.exit(f"native factory image did not reach esptool: {cmd}")
|
||||
|
||||
print_leaked_modules()
|
||||
@@ -175,6 +175,27 @@ def test_esptool_upload_fast_path_does_not_import_heavy_modules(
|
||||
)
|
||||
|
||||
|
||||
def test_native_upload_fast_path_does_not_import_heavy_modules(
|
||||
fixture_path: Path,
|
||||
probe_env: dict[str, str],
|
||||
) -> None:
|
||||
"""The native-toolchain serial upload dispatches through a
|
||||
toolchain-keyed backend table; resolving the flash image must not drag
|
||||
in the esp32 or esp8266 component package or the validation stack.
|
||||
"""
|
||||
leaked = _leaked_from_fixture(
|
||||
fixture_path,
|
||||
probe_env,
|
||||
"native_upload_fast_path.py",
|
||||
extra=("esphome.components.esp8266",),
|
||||
)
|
||||
assert not leaked, (
|
||||
f"native upload_using_esptool pulls in heavy modules: {leaked}. "
|
||||
"The upload fast path skips validation; importing a platform "
|
||||
"component package executes its codegen module by design."
|
||||
)
|
||||
|
||||
|
||||
def test_api_client_does_not_import_heavy_modules() -> None:
|
||||
"""``esphome.api_client`` is on the logs fast path and must stay light.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user