Test Library Fixes
The previous version of test library had a number of problems, most importantly it relies heavily on the lexical variables indent
and outcomes
shared across multiple test runs. This meant that if test execution was terminated halfway and restarted for a set of tests, the indent
and outcomes
might not have been correctly reset, hence resulting wrong test output formatting.
The fixed version:
(defvar *test-output-stream* t)
(defun make-spaces (n)
(make-string n :initial-element #\space))
(defun format-outcome (depth name outcome &optional (output-stream *test-output-stream*))
(format output-stream "~&~ATest `~A` ~:[failed~;passed~].~%" (make-spaces depth) name outcome))
(defun format-context (depth name &optional (output-stream *test-output-stream*))
(format output-stream "~&~A~A~%" (make-spaces depth) name))
(defun format-outcomes (outcomes &optional (output-stream *test-output-stream*))
(format output-stream "~&~A out of ~A tests passed~%" (count t outcomes) (length outcomes)))
(defmacro test (name predicate)
(let ((outcome (gensym)))
`(lambda (depth)
(let ((,outcome ,predicate))
(format-outcome depth ,name ,outcome) (list ,outcome)))))
(defmacro context (name &rest body)
`(lambda (depth)
(format-context depth ,name)
(let ((depth (+ depth 1)))
(reduce #'append (map 'list #'(lambda (f) (apply f `(,depth))) (list ,@body))))))
(defun run (root-context)
(format-outcomes (apply root-context `(,0))))
The newly fixed version removes dependency on shared indent
and outcomes
lexical variables, instead explicitly redefining a depth
lexical variable and passing in depth
to each test and context, hence leveraging the exit of corresponding lexical scopes to restore the correct depth
to use for test output formatting. An outcome
is returned from each test and context, resulting in an accumulated list of outcomes
at the root level. A run
function is also introduced to kickstart a root context, henceforth removing the need for checking depth
to determine if outcomes should be printed.