2026-07-03 08:51:03 +00:00
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
from pacta.lean import LeanTools, build_lean_env, build_lean_invocation, lean_check_files, parse_axiom_output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_axiom_output_multiline():
|
|
|
|
|
output = """
|
|
|
|
|
'CurveFieldProofs.fieldImplementation' depends on axioms:
|
|
|
|
|
[propext, Classical.choice, Quot.sound]
|
|
|
|
|
'CurveFieldProofs.edwardsImplementation' depends on axioms:
|
|
|
|
|
[propext, Classical.choice, Quot.sound]
|
|
|
|
|
"""
|
|
|
|
|
parsed = parse_axiom_output(
|
|
|
|
|
output,
|
|
|
|
|
["CurveFieldProofs.fieldImplementation", "CurveFieldProofs.edwardsImplementation"],
|
|
|
|
|
)
|
|
|
|
|
assert parsed["CurveFieldProofs.fieldImplementation"] == ["propext", "Classical.choice", "Quot.sound"]
|
|
|
|
|
assert parsed["CurveFieldProofs.edwardsImplementation"] == ["propext", "Classical.choice", "Quot.sound"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_axiom_output_no_axioms_wording():
|
|
|
|
|
output = "'CurveFieldProofs.fieldImplementation' does not depend on any axioms\n"
|
|
|
|
|
parsed = parse_axiom_output(output, ["CurveFieldProofs.fieldImplementation"])
|
|
|
|
|
assert parsed["CurveFieldProofs.fieldImplementation"] == []
|
|
|
|
|
|
|
|
|
|
|
2026-07-16 09:04:59 +00:00
|
|
|
def test_parse_axiom_output_axiom_free_cert_does_not_steal_next_cone():
|
|
|
|
|
# Regression (found by the entry-13 rehearsal, 2026-07-16): an
|
|
|
|
|
# axiom-free certificate is followed by a cone-carrying one. The old
|
|
|
|
|
# windowed search reached past the bracketless "does not depend"
|
|
|
|
|
# sentence and attributed the NEXT certificate's cone. The
|
|
|
|
|
# accumulator corpus is the first subject with axiom-free
|
|
|
|
|
# certificates (domsep, Hash, instDecidableEqHash, take_append_drop),
|
|
|
|
|
# so no fork attestation ever exercised this path.
|
|
|
|
|
output = (
|
|
|
|
|
"'LTLAcc.domsep' does not depend on any axioms\n"
|
|
|
|
|
"'LTLAcc.eq_dropLast_append_of_getLast?' depends on axioms: [propext]\n"
|
|
|
|
|
)
|
|
|
|
|
parsed = parse_axiom_output(output, ["LTLAcc.domsep", "LTLAcc.eq_dropLast_append_of_getLast?"])
|
|
|
|
|
assert parsed["LTLAcc.domsep"] == []
|
|
|
|
|
assert parsed["LTLAcc.eq_dropLast_append_of_getLast?"] == ["propext"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_axiom_output_exact_name_not_prefix():
|
|
|
|
|
# 'LTLAcc.MTH' must not match the line for 'LTLAcc.MTH_single' even
|
|
|
|
|
# when the latter comes first in the output.
|
|
|
|
|
output = (
|
|
|
|
|
"'LTLAcc.MTH_single' depends on axioms: [propext, LTLAcc.sha256, Quot.sound]\n"
|
|
|
|
|
"'LTLAcc.MTH' depends on axioms: [propext, LTLAcc.sha256, Quot.sound]\n"
|
|
|
|
|
)
|
|
|
|
|
parsed = parse_axiom_output(output, ["LTLAcc.MTH"])
|
|
|
|
|
assert parsed["LTLAcc.MTH"] == ["propext", "LTLAcc.sha256", "Quot.sound"]
|
|
|
|
|
|
|
|
|
|
|
2026-07-03 08:51:03 +00:00
|
|
|
def test_mac_safe_lean_command_is_argument_list():
|
|
|
|
|
tools = LeanTools(lean="/usr/local/bin/lean", lake=None)
|
2026-07-03 11:03:58 +00:00
|
|
|
cmd = build_lean_invocation(Path("Proofs/A.lean"), tools, output_path=Path("Proofs/A.olean"), root_path=Path("."))
|
|
|
|
|
assert cmd == ["/usr/local/bin/lean", "--root=.", "-o", "Proofs/A.olean", "Proofs/A.lean"]
|
2026-07-03 08:51:03 +00:00
|
|
|
assert "timeout" not in cmd
|
|
|
|
|
assert "taskset" not in cmd
|
|
|
|
|
assert "free" not in cmd
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_lean_path_uses_python_pathsep(tmp_path):
|
|
|
|
|
verification = tmp_path / "verification"
|
|
|
|
|
(verification / "gen").mkdir(parents=True)
|
|
|
|
|
env = build_lean_env(verification, base_env={"LEAN_PATH": "old"})
|
|
|
|
|
assert str(verification / "gen") in env["LEAN_PATH"]
|
|
|
|
|
assert str(verification) in env["LEAN_PATH"]
|
|
|
|
|
assert "old" in env["LEAN_PATH"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_lean_check_requires_discovered_files(tmp_path):
|
|
|
|
|
result = lean_check_files([], tmp_path / "verification")
|
|
|
|
|
assert not result.attempted
|
|
|
|
|
assert "No Lean files" in result.diagnostics[0]
|