How to simulate I/O errors when writing integration tests for a binary crate?
⚓ Rust 📅 2026-05-09 👤 surdeus 👁️ 1I'm writing integration tests for my binary crate (CLI application) and I'm trying to achieve full code coverage. Currently, I'm at 95% code coverage, which I think is quite good already, but the only remaining code paths that are not currently reached by any tests all are code paths that are related to error handling — specifically, they are related to handling various kinds of I/O errors.
How can I "simulate" I/O errors in the integration tests? ![]()
Here is what I have got, so far:
-
Simulating a failure of
File::open()is trivial. Just pass a path to a non-existing file. -
Simulating a failure of
File.read()is not so trivial. I have found that, on Linux, I can pass special path/proc/self/memas input file, because this file opens fine; meanwhile anyread()attempt is going to fail reliably. But this is a Linux-only solution. And I'm not even sure whether Linux gurantees this behavior for all distrubutions and for all future kernel versions. It seems a bit brittle. Is there a more general, platform-independant solution?I thought about writing a custom Linux device driver (kernel module) that creates a virtual file
/proc/fail, which always opens successfully — theopen()implementation would be just a NOP — and that fails on any read or write attempt — theread()andwrite()implementations would simply returnEIOright away. But this doesn't really solve the platform-specific issue. And it makes the test setup even more complex. -
Simulating a failure of
ReadDir.next()seems to be very hard. I have not found a way to simulate the case that a directory can be opened successfully but then fails to iterate the dir entries! Maybe this could be simulated with a custom device driver, but it seems to go beyond whatprocfscan do, and therefore I don't really have an idea to implement it. -
Simulating a failure of
write!()to thestdoutstream is easy again. I can simply create a pipe, close the "read" handle of the pipe immediately, and set up the remaining "write" handle of the half-closed pipe as theCommand::stdout()of my child process. This causes anywrite!()tostdoutinside the child process to fail reliably, with a "broken pipe" error. -
Simulating a failure of
read!()from thestdinstream is difficult. Setting theCommand::stdin()of the child process to the "read" handle of a pipe whose "write" handle has already been closed, does not seem to trigger a failure ofread!()from thestdininside the child process. I have not found a way to makeread!()fromstdinfail!
So, is there a simpler way to simulate these errors?
Or, is there at least some way to simulate the error cases that I currently could not simulate?
Regards.
13 posts - 6 participants
🏷️ Rust_feed