(This is a repost of an old blog post of mine from Teknik.)
While working on the LFE Swank Server, I noticed that Erlang's architecture rewards creating multiple tiny specialized processes. Because of this, I will want to have a simple test framework in LFE that leverages this architecture and is able to create "mock" processes. Essentially, what I need is a macro that is capable of spawning processes, binding their PIDs to variables, and cleaning them up after the test is over.
But, since the land of Erlang prefers underscores, I instead named it
Writing macros in LFE is a really weird feeling to me, at least when compared to Common Lisp. I have no destructuring lambda lists which I am used to, but I have pattern matching which is slightly more powerful than CL's destructuring. I also got to know Erlang's/LFE's
try/catch/after form - from a Lisper's perspective, it's
See the code for the macro and a very simple test case below. To make sure that the processes actually die, substitute the
echo function with a call to
(timer:sleep 100000), and then call the
(i) function inside and just outside the
WITH_PROCESSES body to verify there are
timer:sleep calls in the process list inside the macro body, but none of them remain after it is finished.
(defmacro with_processes (`(() . ,body) `(progn ,@body)) (`(((,symbol ,function) . ,rest) . ,body) `(let ((,symbol (spawn ,function))) (try (with-processes ,rest ,@body) (after (erlang:exit ,symbol 'kill))))))
(defun test_with_processes () (flet ((echo () (receive (`#(,pid ,x) (! pid x))))) (with-processes ((foo #'echo/0) (bar #'echo/0)) (! foo `#(,(self) 2)) (receive (2 'ok) (after 1000 (erlang:error "test failed"))) (! bar `#(,(self) 10)) (receive (10 'ok) (after 1000 (erlang:error "test failed"))))))