mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
94 lines
2.9 KiB
Python
94 lines
2.9 KiB
Python
"""Tests for esphome.platformio.runner."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
import io
|
|
import sys
|
|
from types import ModuleType
|
|
|
|
import pytest
|
|
|
|
from esphome.platformio import runner
|
|
|
|
|
|
def _prepare_main(
|
|
monkeypatch: pytest.MonkeyPatch, pio_main: Callable[[], int]
|
|
) -> io.BytesIO:
|
|
"""Point ``runner.main()`` at a fake PlatformIO with a fake stdout.
|
|
|
|
The real ``main`` patches PlatformIO internals and then hands control to
|
|
it; both are stubbed out so only the stream wrapping is exercised. The
|
|
fake stdout is block buffered like a pipe, so the caller can see what
|
|
actually left the wrapper.
|
|
"""
|
|
buf = io.BytesIO()
|
|
stream = io.TextIOWrapper(buf, encoding="utf-8", newline="\n", line_buffering=False)
|
|
|
|
monkeypatch.setattr(sys, "argv", ["pio", "run"])
|
|
monkeypatch.setattr(sys, "stdout", stream)
|
|
monkeypatch.setattr(sys, "stderr", stream)
|
|
monkeypatch.setattr(runner, "patch_structhash", lambda: None)
|
|
monkeypatch.setattr(runner, "patch_file_downloader", lambda: None)
|
|
|
|
platformio = ModuleType("platformio")
|
|
platformio_main = ModuleType("platformio.__main__")
|
|
platformio_main.main = pio_main # type: ignore[attr-defined]
|
|
platformio.__main__ = platformio_main # type: ignore[attr-defined]
|
|
monkeypatch.setitem(sys.modules, "platformio", platformio)
|
|
monkeypatch.setitem(sys.modules, "platformio.__main__", platformio_main)
|
|
|
|
return buf
|
|
|
|
|
|
def test_main_drains_a_partial_line_on_a_clean_run(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""A build ending mid line still shows that line."""
|
|
|
|
def pio_main() -> int:
|
|
print("Linking .pioenvs/firmware.elf\n", end="")
|
|
print("Building took 12.4 seconds", end="")
|
|
return 0
|
|
|
|
buf = _prepare_main(monkeypatch, pio_main)
|
|
|
|
assert runner.main() == 0
|
|
assert buf.getvalue().decode("utf-8") == (
|
|
"Linking .pioenvs/firmware.elf\nBuilding took 12.4 seconds\n"
|
|
)
|
|
|
|
|
|
def test_main_drains_when_platformio_exits_early(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""Leaving through ``sys.exit`` still drains, because it runs in a finally."""
|
|
|
|
def pio_main() -> int:
|
|
print("*** [.pioenvs/firmware.elf] Error 1", end="")
|
|
sys.exit(1)
|
|
|
|
buf = _prepare_main(monkeypatch, pio_main)
|
|
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
runner.main()
|
|
|
|
assert excinfo.value.code == 1
|
|
assert buf.getvalue().decode("utf-8") == "*** [.pioenvs/firmware.elf] Error 1\n"
|
|
|
|
|
|
def test_main_still_filters_a_drained_partial_line(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""Releasing a held line does not smuggle noise past the filter."""
|
|
|
|
def pio_main() -> int:
|
|
# Matches FILTER_PLATFORMIO_LINES, and arrives without a terminator.
|
|
print("Verbose mode can be enabled via `-v, --verbose` option", end="")
|
|
return 0
|
|
|
|
buf = _prepare_main(monkeypatch, pio_main)
|
|
|
|
assert runner.main() == 0
|
|
assert buf.getvalue() == b""
|