From 5fb4928296624997dfb6c168a8c8a5c1dcecb033 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Sat, 24 Oct 2026 11:12:42 -0700 Subject: [PATCH 1/2] Forward regeneration test output to the caller on Windows Pass the redirected streams explicitly when launching the no-console test child, preserving failure status and readable diagnostics. Verify stdout/stderr on real success and failure subprocesses. Refs beads-h6si. --- tests/test_regen_project_output.py | 48 ++++++++++++++++++++++++++++++ tools/regen_project.py | 3 ++ 2 files changed, 51 insertions(+) create mode 100644 tests/test_regen_project_output.py diff --git a/tests/test_regen_project_output.py b/tests/test_regen_project_output.py new file mode 100644 index 00000000..b540efdb --- /dev/null +++ b/tests/test_regen_project_output.py @@ -0,0 +1,48 @@ +"""The regeneration driver's real no-console child must retain its output.""" +from pathlib import Path +import subprocess +import sys + +import pytest + +ROOT = Path(__file__).resolve().parents[1] + + +@pytest.mark.parametrize("exit_code", [0, 7], ids=["success", "failure"]) +def test_framework_test_output_survives_no_console_launch(tmp_path, exit_code): + engine = tmp_path / "fixture-engine" + tests = engine / "tests" + tests.mkdir(parents=True) + (tests / "run_tests.py").write_text( + "import sys\n" + "print('framework child stdout', flush=True)\n" + "print('framework child stderr', file=sys.stderr, flush=True)\n" + f"raise SystemExit({exit_code})\n", + encoding="utf-8", + ) + project = tmp_path / "game" + project.mkdir() + driver = tmp_path / "driver.py" + # Generation/ROM discovery are unrelated to child-process forwarding. + # Keep the real main() and subprocess launch; only replace those stages. + driver.write_text( + "import sys\nfrom pathlib import Path\n" + f"sys.path.insert(0, {str(ROOT)!r})\n" + "from tools import regen_project as regen\n" + f"regen.ROOT = Path({str(engine)!r})\n" + "regen.ensure_tool_paths = lambda root: None\n" + "regen.load_recipe = lambda cfg: None\n" + "regen.parse = lambda path: {'rom_file': 'fixture.sfc'}\n" + "regen.generate = lambda **options: None\n" + f"raise SystemExit(regen.main(['--project-root', {str(project)!r}]))\n", + encoding="utf-8", + ) + result = subprocess.run( + [sys.executable, str(driver)], capture_output=True, text=True, + encoding="utf-8", errors="replace", + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), + timeout=30, + ) + assert (result.returncode == 0) == (exit_code == 0) + assert "framework child stdout" in result.stdout + assert "framework child stderr" in result.stderr diff --git a/tools/regen_project.py b/tools/regen_project.py index 5fbe1d5e..06b257dd 100644 --- a/tools/regen_project.py +++ b/tools/regen_project.py @@ -55,6 +55,9 @@ def main(argv=None): if not args.no_tests: subprocess.run([sys.executable, str(ROOT / "tests/run_tests.py")], cwd=ROOT, check=True, + # CREATE_NO_WINDOW has no console handles to inherit. + # Forward the caller's redirected streams explicitly. + stdout=sys.stdout, stderr=sys.stderr, creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0)) return 0 From 7e6a7ea38669c578f7dc7ecf88bba83a839a093d Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Sat, 24 Oct 2026 11:41:21 -0700 Subject: [PATCH 2/2] Isolate scaffold fixtures from inherited framework overrides --- tests/test_new_project.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_new_project.py b/tests/test_new_project.py index fb59b8cc..c756bec1 100644 --- a/tests/test_new_project.py +++ b/tests/test_new_project.py @@ -32,6 +32,10 @@ def _use_test_interpreter(monkeypatch): # Shell subprocesses must use the same installed Python as pytest, not # Windows' python3 Store alias or an unrelated MSYS installation. monkeypatch.setenv("PYTHON", pathlib.Path(sys.executable).as_posix()) + # Most fixtures deliberately have no framework submodule. A caller's + # checkout override would let them generate anyway and mask that case. + # Tests that need a framework pass their own explicit override. + monkeypatch.delenv("SNESRECOMP_ROOT", raising=False) def _fixture_rom(path: pathlib.Path) -> None: