340
mini-lsm/src/tests/day2.rs
Normal file
340
mini-lsm/src/tests/day2.rs
Normal file
@@ -0,0 +1,340 @@
|
||||
use std::ops::Bound;
|
||||
|
||||
use bytes::Bytes;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use crate::{
|
||||
iterators::{merge_iterator::MergeIterator, StorageIterator},
|
||||
lsm_iterator::FusedIterator,
|
||||
lsm_storage::{LsmStorageInner, LsmStorageOptions},
|
||||
mem_table::MemTable,
|
||||
};
|
||||
|
||||
use super::harness::MockIterator;
|
||||
|
||||
#[test]
|
||||
fn test_task1_memtable_iter() {
|
||||
use std::ops::Bound;
|
||||
let memtable = MemTable::create(0);
|
||||
memtable.put(b"key1", b"value1").unwrap();
|
||||
memtable.put(b"key2", b"value2").unwrap();
|
||||
memtable.put(b"key3", b"value3").unwrap();
|
||||
|
||||
{
|
||||
let mut iter = memtable.scan(Bound::Unbounded, Bound::Unbounded);
|
||||
assert_eq!(iter.key(), b"key1");
|
||||
assert_eq!(iter.value(), b"value1");
|
||||
assert!(iter.is_valid());
|
||||
iter.next().unwrap();
|
||||
assert_eq!(iter.key(), b"key2");
|
||||
assert_eq!(iter.value(), b"value2");
|
||||
assert!(iter.is_valid());
|
||||
iter.next().unwrap();
|
||||
assert_eq!(iter.key(), b"key3");
|
||||
assert_eq!(iter.value(), b"value3");
|
||||
assert!(iter.is_valid());
|
||||
iter.next().unwrap();
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
|
||||
{
|
||||
let mut iter = memtable.scan(Bound::Included(b"key1"), Bound::Included(b"key2"));
|
||||
assert_eq!(iter.key(), b"key1");
|
||||
assert_eq!(iter.value(), b"value1");
|
||||
assert!(iter.is_valid());
|
||||
iter.next().unwrap();
|
||||
assert_eq!(iter.key(), b"key2");
|
||||
assert_eq!(iter.value(), b"value2");
|
||||
assert!(iter.is_valid());
|
||||
iter.next().unwrap();
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
|
||||
{
|
||||
let mut iter = memtable.scan(Bound::Excluded(b"key1"), Bound::Excluded(b"key3"));
|
||||
assert_eq!(iter.key(), b"key2");
|
||||
assert_eq!(iter.value(), b"value2");
|
||||
assert!(iter.is_valid());
|
||||
iter.next().unwrap();
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task1_empty_memtable_iter() {
|
||||
use std::ops::Bound;
|
||||
let memtable = MemTable::create(0);
|
||||
{
|
||||
let iter = memtable.scan(Bound::Excluded(b"key1"), Bound::Excluded(b"key3"));
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
{
|
||||
let iter = memtable.scan(Bound::Included(b"key1"), Bound::Included(b"key2"));
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
{
|
||||
let iter = memtable.scan(Bound::Unbounded, Bound::Unbounded);
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bytes(x: &[u8]) -> Bytes {
|
||||
Bytes::copy_from_slice(x)
|
||||
}
|
||||
|
||||
fn check_iter_result(iter: &mut impl StorageIterator, expected: Vec<(Bytes, Bytes)>) {
|
||||
for (k, v) in expected {
|
||||
assert!(iter.is_valid());
|
||||
assert_eq!(
|
||||
k,
|
||||
iter.key(),
|
||||
"expected key: {:?}, actual key: {:?}",
|
||||
k,
|
||||
as_bytes(iter.key()),
|
||||
);
|
||||
assert_eq!(
|
||||
v,
|
||||
iter.value(),
|
||||
"expected value: {:?}, actual value: {:?}",
|
||||
v,
|
||||
as_bytes(iter.value()),
|
||||
);
|
||||
iter.next().unwrap();
|
||||
}
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
|
||||
fn expect_iter_error(mut iter: impl StorageIterator) {
|
||||
loop {
|
||||
match iter.next() {
|
||||
Ok(_) if iter.is_valid() => continue,
|
||||
Ok(_) => panic!("expect an error"),
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task2_merge_1() {
|
||||
let i1 = MockIterator::new(vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.1")),
|
||||
(Bytes::from("c"), Bytes::from("3.1")),
|
||||
]);
|
||||
let i2 = MockIterator::new(vec![
|
||||
(Bytes::from("a"), Bytes::from("1.2")),
|
||||
(Bytes::from("b"), Bytes::from("2.2")),
|
||||
(Bytes::from("c"), Bytes::from("3.2")),
|
||||
(Bytes::from("d"), Bytes::from("4.2")),
|
||||
]);
|
||||
let i3 = MockIterator::new(vec![
|
||||
(Bytes::from("b"), Bytes::from("2.3")),
|
||||
(Bytes::from("c"), Bytes::from("3.3")),
|
||||
(Bytes::from("d"), Bytes::from("4.3")),
|
||||
]);
|
||||
|
||||
let mut iter = MergeIterator::create(vec![
|
||||
Box::new(i1.clone()),
|
||||
Box::new(i2.clone()),
|
||||
Box::new(i3.clone()),
|
||||
]);
|
||||
|
||||
check_iter_result(
|
||||
&mut iter,
|
||||
vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.1")),
|
||||
(Bytes::from("c"), Bytes::from("3.1")),
|
||||
(Bytes::from("d"), Bytes::from("4.2")),
|
||||
],
|
||||
);
|
||||
|
||||
let mut iter = MergeIterator::create(vec![Box::new(i3), Box::new(i1), Box::new(i2)]);
|
||||
|
||||
check_iter_result(
|
||||
&mut iter,
|
||||
vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.3")),
|
||||
(Bytes::from("c"), Bytes::from("3.3")),
|
||||
(Bytes::from("d"), Bytes::from("4.3")),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task2_merge_2() {
|
||||
let i1 = MockIterator::new(vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.1")),
|
||||
(Bytes::from("c"), Bytes::from("3.1")),
|
||||
]);
|
||||
let i2 = MockIterator::new(vec![
|
||||
(Bytes::from("d"), Bytes::from("1.2")),
|
||||
(Bytes::from("e"), Bytes::from("2.2")),
|
||||
(Bytes::from("f"), Bytes::from("3.2")),
|
||||
(Bytes::from("g"), Bytes::from("4.2")),
|
||||
]);
|
||||
let i3 = MockIterator::new(vec![
|
||||
(Bytes::from("h"), Bytes::from("1.3")),
|
||||
(Bytes::from("i"), Bytes::from("2.3")),
|
||||
(Bytes::from("j"), Bytes::from("3.3")),
|
||||
(Bytes::from("k"), Bytes::from("4.3")),
|
||||
]);
|
||||
let i4 = MockIterator::new(vec![]);
|
||||
let result = vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.1")),
|
||||
(Bytes::from("c"), Bytes::from("3.1")),
|
||||
(Bytes::from("d"), Bytes::from("1.2")),
|
||||
(Bytes::from("e"), Bytes::from("2.2")),
|
||||
(Bytes::from("f"), Bytes::from("3.2")),
|
||||
(Bytes::from("g"), Bytes::from("4.2")),
|
||||
(Bytes::from("h"), Bytes::from("1.3")),
|
||||
(Bytes::from("i"), Bytes::from("2.3")),
|
||||
(Bytes::from("j"), Bytes::from("3.3")),
|
||||
(Bytes::from("k"), Bytes::from("4.3")),
|
||||
];
|
||||
|
||||
let mut iter = MergeIterator::create(vec![
|
||||
Box::new(i1.clone()),
|
||||
Box::new(i2.clone()),
|
||||
Box::new(i3.clone()),
|
||||
Box::new(i4.clone()),
|
||||
]);
|
||||
check_iter_result(&mut iter, result.clone());
|
||||
|
||||
let mut iter = MergeIterator::create(vec![
|
||||
Box::new(i2.clone()),
|
||||
Box::new(i4.clone()),
|
||||
Box::new(i3.clone()),
|
||||
Box::new(i1.clone()),
|
||||
]);
|
||||
check_iter_result(&mut iter, result.clone());
|
||||
|
||||
let mut iter =
|
||||
MergeIterator::create(vec![Box::new(i4), Box::new(i3), Box::new(i2), Box::new(i1)]);
|
||||
check_iter_result(&mut iter, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task2_merge_empty() {
|
||||
let mut iter = MergeIterator::<MockIterator>::create(vec![]);
|
||||
check_iter_result(&mut iter, vec![]);
|
||||
|
||||
let i1 = MockIterator::new(vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.1")),
|
||||
(Bytes::from("c"), Bytes::from("3.1")),
|
||||
]);
|
||||
let i2 = MockIterator::new(vec![]);
|
||||
let mut iter = MergeIterator::<MockIterator>::create(vec![Box::new(i1), Box::new(i2)]);
|
||||
check_iter_result(
|
||||
&mut iter,
|
||||
vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.1")),
|
||||
(Bytes::from("c"), Bytes::from("3.1")),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task2_merge_error() {
|
||||
let mut iter = MergeIterator::<MockIterator>::create(vec![]);
|
||||
check_iter_result(&mut iter, vec![]);
|
||||
|
||||
let i1 = MockIterator::new(vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.1")),
|
||||
(Bytes::from("c"), Bytes::from("3.1")),
|
||||
]);
|
||||
let i2 = MockIterator::new_with_error(
|
||||
vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("b"), Bytes::from("2.1")),
|
||||
(Bytes::from("c"), Bytes::from("3.1")),
|
||||
],
|
||||
1,
|
||||
);
|
||||
let iter = MergeIterator::<MockIterator>::create(vec![Box::new(i1), Box::new(i2)]);
|
||||
// your implementation should correctly throw an error instead of panic
|
||||
expect_iter_error(iter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task3_fused_iterator() {
|
||||
let iter = MockIterator::new(vec![]);
|
||||
let mut fused_iter = FusedIterator::new(iter);
|
||||
assert!(!fused_iter.is_valid());
|
||||
fused_iter.next().unwrap();
|
||||
fused_iter.next().unwrap();
|
||||
fused_iter.next().unwrap();
|
||||
assert!(!fused_iter.is_valid());
|
||||
|
||||
let iter = MockIterator::new_with_error(
|
||||
vec![
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
(Bytes::from("a"), Bytes::from("1.1")),
|
||||
],
|
||||
1,
|
||||
);
|
||||
let mut fused_iter = FusedIterator::new(iter);
|
||||
assert!(fused_iter.is_valid());
|
||||
assert!(fused_iter.next().is_err());
|
||||
assert!(!fused_iter.is_valid());
|
||||
assert!(fused_iter.next().is_err());
|
||||
assert!(fused_iter.next().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task4_integration() {
|
||||
let dir = tempdir().unwrap();
|
||||
let storage =
|
||||
LsmStorageInner::open(dir.path(), LsmStorageOptions::default_for_week1_test()).unwrap();
|
||||
storage.put(b"1", b"233").unwrap();
|
||||
storage.put(b"2", b"2333").unwrap();
|
||||
storage.put(b"3", b"23333").unwrap();
|
||||
storage
|
||||
.force_freeze_memtable(&storage.state_lock.lock())
|
||||
.unwrap();
|
||||
storage.delete(b"1").unwrap();
|
||||
storage.delete(b"2").unwrap();
|
||||
storage.put(b"3", b"2333").unwrap();
|
||||
storage.put(b"4", b"23333").unwrap();
|
||||
storage
|
||||
.force_freeze_memtable(&storage.state_lock.lock())
|
||||
.unwrap();
|
||||
storage.put(b"1", b"233333").unwrap();
|
||||
storage.put(b"3", b"233333").unwrap();
|
||||
{
|
||||
let mut iter = storage.scan(Bound::Unbounded, Bound::Unbounded).unwrap();
|
||||
check_iter_result(
|
||||
&mut iter,
|
||||
vec![
|
||||
(Bytes::from_static(b"1"), Bytes::from_static(b"233333")),
|
||||
(Bytes::from_static(b"3"), Bytes::from_static(b"233333")),
|
||||
(Bytes::from_static(b"4"), Bytes::from_static(b"23333")),
|
||||
],
|
||||
);
|
||||
assert!(!iter.is_valid());
|
||||
iter.next().unwrap();
|
||||
iter.next().unwrap();
|
||||
iter.next().unwrap();
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
{
|
||||
let mut iter = storage
|
||||
.scan(Bound::Included(b"2"), Bound::Included(b"3"))
|
||||
.unwrap();
|
||||
check_iter_result(
|
||||
&mut iter,
|
||||
vec![(Bytes::from_static(b"3"), Bytes::from_static(b"233333"))],
|
||||
);
|
||||
assert!(!iter.is_valid());
|
||||
iter.next().unwrap();
|
||||
iter.next().unwrap();
|
||||
iter.next().unwrap();
|
||||
assert!(!iter.is_valid());
|
||||
}
|
||||
}
|
||||
70
mini-lsm/src/tests/harness.rs
Normal file
70
mini-lsm/src/tests/harness.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use anyhow::{bail, Result};
|
||||
use bytes::Bytes;
|
||||
|
||||
use crate::iterators::StorageIterator;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MockIterator {
|
||||
pub data: Vec<(Bytes, Bytes)>,
|
||||
pub error_when: Option<usize>,
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl MockIterator {
|
||||
pub fn new(data: Vec<(Bytes, Bytes)>) -> Self {
|
||||
Self {
|
||||
data,
|
||||
index: 0,
|
||||
error_when: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_error(data: Vec<(Bytes, Bytes)>, error_when: usize) -> Self {
|
||||
Self {
|
||||
data,
|
||||
index: 0,
|
||||
error_when: Some(error_when),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StorageIterator for MockIterator {
|
||||
fn next(&mut self) -> Result<()> {
|
||||
if self.index < self.data.len() {
|
||||
self.index += 1;
|
||||
}
|
||||
if let Some(error_when) = self.error_when {
|
||||
if self.index == error_when {
|
||||
bail!("fake error!");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn key(&self) -> &[u8] {
|
||||
if let Some(error_when) = self.error_when {
|
||||
if self.index >= error_when {
|
||||
panic!("invalid access after next returns an error!");
|
||||
}
|
||||
}
|
||||
self.data[self.index].0.as_ref()
|
||||
}
|
||||
|
||||
fn value(&self) -> &[u8] {
|
||||
if let Some(error_when) = self.error_when {
|
||||
if self.index >= error_when {
|
||||
panic!("invalid access after next returns an error!");
|
||||
}
|
||||
}
|
||||
self.data[self.index].1.as_ref()
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> bool {
|
||||
if let Some(error_when) = self.error_when {
|
||||
if self.index >= error_when {
|
||||
panic!("invalid access after next returns an error!");
|
||||
}
|
||||
}
|
||||
self.index < self.data.len()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user