mod testing;
use std::io::{Seek, SeekFrom, Write};
use xpct::{be_empty, be_err, be_ok, equal, expect, match_pattern, pattern};
use liteboxfs::{
Connection, CreateOptions, Error, FileBy, FileOrigin,
metadata::{FileKind, Owner},
};
use testing::{Case, random_string};
#[test]
fn empty_file_has_no_holes() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;
expect!(file.holes()).to(be_ok()).to(be_empty());
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn file_with_only_data_has_no_holes() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
let data = random_string(100, Case::Lower);
conn.exec(|fs| {
let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;
file.write_all(data.as_bytes())?;
expect!(file.holes()).to(be_ok()).to(be_empty());
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn set_len_on_empty_file_creates_one_hole() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;
file.set_len(100)?;
expect!(file.holes()).to(be_ok()).to(equal(vec![0..100]));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn hole_after_data() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
let data = random_string(50, Case::Lower);
conn.exec(|fs| {
let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;
file.write_all(data.as_bytes())?;
file.set_len(200)?;
expect!(file.holes()).to(be_ok()).to(equal(vec![50..200]));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn hole_between_data_regions() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
let prefix = random_string(10, Case::Lower);
let suffix = random_string(10, Case::Upper);
conn.exec(|fs| {
let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;
file.write_all(prefix.as_bytes())?;
file.set_len(100)?;
file.seek(SeekFrom::Start(100))?;
file.write_all(suffix.as_bytes())?;
expect!(file.holes()).to(be_ok()).to(equal(vec![10..100]));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn truncating_removes_holes() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
let data = random_string(50, Case::Lower);
conn.exec(|fs| {
let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;
file.write_all(data.as_bytes())?;
file.set_len(200)?;
file.set_len(50)?;
expect!(file.holes()).to(be_ok()).to(be_empty());
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn dedup_data_block_with_same_length_hole() -> liteboxfs::Result<()> {
const BLOCK_SIZE: usize = 32 * 1024;
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
let data = random_string(BLOCK_SIZE, Case::Lower);
conn.exec(|fs| {
let mut file_a = fs.create("a.txt", FileKind::Regular, Owner::ROOT)?;
file_a.write_all(data.as_bytes())?;
drop(file_a);
let mut file_b = fs.create("b.txt", FileKind::Regular, Owner::ROOT)?;
file_b.set_len(BLOCK_SIZE as u64)?;
drop(file_b);
let mut file_c = fs.create("c.txt", FileKind::Regular, Owner::ROOT)?;
expect!(file_c.write_all(data.as_bytes())).to(be_ok());
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn adjacent_hole_blocks_appear_as_one_range() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;
file.set_len(50)?;
file.set_len(150)?;
expect!(file.holes()).to(be_ok()).to(equal(vec![0..150]));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn holes_on_directory_returns_not_a_regular_file() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut file = fs.create("dir", FileKind::Dir, Owner::ROOT)?;
expect!(file.holes())
.to(be_err())
.to(match_pattern(pattern!(
Error::NotARegularFile {
file: FileOrigin::Litebox { locator, .. }, ..
} if locator == &FileBy::from(file.file_id())
)));
liteboxfs::Result::Ok(())
})?;
Ok(())
}