We have all written some form of library to wrap calls to basic C IO functions (read, write, send, recv) with nicer APIs - usally adding suffixes such as _all or _exact.
Some of us even had the pleasure to write these functions more than once.
Testio tries to create a simple way to test those libraries.
- python 3
- pytest
- fuse3
-
Write your IO library, make sure it implements the
test.hheader present in the root directory of this project. -
Compile your library as a shared object (for Linux/GNU x86_64)
-
Build the testio project (or use the binaries in the GitHub releases page)
-
Run
./test.py --lib <path to your lib>
An example library is found under example, you can compile it using make.
You can also pass the following flags to test.py:
--verbose-fuse- Increase log verbosity of the testfs fuse--verbose-tester- Increase log verbosity of the low-level tester--fuse-bin=FUSE_BIN- Path to the testfs fuse binary (./target/debug/testioby default)--tester-bin=TESTER_BIN- Path to the tester binary (./target/debug/testerby default)--lib=LIB- Path to the library that will be tested (./example/libexample.soby default)- any other flag accepted by
pytest
Building this project requires a Rust toolchain.
To build run:
cargo build
To run the testfs fuse without the tester for debugging:
cargo run --bin testio <mount path>
Testio creates a FUSE filesystem, that pits the tested library against various edge cases that can happen
during read / write - such as incomplete read / write calls.
The filesystem defines files with different read / write handler functions (src/bin/testio.rs:create_files) that affect the result of each IO call.
For reading, the API allows specifying a lambda that receives the count of the current call to read, and returns a new count that will be used instead.
For writing, the API allows specifying a lambda that receives the array of bytes of the current call to write, and returns a new array that will be written instead.