Sunday, February 13, 2005

Nice filesystem testing pattern

I've come across a useful pattern for unit testing with the filesystem: frequently I want to create some "sandbox" directory where I'll stick a bunch of temporary files and subdirectories for testing, and when the test is over, I want to delete the whole thing. But any intermediate files or directories I create during testing should not be deleted, because I may still need them later in the test. So only the first directory I create should be automatically deleted.

This requires two things: 1) a macro for creating a new directory and evaluating subexpressions in the context of that directory, and 2) a facility for enabling and disabling the automatic deletion of temporary directories.
(define keep-new-directories? (make-parameter #f))

(define-syntax in-new-directory
(syntax-rules ()
[(_ dir-e e1 e2 ...)
(let ([dir dir-e])
(rm-rf dir)
(make-directory dir)
(let ([result (parameterize ([current-directory dir]
[keep-new-directories? #t])
e1 e2 ...)])
(unless (keep-new-directories?)
(rm-rf dir))
Another nice property of this pattern is that if I want to test my test, i.e., to inspect the files it's creating, I can temporarily disable the automatic deletion of the sandbox directory by wrapping the whole thing in a (parameterize ([keep-new-directories? #t]) ...).

Meta-conclusion: dynamic scope is useful.

No comments: