54 lines
1.7 KiB
Python
54 lines
1.7 KiB
Python
from __future__ import annotations
|
|
|
|
from difflib import unified_diff
|
|
from pathlib import Path
|
|
|
|
from engine.models import BaselineSnapshot, TaskSpec
|
|
|
|
|
|
class MutationValidationError(ValueError):
|
|
pass
|
|
|
|
|
|
def _count_changed_lines(before: str, after: str, path: Path) -> int:
|
|
diff = unified_diff(
|
|
before.splitlines(keepends=True),
|
|
after.splitlines(keepends=True),
|
|
fromfile=f"{path.as_posix()} (before)",
|
|
tofile=f"{path.as_posix()} (after)",
|
|
)
|
|
changed_lines = 0
|
|
for line in diff:
|
|
if line.startswith(("---", "+++", "@@")):
|
|
continue
|
|
if line.startswith(("+", "-")):
|
|
changed_lines += 1
|
|
return changed_lines
|
|
|
|
|
|
def validate_candidate_changes(task: TaskSpec, snapshot: BaselineSnapshot) -> None:
|
|
changed_files = 0
|
|
changed_lines = 0
|
|
allowed_file_types = set(task.mutation.allowed_file_types)
|
|
|
|
for path, baseline_text in snapshot.file_contents.items():
|
|
current_text = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
if current_text == baseline_text:
|
|
continue
|
|
|
|
changed_files += 1
|
|
if path.suffix not in allowed_file_types:
|
|
raise MutationValidationError(f"disallowed file type: {path.suffix}")
|
|
|
|
changed_lines += _count_changed_lines(baseline_text, current_text, path)
|
|
|
|
if changed_files > task.artifacts.max_files_per_iteration:
|
|
raise MutationValidationError(
|
|
f"too many changed files: {changed_files} > {task.artifacts.max_files_per_iteration}"
|
|
)
|
|
|
|
if changed_lines > task.mutation.max_changed_lines:
|
|
raise MutationValidationError(
|
|
f"too many changed lines: {changed_lines} > {task.mutation.max_changed_lines}"
|
|
)
|