If you want to avoid any mocking whatsoever, your best bet is simply provoking errors by directly hitting OS limits. For example, inotify_init
can fail with EMFILE
errno, if the calling process has reached it's limit on number of open file descriptors. To reach such conditions with 100% precision you can use two tricks:
- Dynamically manipulate limits of running process by changing values in procfs
- Assign your app process to dedicated cgroup and "suspend" it by giving it ~0% CPU time via cgroups API (this is how Android throttles background apps and implements it's energy-saving "Doze" mode).
All possible errors conditions of inotify are documented in man pages of inotify
, inotify_init
and inotify_add_watch
(I don't think that inotify_rm_watch
can fail except for purely programming errors in your code).
Aside from ordinary errors (such as going over /proc/sys/fs/inotify/max_user_watches
) inotify has several fault modes (queue space exhaustion, watch ID reuse), but those aren't "failures" in strict sense of word.
Queue exhaustion happens when someone performs filesystem changes faster than you can react. It is easy to reproduce: use cgroups to pause your program while it has an inotify descriptor open (so the event queue isn't drained) and rapidly generate lots of notifications by modifying the observed files/directories. Once you have /proc/sys/fs/inotify/max_queued_events
of unhandled events, and unpause your program, it will receive IN_Q_OVERFLOW
(and potentially miss some events, that didn't fit into queue).
Watch ID reuse is tedious to reproduce, because modern kernels switched from file descriptor-like behavior to PID-like behavior for watch-IDs. You should use the same approach as when testing for PID reuse — create and destroy lots of inotify watches until the integer watch ID wraps around.
Inotify also has a couple of tricky corner-cases, that rarely occur during normal operation (for example, all Java bindings I know, including Android and OpenJDK, do not handle all of them correctly): same-inode problem and handling IN_UNMOUNT
.
Same-inode problem is well-explained in inotify documentation:
A successful call to inotify_add_watch() returns a unique watch descriptor for this inotify instance, for the filesystem object (inode) that corresponds to pathname. If the filesystem object was not previously being watched by this inotify instance, then the watch descriptor is newly allocated. If the filesystem object was already being watched (perhaps via a different link to the same object), then the descriptor for the existing watch is returned.
In plain words: if you watch two hard-links to the same file, their numeric watch IDs will be the same. This behavior can easily result in losing track of the second inotify watch, if you store watches in something like hashmap, keyed with integer watch IDs.
Second issue is even harder to observe, thus rarely properly supported despite not even being error mode: unmounting a partition, currently observed via inotify. The tricky part is: Linux filesystems do not allow you to unmount themselves when they you have file descriptors opened them, but observing a file via inotify does not prevent the filesystem unmounting. If your app observes files on separate filesystem, and user unmounts that filesystem, you have to be prepared to handle the resulting IN_UNMOUNT
event.
All of tests above should be possible to perform on tmpfs filesystem.