diff --git a/src/btree.rs b/src/btree.rs index 42dd4b5..10c79e7 100644 --- a/src/btree.rs +++ b/src/btree.rs @@ -194,3 +194,288 @@ where } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::record::Record; + use crate::storage::InMemoryStorage; + + // Helper function to generate a large number of keys and values + fn generate_large_dataset(size: usize) -> (Vec, Vec) { + let keys: Vec = (1..=size as i32).collect(); + let values: Vec = keys.iter().map(|&k| [k; 6]).collect(); + (keys, values) + } + + // Helper function to generate random keys and values + fn generate_random_dataset(size: usize) -> (Vec, Vec) { + use rand::Rng; + let mut rng = rand::rng(); + let keys: Vec = (0..size).map(|_| rng.random_range(1..10000)).collect(); + let values: Vec = keys.iter().map(|&k| [k; 6]).collect(); + (keys, values) + } + + #[test] + fn test_initialization() { + let storage = InMemoryStorage::new(); + let tree = BPlusTree::open(storage); + assert_eq!(tree.root_loc, 0); + } + + #[test] + fn test_insert_and_find() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let key = 1; + let value = [1, 2, 3, 4, 5, 6]; // Example Record as [i32; 6] + tree.insert(key, value); + assert_eq!(tree.find(key), Some(value)); + } + + #[test] + fn test_multiple_inserts_and_finds() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let keys = [1, 2, 3, 4, 5]; + let values = [ + [1, 2, 3, 4, 5, 6], + [7, 8, 9, 10, 11, 12], + [13, 14, 15, 16, 17, 18], + [19, 20, 21, 22, 23, 24], + [25, 26, 27, 28, 29, 30], + ]; + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_leaf_split() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let keys = [1, 2, 3, 4, 5]; // Assuming MAX_KEYS is 4, this will trigger a split + let values = [ + [1, 2, 3, 4, 5, 6], + [7, 8, 9, 10, 11, 12], + [13, 14, 15, 16, 17, 18], + [19, 20, 21, 22, 23, 24], + [25, 26, 27, 28, 29, 30], + ]; + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + // Verify that all keys can still be found after splits + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_internal_split() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + // Insert enough keys to trigger internal splits + let keys = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let values = [ + [1, 2, 3, 4, 5, 6], + [7, 8, 9, 10, 11, 12], + [13, 14, 15, 16, 17, 18], + [19, 20, 21, 22, 23, 24], + [25, 26, 27, 28, 29, 30], + [31, 32, 33, 34, 35, 36], + [37, 38, 39, 40, 41, 42], + [43, 44, 45, 46, 47, 48], + [49, 50, 51, 52, 53, 54], + [55, 56, 57, 58, 59, 60], + [61, 62, 63, 64, 65, 66], + ]; + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + // Verify that all keys can still be found after splits + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_update() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let key = 1; + let initial_value = [1, 2, 3, 4, 5, 6]; + let updated_value = [7, 8, 9, 10, 11, 12]; + tree.insert(key, initial_value); + assert_eq!(tree.find(key), Some(initial_value)); + tree.insert(key, updated_value); + assert_eq!(tree.find(key), Some(updated_value)); + } + + #[test] + fn test_non_existent_key() { + let storage = InMemoryStorage::new(); + let tree = BPlusTree::open(storage); + assert_eq!(tree.find(999), None); // Assuming 999 is not in the tree + } + + #[test] + fn test_out_of_order_inserts() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let keys = [5, 3, 1, 4, 2]; + let values = [ + [1, 2, 3, 4, 5, 6], + [7, 8, 9, 10, 11, 12], + [13, 14, 15, 16, 17, 18], + [19, 20, 21, 22, 23, 24], + [25, 26, 27, 28, 29, 30], + ]; + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + // Verify that all keys can be found + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_large_number_of_inserts() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let num_keys = 100; + let keys: Vec = (1..=num_keys).collect(); + let values: Vec = (1..=num_keys) + .map(|i| [i, i + 1, i + 2, i + 3, i + 4, i + 5]) + .collect(); + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + // Verify that all keys can be found + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_duplicate_keys() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let key = 1; + let initial_value = [1, 2, 3, 4, 5, 6]; + let updated_value = [7, 8, 9, 10, 11, 12]; + tree.insert(key, initial_value); + assert_eq!(tree.find(key), Some(initial_value)); + tree.insert(key, updated_value); + assert_eq!(tree.find(key), Some(updated_value)); + } + + #[test] + fn test_recursive_splitting_up_to_root() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + + // Insert enough keys to cause multiple splits, including root split + let keys = (1..=20).collect::>(); + let values: Vec = keys.iter().map(|&k| [k; 6]).collect(); + + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + + // Verify all keys are present + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_large_dataset() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let (keys, values) = generate_large_dataset(10000); + + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + + // Verify all keys are present + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_boundary_values() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + + // Test with minimum and maximum i32 values + let min_key = i32::MIN; + let max_key = i32::MAX; + let min_value = [i32::MIN; 6]; + let max_value = [i32::MAX; 6]; + + tree.insert(min_key, min_value); + tree.insert(max_key, max_value); + + assert_eq!(tree.find(min_key), Some(min_value)); + assert_eq!(tree.find(max_key), Some(max_value)); + } + + #[test] + fn test_sequential_insertions() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let (keys, values) = generate_large_dataset(100); + + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + + // Verify all keys are present + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_random_insertions() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + let (keys, values) = generate_random_dataset(100); + + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + + // Verify all keys are present + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } + + #[test] + fn test_node_splitting_and_balance() { + let storage = InMemoryStorage::new(); + let mut tree = BPlusTree::open(storage); + + // Insert keys that will cause multiple splits + let keys = (1..=50).collect::>(); + let values: Vec = keys.iter().map(|&k| [k; 6]).collect(); + + for (i, key) in keys.iter().enumerate() { + tree.insert(*key, values[i]); + } + + // Verify all keys are present and tree is balanced + for (i, key) in keys.iter().enumerate() { + assert_eq!(tree.find(*key), Some(values[i])); + } + } +} diff --git a/src/main.rs b/src/main.rs index 3712f0b..bc7649e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use crate::btree::BPlusTree; use crate::storage::InMemoryStorage; fn main() { - let storage = InMemoryStorage { nodes: Vec::new() }; + let storage = InMemoryStorage::new(); let mut tree = BPlusTree::open(storage); // Insert some key-value pairs diff --git a/src/storage.rs b/src/storage.rs index fa7e077..78862cf 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -11,6 +11,12 @@ pub struct InMemoryStorage { pub nodes: Vec>, } +impl InMemoryStorage { + pub fn new() -> Self { + Self { nodes: Vec::new() } + } +} + impl Storage for InMemoryStorage { fn read_node(&self, loc: usize) -> Option { self.nodes.get(loc)?.clone()