From 95f96f82e30e003d213a624a988b8665db5bb52a Mon Sep 17 00:00:00 2001 From: Daniel Meppiel Date: Mon, 11 May 2026 13:33:08 +0200 Subject: [PATCH] fix(tests): set hermetic HOME at conftest import time for Windows xdist workers The previous session-scoped autouse fixture in tests/unit/conftest.py fixed most Windows runner failures, but a single xdist worker (gw2) still hit 53 'RuntimeError: Could not determine home directory' on the windows-2025-vs2026 image -- some test or fixture resolved Path.home() before the autouse fixture's setup ran on that worker. Move the env mutation to module-level import time. Each xdist worker imports tests/unit/conftest.py once, before any fixture or collection runs, so HOME / USERPROFILE / HOMEDRIVE / HOMEPATH are guaranteed to be set before anything in the worker process can call Path.home(). Refs run https://github.com/microsoft/apm/actions/runs/25665840560 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CHANGELOG.md | 1 + tests/unit/conftest.py | 51 ++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1afef15e6..b30046b00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Pin `Path.home()` under unit tests via a session-scoped autouse conftest fixture, fixing 56 Windows runner failures on the new `windows-2025-vs2026` GitHub-hosted image where `USERPROFILE`/`HOMEDRIVE`+`HOMEPATH` are not seeded for pytest workers; also patch the `_check_and_notify_updates` import binding in the disabled-self-update test so it no longer races on the version-check cache. (#1270) - `apm install` now works on macOS git 2.53.0 (Homebrew): bare-cache commands switch to `--git-dir` to satisfy the `safe.bareRepository=explicit` default; fetched SHAs are pinned as synthetic refs so `git clone --local --shared` no longer silently omits them. (#1268) +- Set the unit-test hermetic HOME at conftest import time so a single xdist worker on the `windows-2025-vs2026` runner can no longer race fixture setup and re-trigger the 53 `Path.home()` failures the session-scoped autouse fixture was supposed to prevent. (#1271) ## [0.13.0] - 2026-05-11 diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index ba9a55ef6..5566b2c59 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -1,7 +1,8 @@ """Unit-test conftest: hermetic HOME isolation. -The session-scoped autouse fixture below pins ``Path.home()`` to a tmp -directory for the entire unit-test session. Two reasons: +This module sets ``HOME`` (POSIX) / ``USERPROFILE`` + ``HOMEDRIVE`` + +``HOMEPATH`` (Windows) to a process-wide tmp directory **at import +time**, before any fixture or test resolution. Two reasons: 1. Hermeticity. Unit tests must not read or write the contributor's real ``~`` (config files, runtimes, caches). Tests that call @@ -9,31 +10,41 @@ whatever HOME the runner had. 2. Windows runner robustness. On the ``windows-2025-vs2026`` GitHub Actions image the ``USERPROFILE`` / ``HOMEDRIVE`` + ``HOMEPATH`` - triplet is not set under the pytest worker subprocess, so + triplet is not set under the pytest-xdist worker subprocess, so ``Path.home()`` raises ``RuntimeError: Could not determine home - directory.`` This fixture sets the platform-correct trio so - ``Path.home()`` always resolves to ```` regardless of runner - image. + directory.`` + +Why import-time and not a session-scoped autouse fixture: a previous +attempt used ``@pytest.fixture(scope="session", autouse=True)`` and +still failed on a single xdist worker (``gw2`` on Windows). Whatever +the cause (worker scheduling order, a downstream fixture resolving +``Path.home()`` before the autouse fixture's setup completed), running +the env mutation at conftest import time guarantees the env vars are +in place before pytest collects, schedules, or runs anything in this +worker process. Pytest imports each test directory's conftest.py once +per worker, before any fixtures run. Per-test fixtures that need a different HOME (e.g. tests/unit/integration that exercise scope resolution) keep using ``monkeypatch.setenv`` and -override this baseline; the function-scoped monkeypatch wins over the -session-scoped baseline for the duration of the test. +override this baseline; monkeypatch's snapshot/restore cycle preserves +this baseline across tests. """ from __future__ import annotations import os +import tempfile from pathlib import Path -import pytest - def _set_home_env(home: Path) -> None: """Set ``HOME`` and the Windows-equivalent vars to ``home``. ``Path.home()`` consults ``HOME`` on POSIX but ``USERPROFILE`` - (with ``HOMEDRIVE`` + ``HOMEPATH`` fallback) on Windows. + (with ``HOMEDRIVE`` + ``HOMEPATH`` fallback) on Windows. We + overwrite unconditionally because the Windows runner sometimes + leaves these keys present but empty, which still trips + ``Path.home()``. """ home_str = str(home) os.environ["HOME"] = home_str @@ -45,19 +56,5 @@ def _set_home_env(home: Path) -> None: os.environ["HOMEPATH"] = tail -@pytest.fixture(scope="session", autouse=True) -def _hermetic_home(tmp_path_factory: pytest.TempPathFactory) -> None: - """Pin ``Path.home()`` to a per-session tmp dir for all unit tests.""" - home = tmp_path_factory.mktemp("apm-unit-home") - previous = { - key: os.environ.get(key) for key in ("HOME", "USERPROFILE", "HOMEDRIVE", "HOMEPATH") - } - _set_home_env(home) - try: - yield - finally: - for key, value in previous.items(): - if value is None: - os.environ.pop(key, None) - else: - os.environ[key] = value +_TMP_HOME = Path(tempfile.mkdtemp(prefix="apm-unit-home-")) +_set_home_env(_TMP_HOME)