use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use grebedb::vfs::{MemoryVfs, Vfs, VfsSyncOption};
use tempfile::TempDir;
#[allow(dead_code)]
pub fn make_tempdir() -> TempDir {
tempfile::Builder::new()
.prefix("grebetest_")
.tempdir()
.unwrap()
}
#[macro_export]
macro_rules! matrix_test {
($fn_name:ident) => {
multiple_config_test!($fn_name, cfg(test));
};
}
#[macro_export]
macro_rules! matrix_test_ignore {
($fn_name:ident) => {
multiple_config_test!($fn_name, ignore);
};
}
#[macro_export]
macro_rules! multiple_vfs_test {
($fn_name:ident, $option_name:expr, $options:expr, $ignore:meta) => {
paste::paste! {
#[test]
#[$ignore]
fn [<test_ $fn_name _memory_ $option_name>]() {
let db = grebedb::Database::open_memory($options).unwrap();
$fn_name(db).unwrap();
}
}
paste::paste! {
#[test]
#[$ignore]
fn [<test_ $fn_name _disk_ $option_name>]() {
let temp_dir = $crate::common::make_tempdir();
let db = grebedb::Database::open_path(temp_dir.path(), $options).unwrap();
$fn_name(db).unwrap();
}
}
};
}
#[macro_export]
macro_rules! multiple_config_test {
($fn_name:ident, $ignore:meta) => {
multiple_vfs_test!($fn_name, "default", grebedb::Options::default(), $ignore);
multiple_vfs_test!(
$fn_name,
"small_options",
grebedb::Options {
keys_per_node: 128,
page_cache_size: 8,
..Default::default()
},
$ignore
);
multiple_vfs_test!(
$fn_name,
"tiny_options",
grebedb::Options {
keys_per_node: 16,
page_cache_size: 8,
..Default::default()
},
$ignore
);
};
}
#[derive(Clone)]
pub struct CrashingVfs {
inner: MemoryVfs,
pub metadata_rename_crash: Arc<AtomicBool>,
pub after_metadata_rename_crash: Arc<AtomicBool>,
metadata_found: Arc<AtomicBool>,
}
impl CrashingVfs {
#[allow(dead_code)]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
inner: MemoryVfs::default(),
metadata_rename_crash: Arc::new(AtomicBool::new(false)),
after_metadata_rename_crash: Arc::new(AtomicBool::new(false)),
metadata_found: Arc::new(AtomicBool::new(false)),
}
}
fn make_crash_error() -> grebedb::Error {
grebedb::Error::Io(std::io::Error::new(std::io::ErrorKind::Other, "crash"))
}
}
impl Vfs for CrashingVfs {
fn lock(&mut self, path: &str) -> Result<(), grebedb::Error> {
self.inner.lock(path)
}
fn unlock(&mut self, path: &str) -> Result<(), grebedb::Error> {
self.inner.unlock(path)
}
fn read(&self, path: &str) -> Result<Vec<u8>, grebedb::Error> {
eprintln!("read {}", path);
self.inner.read(path)
}
fn write(
&mut self,
path: &str,
data: &[u8],
sync_option: VfsSyncOption,
) -> Result<(), grebedb::Error> {
eprintln!("write {}", path);
self.inner.write(path, data, sync_option)
}
fn sync_file(&mut self, path: &str, sync_option: VfsSyncOption) -> Result<(), grebedb::Error> {
eprintln!("sync_file {}", path);
self.inner.sync_file(path, sync_option)
}
fn remove_file(&mut self, path: &str) -> Result<(), grebedb::Error> {
eprintln!("remove_file {}", path);
self.inner.remove_file(path)
}
fn read_dir(&self, path: &str) -> Result<Vec<String>, grebedb::Error> {
eprintln!("read_dir {}", path);
self.inner.read_dir(path)
}
fn create_dir(&mut self, path: &str) -> Result<(), grebedb::Error> {
eprintln!("create_dir {}", path);
self.inner.create_dir(path)
}
fn remove_dir(&mut self, path: &str) -> Result<(), grebedb::Error> {
eprintln!("remove_dir {}", path);
self.inner.remove_dir(path)
}
fn rename_file(&mut self, old_path: &str, new_path: &str) -> Result<(), grebedb::Error> {
eprintln!("rename_file {} {}", old_path, new_path);
if new_path == "grebedb_meta.grebedb" {
if self.after_metadata_rename_crash.load(Ordering::Relaxed) {
self.metadata_found.store(true, Ordering::Relaxed);
}
if self.metadata_rename_crash.load(Ordering::Relaxed) {
eprintln!("crash on metadata rename");
return Err(Self::make_crash_error());
}
} else if self.after_metadata_rename_crash.load(Ordering::Relaxed)
&& self.metadata_found.load(Ordering::Relaxed)
{
eprintln!("crash on after metadata rename");
return Err(Self::make_crash_error());
}
self.inner.rename_file(old_path, new_path)
}
fn is_dir(&self, path: &str) -> Result<bool, grebedb::Error> {
eprintln!("is_dir {}", path);
self.inner.is_dir(path)
}
fn exists(&self, path: &str) -> Result<bool, grebedb::Error> {
self.inner.exists(path)
}
}