mikeljl's picture
use pypantograph for checking submitted statements
0115dcf
import Pantograph.Frontend
import Pantograph.Environment
import Test.Common
open Lean Pantograph Frontend
namespace Pantograph.Test.Frontend.Collect
abbrev Test := Environment → TestT IO Unit
def runFrontend { α } (env : Environment) (source: String) (f : CompilationStep → FrontendM α) (timeout : UInt32 := 0)
: IO (List α) := do
let (context, state) ← do createContextStateFromFile source (env? := env)
let m := mapCompilationSteps f
let cancelTk? ← match timeout with
| 0 => pure .none
| timeout => .some <$> spawnCancelToken timeout
m.run { cancelTk? } |>.run context |>.run' state
def test_open : Test := λ env => do
let sketch := "
open Nat
example : ∀ (n : Nat), n + 1 = Nat.succ n := by
intro
apply add_one
"
let errors ← runFrontend env sketch λ step => step.msgs.mapM (·.toString)
checkEq "errors" errors [[], []]
def collectNewConstants (env : Environment) (source: String) : IO (List (List Name)) := do
let (context, state) ← do Frontend.createContextStateFromFile source (env? := env)
let m := show FrontendM _ from Frontend.mapCompilationSteps λ step => do
step.newConstants
let result ← m.run {} |>.run context |>.run' state
return result.map (·.toList)
def test_collect_one_constant : Test := λ env => do
let input := "
def mystery : Nat := 123
"
let names ← collectNewConstants env input
checkEq "constants" names [[`mystery]]
def test_collect_one_theorem : Test := λ env => do
let input := "
theorem mystery [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
match as, i with
| a::as, ⟨0, _⟩ => simp_arith [get]
| a::as, ⟨i+1, h⟩ =>
have ih := sizeOf_get as ⟨i, Nat.le_of_succ_le_succ h
apply Nat.lt_trans ih
simp_arith
"
let names ← collectNewConstants env input
checkEq "constants" names [[`mystery]]
def test_collect_stub : Test := λ env => do
let input := "
theorem mystery [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := sorry
"
let names ← collectNewConstants env input
checkEq "constants" names [[`mystery]]
def checkFileConflicts (env : Environment) (src dst : String) : IO (Except String Environment):= do
let srcState ← collectOne src
let dstState ← collectOne dst
ExceptT.run $ checkEnvConflicts env srcState.env dstState.env
where
collectOne (source : String) : IO _ := do
let (context, state) ← do createContextStateFromFile source (env? := env)
let m := collectEndState
m.run { } |>.run context |>.run' state
def test_conflict_simple : Test := λ env => do
let src := "
def x : Nat := sorry
"
let dst := "
def x : Nat := 123
"
let result? ← checkFileConflicts env src dst
match result? with
| .ok _ => checkTrue "ok" result?.isOk
| .error e => fail s!"Failed with {e}"
def test_conflict_poly : Test := λ env => do
let src := "
def mystery : List α → List α := sorry
"
let dst := "
def helper (li : List β) := li.reverse
def mystery (li : List α) := (helper li) ++ li
"
let result? ← checkFileConflicts env src dst
match result? with
| .ok _ => checkTrue "ok" result?.isOk
| .error e => fail s!"Failed with {e}"
def test_conflict_auxiliary : Test := λ env => do
let src := "
def f : Nat → Nat := sorry
"
let dst := "
def x : Nat := 123
def f : Nat → Nat := λ y => y + x
"
let result? ← checkFileConflicts env src dst
match result? with
| .ok _ => checkTrue "ok" result?.isOk
| .error e => fail s!"Failed with {e}"
def test_conflict_axiom : Test := λ env => do
let src := "
axiom α : Type
axiom ne : Nonempty α
noncomputable def f : α := sorry
"
let dst := "
axiom α : Type
axiom ne : Nonempty α
noncomputable def f : α := @Classical.choice α ne
"
let result? ← checkFileConflicts env src dst
match result? with
| .ok _ => checkTrue "ok" result?.isOk
| .error e => fail s!"Failed with {e}"
/-- from `GasStationManager/SafeVerify` -/
def test_conflict_simple_def : Test := λ env => do
let src := "
def solveAdd (a b:Int):{c:Int//a+c=b} := sorry
"
let dst := "
def solveAdd (a b:Int):{c:Int//a+c=b} := ⟨b-a, by omega⟩
"
let result? ← checkFileConflicts env src dst
match result? with
| .ok _ => checkTrue "ok" result?.isOk
| .error e => fail s!"Failed with {e}"
/-- from `GasStationManager/SafeVerify` -/
def test_conflict_fake_implementation : Test := λ env => do
let src := "
noncomputable def definitely_at_least_two : Nat := sorry
theorem definitely_at_least_two_spec : 2 ≤ definitely_at_least_two := sorry
"
let dst := "
@[implemented_by Nat.zero]
noncomputable def definitely_at_least_two : Nat :=
Exists.choose (⟨3, by simp⟩ : ∃ x, 2 ≤ x)
theorem definitely_at_least_two_spec : 2 ≤ definitely_at_least_two :=
Exists.choose_spec _
"
let result? ← checkFileConflicts env src dst
match result? with
| .ok _ => checkTrue "ok" result?.isOk
| .error e => fail s!"Failed with {e}"
def test_conflict_fail_idempotent : Test := λ env => do
let src := "
def x : Nat := sorry
"
let .error e ← checkFileConflicts env src src
| fail "Must fail"
checkEq "message" e "Definition value has sorry: x"
def test_conflict_fail_delete_definition : Test := λ env => do
let src := "
def x : Nat := sorry
def y : Nat := sorry
"
let dst := "
def x : Nat := 123
"
let .error e ← checkFileConflicts env src dst
| fail "Must fail"
checkEq "message" e "[y] not accounted for"
def test_conflict_fail_inductive_modification : Test := λ env => do
let src := "
inductive A where
| a
| b
"
let dst := "
inductive A where
| a
| b
| c
"
let .error e ← checkFileConflicts env src dst
| fail "Must fail"
checkEq "message" e "Type clash of A.casesOn"
/-- from `GasStationManager/SafeVerify` -/
def test_conflict_fail_noncomputable : Test := λ env => do
let src := "
axiom α : Type
axiom ne : Nonempty α
def f : α := sorry
"
let dst := "
axiom α : Type
axiom ne : Nonempty α
noncomputable def f : α := @Classical.choice α ne
"
let .error e ← checkFileConflicts env src dst
| fail "Must fail"
checkEq "message" e "Must not modify computability on f"
def test_conflict_fail_add_axiom : Test := λ env => do
let src := "
theorem mystery : False := sorry
"
let dst := "
axiom z : False
theorem mystery : False := z
"
let .error e ← checkFileConflicts env src dst
| fail "Must fail"
checkEq "message" e "Adding axiom is not allowed: z"
def suite (env : Environment): List (String × IO LSpec.TestSeq) :=
let tests := [
("open", test_open),
("collect_one_constant", test_collect_one_constant),
("collect_one_theorem", test_collect_one_theorem),
("collect_stub", test_collect_stub),
("conflict simple", test_conflict_simple),
("conflict poly", test_conflict_poly),
("conflict auxiliary", test_conflict_auxiliary),
("conflict simple def", test_conflict_simple_def),
("conflict axiom", test_conflict_axiom),
("conflict fake implementation", test_conflict_fake_implementation),
("conflict fail idempotent", test_conflict_fail_idempotent),
("conflict fail delete definition", test_conflict_fail_delete_definition),
("conflict fail inductive modification", test_conflict_fail_inductive_modification),
("conflict fail noncomputable", test_conflict_fail_noncomputable),
("conflict fail add axiom", test_conflict_fail_add_axiom),
]
tests.map (fun (name, test) => (name, runTest $ test env))