use random_access_disk as rad;
use random_access_storage::RandomAccess;
use std::io::Read;
use tempfile::Builder;
#[cfg(feature = "async-std")]
use async_std::test as async_test;
#[cfg(feature = "tokio")]
use tokio::test as async_test;
#[async_test]
async fn can_call_new() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let _file = rad::RandomAccessDisk::open(dir.path().join("1.db"))
.await
.unwrap();
}
#[async_test]
async fn can_open_buffer() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::open(dir.path().join("2.db"))
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
}
#[async_test]
async fn can_write() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::open(dir.path().join("3.db"))
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
}
#[async_test]
async fn can_read() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::open(dir.path().join("4.db"))
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
let text = file.read(0, 11).await.unwrap();
assert_eq!(String::from_utf8(text.to_vec()).unwrap(), "hello world");
}
#[async_test]
async fn can_truncate_lt() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::open(dir.path().join("5.db"))
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
file.truncate(7).await.unwrap();
let text = file.read(0, 7).await.unwrap();
assert_eq!(String::from_utf8(text.to_vec()).unwrap(), "hello w");
if file.read(0, 8).await.is_ok() {
panic!("file is too big. read past the end should have failed");
};
let mut c_file = std::fs::File::open(dir.path().join("5.db")).unwrap();
let mut c_contents = String::new();
c_file.read_to_string(&mut c_contents).unwrap();
assert_eq!(c_contents, "hello w");
}
#[async_test]
async fn can_truncate_gt() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::open(dir.path().join("6.db"))
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
file.truncate(15).await.unwrap();
let text = file.read(0, 15).await.unwrap();
assert_eq!(
String::from_utf8(text.to_vec()).unwrap(),
"hello world\0\0\0\0"
);
if file.read(0, 16).await.is_ok() {
panic!("file is too big. read past the end should have failed");
};
let mut c_file = std::fs::File::open(dir.path().join("6.db")).unwrap();
let mut c_contents = String::new();
c_file.read_to_string(&mut c_contents).unwrap();
assert_eq!(c_contents, "hello world\0\0\0\0");
}
#[async_test]
async fn can_truncate_eq() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::open(dir.path().join("7.db"))
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
file.truncate(11).await.unwrap();
let text = file.read(0, 11).await.unwrap();
assert_eq!(String::from_utf8(text.to_vec()).unwrap(), "hello world");
if file.read(0, 12).await.is_ok() {
panic!("file is too big. read past the end should have failed");
};
let mut c_file = std::fs::File::open(dir.path().join("7.db")).unwrap();
let mut c_contents = String::new();
c_file.read_to_string(&mut c_contents).unwrap();
assert_eq!(c_contents, "hello world");
}
#[async_test]
async fn can_len() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::open(dir.path().join("8.db"))
.await
.unwrap();
assert_eq!(file.len().await.unwrap(), 0);
file.write(0, b"hello").await.unwrap();
assert_eq!(file.len().await.unwrap(), 5);
file.write(5, b" world").await.unwrap();
assert_eq!(file.len().await.unwrap(), 11);
file.truncate(15).await.unwrap();
assert_eq!(file.len().await.unwrap(), 15);
file.truncate(8).await.unwrap();
assert_eq!(file.len().await.unwrap(), 8);
}
#[async_test]
async fn can_is_empty() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::open(dir.path().join("9.db"))
.await
.unwrap();
assert!(file.is_empty().await.unwrap());
file.write(0, b"hello").await.unwrap();
assert!(!file.is_empty().await.unwrap());
file.truncate(0).await.unwrap();
assert!(file.is_empty().await.unwrap());
file.truncate(1).await.unwrap();
assert!(!file.is_empty().await.unwrap());
file.truncate(0).await.unwrap();
assert!(file.is_empty().await.unwrap());
file.write(0, b"what").await.unwrap();
assert!(!file.is_empty().await.unwrap());
}
#[async_test]
#[cfg(feature = "async-std")]
async fn explicit_no_auto_sync() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::builder(dir.path().join("10.db"))
.auto_sync(false)
.build()
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
file.truncate(11).await.unwrap();
file.sync_all().await.unwrap();
let text = file.read(0, 11).await.unwrap();
assert_eq!(String::from_utf8(text.to_vec()).unwrap(), "hello world");
if file.read(0, 12).await.is_ok() {
panic!("file is too big. read past the end should have failed");
};
let mut c_file = std::fs::File::open(dir.path().join("10.db")).unwrap();
let mut c_contents = String::new();
c_file.read_to_string(&mut c_contents).unwrap();
assert_eq!(c_contents, "hello world");
}
#[async_test]
async fn auto_sync() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::builder(dir.path().join("11.db"))
.build()
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
file.truncate(11).await.unwrap();
let text = file.read(0, 11).await.unwrap();
assert_eq!(String::from_utf8(text.to_vec()).unwrap(), "hello world");
if file.read(0, 12).await.is_ok() {
panic!("file is too big. read past the end should have failed");
};
let mut c_file = std::fs::File::open(dir.path().join("11.db")).unwrap();
let mut c_contents = String::new();
c_file.read_to_string(&mut c_contents).unwrap();
assert_eq!(c_contents, "hello world");
}
#[async_test]
async fn auto_sync_with_sync_call() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::builder(dir.path().join("12.db"))
.build()
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
file.truncate(11).await.unwrap();
file.sync_all().await.unwrap();
let text = file.read(0, 11).await.unwrap();
assert_eq!(String::from_utf8(text.to_vec()).unwrap(), "hello world");
if file.read(0, 12).await.is_ok() {
panic!("file is too big. read past the end should have failed");
};
let mut c_file = std::fs::File::open(dir.path().join("12.db")).unwrap();
let mut c_contents = String::new();
c_file.read_to_string(&mut c_contents).unwrap();
assert_eq!(c_contents, "hello world");
}
#[async_test]
async fn can_del_short() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::builder(dir.path().join("13.db"))
.build()
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
file.write(5, b" world").await.unwrap();
file.write(11, b" people").await.unwrap();
file.del(5, 6).await.unwrap();
let hello = file.read(0, 5).await.unwrap();
assert_eq!(String::from_utf8(hello.to_vec()).unwrap(), "hello");
let zeros = file.read(5, 6).await.unwrap();
assert_eq!(zeros, vec![0; 6]);
let people = file.read(12, 6).await.unwrap();
assert_eq!(String::from_utf8(people.to_vec()).unwrap(), "people");
}
#[async_test]
async fn can_del_long_middle() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::builder(dir.path().join("14.db"))
.build()
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
const MULTI_BLOCK_LEN: usize = 4096 * 3;
let multi_block = &[0x61_u8; MULTI_BLOCK_LEN];
file.write(5, multi_block).await.unwrap();
file
.write((MULTI_BLOCK_LEN + 5) as u64, b"to all the ")
.await
.unwrap();
file
.write((MULTI_BLOCK_LEN + 16) as u64, b"people")
.await
.unwrap();
file.del(5, MULTI_BLOCK_LEN as u64).await.unwrap();
let hello = file.read(0, 5).await.unwrap();
assert_eq!(String::from_utf8(hello.to_vec()).unwrap(), "hello");
let zeros = file.read(5, 10).await.unwrap();
assert_eq!(zeros, vec![0; 10]);
let zeros = file.read(MULTI_BLOCK_LEN as u64, 5).await.unwrap();
assert_eq!(zeros, vec![0; 5]);
let zeros = file.read((MULTI_BLOCK_LEN / 2) as u64, 5).await.unwrap();
assert_eq!(zeros, vec![0; 5]);
let to_all_the_people =
file.read((MULTI_BLOCK_LEN + 5) as u64, 17).await.unwrap();
assert_eq!(
String::from_utf8(to_all_the_people.to_vec()).unwrap(),
"to all the people"
);
file.del((MULTI_BLOCK_LEN + 7) as u64, 4).await.unwrap();
let zeros = file.read((MULTI_BLOCK_LEN + 7) as u64, 4).await.unwrap();
assert_eq!(zeros, vec![0; 4]);
let to = file.read((MULTI_BLOCK_LEN + 5) as u64, 2).await.unwrap();
assert_eq!(String::from_utf8(to.to_vec()).unwrap(), "to");
}
#[async_test]
async fn can_del_long_exact_block() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::builder(dir.path().join("15.db"))
.build()
.await
.unwrap();
const BLOCK_LEN: usize = 4096;
let block = &[0x61_u8; BLOCK_LEN + 1];
file.write(0, block).await.unwrap();
file.del(0, BLOCK_LEN as u64).await.unwrap();
let zeros = file.read(0, 5).await.unwrap();
assert_eq!(zeros, vec![0; 5]);
let zeros = file.read(BLOCK_LEN as u64 - 5, 5).await.unwrap();
assert_eq!(zeros, vec![0; 5]);
file.del(0, (BLOCK_LEN + 1) as u64).await.unwrap();
assert_eq!(0, file.len().await.unwrap());
}
#[async_test]
async fn can_del_long_more_than_block() {
let dir = Builder::new()
.prefix("random-access-disk")
.tempdir()
.unwrap();
let mut file = rad::RandomAccessDisk::builder(dir.path().join("16.db"))
.build()
.await
.unwrap();
file.write(0, b"hello").await.unwrap();
const MORE_THAN_BLOCK_LEN: usize = 4096 + 1000;
let more_than_block = &[0x61_u8; MORE_THAN_BLOCK_LEN + 1];
file.write(5, more_than_block).await.unwrap();
file.del(5, MORE_THAN_BLOCK_LEN as u64).await.unwrap();
let zeros = file.read(5, 5).await.unwrap();
assert_eq!(zeros, vec![0; 5]);
let zeros = file.read(MORE_THAN_BLOCK_LEN as u64, 5).await.unwrap();
assert_eq!(zeros, vec![0; 5]);
const EXACT_TO_THIRD_BLOCK_LEN: usize = 4096 * 2 - 5;
let exact_to_third_block = &[0x61_u8; EXACT_TO_THIRD_BLOCK_LEN + 1];
file.write(5, exact_to_third_block).await.unwrap();
file.del(5, EXACT_TO_THIRD_BLOCK_LEN as u64).await.unwrap();
let zeros = file.read(5, 5).await.unwrap();
assert_eq!(zeros, vec![0; 5]);
let zeros = file.read(EXACT_TO_THIRD_BLOCK_LEN as u64, 5).await.unwrap();
assert_eq!(zeros, vec![0; 5]);
file
.del(5, (EXACT_TO_THIRD_BLOCK_LEN * 2) as u64)
.await
.unwrap();
assert_eq!(5, file.len().await.unwrap());
}