diff --git a/storage_engine/src/storage_engine.rs b/storage_engine/src/storage_engine.rs index 26398f3..01f87d0 100644 --- a/storage_engine/src/storage_engine.rs +++ b/storage_engine/src/storage_engine.rs @@ -202,3 +202,341 @@ impl Store { Ok(bytes) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::segments::entry::{Entry, EntryDetailed}; + use crate::cursor::{ReadCursor, WriteCursor, CursorWithStoreHeader, CursorWithWriteAccessToIndex, CursorWithAccessToIndex}; + + impl Drop for Store { + fn drop(&mut self) { + println!("DROPPING TEST FOLDER"); + let table_folder = self.header.table_folder.clone(); + // Seems no one has figured out how to do AsyncDrop yet. + std::fs::remove_dir_all(table_folder).unwrap(); + } + } + + + #[tokio::test] + async fn test_create() { + type Data = u32; + + let table_path = "test_table_0"; + let number_of_columns = 5; + let primary_column = 0; + let store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + assert!(store.header.number_of_columns == number_of_columns); + assert!(store.header.total_count == 0); + assert!(store.header.deleted_count == 0); + assert!(store.header.primary_column == primary_column); + } + + #[tokio::test] + async fn test_insert() { + type Data = u32; + + let table_path = "test_table_1"; + let number_of_columns = 5; + let primary_column = 0; + let mut store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + { + let mut cursor = store.write_cursor().await.unwrap(); + + let entry0: Entry = Entry::new(vec![1, 2, 3, 4, 5]); + cursor.insert_entry(entry0).await.unwrap(); + + let entry1: Entry = Entry::new(vec![6, 7, 8, 9, 10]); + cursor.insert_entry(entry1).await.unwrap(); + + assert!(store.header.total_count == 2); + } + } + + #[tokio::test] + async fn test_select_next() { + type Data = u32; + + let table_path = "test_table_2"; + let number_of_columns = 5; + let primary_column = 0; + let mut store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + { + let mut cursor = store.write_cursor().await.unwrap(); + + let entry0: Entry = Entry::new(vec![1, 2, 3, 4, 5]); + cursor.insert_entry(entry0).await.unwrap(); + + let entry1: Entry = Entry::new(vec![6, 7, 8, 9, 10]); + cursor.insert_entry(entry1).await.unwrap(); + + assert!(store.header.total_count == 2); + } + + { + let mut cursor = store.read_cursor().await.unwrap(); + + let entry0 = cursor.next().await.unwrap().unwrap(); + let entry1 = cursor.next().await.unwrap().unwrap(); + + assert!(entry0.data == vec![1,2,3,4,5]); + assert!(entry1.data == vec![6,7,8,9,10]); + } + } + + #[tokio::test] + async fn test_select_all() { + type Data = u32; + + let table_path = "test_table_3"; + let number_of_columns = 5; + let primary_column = 0; + let mut store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + { + let mut cursor = store.write_cursor().await.unwrap(); + + let entry0: Entry = Entry::new(vec![1, 2, 3, 4, 5]); + cursor.insert_entry(entry0).await.unwrap(); + + let entry1: Entry = Entry::new(vec![6, 7, 8, 9, 10]); + cursor.insert_entry(entry1).await.unwrap(); + + assert!(store.header.total_count == 2); + } + + { + let mut cursor = store.read_cursor().await.unwrap(); + + let mut entries = vec![]; + while let Some(entry) = cursor.next().await.unwrap() { + entries.push(entry) + } + + assert!(entries.len() == 2); + assert!(entries[0].data == vec![1,2,3,4,5]); + assert!(entries[1].data == vec![6,7,8,9,10]); + } + } + + #[tokio::test] + async fn test_select_eq() { + type Data = u32; + + let table_path = "test_table_4"; + let number_of_columns = 5; + let primary_column = 0; + let mut store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + let value = 200; + { + let mut cursor = store.write_cursor().await.unwrap(); + + let entry0: Entry = Entry::new(vec![1, value, 3, 4, 5]); + cursor.insert_entry(entry0).await.unwrap(); + + let entry1: Entry = Entry::new(vec![6, 7, 8, 9, 10]); + cursor.insert_entry(entry1).await.unwrap(); + + let entry2: Entry = Entry::new(vec![11, 2, 10, 10, 10]); + cursor.insert_entry(entry2).await.unwrap(); + + let entry3: Entry = Entry::new(vec![1, value, 100, 50, 40]); + cursor.insert_entry(entry3).await.unwrap(); + + assert!(store.header.total_count == 4); + } + + { + let mut cursor = store.read_cursor().await.unwrap(); + let column = 1; + + let entries = cursor.select_entries_where_eq(column, &value).await.unwrap(); + + assert!(entries.len() == 2); + assert!(entries[0].data == vec![1, value, 3, 4, 5]); + assert!(entries[1].data == vec![1, value, 100, 50, 40]); + } + } + + #[tokio::test] + async fn test_select_eq_indexed() { + type Data = u32; + + let table_path = "test_table_5"; + let number_of_columns = 5; + let primary_column = 0; + let mut store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + let column: Column = 1; + + assert!(store.indexes[column as usize].is_none()); + store.attach_index(column).await.unwrap(); + assert!(store.indexes[column as usize].is_some()); + + let value = 200; + { + let mut cursor = store.write_cursor().await.unwrap(); + + let entry0: Entry = Entry::new(vec![1, value, 3, 4, 5]); + cursor.insert_entry(entry0).await.unwrap(); + + let entry1: Entry = Entry::new(vec![6, 7, 8, 9, 10]); + cursor.insert_entry(entry1).await.unwrap(); + + let entry2: Entry = Entry::new(vec![11, 2, 10, 10, 10]); + cursor.insert_entry(entry2).await.unwrap(); + + let entry3: Entry = Entry::new(vec![1, value, 100, 50, 40]); + cursor.insert_entry(entry3).await.unwrap(); + + assert!(store.header.total_count == 4); + } + + { + let mut cursor = store.read_cursor().await.unwrap(); + let column = 1; + + let entries = cursor.select_entries_where_eq(column, &value).await.unwrap(); + assert!(entries.len() == 2); + // Order may be non-deterministic. + assert!(entries[0].data[column as usize] == value); + assert!(entries[1].data[column as usize] == value); + } + } + + #[tokio::test] + async fn test_delete_entry() { + type Data = u32; + + let table_path = "test_table_6"; + let number_of_columns = 5; + let primary_column = 0; + let mut store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + let value = 200; + let (_file_position0, file_position1, _file_position2, _file_position3) = { + let mut cursor = store.write_cursor().await.unwrap(); + + let entry0: Entry = Entry::new(vec![1, value, 3, 4, 5]); + let file_position0 = cursor.insert_entry(entry0).await.unwrap(); + + let entry1: Entry = Entry::new(vec![6, 7, 8, 9, 10]); + let file_position1 = cursor.insert_entry(entry1).await.unwrap(); + + let entry2: Entry = Entry::new(vec![11, 2, 10, 10, 10]); + let file_position2 = cursor.insert_entry(entry2).await.unwrap(); + + let entry3: Entry = Entry::new(vec![1, value, 100, 50, 40]); + let file_position3 = cursor.insert_entry(entry3).await.unwrap(); + + assert!(store.header.total_count == 4); + (file_position0, file_position1, file_position2, file_position3) + }; + + { + assert!(store.header.deleted_count == 0); + let mut cursor = store.write_cursor().await.unwrap(); + cursor.mark_deleted_at(file_position1, false).await.unwrap(); + assert!(store.header.deleted_count == 1); + } + } + + #[tokio::test] + async fn test_delete_where_eq() { + type Data = u32; + + let table_path = "test_table_7"; + let number_of_columns = 5; + let primary_column = 0; + let mut store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + let column: Column = 1; + + assert!(store.indexes[column as usize].is_none()); + store.attach_index(column).await.unwrap(); + assert!(store.indexes[column as usize].is_some()); + + let value = 200; + + let (_file_position0, _file_position1, _file_position2, _file_position3) = { + let mut cursor = store.write_cursor().await.unwrap(); + + let entry0: Entry = Entry::new(vec![1, value, 3, 4, 5]); + let file_position0 = cursor.insert_entry(entry0).await.unwrap(); + + let entry1: Entry = Entry::new(vec![6, 7, 8, 9, 10]); + let file_position1 = cursor.insert_entry(entry1).await.unwrap(); + + let entry2: Entry = Entry::new(vec![11, 2, 10, 10, 10]); + let file_position2 = cursor.insert_entry(entry2).await.unwrap(); + + let entry3: Entry = Entry::new(vec![1, value, 100, 50, 40]); + let file_position3 = cursor.insert_entry(entry3).await.unwrap(); + + assert!(store.header.total_count == 4); + (file_position0, file_position1, file_position2, file_position3) + }; + + { + assert!(store.header.deleted_count == 0); + let mut cursor = store.write_cursor().await.unwrap(); + cursor.delete_entries_where_eq(column, &value, false).await.unwrap(); + assert!(store.header.deleted_count == 2); + } + } + + #[tokio::test] + async fn test_garbage_collection() { + type Data = u32; + + let table_path = "test_table_8"; + let number_of_columns = 5; + let primary_column = 0; + let mut store: Store = Store::new(table_path, number_of_columns, primary_column).await.unwrap(); + + let column: Column = 1; + + assert!(store.indexes[column as usize].is_none()); + store.attach_index(column).await.unwrap(); + assert!(store.indexes[column as usize].is_some()); + + let value = 200; + + let (_file_position0, _file_position1, _file_position2, _file_position3) = { + let mut cursor = store.write_cursor().await.unwrap(); + + let entry0: Entry = Entry::new(vec![1, value, 3, 4, 5]); + let file_position0 = cursor.insert_entry(entry0).await.unwrap(); + + let entry1: Entry = Entry::new(vec![6, 7, 8, 9, 10]); + let file_position1 = cursor.insert_entry(entry1).await.unwrap(); + + let entry2: Entry = Entry::new(vec![11, 2, 10, 10, 10]); + let file_position2 = cursor.insert_entry(entry2).await.unwrap(); + + let entry3: Entry = Entry::new(vec![1, value, 100, 50, 40]); + let file_position3 = cursor.insert_entry(entry3).await.unwrap(); + + assert!(store.header.total_count == 4); + (file_position0, file_position1, file_position2, file_position3) + }; + + { + assert!(store.header.deleted_count == 0); + let mut cursor = store.write_cursor().await.unwrap(); + cursor.delete_entries_where_eq(column, &value, false).await.unwrap(); + assert!(cursor.header().deleted_count == 2); + assert!(cursor.header().total_count == 4); + + cursor.initiate_garbage_collection().await.unwrap(); + assert!(cursor.header().deleted_count == 0); + assert!(cursor.header().total_count == 2); + } + } + +}