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
6 changes: 6 additions & 0 deletions runtime-recomp/tests/agent_hooks_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
ROOT = Path(__file__).resolve().parents[2]
HOOKS = ROOT / "scripts/agent_hooks"
HOOK_SCRIPTS = {"session_start.py", "pre_edit_guard.py", "post_edit_check.py", "stop_check.py"}
# The hooks act only inside a git checkout. Packaged source copies (the macOS
# build kit) have no .git, so the tests that drive them against this repo skip.
IN_CHECKOUT = (ROOT / ".git").exists()
SKIP_OUTSIDE_CHECKOUT = unittest.skipUnless(IN_CHECKOUT, "needs a git checkout of the repository")
SPEC = importlib.util.spec_from_file_location("hooklib", HOOKS / "hooklib.py")
hooklib = importlib.util.module_from_spec(SPEC)
sys.modules["hooklib"] = hooklib # dataclasses resolve their module by name
Expand Down Expand Up @@ -59,6 +63,7 @@ def git(root, *args):
)


@SKIP_OUTSIDE_CHECKOUT
class PreEditGuardTests(unittest.TestCase):
def test_checkout_is_found_from_a_nested_directory(self):
self.assertEqual(hooklib.checkout_of(HOOKS), ROOT)
Expand Down Expand Up @@ -383,6 +388,7 @@ def test_claude_commands_are_rooted_on_the_project_dir(self):
for command in commands:
self.assertTrue(command.startswith('"$CLAUDE_PROJECT_DIR"/scripts/agent_hooks/run '), command)

@SKIP_OUTSIDE_CHECKOUT
def test_session_state_is_gitignored(self):
for path in (".claude/session-base", ".claude/settings.local.json"):
result = subprocess.run(["git", "-C", str(ROOT), "check-ignore", "-q", path])
Expand Down
6 changes: 4 additions & 2 deletions runtime-recomp/tests/developer_entrypoint_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,14 @@ def test_failed_build_kit_does_not_poison_retry(self):
"Build-macOS.sh",
"Setup-macOS.sh",
"Run-macOS.sh",
".claude/settings.json",
".codex/hooks.json",
):
path = self.root / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("1.0.5\n")
for name in ("assets", "patches", "runtime-recomp"):
(self.root / name).mkdir()
for name in ("assets", "patches", "runtime-recomp", "tests/fixtures/pattern-guard"):
(self.root / name).mkdir(parents=True)
self.executable(self.bin / "rsync", "exit 0")
self.executable(self.bin / "tar", 'echo partial > "$4"; exit "${DKR_TEST_EXIT:-9}"')
self.env.update(DKR_RELEASE_VERSION="1.0.5", DKR_MAC_KIT_STAGE_ROOT=str(self.root / "stage"))
Expand Down
14 changes: 14 additions & 0 deletions runtime-recomp/tests/pattern_guard_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ def test_every_rule_explains_itself(self):
self.assertEqual(run_guard(ROOT, "--explain", "PG999")[0], 2)


class CheckContentTests(unittest.TestCase):
def test_a_patch_registered_before_it_is_written_is_still_checked(self):
# Registering first must not hide PG008: PG007 hashes the planned
# content instead of reading a file that doesn't exist yet.
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary) / "repo"
shutil.copytree(FIXTURES / "PG008/positive", root)
patch = root / "patches/rt64/0001-sidebar.patch"
text = patch.read_text(encoding="utf-8")
patch.unlink()
findings = pattern_guard.check_content(root, "patches/rt64/0001-sidebar.patch", text)
self.assertIn("PG008", {finding.rule for finding in findings})


class CMakeTokenizerTests(unittest.TestCase):
def test_comments_quotes_brackets_and_lines(self):
commands = pattern_guard.parse_cmake(
Expand Down
6 changes: 6 additions & 0 deletions scripts/Package-macOS-Build-Kit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ for directory in assets packaging patches scripts; do
done
rsync -a --exclude=.git --exclude=run-data --exclude=__pycache__ \
"${project_root}/runtime-recomp/" "${stage}/runtime-recomp/"
# Inputs of the pattern-guard and agent-hook tests that Build-macOS.sh runs
# through CTest.
mkdir -p "${stage}/tests/fixtures" "${stage}/.claude" "${stage}/.codex"
rsync -a "${project_root}/tests/fixtures/pattern-guard/" "${stage}/tests/fixtures/pattern-guard/"
install -m 0644 "${project_root}/.claude/settings.json" "${stage}/.claude/settings.json"
install -m 0644 "${project_root}/.codex/hooks.json" "${stage}/.codex/hooks.json"
# Preparation fetches the pinned Git checkouts from dependencies.lock.json.
# Source copies without .git cannot be verified or patched by that pipeline.

Expand Down
8 changes: 7 additions & 1 deletion scripts/pattern_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,12 @@ def is_scanned(self, path: str) -> bool:
def has_file(self, path: str) -> bool:
return path in self.overlay or (self.root / path).is_file()

def data(self, path: str) -> bytes:
"""Raw file bytes, from the overlay (planned content) when present."""
if path in self.overlay:
return self.overlay[path].encode("utf-8")
return (self.root / path).read_bytes()

@functools.cache
def text(self, path: str) -> str | None:
"""Decoded file text, or None for binary or unreadable files."""
Expand Down Expand Up @@ -755,7 +761,7 @@ def unregistered_dependency_patch(tree: Tree, path: str) -> Iterator[Finding]:
if path.endswith(".patch") and "/".join(path.split("/")[:2]) in tree.dependency_dirs:
if path not in registered:
yield Finding(path, 1, "PG007", "dependency patch is not in patches/manifest.json")
elif registered[path] != hashlib.sha256((tree.root / path).read_bytes()).hexdigest():
elif registered[path] != hashlib.sha256(tree.data(path)).hexdigest():
yield Finding(path, 1, "PG007", "sha256 differs from patches/manifest.json")
if is_cmake(path):
for command in tree.cmake(path):
Expand Down
Loading