Skip to content

fix(windows-tests): address parallel runner encoding and symlink privilege errors - #39513

Closed
aniruddhaadak80 wants to merge 1 commit into
NousResearch:mainfrom
aniruddhaadak80:fix/windows-test-compat
Closed

fix(windows-tests): address parallel runner encoding and symlink privilege errors#39513
aniruddhaadak80 wants to merge 1 commit into
NousResearch:mainfrom
aniruddhaadak80:fix/windows-test-compat

Conversation

@aniruddhaadak80

@aniruddhaadak80 aniruddhaadak80 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

This PR addresses Windows compatibility test failures (Fixes #39480). It keeps the parallel-runner encoding fix and the symlink privilege handling, but drops the obsolete timeout edit and cosmetic churn as requested by reviewers.

@aniruddhaadak80
aniruddhaadak80 requested review from a team and Copilot June 5, 2026 02:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Introduces a new per-file parallel pytest runner and updates the test infrastructure to rely on per-file process isolation, alongside adding symlink-gated tests and bumping a core dependency to address a crash.

Changes:

  • Add scripts/run_tests_parallel.py to run pytest in parallel with one subprocess per test file (with optional slicing and per-file timeout).
  • Update tests/conftest.py to remove cross-test state-reset fixtures and add a require_symlinks marker with an environment capability check.
  • Add/adjust tests to use @pytest.mark.require_symlinks and add coverage for secure_parent_dir() behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/test_hermes_constants.py Adds tests for secure_parent_dir() to prevent chmod on unsafe root/top-level directories.
tests/test_atomic_replace_symlinks.py Marks symlink-dependent tests with require_symlinks for environment portability.
tests/conftest.py Removes per-test state reset/timeouts; adds require_symlinks marker and skip logic.
tests/cli/test_worktree_security.py Marks symlink-dependent worktree security tests with require_symlinks.
scripts/run_tests_parallel.py New per-file parallel test runner with discovery, slicing, timeouts, and summaries.
pyproject.toml Bumps pydantic; removes xdist/split from dev extras; adjusts pytest addopts for new runner model.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/conftest.py Outdated
Comment on lines +524 to +540
def _check_symlink_support() -> bool:
global _symlink_supported_cache
if _symlink_supported_cache is not None:
return _symlink_supported_cache

import tempfile
try:
with tempfile.TemporaryDirectory() as d:
src = Path(d) / "src"
src.touch()
lnk = Path(d) / "lnk"
lnk.symlink_to(src)
_symlink_supported_cache = True
return True
except OSError:
_symlink_supported_cache = False
return False
Comment thread tests/conftest.py
Comment on lines +425 to +429
# ── Per-test timeout — handled by the isolation plugin ─────────────────────
#
# The subprocess-per-test plugin enforces the configured ``isolate_timeout``
# ini key by terminating the child if it overruns. The old SIGALRM-based
# fixture (POSIX-only, didn't work on Windows) is gone.
Comment on lines +6 to +7
unless explicitly requested), then runs one ``python -m pytest <file>``
subprocess per file, with bounded parallelism (default: ``os.cpu_count()``).
Comment on lines +195 to +205
if sys.platform == "win32":
try:

subprocess.run(
["taskkill", "/F", "/T", "/PID", str(proc.pid)],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=10,
) # windows-footgun: ok
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
pass
Comment on lines +306 to +310
# Happy path: pytest exited on its own. The child process already
# cleaned up its grandchildren if it's well-behaved, but
# well-behaved is not universal — kill the group anyway. Already-
# dead processes are a no-op.
_kill_tree(proc, pgid=pgid)
Comment on lines +774 to +775
for fut in futures:
fut.result() if fut.exception() is None else None
@aniruddhaadak80
aniruddhaadak80 force-pushed the fix/windows-test-compat branch from e19d9f7 to f0f23d1 Compare June 5, 2026 02:55
@alt-glitch alt-glitch added type/test Test coverage or test infrastructure P3 Low — cosmetic, nice to have type/bug Something isn't working labels Jun 5, 2026

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixed bag — one part is obsolete and would regress main, but two parts are genuinely useful. Recommend salvaging the good pieces into a fresh PR off current main.

1. Timeout fix — 🔴 obsolete, would regress

This PR changes addopts to drop the timeout method (--timeout=30 --timeout-method=signal → just --timeout=30). But current origin/main already carries the canonical cross-platform fix:

addopts = "-m 'not integration' --timeout=30 --timeout-method=thread"

Merging this would downgrade main from an explicit thread method to no method at all. The SIGALRM-on-Windows problem is already solved on main (and #43182 was the superseding work). Drop this hunk.

2. Parallel-runner encoding fix — ✅ valid, net-new

sys.stdout/stderr.reconfigure(encoding='utf-8') guarded by hasattr + try/except. No-op on POSIX (already UTF-8), fixes UnicodeEncodeError on Windows cp1252 consoles, degrades safely. Not on main. Keep this.

3. Symlink privilege handling — ✅ reasonable, but coordinate

require_symlinks marker + runtime _check_symlink_support() probe + pytest_runtest_setup skip hook. Sound and POSIX-safe. But it overlaps conceptually with the still-open #40891 ("harden Windows portability tests and paths") — reconcile to avoid duplication.

Mechanics

mergeable: CONFLICTING / DIRTY — branch is ~678 commits behind main, the pyproject.toml timeout line and tests/conftest.py will conflict directly. Also minor noise: a stray blank line splitting import os/import sys (isort regression) and a cosmetic # windows-footgun: ok comment on an unchanged killpg guard.

Best path: cut a fresh branch from current main with just the encoding reconfigure (and the symlink marker, coordinated with #40891), dropping the obsolete timeout edit and the cosmetic churn.

@aniruddhaadak80 aniruddhaadak80 changed the title fix(windows-tests): address timeout crash, parallel runner encoding, and symlink privilege errors fix(windows-tests): address parallel runner encoding and symlink privilege errors Jul 5, 2026
@aniruddhaadak80
aniruddhaadak80 force-pushed the fix/windows-test-compat branch from f0f23d1 to 19bfd61 Compare July 5, 2026 12:49
@aniruddhaadak80
aniruddhaadak80 force-pushed the fix/windows-test-compat branch from 19bfd61 to a81b49c Compare July 5, 2026 12:50

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for preserving the focused encoding and symlink-compatibility work. The encoding premise remains on current main: scripts/run_tests_parallel.py:390-467 prints Unicode glyphs and does not configure its streams before main() at :594.

Problems

  • The PR says Fixes #39480, but current issue #39480 concerns profile-scoped dotenv loading during cron MCP initialization, not Windows test encoding or symlinks. Please remove that unrelated closing reference.
  • tests/cli/test_worktree_security.py:111 skips the directory include test when symlinks are unavailable. That hides the deliberate Windows fallback in cli.py:1542-1556, which catches symlink errors and uses copytree; the test should validate that fallback instead.
  • Direct symlink creation remains ungated at tests/cli/test_worktree.py:540 and tests/cli/test_cli_file_drop.py:246.

Suggested changes

  • Keep the directory include test runnable and accept either a symlink or copied directory while checking its contents.
  • Gate or locally skip the remaining direct-symlink tests that require privilege.

Automated hermes-sweeper review.

@@ -110,6 +111,7 @@ def test_allows_valid_file_include(self, git_repo):
finally:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test exercises _setup_worktree's Windows copytree fallback (cli.py:1542-1556) when symlink creation is unavailable. Skipping it removes coverage of that intentional path; keep it runnable and assert the included directory is usable whether it is a symlink or a copied directory.

@teknium1 teknium1 added sweeper:risk-automation Sweeper risk: may affect CI, automerge, label sync, or maintainer automation sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
aniruddhaadak80 added a commit to aniruddhaadak80/hermes-agent that referenced this pull request Jul 15, 2026
@teknium1

teknium1 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Half merged in #81965: your require_symlinks marker infra + test markings are on main with authorship. The runner-encoding half was superseded by main's July runner work. Thanks!

@teknium1 teknium1 closed this Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-automation Sweeper risk: may affect CI, automerge, label sync, or maintainer automation sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working type/test Test coverage or test infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants