| Crates.io | faine |
| lib.rs | faine |
| version | 0.2.1 |
| created_at | 2025-09-22 17:37:54.503227+00 |
| updated_at | 2025-10-14 22:38:03.268562+00 |
| description | Failpoints implementation with automatic path exploration |
| homepage | https://github.com/AMDmi3/faine |
| repository | https://github.com/AMDmi3/faine |
| max_upload_size | |
| id | 1850434 |
| size | 73,151 |
faine stands for FAultpoint INjection, Exhaustible/Exploring and is an
implementation of testing technique known as
fail points,
fault injection,
or chaos engineering,
which allows testing otherwise hard or impossible to reproduce conditions
such as I/O errors.
On top of supporting that, faine implements automated execution path exploration,
running a tested code multiple times with different combinations of failpoints enabled
and disabled (NB: in much more effective way than trying all N² possible combinations).
This allows simpler tests (which do not know inner workings of the code, that is to
know which failpoints to trigger and which effects to expect), with much higher coverage
(as all possible code paths are tested).
Let's test a code which is supposed to atomically replace a file with given content.
Instrument the code by adding failpoint macros before (or around) each operation you want to simulate failures of:
use faine::inject_return_io_error;
fn atomic_replace_file(path: &Path, content: &str) -> io::Result<()> {
inject_return_io_error!("create file"); // <- added failpoint
let mut file = File::create(path)?;
inject_return_io_error!("write file"); // <- added failpoint
file.write_all(content.as_bytes())?;
Ok(())
}
Now write a test, utilizing faine::Runner:
use faine::Runner;
#[test]
fn test_replace_file_is_atomic() {
Runner::default().run(|_| {
// prepare filesystem state for testing
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("myfile");
File::create(&path).unwrap().write_all(b"old").unwrap();
// run the tested code
let res = atomic_replace_file(&path, "new");
// check resulting filesystem state
let contents = read_to_string(path).unwrap();
assert!(
res.is_ok() && contents == "new" ||
res.is_err() && contents == "old"
); // fires!
}).unwrap();
}
See examples/atomic_replace_file.rs for complete code for this example.
See https://docs.rs/faine/latest/faine/ for complete documentation.
Neither supports path exploration as far as I know.