use std::{
fs::OpenOptions,
io::{self, Seek, SeekFrom, Write},
panic,
path::{Path, PathBuf},
};
use loopdev::{LoopControl, LoopDevice};
use tempfile::{self, TempDir};
use crate::{
consts::IEC,
testing::{logger::init_logger, test_lib::clean_up},
units::{Bytes, Sectors, SECTOR_SIZE},
};
fn write_sectors<P: AsRef<Path>>(
path: P,
offset: Sectors,
length: Sectors,
buf: &[u8; SECTOR_SIZE],
) -> io::Result<()> {
let mut f = OpenOptions::new().write(true).open(path)?;
f.seek(SeekFrom::Start((*offset.bytes()).try_into().unwrap()))?;
for _ in 0..*length {
f.write_all(buf)?;
}
f.sync_all()?;
Ok(())
}
fn wipe_sectors<P: AsRef<Path>>(path: P, offset: Sectors, length: Sectors) -> io::Result<()> {
write_sectors(path, offset, length, &[0u8; SECTOR_SIZE])
}
pub struct LoopTestDev {
ld: LoopDevice,
}
impl LoopTestDev {
fn new(lc: &LoopControl, path: &Path) -> LoopTestDev {
OpenOptions::new()
.read(true)
.write(true)
.open(path)
.unwrap();
let ld = lc.next_free().unwrap();
ld.attach_file(path).unwrap();
wipe_sectors(
ld.path().unwrap(),
Sectors(0),
Bytes(u128::from(IEC::Mi)).sectors(),
)
.unwrap();
LoopTestDev { ld }
}
fn path(&self) -> PathBuf {
self.ld.path().unwrap()
}
fn detach(&self) {
self.ld.detach().unwrap()
}
}
impl Drop for LoopTestDev {
fn drop(&mut self) {
self.detach()
}
}
fn get_devices(count: u8, dir: &TempDir) -> Vec<LoopTestDev> {
let lc = LoopControl::open().unwrap();
let mut loop_devices = Vec::new();
for index in 0..count {
let path = dir.path().join(format!("store{}", &index));
let f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(&path)
.unwrap();
nix::unistd::ftruncate(&f, IEC::Gi as nix::libc::off_t).unwrap();
f.sync_all().unwrap();
let ltd = LoopTestDev::new(&lc, &path);
loop_devices.push(ltd);
}
loop_devices
}
pub fn test_with_spec<F>(count: u8, test: F)
where
F: Fn(&[&Path]) + panic::RefUnwindSafe,
{
init_logger();
clean_up().unwrap();
let tmpdir = tempfile::Builder::new()
.prefix("devicemapper")
.tempdir()
.unwrap();
let loop_devices: Vec<LoopTestDev> = get_devices(count, &tmpdir);
let device_paths: Vec<PathBuf> = loop_devices.iter().map(|x| x.path()).collect();
let device_paths: Vec<&Path> = device_paths.iter().map(|x| x.as_path()).collect();
let result = panic::catch_unwind(|| test(&device_paths));
let tear_down = clean_up();
result.unwrap();
tear_down.unwrap();
}