-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNestUnit.lean
143 lines (118 loc) · 4.21 KB
/
NestUnit.lean
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
import NestCore
import Lean.Elab
namespace Nest
namespace Unit
/--
Thrown by `UnitM` when an assertion failed.
-/
structure AssertionError where
/--
The failure message, this is supposed to be set by the asseertion itself
-/
message : String
/--
The position where it failed, this will be set by the framework through
the `assert` macro.
-/
pos : Option (String × Lean.Position) := none
deriving Repr
/--
A monad to run assertions in.
-/
abbrev UnitM (α : Type) := ExceptT AssertionError IO α
/--
The type for which this library implements `Nest.Core.IsTest`, it is `UnitM Unit`
so we can run arbitrary assertions inside of it.
-/
abbrev UnitTest := UnitM Unit
instance : Nest.Core.IsTest UnitTest where
run _ assertion := do
let result ← ExceptT.run assertion
match result with
| .ok .. =>
return {
outcome := .success,
description := "all assertions succeeded",
shortDescription := "all assertions succeeded",
details := ""
}
| .error err =>
let (filename, ⟨line, column⟩) := err.pos.get!
return {
outcome := .failure .generic
description := s!"assertion failure: {err.message}",
shortDescription := s!"assertion failure: {err.message}",
details := s!"file: '{filename}', line: {line}, col: {column}"
}
/--
Used by `Assertable` implementations to indicate failure.
-/
def assertFailure (str : String) : UnitM Unit := do
throw <| { message := str }
/--
Marks a type as a property that can be checked as an assertion
-/
class Assertable {α : Sort u} (x : α) where
/--
Checks the property that is passed to it and calls `assertFailure` if appropriate.
Note that `assert` is the intended API for this function
-/
assertThis : UnitM Unit
export Assertable (assertThis)
instance : Assertable (b : Bool) where
assertThis := do
unless b do
assertFailure "boolean was false"
instance [forall (x : t), Assertable x] (xs : List t) : Assertable xs where
assertThis := xs.forM (assertThis ·)
instance [forall (x : t), Assertable x] (xs : Array t) : Assertable xs where
assertThis := xs.forM (assertThis ·)
-- TODO: PrintableProp style stuff like with SlimCheck
instance (priority := low) {p : Prop} [Decidable p] : Assertable p where
assertThis := do
unless decide p do
assertFailure "property was not true"
instance [DecidableEq α] [Repr α] {x y : α} : Assertable (x = y) where
assertThis := do
unless x = y do
assertFailure s!"equality failed, left: '{repr x}', right: '{repr y}'"
instance [DecidableEq α] [Repr α] {x y : α} : Assertable (x ≠ y) where
assertThis := do
unless x ≠ y do
assertFailure s!"inequality failed, left: '{repr x}', right: '{repr y}'"
instance [LT α] [DecidableRel (· < · : α → α → Prop)] [Repr α] {x y : α} : Assertable (x < y) where
assertThis := do
unless x < y do
assertFailure s!"less than failed, left: '{repr x}', right: '{repr y}'"
instance [LT α] [DecidableRel (· < · : α → α → Prop)] [Repr α] {x y : α} : Assertable (x > y) where
assertThis := do
unless x > y do
assertFailure s!"greater than failed, left: '{repr x}', right: '{repr y}'"
instance [LE α] [DecidableRel (· ≤ · : α → α → Prop)] [Repr α] {x y : α} : Assertable (x ≤ y) where
assertThis := do
unless x ≤ y do
assertFailure s!"less than or equal failed, left: '{repr x}', right: '{repr y}'"
instance [LE α] [DecidableRel (· ≥ · : α → α → Prop)] [Repr α] {x y : α} : Assertable (x ≥ y) where
assertThis := do
unless x ≥ y do
assertFailure s!"greater than or equal failed, left: '{repr x}', right: '{repr y}'"
open Lean in
scoped elab "pos%" : term => do
let some pos := (← getRef).getPos?
| throwError "no source info"
let pos := (← getFileMap).toPosition pos
let posExpr := toExpr (← getFileName, pos)
return posExpr
def assert (t : α) [Assertable t] (pos : (String × Lean.Position) := by exact pos%) : UnitM Unit :=
try
assertThis t
catch e =>
throw <| { e with pos := some pos }
end Unit
namespace Core
namespace TestTree
def unitTest (name : String) (t : Unit.UnitTest) : TestTree :=
TestTree.single name t
end TestTree
end Core
end Nest