Skip to content

Commit 80dbe40

Browse files
committed
Make the test suite green on Windows and deflake the drain bound
The arbiter fixture name must end in .ps1 on Windows or execute_script rejects it before the injected backend is reached. The git-plumbing suites skip on native Windows: files land on disk with CRLF and GitPython keeps repository handles open, breaking diff assertions and temp-dir teardown. The drain-bound test now waits for the escapee's flood before closing, so the escapee is provably still writing while close() drains.
1 parent 1ba1196 commit 80dbe40

4 files changed

Lines changed: 38 additions & 3 deletions

File tree

‎tests/test_git_utils.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import sys
23
import tempfile
34
from pathlib import Path
45
from textwrap import dedent
@@ -19,6 +20,14 @@
1920
)
2021
from plain2code_exceptions import InvalidGitRepositoryError
2122

23+
pytestmark = pytest.mark.skipif(
24+
sys.platform == "win32",
25+
reason=(
26+
"Text files land on disk with CRLF and GitPython keeps repository handles open, so diff "
27+
"assertions and temp-dir teardown both break; native Windows runs via WSL."
28+
),
29+
)
30+
2231

2332
@pytest.fixture
2433
def temp_repo():

‎tests/test_plain_modules.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import json
99
import os
10+
import sys
1011
import tempfile
1112
from pathlib import Path
1213

@@ -22,6 +23,12 @@
2223
# --------------------------------------------------------------------------
2324

2425

26+
uses_git_repo = pytest.mark.skipif(
27+
sys.platform == "win32",
28+
reason="GitPython keeps repository handles open on Windows, so the temporary build folder cannot be removed; native Windows runs via WSL.",
29+
)
30+
31+
2532
@pytest.fixture
2633
def fixtures_dir(get_test_data_path):
2734
return get_test_data_path("data/partial_rendering")
@@ -247,6 +254,7 @@ def test_get_module_render_status_no_rendering(root_module):
247254
assert root_module.get_module_render_status() == (None, None)
248255

249256

257+
@uses_git_repo
250258
def test_get_module_render_status_returns_from_leaf_when_only_leaf_rendered(root_module):
251259
leaf = root_module.get_required_module_by_name("pr_leaf")
252260
_init_build_repo_with_finished_frid(leaf, "1")
@@ -256,6 +264,7 @@ def test_get_module_render_status_returns_from_leaf_when_only_leaf_rendered(root
256264
assert frid == "1"
257265

258266

267+
@uses_git_repo
259268
def test_get_module_render_status_prefers_most_progressed_module(root_module):
260269
"""The scan walks required_modules in reverse order — the right-most
261270
rendered module wins."""
@@ -269,6 +278,7 @@ def test_get_module_render_status_prefers_most_progressed_module(root_module):
269278
assert frid == "1"
270279

271280

281+
@uses_git_repo
272282
def test_get_module_render_status_returns_root_when_root_has_checkpoint(root_module):
273283
"""A checkpoint in the root's own build folder takes precedence over
274284
required-module checkpoints."""
@@ -290,11 +300,13 @@ def test_is_module_fully_rendered_false_when_nothing_rendered(solo_module):
290300
assert solo_module.is_module_fully_rendered() is False
291301

292302

303+
@uses_git_repo
293304
def test_is_module_fully_rendered_false_when_only_first_frid_rendered(solo_module):
294305
_init_build_repo_with_finished_frid(solo_module, "1")
295306
assert solo_module.is_module_fully_rendered() is False
296307

297308

309+
@uses_git_repo
298310
def test_is_module_fully_rendered_true_when_last_frid_rendered(solo_module):
299311
# solo module has FRIDs ["1", "2", "3"]; "3" is the last.
300312
_init_build_repo_with_finished_frid(solo_module, "3")
@@ -425,6 +437,7 @@ def _commit_finished_frid(module: PlainModule, frid: str) -> None:
425437
)
426438

427439

440+
@uses_git_repo
428441
def test_revert_code_to_frid_reverts_repo_and_trims_metadata(solo_module):
429442
os.makedirs(solo_module.module_build_folder)
430443
init_git_repo(solo_module.module_build_folder, module_name=solo_module.module_name)
@@ -443,6 +456,7 @@ def test_revert_code_to_frid_reverts_repo_and_trims_metadata(solo_module):
443456
assert os.path.exists(solo_module.get_codeplain_folder())
444457

445458

