use std::fs;
use std::path::PathBuf;
use std::sync::Arc;
use log::info;
use rotbl::storage::impls::fs::FsStorage;
use rotbl::v001::Builder;
use rotbl::v001::Config;
use rotbl::v001::Rotbl;
use rotbl::v001::RotblMeta;
use rotbl::v001::SeqMarked;
const COMPAT_DIR: &str = "tests/compat";
const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
#[test]
#[ignore]
fn generate_data() -> anyhow::Result<()> {
let mut config = Config::default();
config.block_config.max_items = Some(20);
let version_dir = get_version_dir(CURRENT_VERSION);
let db_dir = format!("{}/db", version_dir);
let file_name = "x.rot";
let dump_path = format!("{}/dump.txt", version_dir);
fs::create_dir_all(&db_dir)?;
let storage = FsStorage::new(PathBuf::from(&db_dir));
let n_keys = 512;
let mut key = ss("aaa");
let rotbl_meta = RotblMeta::new(5, "hello");
let mut builder = Builder::new(storage, config, file_name)?;
for i in 0..n_keys {
let v = if i % 2 == 0 {
SeqMarked::new_tombstone(i)
} else {
let value = key.clone().into_bytes();
SeqMarked::new_normal(i, value)
};
builder.append_kv(&key, v)?;
key = next(&key);
}
let t = builder.commit(rotbl_meta)?;
let t = Arc::new(t);
let dump = t.dump().collect::<Result<Vec<_>, _>>()?;
fs::write(dump_path, dump.join("\n"))?;
Ok(())
}
#[test]
fn test_compat() -> anyhow::Result<()> {
do_test_compat(CURRENT_VERSION)
}
#[test]
fn test_backward_compat() -> anyhow::Result<()> {
let versions = fs::read_dir(COMPAT_DIR)?
.map(|entry| entry.unwrap().path())
.filter(|path| path.is_dir())
.map(|path| path.file_name().unwrap().to_str().unwrap().to_string())
.collect::<Vec<_>>();
for version in versions {
do_test_compat(&version)?;
}
Ok(())
}
fn do_test_compat(version: &str) -> anyhow::Result<()> {
info!("testing compat with version {}", version);
let version_dir = get_version_dir(version);
let db_dir = format!("{}/db", version_dir);
let file_name = "x.rot";
let dump_path = format!("{}/dump.txt", version_dir);
let storage = FsStorage::new(PathBuf::from(db_dir));
let config = Config::default();
let t = Arc::new(Rotbl::open(storage, config, file_name)?);
let data = t.dump().collect::<Result<Vec<_>, _>>()?;
let data = data.join("\n");
let dump = fs::read_to_string(dump_path)?.replace("\r\n", "\n");
assert_eq!(data, dump);
Ok(())
}
fn get_version_dir(version: &str) -> String {
format!("{}/{}", COMPAT_DIR, version)
}
fn ss(x: impl ToString) -> String {
x.to_string()
}
pub fn next(k: &str) -> String {
let mut chars: Vec<char> = k.chars().collect();
for i in (0..chars.len()).rev() {
if chars[i] == 'z' {
chars[i] = 'a';
if i == 0 {
unreachable!("exhausted");
}
} else {
chars[i] = (chars[i] as u8 + 1) as char;
break;
}
}
chars.into_iter().collect()
}
#[cfg(test)]
mod tests {
use super::next;
#[test]
fn test_simple_increment() {
assert_eq!(next("aaaaa"), "aaaab");
assert_eq!(next("aaaay"), "aaaaz");
}
#[test]
fn test_wrap_around() {
assert_eq!(next("aaaaz"), "aaaba");
}
#[test]
fn test_multiple_wrap_around() {
assert_eq!(next("aaazz"), "aabaa");
assert_eq!(next("aazzz"), "abaaa");
}
#[test]
fn test_no_initial_z() {
assert_eq!(next("abcde"), "abcdf");
assert_eq!(next("asdfg"), "asdfh");
}
#[test]
fn test_3_chars() {
assert_eq!(next("abc"), "abd");
}
}