liteboxfs 0.1.0

A modern POSIX filesystem in a SQLite database
Documentation
#![cfg(all(feature = "fs", target_os = "linux"))]

use xpct::{be_ok, equal, expect, match_pattern, pattern};

use crate::{
    Connection, Error,
    file_metadata::{FileKind, Owner},
    settings::Settings,
};

fn open() -> crate::Result<Connection> {
    Connection::open_for_testing(&Settings::default())
}

#[test]
fn creates_a_link() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let mut file = fs.create("/a.txt", FileKind::Regular, Owner::ROOT)?;
        expect!(file.link("/b.txt")).to(be_ok());
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn link_count_increases() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let mut file = fs.create("/a.txt", FileKind::Regular, Owner::ROOT)?;
        expect!(file.link_count()).to(be_ok()).to(equal(1u32));
        file.link("/b.txt")?;
        expect!(file.link_count()).to(be_ok()).to(equal(2u32));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn file_is_accessible_at_link_path() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let mut file = fs.create("/a.txt", FileKind::Regular, Owner::ROOT)?;
        file.link("/b.txt")?;
        drop(file);

        expect!(fs.open("/b.txt")).to(be_ok());
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn link_shares_file_id() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let mut orig = fs.create("/a.txt", FileKind::Regular, Owner::ROOT)?;
        orig.link("/b.txt")?;
        let orig_id = orig.file_id();
        drop(orig);

        let linked = fs.open("/b.txt")?;
        expect!(linked.file_id()).to(equal(orig_id));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn fails_for_directories() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let mut dir = fs.create("/a", FileKind::Dir, Owner::ROOT)?;
        expect!(dir.link("/b")).to(match_pattern(pattern!(Err(Error::NotARegularFile { .. }))));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn fails_if_link_already_exists() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let file = fs.create("/a.txt", FileKind::Regular, Owner::ROOT)?;
        drop(file);

        // /a.txt already exists.
        let mut file = fs.open("/a.txt")?;
        expect!(file.link("/a.txt")).to(match_pattern(pattern!(Err(
            Error::FileAlreadyExists { .. }
        ))));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn fails_if_parent_does_not_exist() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let mut file = fs.create("/a.txt", FileKind::Regular, Owner::ROOT)?;
        expect!(file.link("/nonexistent/b.txt")).to(match_pattern(pattern!(Err(
            Error::NoParentDirectory { .. }
        ))));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn temp_file_can_be_linked() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let mut tmp = fs.create_temp(Owner::ROOT)?;
        tmp.link("/permanent.txt")?;
        crate::Result::Ok(())
    })?;

    // File handle dropped; because it has a link now, delete_file_if_unlinked is a no-op.
    conn.exec(|fs| {
        expect!(fs.open("/permanent.txt")).to(be_ok());
        crate::Result::Ok(())
    })?;

    Ok(())
}