The amount of tests in Oden has been increasing steadily since the beginning of this compiler iteration. I like to have tests run automatically when both library and test source code changes, so I have previously used nodemon to watch for file changes and execute
cabal test or the like. It was starting to get a bit slow so decided to search for a new setup. This post explains how it works.
As I already use tmux for all work I figured I could use that together with GHCi somehow. Tmux has a command called
send-keys that lets you programmatically send keys as though they were typed in at the target pane.
tmux send-keys -t "mysession:0.1" "echo hello, world" Enter
echo hello, world followed by a line break to the pane with index 1, in the window with index 0, in the session called mysession. The
-t switch handles convenient tokens such as
left so you don’t have to figure out indices. See the tmux man page for all variants.
I also use tmuxinator to setup windows and panes for my projects, which means I know what indices they will have. The following is an excerpt of my new Oden tmuxinator configuration.
name: oden root: ~/Projects/oden-lang windows: - oden: root: ~/Projects/oden-lang/oden panes: - vim - cabal exec ghci -- -isrc -itest Main - test-watch: root: ~/Projects/oden-lang/oden panes: - > nodemon \ --watch src \ --watch test \ -e hs \ --exec 'tmux send-keys -t "oden:0.1" :r Enter main Enter'
This tmuxinator configuration launches one split window with Vim and GHCi as well as another window running
nodemon. When Haskell source files in
test change the GHCi REPL in the first window gets the
:r command executed, which reloads all modules, followed by the invocation of
main which runs the tests again.
The reason I use
cabal exec -- -isrc -itest Main is to have both targets loaded as interpreted sources in GHCi. When you run
cabal repl <target> only that target’s sources can be reloaded. I want to reload changed source files both in the library and in the test targets.
If you are using Stack just replace
stack in these commands and it should work as well.
With this I have hundreds of tests run automatically in about a second, which I think is fine for Haskell. My previous setup had a latency at about 4 seconds. All in all, I’m very happy with this workflow.
If you have any improvements or recommendations you would like to share then please post a comment below.
Even though it is somewhat slower, I use
stack test --file-watch more often in the beginning of a project as it handles changes in the Cabal file. I have also started using the interactive session support in the Emacs haskell-mode to reload test modules using
C-c C-l and then run the individual tests with
hspec spec in the Emacs GHCi session. Maybe later I will create an Emacs keybinding for reloading a test module and running the specs.