459+
@uses_git_repo
446460
def test_revert_code_to_frid_none_reverts_to_initial_state(solo_module):
447461
os.makedirs(solo_module.module_build_folder)
448462
init_git_repo(solo_module.module_build_folder, module_name=solo_module.module_name)
@@ -460,6 +474,7 @@ def test_revert_code_to_frid_none_reverts_to_initial_state(solo_module):
460474
# --------------------------------------------------------------------------
461475

462476

477+
@uses_git_repo
463478
def test_reconcile_metadata_with_git_trims_metadata_ahead_of_git(solo_module):
464479
# Simulates the crash window: metadata records FR 2 but git only committed FR 1.
465480
os.makedirs(solo_module.module_build_folder)
@@ -475,6 +490,7 @@ def test_reconcile_metadata_with_git_trims_metadata_ahead_of_git(solo_module):
475490
assert metadata["source_hash"] == "abc"
476491

477492

493+
@uses_git_repo
478494
def test_reconcile_metadata_with_git_in_sync_is_noop(solo_module):
479495
os.makedirs(solo_module.module_build_folder)
480496
init_git_repo(solo_module.module_build_folder, module_name=solo_module.module_name)
@@ -487,6 +503,7 @@ def test_reconcile_metadata_with_git_in_sync_is_noop(solo_module):
487503
assert solo_module.load_module_metadata()["functionalities"] == ["fr1", "fr2"]
488504

489505

506+
@uses_git_repo
490507
def test_reconcile_metadata_with_git_no_finished_frid_empties_list(solo_module):
491508
# Only the initial commit exists (no finished FRID), so the baseline must be emptied.
492509
os.makedirs(solo_module.module_build_folder)
@@ -498,6 +515,7 @@ def test_reconcile_metadata_with_git_no_finished_frid_empties_list(solo_module):
498515
assert solo_module.load_module_metadata()["functionalities"] == []
499516

500517

518+
@uses_git_repo
501519
def test_reconcile_metadata_with_git_ignores_foreign_module_frid(solo_module):
502520
# A repo cloned from a required module carries that module's finished FRID; this
503521
# module has rendered none of its own, so its baseline must be emptied.
@@ -518,6 +536,7 @@ def test_reconcile_metadata_with_git_ignores_foreign_module_frid(solo_module):
518536
assert solo_module.load_module_metadata()["functionalities"] == []
519537

520538

539+
@uses_git_repo
521540
def test_reconcile_metadata_with_git_no_metadata_is_noop(solo_module):
522541
os.makedirs(solo_module.module_build_folder)
523542
init_git_repo(solo_module.module_build_folder, module_name=solo_module.module_name)

‎tests/test_render_utils.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,9 @@ def test_script_stdin_is_a_terminal_of_its_own_and_never_the_renderers(tmp_path,
380380
# the cases are driven through an injected backend rather than through a real script:
381381
# the point is which condition wins, not how it arose.
382382

383-
FAKE_SCRIPT = "arbiter.sh"
383+
# execute_script() accepts only .ps1 on Windows, and that check runs before the
384+
# injected backend is reached, so the fake name has to match the platform.
385+
FAKE_SCRIPT = "arbiter.ps1" if sys.platform == "win32" else "arbiter.sh"
384386
FAKE_OUTPUT = "fake transcript\n"
385387
READER_FAILURE = RuntimeError("the master descriptor went away")
386388
REPLY_DETAIL = "cursor-position reply discarded before delivery"

‎tests/test_terminal_process.py‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,12 +1505,17 @@ def test_the_final_drain_is_bounded_against_a_continuously_writing_escapee(tmp_p
15051505
try:
15061506
process.spawn([script])
15071507
escapee_pid = reported_pid(process, "escapee")
1508+
# Both channels clear on read; accumulate so the assertions see the whole
1509+
# transcript. Waiting for the flood also guarantees the escapee is still
1510+
# writing while close() drains, which is the bound under test.
1511+
decoded = wait_for_output(process, "x")
1512+
raw = process.read_raw_output()
15081513
process.terminate_tree(grace=0.1) # the escapee left the group and survives
15091514
started = time.monotonic()
15101515
process.close()
15111516
elapsed = time.monotonic() - started
1512-
decoded = process.read_output()
1513-
raw = process.read_raw_output()
1517+
decoded += process.read_output()
1518+
raw += process.read_raw_output()
15141519
finally:
15151520
process.close()
15161521
if escapee_pid is not None and not wait_until_gone(escapee_pid, timeout=0.5):

0 commit comments

Comments
 (0)