Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions tools/check_wave_corpus.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,24 @@ def _resolve_compiler_path(candidate: Path | str) -> Path | None:
return None


def _require_launchable(path: Path) -> Path:
if os.name != "nt" and not os.access(path, os.X_OK):
raise PermissionError(f"wavec executable is not launchable: {path}")
return path


def resolve_wavec(explicit: Path | None) -> Path:
if explicit is not None:
resolved = _resolve_compiler_path(explicit)
if resolved is not None:
return resolved
return _require_launchable(resolved)
raise FileNotFoundError(f"wavec executable not found at {explicit}")

env_wavec = os.environ.get("WAVEC")
if env_wavec and env_wavec.strip():
resolved = _resolve_compiler_path(env_wavec)
if resolved is not None:
return resolved
return _require_launchable(resolved)
raise FileNotFoundError(f"wavec executable specified by WAVEC not found: {env_wavec}")

candidates = [
Expand All @@ -108,7 +114,7 @@ def resolve_wavec(explicit: Path | None) -> Path:

for candidate in candidates:
if candidate.is_file():
return candidate
return _require_launchable(candidate)

raise FileNotFoundError("wavec not found; build it or pass --wavec")

Expand Down Expand Up @@ -168,7 +174,7 @@ def main(argv: list[str] | None = None) -> int:
args = parse_args(argv)
try:
wavec = resolve_wavec(args.wavec)
except FileNotFoundError as error:
except OSError as error:
print(error, file=sys.stderr)
return 2

Expand Down
23 changes: 18 additions & 5 deletions tools/test_check_wave_corpus.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def test_valid_explicit_path_selected(self):
root = Path(td)
custom = root / "bin" / "custom_wavec"
custom.parent.mkdir(parents=True)
custom.touch()
custom.touch(mode=0o700)

fallback = root / "target" / "release" / "wavec"
fallback.parent.mkdir(parents=True)
Expand All @@ -147,11 +147,11 @@ def test_valid_wavec_env_selected(self):
root = Path(td)
custom = root / "bin" / "custom_wavec"
custom.parent.mkdir(parents=True)
custom.touch()
custom.touch(mode=0o700)

fallback = root / "target" / "release" / "wavec"
fallback.parent.mkdir(parents=True)
fallback.touch()
fallback.touch(mode=0o700)

with patch.object(check_wave_corpus, "ROOT", root):
with patch.dict(os.environ, {"WAVEC": str(custom)}, clear=False):
Expand All @@ -162,7 +162,7 @@ def test_no_override_falls_back_to_discovery(self):
root = Path(td)
fallback = root / "target" / "release" / "wavec"
fallback.parent.mkdir(parents=True)
fallback.touch()
fallback.touch(mode=0o700)

with patch.object(check_wave_corpus, "ROOT", root):
env = os.environ.copy()
Expand All @@ -175,7 +175,7 @@ def test_empty_wavec_env_falls_back_to_discovery(self):
root = Path(td)
fallback = root / "target" / "release" / "wavec"
fallback.parent.mkdir(parents=True)
fallback.touch()
fallback.touch(mode=0o700)

with patch.object(check_wave_corpus, "ROOT", root):
with patch.dict(os.environ, {"WAVEC": " "}, clear=False):
Expand All @@ -193,6 +193,19 @@ def test_no_override_and_no_binary_raises_file_not_found(self):

self.assertIn("wavec not found; build it or pass --wavec", str(cm.exception))

@unittest.skipIf(os.name == "nt", "POSIX executable bits do not apply")
def test_main_rejects_non_executable_compiler_without_traceback(self):
with tempfile.TemporaryDirectory() as td:
compiler = Path(td) / "wavec"
compiler.touch(mode=0o600)
stderr = io.StringIO()
with patch("sys.stderr", stderr):
self.assertEqual(main(["--wavec", str(compiler)]), 2)

self.assertIn(str(compiler), stderr.getvalue())
self.assertIn("not launchable", stderr.getvalue())
self.assertNotIn("Traceback", stderr.getvalue())


if __name__ == "__main__":
unittest.main()
Loading