[tests] Sandbox PlatformIO paths in test_writer to fix xdist race (#16619)
CI / Create common environment (push) Has been cancelled
CI / Check pylint (push) Has been cancelled
CI / Run script/ci-custom (push) Has been cancelled
CI / Check import esphome.__main__ time (push) Has been cancelled
CI / Test downstream esphome/device-builder (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.11) (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.14) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.13) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.14) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.11) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.14) (push) Has been cancelled
CI / Determine which jobs to run (push) Has been cancelled
CI / Run integration tests (${{ matrix.bucket.name }}) (push) Has been cancelled
CI / Run C++ unit tests (push) Has been cancelled
CI / Run CodSpeed benchmarks (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF (push) Has been cancelled
CI / Run script/clang-tidy for ESP8266 (push) Has been cancelled
CI / Run script/clang-tidy for ZEPHYR (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Has been cancelled
CI / Test components batch (${{ matrix.components }}) (push) Has been cancelled
CI / Test components with native ESP-IDF (push) Has been cancelled
CI / pre-commit.ci lite (push) Has been cancelled
CI / Build target branch for memory impact (push) Has been cancelled
CI / Build PR branch for memory impact (push) Has been cancelled
CI / Comment memory impact (push) Has been cancelled
CI / CI Status (push) Has been cancelled

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
rwrozelle
2026-05-27 15:50:43 -04:00
committed by GitHub
co-authored by Jonathan Swoboda
parent ac29fad120
commit 9a6157b469
+36
View File
@@ -44,6 +44,42 @@ from esphome.writer import (
)
@pytest.fixture(autouse=True)
def _isolate_platformio_paths(tmp_path_factory: pytest.TempPathFactory) -> Any:
"""Sandbox PlatformIO path lookups so tests never touch ~/.platformio.
`clean_all` and `clean_build` both query ProjectConfig for paths like
`cache_dir` and `core_dir` and `rmtree` anything that exists. By
default `core_dir` resolves to ~/.platformio, which is global state
shared across pytest-xdist workers — multiple workers can each pass
`is_dir()` and then race inside `shutil.rmtree`, producing
FileNotFoundError flakes (and trashing developers' local PIO state
when the suite is run outside CI).
14 of the 18 `clean_*` tests in this file invoke `clean_all` /
`clean_build` without installing their own ProjectConfig mock, so
making the fixture autouse is simpler than tagging each test
individually.
Patch ProjectConfig.get_instance to point every PIO dir at a unique
tmp directory that doesn't actually exist on disk — `is_dir()`
returns False, so the rmtree loop is skipped entirely. Tests that
want to verify the PIO-cleanup branch (e.g. test_clean_all,
test_clean_all_partial_exists) install their own inner patch which
stacks on top of this one and wins for the duration of their block.
"""
pio_root = tmp_path_factory.mktemp("isolated_pio") / "nonexistent"
mock_cfg = MagicMock()
mock_cfg.get.side_effect = lambda section, option: (
str(pio_root / option) if section == "platformio" else ""
)
with patch(
"platformio.project.config.ProjectConfig.get_instance",
return_value=mock_cfg,
):
yield
@pytest.fixture
def mock_copy_src_tree():
"""Mock copy_src_tree to avoid side effects during tests."""