One of the more common side-effects for functions to have is writing output to stdout or stderr. This poses a problem when writing unit tests in OUnit2, both because having a whole bunch of output printed to the console while running unit tests is annoying, and because sometimes it would be nice to write a unit test ensuring a function outputs a specific message.
One way to do this is to parameterize every function with two Format.formatter arguments for out and err output. However, this is fairly heavyweight when sometimes the message is printed out way down the call stack. Is there a way to just capture stdout and stderr of the current process into a buffer?
From memory, this can be done using a pipe. Something like (not tested):
let saved_stdout =
Unix.dup Unix.stdout (* save current stdout to restore it later *)
let stdout_read =
let pipe_read, pipe_write = Unix.pipe () in
Unix.dup2 pipe_write Unix.stdout; (* Unix.stdout now points to pipe_write *)
Unix.close pipe_write;
pipe_read
Now you can read from stdout_read in order to “read the stdout” of the current program. If you ever want to restore the “old” stdout you do so by
let () =
Unix.dup2 saved_stdout Unix.stdout;
Unix.close saved_stdout
This is tricky because the pipe has a limited amount of buffering, so it will lose data. To avoid this you’d need to concurrently read off the pipe. I have an example here: capture_stdout_logs
Regarding @lukstafi’s code I would rather do that with a thread than a domain. Domains are not really expected to be used for short lived concurrency.