Using `entr` as a test watcher
Published on
When I'm programming, I like to get feedback about my code as fast as possible. I like to learn about all of my bugs every time I'm "done thinking" (every time I save a file). Nowadays, LSP integrations have made it possible to get syntax and type checking on every keystroke, which is even faster than that! But everything else has to run on save.
When I discovered entr, I knew it would immediately change my workflow. I use it all the time for iterating on little one-off scripts. I edit in one terminal window and run this in another:
ls shenanigans.py | entr -c python /_
If it ever depends on multiple files (for larger projects or separate test data), I'll combine it with fd like this:
fd . | entr -c make test
But I ran into a problem using this when adding or removing files because entr (intentionally) doesn't update the list of files by itself.
Its manual has an example for solving this (using its -d option and a bash loop):
while true; do ls src/*.rb | entr -d make; done
But I would sometimes get double-runs of my tests, which was weird and slow. So far, I've narrowed it down to an interaction between Neovim, ALE, Rubocop, and the write-tempfile-and-rename-into-place pattern for atomic rewrites. In other words, I have no idea.
But I do have a fix! The answer up front:
until fd . | entr -cdp make test ; do
echo 'Edit a file or press Space to run tests.'
done
The trick turned out to be two parts:
- The
-poption to delay execution until something changes. - Accepting the limitation of needing to wait for a file save for the first test run.
Here's how it works:
fd .finds all the files and directories starting from the current working directory.entr -cdpdoes nothing yet because-pmakes it wait for a change.- I save a file or press Space.
entrnotices that, clears the screen (-c), and runsmake test.- If no directories changed (no files added or removed), goto 3.
- If a directory changed,
entrexits with status code 2 (which is falsy in bash), sountil's condition has not been met. - The body of the loop runs, so the
echomessage is printed. untilloops, so goto 1.
I thought this was pretty clever, because the instructions are there when I run the command and when it "stops" on a directory change.
If you don't like telling yourself what to do, replace the echo command with : (no-op) or true