From b0dd53fd36e9a3a8b402f578ca99d3e4f084c504 Mon Sep 17 00:00:00 2001 From: akacross Date: Thu, 22 Oct 2026 20:48:07 -0400 Subject: [PATCH] Tolerate a locked harness executable when MP contracts clean up On the Windows ARM64 runners, mp_match_authentication_contract.py, mp_match_pause_contract.py and mp_match_teams_contract.py complete their checks and then fail in TemporaryDirectory cleanup: deleting the harness executable they just ran raises PermissionError (WinError 5). The imported game regression suite therefore reports 71/74, failing the Windows ARM64 native test step on main and every PR. Their temporary directories now pass ignore_cleanup_errors=True, so a leftover file in .tmp no longer turns a pass into a failure. Failures in the checks themselves still fail the test. Linux runs are unchanged and still remove everything. Co-Authored-By: Claude Opus 5.5 --- tools/tests/mp_match_authentication_contract.py | 5 ++++- tools/tests/mp_match_pause_contract.py | 15 ++++++++++++--- tools/tests/mp_match_teams_contract.py | 5 ++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tools/tests/mp_match_authentication_contract.py b/tools/tests/mp_match_authentication_contract.py index 00dbdc68..797289b0 100644 --- a/tools/tests/mp_match_authentication_contract.py +++ b/tools/tests/mp_match_authentication_contract.py @@ -486,7 +486,10 @@ def locate_msvc() -> tuple[Path, str] | None: def executable_contract() -> None: temp_root = ROOT / ".tmp" temp_root.mkdir(exist_ok=True) - with tempfile.TemporaryDirectory(prefix="match-auth-", dir=temp_root) as temp: + # The Windows ARM64 runners refuse to delete the just-run harness executable + # (WinError 5) after the checks pass; a leftover file in .tmp must not turn + # that pass into a failure. + with tempfile.TemporaryDirectory(prefix="match-auth-", dir=temp_root, ignore_cleanup_errors=True) as temp: temp_dir = Path(temp) harness = temp_dir / "match_authentication_contract.cpp" executable = temp_dir / "match_authentication_contract.exe" diff --git a/tools/tests/mp_match_pause_contract.py b/tools/tests/mp_match_pause_contract.py index 6e609890..09bfd753 100644 --- a/tools/tests/mp_match_pause_contract.py +++ b/tools/tests/mp_match_pause_contract.py @@ -372,7 +372,10 @@ def compile_deadline_harness() -> None: tmp_root = ROOT / ".tmp" tmp_root.mkdir(exist_ok=True) - with tempfile.TemporaryDirectory(prefix="mp-pause-contract-", dir=tmp_root) as raw: + # The Windows ARM64 runners refuse to delete the just-run harness executable + # (WinError 5) after the checks pass; a leftover file in .tmp must not turn + # that pass into a failure. + with tempfile.TemporaryDirectory(prefix="mp-pause-contract-", dir=tmp_root, ignore_cleanup_errors=True) as raw: temp = Path(raw) source = temp / "deadline_harness.cpp" source.write_text( @@ -484,7 +487,10 @@ def compile_prediction_clock_harness() -> None: compiler = shutil.which("clang++") or shutil.which("g++") or shutil.which("c++") if compiler is None: raise AssertionError("client prediction clock check requires a C++ compiler") - with tempfile.TemporaryDirectory(prefix="mp-pause-prediction-", dir=ROOT / ".tmp") as raw: + # The Windows ARM64 runners refuse to delete the just-run harness executable + # (WinError 5) after the checks pass; a leftover file in .tmp must not turn + # that pass into a failure. + with tempfile.TemporaryDirectory(prefix="mp-pause-prediction-", dir=ROOT / ".tmp", ignore_cleanup_errors=True) as raw: temp = Path(raw) source, executable = temp / "prediction.cpp", temp / "prediction.exe" source.write_text(source_text, encoding="utf-8") @@ -658,7 +664,10 @@ def compile_match_clock_harness() -> None: tmp_root = ROOT / ".tmp" tmp_root.mkdir(exist_ok=True) - with tempfile.TemporaryDirectory(prefix="mp-match-clock-", dir=tmp_root) as raw: + # The Windows ARM64 runners refuse to delete the just-run harness executable + # (WinError 5) after the checks pass; a leftover file in .tmp must not turn + # that pass into a failure. + with tempfile.TemporaryDirectory(prefix="mp-match-clock-", dir=tmp_root, ignore_cleanup_errors=True) as raw: temp = Path(raw) source = temp / "match_clock_harness.cpp" source.write_text(MATCH_CLOCK_HARNESS.lstrip(), encoding="utf-8", newline="\n") diff --git a/tools/tests/mp_match_teams_contract.py b/tools/tests/mp_match_teams_contract.py index 9b6d0666..9dedd5e5 100644 --- a/tools/tests/mp_match_teams_contract.py +++ b/tools/tests/mp_match_teams_contract.py @@ -812,7 +812,10 @@ def locate_msvc() -> tuple[Path, str] | None: def executable_contract() -> None: temp_root = ROOT / ".tmp" temp_root.mkdir(exist_ok=True) - with tempfile.TemporaryDirectory(prefix="match-teams-", dir=temp_root) as temp: + # The Windows ARM64 runners refuse to delete the just-run harness executable + # (WinError 5) after the checks pass; a leftover file in .tmp must not turn + # that pass into a failure. + with tempfile.TemporaryDirectory(prefix="match-teams-", dir=temp_root, ignore_cleanup_errors=True) as temp: temp_dir = Path(temp) harness = temp_dir / "match_teams_contract.cpp" executable = temp_dir / "match_teams_contract.exe"