diff --git a/tests/_bash.py b/tests/_bash.py index 868f7fa1..a4e56adf 100644 --- a/tests/_bash.py +++ b/tests/_bash.py @@ -17,6 +17,7 @@ def _probe_bash(bash_path: str) -> bool: @functools.lru_cache(maxsize=1) def _check_bash_state() -> tuple[str | None, str]: + reasons = [] if os.name == 'nt': for candidate in ( r'C:\Program Files\Git\bin\bash.exe', @@ -27,12 +28,15 @@ def _check_bash_state() -> tuple[str | None, str]: if os.path.exists(candidate): if _probe_bash(candidate): return candidate, "" - return None, f"bash present at {candidate} but unusable" + reasons.append(f"present at {candidate} but unusable") bash = shutil.which('bash') if bash and 'WindowsApps' not in bash: if _probe_bash(bash): return bash, "" - return None, f"bash present at {bash} but unusable" + reasons.append(f"present at {bash} but unusable") + + if reasons: + return None, "bash found but broken: " + "; ".join(reasons) return None, "Requires bash" def get_bash_executable() -> str | None: diff --git a/tests/test_pre_push_hook_contract.py b/tests/test_pre_push_hook_contract.py index 4606142b..d16f15f3 100644 --- a/tests/test_pre_push_hook_contract.py +++ b/tests/test_pre_push_hook_contract.py @@ -5,7 +5,7 @@ import subprocess import unittest from pathlib import Path -from tests._bash import get_bash_executable, require_bash, bash_skip_reason +from tests._bash import get_bash_executable, require_bash, bash_skip_reason, _check_bash_state REPO_ROOT = Path(__file__).resolve().parents[1] @@ -13,6 +13,14 @@ PRE_PUSH_HOOK = REPO_ROOT / '.github' / 'hooks' / 'pre-push' class PrePushHookContractTests(unittest.TestCase): + def setUp(self) -> None: + _check_bash_state.cache_clear() + super().setUp() + + def tearDown(self) -> None: + _check_bash_state.cache_clear() + super().tearDown() + @unittest.skipUnless(require_bash(), bash_skip_reason()) def test_skip_escape_hatch_exits_successfully_with_stderr_notice(self) -> None: bash_cmd = get_bash_executable() or 'bash' diff --git a/tests/test_roadmap_helpers.py b/tests/test_roadmap_helpers.py index 58d40842..55502213 100644 --- a/tests/test_roadmap_helpers.py +++ b/tests/test_roadmap_helpers.py @@ -14,7 +14,7 @@ REPO_ROOT = Path(__file__).resolve().parents[1] NEXT_ID = REPO_ROOT / 'scripts' / 'roadmap-next-id.sh' DOGFOOD_PROBE = REPO_ROOT / 'scripts' / 'dogfood-probe.py' -from tests._bash import get_bash_executable, require_bash, bash_skip_reason +from tests._bash import get_bash_executable, require_bash, bash_skip_reason, _check_bash_state def run_next_id(roadmap: Path, script: Path = NEXT_ID) -> subprocess.CompletedProcess[str]: @@ -40,6 +40,14 @@ def run_dogfood_probe(args: list[str]) -> subprocess.CompletedProcess[str]: class RoadmapHelperTests(unittest.TestCase): + def setUp(self) -> None: + _check_bash_state.cache_clear() + super().setUp() + + def tearDown(self) -> None: + _check_bash_state.cache_clear() + super().tearDown() + @unittest.skipUnless(require_bash(), bash_skip_reason()) def test_roadmap_next_id_prints_only_next_id_after_duplicate_check(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: