sourcelibs/Check/src/Check.xtl
1⍝# Check: assertions that report as text, for tests and teaching.
2⍝# Import it with an alias of your choice: "k:" u_se< "Check".
3⍝# Put libs/Check/src on XETAL_PATH ("just path"); the reference is libs/Check/docs.
4⍝# Names with l: are exported; those under h: are private to this file.
5⍝#
6⍝# A check is a line of text, "ok" or "FAIL: ..." (X_eTaL has no
7⍝# assert and no way to stop with an error of your own yet): it prints
8⍝# when it is a statement, and checks join into a report with a_nd.
9⍝# 6 k:i_s '+ r_/ 1 2 3 # ok
10⍝# "sum" k:t_est 5 k:i_s '+ r_/ 1 2 3 # FAIL: sum: expected 5, got 6
11⍝# k:r_eport (a k:a_nd b k:a_nd c) # the lines, then "3 checks, all passed"
12
13ʰn̲l ← { @ → f̲irst "\n" } ⍝ the newline character
14
15⍝# t, its newlines shown as ";", so a value takes one line.
16ʰo̲neLine ← { t →
17 m ← t = ʰn̲l @
18 ((r̲ange t̲ally t) + m × (1 + t̲ally t) − r̲ange t̲ally t) s̲elect t c̲at ";"
19}
20
21⍝# t on one line; when longer than 60 characters, its start and its
22⍝# shape: "1 2 3 ... (shape 100)".
23ʰs̲hown ← { t →
24 s ← ʰo̲neLine f̲ormat t
25 60 ≥ t̲ally s ? s
26 (48 t̲ake s) c̲at " ... (shape " c̲at (f̲ormat s̲hape t) c̲at ")"
27}
28
29ʰf̲ail ← { w g → "FAIL: expected " c̲at (ʰs̲hown w) c̲at ", got " c̲at ʰs̲hown g }
30
31⍝## Checks
32
33⍝# want i_s got: ok when got has want's shape and items (m_atch).
34ˡi̲s ← { w g → w m̲atch g ? "ok"◆ w ʰf̲ail g }
35
36⍝# want n_ear got: ok when the shapes match and every item is equal
37⍝# within e_q~'s relative tolerance, or within 1e-12 of it (so a
38⍝# computed 1e-17 is near an exact 0, which e_q~ alone never allows).
39ˡn̲ear ← { w g →
40 n̲ot (s̲hape w) m̲atch s̲hape g ? w ʰf̲ail g
41 '∧ r̲/ r̲avel (w e̲q~ g) ∨ 0.000000000001 ≥ a̲bs (f̲loat w) − f̲loat g ? "ok"
42 w ʰf̲ail g
43}
44
45⍝# t_rue c: ok when every item of the condition c is true (1).
46ˡt̲rue ← { c → '∧ r̲/ r̲avel c ? "ok"◆ "FAIL: expected true, got " c̲at ʰs̲hown c }
47
48⍝## Naming a check
49
50⍝# name t_est line: the check's line, named: "ok: name" or "FAIL: name: ...".
51ˡt̲est ← { name line →
52 "ok" m̲atch 2 t̲ake line ? "ok: " c̲at name
53 "FAIL: " c̲at name c̲at ": " c̲at 6 d̲rop line
54}
55
56⍝## Reports
57
58⍝# a a_nd b: two checks (or reports) as one text, a line each.
59ˡa̲nd ← { a b → a c̲at "\n" c̲at b }
60
61⍝# c_ount t: how many checks a text holds (its lines).
62ˡc̲ount ← { t → 1 + t̲ally w̲here t = ʰn̲l @ }
63
64⍝# f_ailures t: how many of its checks failed (lines starting FAIL).
65ˡf̲ailures ← { t →
66 s ← (ʰn̲l @) c̲at t
67 t̲ally w̲here (-1 d̲rop s = ʰn̲l @) ∧ (1 d̲rop s) = f̲irst "F"
68}
69
70⍝# p_assed? t: whether every check passed.
71ˡp̲assed? ← { t → 0 = ˡf̲ailures t }
72
73⍝# r_eport t: the checks, then a summary line.
74ˡr̲eport ← { t →
75 n ← ˡc̲ount t
76 f ← ˡf̲ailures t
77 s ← (f̲ormat n) c̲at " check" c̲at (n ≠ 1) r̲eplicate "s"
78 f = 0 ? t ˡa̲nd s c̲at ", all passed"
79 t ˡa̲nd s c̲at ", " c̲at (f̲ormat f) c̲at " FAILED"
80}