As a first step, you can make a small change to report-result
so it returns the result of the test case it’s reporting.
Now that report-result
returns the result of its test case, it might seem you could just change the **PROGN**
to an **AND**
to combine the results. Unfortunately, **AND**
doesn’t do quite what you want in this case because of its short-circuiting behavior: as soon as one test case fails, **AND**
will skip the rest. On the other hand, if you had a construct that worked like **AND**
without the short-circuiting, you could use it in the place of **PROGN**
, and you’d be done. Common Lisp doesn’t provide such a construct, but that’s no reason you can’t use it: it’s a trivial matter to write a macro to provide it yourself.
(foo)
(bar)
(baz))
and have it mean something like this:
The only tricky bit to writing this macro is that you need to introduce a variable—result
in the previous code—in the expansion. As you saw in the previous chapter, using a literal name for variables in macro expansions can introduce a leak in your macro abstraction, so you’ll need to create a unique name. This is a job for with-gensyms
. You can define combine-results
like this:
(with-gensyms (result)
`(let ((,result t))
,@(loop for f in forms collect `(unless ,f (setf ,result nil)))
With that version of check
, test-+
should emit the results of its three test expressions and then return **T**
to indicate that everything passed.4
pass ... (= (+ 1 2) 3)
pass ... (= (+ 1 2 3) 6)
pass ... (= (+ -1 -3) -4)
And if you change one of the test cases so it fails,5 the final return value changes to **NIL**
.