File size: 7,552 Bytes
0115dcf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | 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))
|