extern crate bitcask_rs;
extern crate failure;
extern crate log;
extern crate uuid;
use std::fs;
use std::panic;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
fn run_test<T>(test: T) -> ()
where
T: FnOnce(&str) -> () + panic::UnwindSafe,
{
setup();
let uuid = uuid::Uuid::new_v4();
let path = format!("target/store/{}", uuid.simple().to_string());
let result = panic::catch_unwind(|| test(&path));
teardown(&path);
assert!(result.is_ok())
}
fn setup() {
bitcask_rs::setup("log4rs.yml");
}
fn teardown(path: &str) {
let _ = fs::remove_dir_all(path);
}
#[test]
fn it_can_escape() {
run_test(|_| {
assert_eq!(
bitcask_rs::escape_tombstone(b"<<>>".to_vec()),
"<<>><<>>".as_bytes().to_vec()
);
assert_eq!(
bitcask_rs::escape_tombstone(b"aa<<>>hel<<>>sdf".to_vec()),
"aa<<>><<>>hel<<>><<>>sdf".as_bytes().to_vec()
);
assert_eq!(
bitcask_rs::escape_tombstone(b"<<>><<>>".to_vec()),
"<<>><<>><<>><<>>".as_bytes().to_vec()
);
});
}
#[test]
fn it_can_unescape() {
run_test(|_| {
assert_eq!(
bitcask_rs::unescape_tombstone(b"<<>><<>>".to_vec()),
"<<>>".as_bytes().to_vec()
);
assert_eq!(
bitcask_rs::unescape_tombstone(b"aa<<>><<>>hel<<>><<>>sdf".to_vec()),
"aa<<>>hel<<>>sdf".as_bytes().to_vec()
);
assert_eq!(
bitcask_rs::unescape_tombstone(b"<<>><<>><<>><<>>".to_vec()),
"<<>><<>>".as_bytes().to_vec()
);
assert_eq!(
bitcask_rs::unescape_tombstone(b"<<>>".to_vec()),
"<<>>".as_bytes().to_vec()
);
})
}
#[test]
fn it_can_parse_config() {
run_test(|_| {
let config = bitcask_rs::Config::new("tests/correct_config.yml");
assert_eq!(config.max_file_id, 1000000000);
assert_eq!(config.path, PathBuf::from("bitcask/test/store"));
})
}
#[test]
fn it_cannot_parse_config() {
run_test(|_| {
let ret = std::panic::catch_unwind(|| bitcask_rs::Config::new("tests/wrong_config.yml"));
assert!(ret.is_err());
})
}
#[test]
fn it_set_a_and_get_a() {
run_test(|path| {
let config = bitcask_rs::ConfigBuilder::default()
.path(PathBuf::from(path))
.build()
.unwrap();
let mut bitcask = bitcask_rs::Bitcask::new(config);
let key = b"1111";
let set_ret = bitcask.set(key.to_vec(), vec![1, 2, 3]);
assert!(set_ret.is_ok());
let ret = bitcask.get(key.as_ref());
assert_eq!(ret.unwrap(), Some(vec![1, 2, 3]));
let _ = bitcask.delete(key.to_vec());
let ret = bitcask.get(key.as_ref());
assert_eq!(ret.unwrap(), None);
let no_exist = bitcask.get(b"hello".as_ref());
assert_eq!(no_exist.unwrap(), None);
bitcask.set(b"hello".to_vec(), b"<<>>".to_vec()).unwrap();
assert_eq!(
bitcask.get(b"hello".as_ref()).unwrap(),
Some("<<>>".as_bytes().to_vec())
);
bitcask.set(b"hello".to_vec(), b"hello<<>><<>>haha".to_vec()).unwrap();
assert_eq!(
bitcask.get(b"hello".as_ref()).unwrap(),
Some(b"hello<<>><<>>haha".to_vec())
);
})
}
fn populate_store(end: u8, bitcask: &mut bitcask_rs::Bitcask) {
for i in 1..end {
let key = format!("{}", i).into_bytes();
let value = (i..(i + 5)).collect();
bitcask.set(key, value).unwrap();
}
}
#[test]
fn it_should_compact() {
run_test(|path| {
let config = bitcask_rs::ConfigBuilder::default()
.path(PathBuf::from(path))
.build()
.unwrap();
let mut bitcask = bitcask_rs::Bitcask::new(config);
populate_store(100, &mut bitcask);
populate_store(50, &mut bitcask);
let ret = bitcask.get(b"1".as_ref());
bitcask.merge(None).expect("compact");
let ret2 = bitcask.get(b"1".as_ref());
assert_eq!(ret.unwrap(), ret2.unwrap());
})
}
#[test]
fn it_should_build_from_segment_file() {
run_test(|path| {
let config = bitcask_rs::ConfigBuilder::default()
.path(PathBuf::from(path))
.build()
.unwrap();
{
let mut bitcask = bitcask_rs::Bitcask::new(config.clone());
populate_store(100, &mut bitcask);
populate_store(50, &mut bitcask);
}
let bitcask = bitcask_rs::Bitcask::open(config);
let ret = bitcask.get(b"1".as_ref());
assert_eq!(ret.expect("u1").expect("u2"), vec![1, 2, 3, 4, 5]);
})
}
#[test]
fn it_should_access_from_multiple_thread() {
run_test(|path| {
let config = bitcask_rs::ConfigBuilder::default()
.path(PathBuf::from(path))
.build()
.unwrap();
let mut bitcask = bitcask_rs::Bitcask::new(config.clone());
populate_store(100, &mut bitcask);
populate_store(50, &mut bitcask);
bitcask.set(b"1".to_vec(), vec![1, 3, 4]).unwrap();
let bitcask_n = bitcask.clone();
let handler = thread::spawn(move || {
thread::sleep(Duration::from_secs(2));
bitcask_n.get(b"1".as_ref())
});
let mut bitcask_n = bitcask.clone();
let handler2 = thread::spawn(move || {
thread::sleep(Duration::from_secs(1));
bitcask_n.set(b"1".to_vec(), vec![1, 3, 4])
});
let ret = bitcask.get(b"1".as_ref());
let ret2 = handler.join().unwrap();
let _ = handler2.join();
assert_eq!(ret.expect("u1"), ret2.unwrap());
})
}
#[test]
fn it_should_compact_while_reading_from_other_thread() {
run_test(|path| {
let config = bitcask_rs::ConfigBuilder::default()
.path(PathBuf::from(path))
.build()
.unwrap();
let mut bitcask = bitcask_rs::Bitcask::new(config.clone());
populate_store(100, &mut bitcask);
populate_store(50, &mut bitcask);
bitcask.set(b"1".to_vec(), vec![1, 3, 4]).unwrap();
let bitcask_n = bitcask.clone();
let handler = thread::spawn(move || {
let mut i = 1000;
while i > 0 {
thread::sleep(Duration::from_millis(1));
assert_eq!(bitcask_n.get(b"1".as_ref()).unwrap(), Some(vec![1, 3, 4]));
i -= 1;
}
});
bitcask.merge(Some(20)).expect("compact");
assert_eq!(bitcask.get(b"1".as_ref()).unwrap(), Some(vec![1, 3, 4]));
handler.join().unwrap();
})
}