Search code examples
unit-testingtestingrust

How can I test a piece of code that works with files?


I'm currently working on a personal project with Rust to learn the language, which basically consists on translating a project I did in college in C to Rust. The project basically consists in a (very) simplified file system based on ext2, and I want to test a part of code responsible of creating a "virtual device" (a file) and read/write to it, but I don't know what's the best approach to test it. This is the code I want to test:

pub struct FileSystem {
    v_device: File,
    size: usize
}

impl FileSystem {
    pub fn new(path: &str) -> Result<Self, io::Error> {
        let v_device = File::create(path)?;
        v_device.set_permissions(fs::Permissions::from_mode(0o666))
            .expect("TODO: panic message");

        Ok (Self { v_device, size: 0 })
    }

    pub fn write_block(&mut self, block_number: u64, buffer: &[u8; BLOCKSIZE as usize]) -> Result<usize, io::Error> {
        let offset = BLOCKSIZE * block_number;

        let bytes_written = self.v_device.write_at(buffer, offset)?;
        self.size += bytes_written;

        Ok(bytes_written)
    }

    pub fn read_block(&self, block_number: u64, buffer: &mut [u8; BLOCKSIZE as usize]) -> Result<(), io::Error> {
        let offset = BLOCKSIZE * block_number;

        self.v_device.read_exact_at(buffer, offset)?;

        Ok(())
    }

}

My idea is to test the creation of the file, that the correct number of bytes are written, that the correct number of bytes can be read, etc., but this would mean I'd have to create several real files only to test the program. Is there a way to test it without having to create a bunch of files? If not, how's the best approach to test this code?

PS: any advice regarding the code I posted is also welcome!


Solution

  • Since read_block and write_block operations that you want to test do not actually require v_device to be a file, but something that implements Read, Write and Seek traits, you can make the FileSystem generic:

    pub struct FileSystem<T: Read + Write + Seek> {
        v_device: T,
        size: usize
    }
    

    Then create another constructor in addition to new which accepts argument of type T. In the tests, instead of a File you can use Cursor which also implements Read + Write + Seek and can operate on the in-memory Vec if you construct it with Cursor::new(Vec::new()).