-
Notifications
You must be signed in to change notification settings - Fork 0
/
mini-fiveam.lisp
115 lines (109 loc) · 4.15 KB
/
mini-fiveam.lisp
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
(defpackage :mini-fiveam
(:use :cl :clog)
(:export #:deftest
#:is
#:run-all-tests
#:run-all-tests-jest))
(in-package :mini-fiveam)
(defvar *test-names* nil)
(defvar *test-lambdas* (make-hash-table))
(defmacro deftest (name &body body)
`(progn
(push ',name *test-names*)
(setf (gethash ',name *test-lambdas*)
(lambda (pass)
,@body))))
(defmacro is (form)
(unless (consp form)
(error "Expected cons: ~S" form))
(destructuring-bind (eql expected expr)
form
`(let ((result ,expr)
(expected-value ,expected))
(if (,eql expected-value result)
(funcall pass)
(let ((message
(format nil "Expected ~S to eval to ~S, but it was ~S"
',expr ',expected result)))
(throw 'failure message))))))
(defun run-all-tests ()
(let ((failures)
(checks 0)
(pass 0)
(fail 0)
(erred 0))
(labels ((pass ()
(incf checks)
(incf pass))
(fail (test reason)
(incf checks)
(incf fail)
(push (list :failure test reason) failures))
(error* (test condition)
(incf checks)
(incf erred)
(push (list :error test condition) failures)))
(dolist (test-name (reverse *test-names*))
(let ((test-report-message
(with-output-to-string (report-message)
(format report-message "~A: " test-name)
(flet ((pass ()
(format report-message ".")
(pass))
(fail (test reason)
(format report-message "f")
(fail test reason))
(error* (test condition)
(format report-message "X")
(error* test condition)))
(handler-case
(let (finished)
(let ((reason
(catch 'failure
(funcall (gethash test-name *test-lambdas*) #'pass)
(setq finished t))))
(unless finished
(fail test-name reason))))
(error (c)
(error* test-name c)))))))
(clog test-report-message)))
(clog (format nil "Did ~A checks." checks))
(clog (format nil "Pass: ~A, Fail: ~A, Erred: ~A" pass fail erred))
(dolist (failure (reverse failures))
(destructuring-bind (kind test-name reason) failure
(funcall (ecase kind (:failure #'cwarn) (:error #'cerror))
(format nil "~A in ~A:"
(ecase kind (:failure "Failure") (:error "Error"))
test-name))
(ecase kind
(:failure (clog (format nil "~A" reason)))
(:error
(let* ((condition reason)
(message
(typecase condition
(simple-error
(format nil
(simple-condition-format-control condition)
(simple-condition-format-arguments condition)))
(type-error
(format nil
"Type error. ~a does not designate a ~a"
(type-error-datum condition)
(type-error-expected-type condition))))))
(clog message))))))
(values (and (zerop fail) (zerop erred))
checks pass fail erred
failures))))
(defun run-all-tests-jest ()
(multiple-value-bind (success
checks pass fail erred
failures)
(run-all-tests)
(jscl::make-new
#j:Array
(jscl::lisp-to-js
success)
(jscl::lisp-to-js
(format nil "Did ~A checks.~%Pass: ~A, Fail: ~A, Erred: ~A"
checks
pass fail erred)))))