From 25950fa116bbc93a8dd0041c092d615648f0303f Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 29 Mar 2026 19:20:23 +0000 Subject: [PATCH 1/5] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/linksplatform/trees-rs/issues/24 --- .gitkeep | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitkeep b/.gitkeep index cfedde0..29bcf74 100644 --- a/.gitkeep +++ b/.gitkeep @@ -1 +1,2 @@ -# .gitkeep file auto-generated at 2026-03-24T09:13:49.526Z for PR creation at branch issue-8-c1619bfb477b for issue https://github.com/linksplatform/trees-rs/issues/8 \ No newline at end of file +# .gitkeep file auto-generated at 2026-03-24T09:13:49.526Z for PR creation at branch issue-8-c1619bfb477b for issue https://github.com/linksplatform/trees-rs/issues/8 +# Updated: 2026-03-29T19:20:23.179Z \ No newline at end of file From 2ea3fc7a948f96d324af0f049c824856c8ffd94e Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 29 Mar 2026 19:27:19 +0000 Subject: [PATCH 2/5] move all tests from src/tests/ to tests/ and add coverage tests Move unit tests to integration tests in the tests/ directory: - src/tests/mod.rs -> tests/common/mod.rs (shared test fixtures) - src/tests/list_tests.rs -> tests/list_tests.rs - src/tests/tree_tests.rs -> tests/tree_tests.rs Add 10 new tests for improved coverage: - test_detach_node_with_right_child_only - test_attach_right_special_case_empty - test_detach_replacement_from_right_subtree - test_get_rightest_single_node - test_get_leftest_single_node - test_get_left_size_no_left_child - test_get_right_size_no_right_child - test_fix_size_leaf_node - test_detach_reinsert_cycle - test_contains_deep_tree Fixes #24 Co-Authored-By: Claude Opus 4.6 --- src/lib.rs | 3 - {src/tests => tests/common}/mod.rs | 56 +++++----- {src/tests => tests}/list_tests.rs | 10 +- {src/tests => tests}/tree_tests.rs | 172 ++++++++++++++++++++++++++++- 4 files changed, 208 insertions(+), 33 deletions(-) rename {src/tests => tests/common}/mod.rs (84%) rename {src/tests => tests}/list_tests.rs (98%) rename {src/tests => tests}/tree_tests.rs (82%) diff --git a/src/lib.rs b/src/lib.rs index d5c4f20..109b045 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,6 @@ mod link_type; mod lists; mod trees; -#[cfg(test)] -mod tests; - pub use link_type::LinkType; pub use lists::{ AbsoluteCircularLinkedList, AbsoluteLinkedList, LinkedList, RelativeCircularLinkedList, diff --git a/src/tests/mod.rs b/tests/common/mod.rs similarity index 84% rename from src/tests/mod.rs rename to tests/common/mod.rs index db30b31..0d139e3 100644 --- a/src/tests/mod.rs +++ b/tests/common/mod.rs @@ -1,12 +1,14 @@ -//! Comprehensive tests for 100% code coverage of platform-trees +//! Shared test implementations for integration tests. +//! +//! These test fixtures provide concrete implementations of the library's traits +//! for use in testing. They serve as clear examples of how to implement each trait. -use crate::{ - AbsoluteCircularLinkedList, AbsoluteLinkedList, IterativeSizeBalancedTree, LinkType, - LinkedList, RecursiveSizeBalancedTree, RelativeCircularLinkedList, RelativeLinkedList, -}; +#![allow(dead_code)] -mod list_tests; -mod tree_tests; +use platform_trees::{ + AbsoluteCircularLinkedList, AbsoluteLinkedList, IterativeSizeBalancedTree, LinkedList, + RecursiveSizeBalancedTree, RelativeCircularLinkedList, RelativeLinkedList, +}; // ============================================================================= // Test implementations @@ -14,21 +16,21 @@ mod tree_tests; /// A simple node structure for testing linked lists #[derive(Debug, Clone, Copy, Default)] -struct Node { - prev: usize, - next: usize, +pub struct Node { + pub prev: usize, + pub next: usize, } /// A simple absolute linked list implementation for testing -struct TestAbsoluteList { - nodes: Vec, - first: usize, - last: usize, - size: usize, +pub struct TestAbsoluteList { + pub nodes: Vec, + pub first: usize, + pub last: usize, + pub size: usize, } impl TestAbsoluteList { - fn new(capacity: usize) -> Self { + pub fn new(capacity: usize) -> Self { let mut nodes = Vec::with_capacity(capacity + 1); // Index 0 is reserved as "null" nodes.resize(capacity + 1, Node::default()); @@ -88,14 +90,14 @@ impl AbsoluteLinkedList for TestAbsoluteList { impl AbsoluteCircularLinkedList for TestAbsoluteList {} /// A relative linked list implementation for testing (list head stored in element) -struct TestRelativeList { - nodes: Vec, +pub struct TestRelativeList { + pub nodes: Vec, // Store first/last/size for each "head" element - heads: Vec<(usize, usize, usize)>, // (first, last, size) + pub heads: Vec<(usize, usize, usize)>, // (first, last, size) } impl TestRelativeList { - fn new(capacity: usize) -> Self { + pub fn new(capacity: usize) -> Self { let mut nodes = Vec::with_capacity(capacity + 1); nodes.resize(capacity + 1, Node::default()); let mut heads = Vec::with_capacity(capacity + 1); @@ -152,19 +154,19 @@ impl RelativeCircularLinkedList for TestRelativeList {} /// A tree node structure for testing `SizeBalancedTree` #[derive(Debug, Clone, Copy, Default)] -struct TreeNode { - left: usize, - right: usize, - size: usize, +pub struct TreeNode { + pub left: usize, + pub right: usize, + pub size: usize, } /// A simple `SizeBalancedTree` implementation for testing -struct TestTree { - nodes: Vec, +pub struct TestTree { + pub nodes: Vec, } impl TestTree { - fn new(capacity: usize) -> Self { + pub fn new(capacity: usize) -> Self { let mut nodes = Vec::with_capacity(capacity + 1); nodes.resize(capacity + 1, TreeNode::default()); Self { nodes } diff --git a/src/tests/list_tests.rs b/tests/list_tests.rs similarity index 98% rename from src/tests/list_tests.rs rename to tests/list_tests.rs index 9645119..9faeaf4 100644 --- a/src/tests/list_tests.rs +++ b/tests/list_tests.rs @@ -1,11 +1,17 @@ -use super::*; +mod common; + +use common::{TestAbsoluteList, TestRelativeList}; +use platform_trees::{ + AbsoluteCircularLinkedList, AbsoluteLinkedList, LinkedList, RelativeCircularLinkedList, + RelativeLinkedList, +}; // ============================================================================= // LinkType trait tests // ============================================================================= mod link_type_tests { - use super::*; + use platform_trees::LinkType; #[test] fn test_funty_zero() { diff --git a/src/tests/tree_tests.rs b/tests/tree_tests.rs similarity index 82% rename from src/tests/tree_tests.rs rename to tests/tree_tests.rs index eba093a..d998eff 100644 --- a/src/tests/tree_tests.rs +++ b/tests/tree_tests.rs @@ -1,4 +1,7 @@ -use super::*; +mod common; + +use common::TestTree; +use platform_trees::{IterativeSizeBalancedTree, RecursiveSizeBalancedTree}; // ============================================================================= // SizeBalancedTree trait tests @@ -898,4 +901,171 @@ mod iterative_size_balanced_tree_tests { } } } + + #[test] + fn test_detach_node_with_right_child_only() { + // Tests the path where we set *root = *right (right_size > 0, left_size == 0) + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 17); + + // Remove 15, which should have only right child (17) after tree balancing + tree.detach(&mut root, 15); + + assert!(!tree.contains(15, root)); + assert!(tree.contains(17, root)); + } + } + + #[test] + fn test_attach_right_special_case_empty() { + // Tests the special case in attach_core where right_left_size == 0 and left_size == 0 + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 20); + // Insert 15 which is left of 20 but right of 10 + // With just 2 nodes, this triggers the right-side special case + tree.attach(&mut root, 15); + + assert!(tree.contains(10, root)); + assert!(tree.contains(15, root)); + assert!(tree.contains(20, root)); + } + } + + #[test] + fn test_detach_replacement_from_right_subtree() { + // Tests detach where replacement comes from right subtree (left_size <= right_size) + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 12); + tree.attach(&mut root, 17); + tree.attach(&mut root, 16); + tree.attach(&mut root, 18); + + // Detach 15 which has both children; right subtree is larger + tree.detach(&mut root, 15); + + assert!(!tree.contains(15, root)); + assert!(tree.contains(12, root)); + assert!(tree.contains(17, root)); + assert!(tree.contains(16, root)); + assert!(tree.contains(18, root)); + } + } + + #[test] + fn test_get_rightest_single_node() { + // Tests get_rightest with a single node (no right child) + let mut tree = TestTree::new(10); + unsafe { + tree.set_size(1, 1); + assert_eq!(tree.get_rightest(1), 1); + } + } + + #[test] + fn test_get_leftest_single_node() { + // Tests get_leftest with a single node (no left child) + let mut tree = TestTree::new(10); + unsafe { + tree.set_size(1, 1); + assert_eq!(tree.get_leftest(1), 1); + } + } + + #[test] + fn test_get_left_size_no_left_child() { + // Tests get_left_size when node has no left child + let mut tree = TestTree::new(10); + unsafe { + tree.set_size(1, 1); + assert_eq!(tree.get_left_size(1), 0); + } + } + + #[test] + fn test_get_right_size_no_right_child() { + // Tests get_right_size when node has no right child + let mut tree = TestTree::new(10); + unsafe { + tree.set_size(1, 1); + assert_eq!(tree.get_right_size(1), 0); + } + } + + #[test] + fn test_fix_size_leaf_node() { + // Tests fix_size on a leaf node (no children) + let mut tree = TestTree::new(10); + unsafe { + tree.fix_size(1); + assert_eq!(tree.get_size(1), 1); // 0 + 0 + 1 + } + } + + #[test] + fn test_detach_reinsert_cycle() { + // Tests detaching all nodes and reinserting them + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + let nodes = [15, 10, 20, 5, 12, 17, 25]; + for &i in &nodes { + tree.attach(&mut root, i); + } + + // Detach all + for &i in &nodes { + tree.detach(&mut root, i); + } + assert_eq!(root, 0); + + // Reinsert all + for &i in &nodes { + tree.attach(&mut root, i); + } + + for &i in &nodes { + assert!(tree.contains(i, root), "Node {i} should be present after reinsert"); + } + assert_eq!(tree.get_size(root), 7); + } + } + + #[test] + fn test_contains_deep_tree() { + // Tests contains on a deep tree traversal + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + // Build a larger tree + for i in [15, 8, 22, 4, 12, 18, 26, 2, 6, 10, 14, 16, 20, 24, 28] { + tree.attach(&mut root, i); + } + + // Search for nodes at various depths + assert!(tree.contains(2, root)); + assert!(tree.contains(28, root)); + assert!(tree.contains(15, root)); + assert!(!tree.contains(1, root)); + assert!(!tree.contains(30, root)); + assert!(!tree.contains(13, root)); + } + } } From 87ccfe66aa562ba5151332da66b6d8323b87359d Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 29 Mar 2026 19:31:21 +0000 Subject: [PATCH 3/5] fix: add changelog fragment and fix formatting Co-Authored-By: Claude Opus 4.6 --- changelog.d/20260329_move_tests_to_integration.md | 7 +++++++ tests/tree_tests.rs | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 changelog.d/20260329_move_tests_to_integration.md diff --git a/changelog.d/20260329_move_tests_to_integration.md b/changelog.d/20260329_move_tests_to_integration.md new file mode 100644 index 0000000..d25cd31 --- /dev/null +++ b/changelog.d/20260329_move_tests_to_integration.md @@ -0,0 +1,7 @@ +--- +bump: patch +--- + +### Changed +- Moved all tests from `src/tests/` to `tests/` folder (integration tests) +- Added 10 new tests for improved code coverage of edge cases and unsafe code paths diff --git a/tests/tree_tests.rs b/tests/tree_tests.rs index d998eff..65ed6c8 100644 --- a/tests/tree_tests.rs +++ b/tests/tree_tests.rs @@ -1041,7 +1041,10 @@ mod iterative_size_balanced_tree_tests { } for &i in &nodes { - assert!(tree.contains(i, root), "Node {i} should be present after reinsert"); + assert!( + tree.contains(i, root), + "Node {i} should be present after reinsert" + ); } assert_eq!(tree.get_size(root), 7); } From a6f4112565f0cac7259f07fc7fd27a7131b71f0b Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 29 Mar 2026 19:37:03 +0000 Subject: [PATCH 4/5] refactor: split tree_tests.rs to stay under 1000-line limit Split iterative tree tests into tests/iterative_tree_tests.rs (650 lines) and kept recursive tree tests in tests/tree_tests.rs (379 lines). Co-Authored-By: Claude Opus 4.6 --- tests/iterative_tree_tests.rs | 650 +++++++++++++++++++++++++++++++ tests/tree_tests.rs | 699 +--------------------------------- 2 files changed, 652 insertions(+), 697 deletions(-) create mode 100644 tests/iterative_tree_tests.rs diff --git a/tests/iterative_tree_tests.rs b/tests/iterative_tree_tests.rs new file mode 100644 index 0000000..ed44f91 --- /dev/null +++ b/tests/iterative_tree_tests.rs @@ -0,0 +1,650 @@ +mod common; + +use common::TestTree; +use platform_trees::{IterativeSizeBalancedTree, RecursiveSizeBalancedTree}; + +// ============================================================================= +// IterativeSizeBalancedTree trait tests +// ============================================================================= + +mod iterative_size_balanced_tree_tests { + use super::*; + + #[test] + fn test_attach_to_empty_tree() { + let mut tree = TestTree::new(10); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 5); + } + + assert_eq!(root, 5); + unsafe { + assert_eq!(tree.get_size(5), 1); + } + } + + #[test] + fn test_attach_single_node_to_left() { + let mut tree = TestTree::new(10); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 5); + tree.attach(&mut root, 3); + } + + unsafe { + assert_eq!(tree.get_left(5), 3); + assert_eq!(tree.get_size(5), 2); + assert_eq!(tree.get_size(3), 1); + } + } + + #[test] + fn test_attach_single_node_to_right() { + let mut tree = TestTree::new(10); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 5); + tree.attach(&mut root, 7); + } + + unsafe { + assert_eq!(tree.get_right(5), 7); + assert_eq!(tree.get_size(5), 2); + assert_eq!(tree.get_size(7), 1); + } + } + + #[test] + fn test_attach_multiple_nodes() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 3); + tree.attach(&mut root, 7); + } + + unsafe { + assert!(tree.contains(10, root)); + assert!(tree.contains(5, root)); + assert!(tree.contains(15, root)); + assert!(tree.contains(3, root)); + assert!(tree.contains(7, root)); + assert_eq!(tree.get_size(root), 5); + } + } + + #[test] + fn test_detach_from_single_node_tree() { + let mut tree = TestTree::new(10); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 5); + tree.detach(&mut root, 5); + } + + assert_eq!(root, 0); + } + + #[test] + fn test_detach_leaf_node() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + + tree.detach(&mut root, 5); + + assert!(!tree.contains(5, root)); + assert!(tree.contains(10, root)); + assert!(tree.contains(15, root)); + } + } + + #[test] + fn test_detach_node_with_one_child() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 3); + + tree.detach(&mut root, 5); + + assert!(!tree.contains(5, root)); + assert!(tree.contains(10, root)); + assert!(tree.contains(3, root)); + } + } + + #[test] + fn test_detach_node_with_two_children() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 3); + tree.attach(&mut root, 7); + + tree.detach(&mut root, 5); + + assert!(!tree.contains(5, root)); + assert!(tree.contains(10, root)); + assert!(tree.contains(15, root)); + assert!(tree.contains(3, root)); + assert!(tree.contains(7, root)); + } + } + + #[test] + fn test_detach_root() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + + tree.detach(&mut root, 10); + + assert!(!tree.contains(10, root)); + assert!(tree.contains(5, root)); + assert!(tree.contains(15, root)); + assert_ne!(root, 0); + } + } + + #[test] + fn test_attach_and_detach_sequence() { + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + // Build tree + for i in [10, 5, 15, 3, 7, 12, 17] { + tree.attach(&mut root, i); + } + + // Verify all nodes present + for i in [10, 5, 15, 3, 7, 12, 17] { + assert!(tree.contains(i, root), "Node {i} should be present"); + } + + // Remove some nodes + tree.detach(&mut root, 3); + tree.detach(&mut root, 17); + tree.detach(&mut root, 10); + + // Verify correct nodes present + assert!(!tree.contains(3, root)); + assert!(!tree.contains(17, root)); + assert!(!tree.contains(10, root)); + assert!(tree.contains(5, root)); + assert!(tree.contains(15, root)); + assert!(tree.contains(7, root)); + assert!(tree.contains(12, root)); + } + } + + #[test] + fn test_tree_balancing() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + // Insert in order that would cause imbalance without rotations + for i in 1..=10 { + tree.attach(&mut root, i); + } + + // All nodes should be reachable + for i in 1..=10 { + assert!(tree.contains(i, root), "Node {i} should be present"); + } + + assert_eq!(tree.get_size(root), 10); + } + } + + #[test] + fn test_detach_all_nodes() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + for i in 1..=5 { + tree.attach(&mut root, i); + } + + for i in 1..=5 { + tree.detach(&mut root, i); + } + + assert_eq!(root, 0); + } + } + + // Additional edge case tests for full coverage + + #[test] + fn test_attach_left_right_rotation_edge_case() { + // Tests the path where we need a left-right double rotation on left side + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 8); // Goes right of 5, should trigger LR rotation + + assert!(tree.contains(10, root)); + assert!(tree.contains(5, root)); + assert!(tree.contains(8, root)); + assert_eq!(tree.get_size(root), 3); + } + } + + #[test] + fn test_attach_descend_left_without_rotation() { + let mut tree = TestTree::new(40); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 20); + tree.attach(&mut root, 10); + tree.attach(&mut root, 30); + tree.attach(&mut root, 25); + tree.attach(&mut root, 35); + tree.attach(&mut root, 5); + tree.attach(&mut root, 2); + + assert!(tree.contains(2, root)); + assert_eq!(tree.get_size(root), 7); + } + } + + #[test] + fn test_attach_special_case_left_right_empty() { + // Tests the special case in attach_core where left_right_size == 0 and right_size == 0 + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 7); + + assert!(tree.contains(10, root)); + assert!(tree.contains(5, root)); + assert!(tree.contains(7, root)); + } + } + + #[test] + fn test_attach_right_left_rotation_edge_case() { + // Tests the path where we need a right-left double rotation on right side + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 20); + tree.attach(&mut root, 15); // Between 10 and 20, triggers RL rotation + + assert!(tree.contains(10, root)); + assert!(tree.contains(15, root)); + assert!(tree.contains(20, root)); + assert_eq!(tree.get_size(root), 3); + } + } + + #[test] + fn test_attach_descend_right_without_rotation() { + let mut tree = TestTree::new(40); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 20); + tree.attach(&mut root, 30); + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 35); + tree.attach(&mut root, 40); + + assert!(tree.contains(40, root)); + assert_eq!(tree.get_size(root), 7); + } + } + + #[test] + fn test_detach_with_double_rotation_left() { + let mut tree = TestTree::new(40); + let mut root: usize = 0; + + unsafe { + for i in [20, 10, 30, 5, 15, 25, 35, 3, 8, 13, 17] { + tree.attach(&mut root, i); + } + + tree.detach(&mut root, 3); + tree.detach(&mut root, 5); + tree.detach(&mut root, 8); + + assert!(!tree.contains(3, root)); + assert!(!tree.contains(5, root)); + assert!(!tree.contains(8, root)); + } + } + + #[test] + fn test_detach_with_double_rotation_right() { + let mut tree = TestTree::new(50); + let mut root: usize = 0; + + unsafe { + for i in [20, 10, 30, 5, 15, 25, 35, 32, 38, 28, 27] { + tree.attach(&mut root, i); + } + + tree.detach(&mut root, 35); + tree.detach(&mut root, 38); + tree.detach(&mut root, 32); + + assert!(!tree.contains(35, root)); + assert!(!tree.contains(38, root)); + assert!(!tree.contains(32, root)); + } + } + + #[test] + fn test_detach_with_left_only_child() { + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 3); + tree.attach(&mut root, 7); + tree.attach(&mut root, 2); + tree.attach(&mut root, 4); + + tree.detach(&mut root, 5); + + assert!(!tree.contains(5, root)); + assert!(tree.contains(3, root)); + assert!(tree.contains(7, root)); + } + } + + #[test] + fn test_detach_node_with_left_child_only() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 3); + + tree.detach(&mut root, 5); + + assert!(!tree.contains(5, root)); + assert!(tree.contains(3, root)); + } + } + + #[test] + fn test_large_sequential_insert() { + let mut tree = TestTree::new(50); + let mut root: usize = 0; + + unsafe { + for i in 1..=20 { + tree.attach(&mut root, i); + } + + for i in 1..=20 { + assert!(tree.contains(i, root), "Node {i} missing"); + } + assert_eq!(tree.get_size(root), 20); + } + } + + #[test] + fn test_large_reverse_insert() { + let mut tree = TestTree::new(50); + let mut root: usize = 0; + + unsafe { + for i in (1..=20).rev() { + tree.attach(&mut root, i); + } + + for i in 1..=20 { + assert!(tree.contains(i, root), "Node {i} missing"); + } + assert_eq!(tree.get_size(root), 20); + } + } + + #[test] + fn test_alternating_insert() { + let mut tree = TestTree::new(50); + let mut root: usize = 0; + + unsafe { + let mut low = 1; + let mut high = 20; + for i in 0..20 { + if i % 2 == 0 { + tree.attach(&mut root, high); + high -= 1; + } else { + tree.attach(&mut root, low); + low += 1; + } + } + + for i in 1..=20 { + assert!(tree.contains(i, root), "Node {i} missing"); + } + } + } + + #[test] + fn test_detach_from_large_tree() { + let mut tree = TestTree::new(100); + let mut root: usize = 0; + + unsafe { + for i in 1..=50 { + tree.attach(&mut root, i); + } + + for i in (1..=50).step_by(2) { + tree.detach(&mut root, i); + } + + for i in (2..=50).step_by(2) { + assert!(tree.contains(i, root), "Node {i} should be present"); + } + for i in (1..=49).step_by(2) { + assert!(!tree.contains(i, root), "Node {i} should be absent"); + } + } + } + + #[test] + fn test_detach_node_with_right_child_only() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 17); + + tree.detach(&mut root, 15); + + assert!(!tree.contains(15, root)); + assert!(tree.contains(17, root)); + } + } + + #[test] + fn test_attach_right_special_case_empty() { + let mut tree = TestTree::new(20); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 20); + tree.attach(&mut root, 15); + + assert!(tree.contains(10, root)); + assert!(tree.contains(15, root)); + assert!(tree.contains(20, root)); + } + } + + #[test] + fn test_detach_replacement_from_right_subtree() { + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + tree.attach(&mut root, 10); + tree.attach(&mut root, 5); + tree.attach(&mut root, 15); + tree.attach(&mut root, 12); + tree.attach(&mut root, 17); + tree.attach(&mut root, 16); + tree.attach(&mut root, 18); + + tree.detach(&mut root, 15); + + assert!(!tree.contains(15, root)); + assert!(tree.contains(12, root)); + assert!(tree.contains(17, root)); + assert!(tree.contains(16, root)); + assert!(tree.contains(18, root)); + } + } + + #[test] + fn test_get_rightest_single_node() { + let mut tree = TestTree::new(10); + unsafe { + tree.set_size(1, 1); + assert_eq!(tree.get_rightest(1), 1); + } + } + + #[test] + fn test_get_leftest_single_node() { + let mut tree = TestTree::new(10); + unsafe { + tree.set_size(1, 1); + assert_eq!(tree.get_leftest(1), 1); + } + } + + #[test] + fn test_get_left_size_no_left_child() { + let mut tree = TestTree::new(10); + unsafe { + tree.set_size(1, 1); + assert_eq!(tree.get_left_size(1), 0); + } + } + + #[test] + fn test_get_right_size_no_right_child() { + let mut tree = TestTree::new(10); + unsafe { + tree.set_size(1, 1); + assert_eq!(tree.get_right_size(1), 0); + } + } + + #[test] + fn test_fix_size_leaf_node() { + let mut tree = TestTree::new(10); + unsafe { + tree.fix_size(1); + assert_eq!(tree.get_size(1), 1); // 0 + 0 + 1 + } + } + + #[test] + fn test_detach_reinsert_cycle() { + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + let nodes = [15, 10, 20, 5, 12, 17, 25]; + for &i in &nodes { + tree.attach(&mut root, i); + } + + for &i in &nodes { + tree.detach(&mut root, i); + } + assert_eq!(root, 0); + + for &i in &nodes { + tree.attach(&mut root, i); + } + + for &i in &nodes { + assert!( + tree.contains(i, root), + "Node {i} should be present after reinsert" + ); + } + assert_eq!(tree.get_size(root), 7); + } + } + + #[test] + fn test_contains_deep_tree() { + let mut tree = TestTree::new(30); + let mut root: usize = 0; + + unsafe { + for i in [15, 8, 22, 4, 12, 18, 26, 2, 6, 10, 14, 16, 20, 24, 28] { + tree.attach(&mut root, i); + } + + assert!(tree.contains(2, root)); + assert!(tree.contains(28, root)); + assert!(tree.contains(15, root)); + assert!(!tree.contains(1, root)); + assert!(!tree.contains(30, root)); + assert!(!tree.contains(13, root)); + } + } +} diff --git a/tests/tree_tests.rs b/tests/tree_tests.rs index 65ed6c8..91792f0 100644 --- a/tests/tree_tests.rs +++ b/tests/tree_tests.rs @@ -1,10 +1,10 @@ mod common; use common::TestTree; -use platform_trees::{IterativeSizeBalancedTree, RecursiveSizeBalancedTree}; +use platform_trees::RecursiveSizeBalancedTree; // ============================================================================= -// SizeBalancedTree trait tests +// SizeBalancedTree trait tests (RecursiveSizeBalancedTree) // ============================================================================= mod size_balanced_tree_tests { @@ -377,698 +377,3 @@ mod size_balanced_tree_tests { } } } - -// ============================================================================= -// IterativeSizeBalancedTree trait tests -// ============================================================================= - -mod iterative_size_balanced_tree_tests { - use super::*; - - #[test] - fn test_attach_to_empty_tree() { - let mut tree = TestTree::new(10); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 5); - } - - assert_eq!(root, 5); - unsafe { - assert_eq!(tree.get_size(5), 1); - } - } - - #[test] - fn test_attach_single_node_to_left() { - let mut tree = TestTree::new(10); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 5); - tree.attach(&mut root, 3); - } - - unsafe { - assert_eq!(tree.get_left(5), 3); - assert_eq!(tree.get_size(5), 2); - assert_eq!(tree.get_size(3), 1); - } - } - - #[test] - fn test_attach_single_node_to_right() { - let mut tree = TestTree::new(10); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 5); - tree.attach(&mut root, 7); - } - - unsafe { - assert_eq!(tree.get_right(5), 7); - assert_eq!(tree.get_size(5), 2); - assert_eq!(tree.get_size(7), 1); - } - } - - #[test] - fn test_attach_multiple_nodes() { - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - tree.attach(&mut root, 3); - tree.attach(&mut root, 7); - } - - unsafe { - assert!(tree.contains(10, root)); - assert!(tree.contains(5, root)); - assert!(tree.contains(15, root)); - assert!(tree.contains(3, root)); - assert!(tree.contains(7, root)); - assert_eq!(tree.get_size(root), 5); - } - } - - #[test] - fn test_detach_from_single_node_tree() { - let mut tree = TestTree::new(10); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 5); - tree.detach(&mut root, 5); - } - - assert_eq!(root, 0); - } - - #[test] - fn test_detach_leaf_node() { - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - - tree.detach(&mut root, 5); - - assert!(!tree.contains(5, root)); - assert!(tree.contains(10, root)); - assert!(tree.contains(15, root)); - } - } - - #[test] - fn test_detach_node_with_one_child() { - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 3); - - tree.detach(&mut root, 5); - - assert!(!tree.contains(5, root)); - assert!(tree.contains(10, root)); - assert!(tree.contains(3, root)); - } - } - - #[test] - fn test_detach_node_with_two_children() { - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - tree.attach(&mut root, 3); - tree.attach(&mut root, 7); - - tree.detach(&mut root, 5); - - assert!(!tree.contains(5, root)); - assert!(tree.contains(10, root)); - assert!(tree.contains(15, root)); - assert!(tree.contains(3, root)); - assert!(tree.contains(7, root)); - } - } - - #[test] - fn test_detach_root() { - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - - tree.detach(&mut root, 10); - - assert!(!tree.contains(10, root)); - assert!(tree.contains(5, root)); - assert!(tree.contains(15, root)); - assert_ne!(root, 0); - } - } - - #[test] - fn test_attach_and_detach_sequence() { - let mut tree = TestTree::new(30); - let mut root: usize = 0; - - unsafe { - // Build tree - for i in [10, 5, 15, 3, 7, 12, 17] { - tree.attach(&mut root, i); - } - - // Verify all nodes present - for i in [10, 5, 15, 3, 7, 12, 17] { - assert!(tree.contains(i, root), "Node {i} should be present"); - } - - // Remove some nodes - tree.detach(&mut root, 3); - tree.detach(&mut root, 17); - tree.detach(&mut root, 10); - - // Verify correct nodes present - assert!(!tree.contains(3, root)); - assert!(!tree.contains(17, root)); - assert!(!tree.contains(10, root)); - assert!(tree.contains(5, root)); - assert!(tree.contains(15, root)); - assert!(tree.contains(7, root)); - assert!(tree.contains(12, root)); - } - } - - #[test] - fn test_tree_balancing() { - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - // Insert in order that would cause imbalance without rotations - for i in 1..=10 { - tree.attach(&mut root, i); - } - - // All nodes should be reachable - for i in 1..=10 { - assert!(tree.contains(i, root), "Node {i} should be present"); - } - - assert_eq!(tree.get_size(root), 10); - } - } - - #[test] - fn test_detach_all_nodes() { - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - for i in 1..=5 { - tree.attach(&mut root, i); - } - - for i in 1..=5 { - tree.detach(&mut root, i); - } - - assert_eq!(root, 0); - } - } - - // Additional edge case tests for full coverage - - #[test] - fn test_attach_left_right_rotation_edge_case() { - // Tests the path where we need a left-right double rotation on left side - let mut tree = TestTree::new(30); - let mut root: usize = 0; - - unsafe { - // Build a tree that will trigger left-right rotation - // Start with 10, then 5, then insert 8 (between 5 and 10) - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 8); // This goes right of 5, should trigger LR rotation - - assert!(tree.contains(10, root)); - assert!(tree.contains(5, root)); - assert!(tree.contains(8, root)); - assert_eq!(tree.get_size(root), 3); - } - } - - #[test] - fn test_attach_descend_left_without_rotation() { - // Tests the case where we descend left without rotation - let mut tree = TestTree::new(40); - let mut root: usize = 0; - - unsafe { - // Build a more balanced tree first - tree.attach(&mut root, 20); - tree.attach(&mut root, 10); - tree.attach(&mut root, 30); - tree.attach(&mut root, 25); - tree.attach(&mut root, 35); - // Now insert something that needs to go left but doesn't trigger rotation - tree.attach(&mut root, 5); - tree.attach(&mut root, 2); - - assert!(tree.contains(2, root)); - assert_eq!(tree.get_size(root), 7); - } - } - - #[test] - fn test_attach_special_case_left_right_empty() { - // Tests the special case in attach_core where left_right_size == 0 and right_size == 0 - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - // Create specific tree structure - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - // Now insert 7 which is right of 5 but left of 10 - // With just 2 nodes, this triggers the special case - tree.attach(&mut root, 7); - - assert!(tree.contains(10, root)); - assert!(tree.contains(5, root)); - assert!(tree.contains(7, root)); - } - } - - #[test] - fn test_attach_right_left_rotation_edge_case() { - // Tests the path where we need a right-left double rotation on right side - let mut tree = TestTree::new(30); - let mut root: usize = 0; - - unsafe { - // Build a tree that will trigger right-left rotation - tree.attach(&mut root, 10); - tree.attach(&mut root, 20); - tree.attach(&mut root, 15); // Between 10 and 20, should trigger RL rotation - - assert!(tree.contains(10, root)); - assert!(tree.contains(15, root)); - assert!(tree.contains(20, root)); - assert_eq!(tree.get_size(root), 3); - } - } - - #[test] - fn test_attach_descend_right_without_rotation() { - // Tests the case where we descend right without rotation - let mut tree = TestTree::new(40); - let mut root: usize = 0; - - unsafe { - // Build a balanced tree - tree.attach(&mut root, 20); - tree.attach(&mut root, 30); - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - // Insert on the right side, descending - tree.attach(&mut root, 35); - tree.attach(&mut root, 40); - - assert!(tree.contains(40, root)); - assert_eq!(tree.get_size(root), 7); - } - } - - #[test] - fn test_detach_with_double_rotation_left() { - // Tests detach causing double rotation on left side - let mut tree = TestTree::new(40); - let mut root: usize = 0; - - unsafe { - // Build larger tree - for i in [20, 10, 30, 5, 15, 25, 35, 3, 8, 13, 17] { - tree.attach(&mut root, i); - } - - // Detach to trigger specific paths - tree.detach(&mut root, 3); - tree.detach(&mut root, 5); - tree.detach(&mut root, 8); - - assert!(!tree.contains(3, root)); - assert!(!tree.contains(5, root)); - assert!(!tree.contains(8, root)); - } - } - - #[test] - fn test_detach_with_double_rotation_right() { - // Tests detach causing double rotation on right side - let mut tree = TestTree::new(50); - let mut root: usize = 0; - - unsafe { - // Build larger tree - for i in [20, 10, 30, 5, 15, 25, 35, 32, 38, 28, 27] { - tree.attach(&mut root, i); - } - - // Detach to trigger specific paths - tree.detach(&mut root, 35); - tree.detach(&mut root, 38); - tree.detach(&mut root, 32); - - assert!(!tree.contains(35, root)); - assert!(!tree.contains(38, root)); - assert!(!tree.contains(32, root)); - } - } - - #[test] - fn test_detach_with_left_only_child() { - // Tests detach where replacement comes from left subtree - let mut tree = TestTree::new(30); - let mut root: usize = 0; - - unsafe { - // Create tree where left subtree is larger - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - tree.attach(&mut root, 3); - tree.attach(&mut root, 7); - tree.attach(&mut root, 2); - tree.attach(&mut root, 4); - - // Detach a node that will use replacement from left (larger) subtree - tree.detach(&mut root, 5); - - assert!(!tree.contains(5, root)); - assert!(tree.contains(3, root)); - assert!(tree.contains(7, root)); - } - } - - #[test] - fn test_detach_node_with_left_child_only() { - // Tests the path where we set *root = *left - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - tree.attach(&mut root, 3); - - // Remove 5, which has only left child (3) - tree.detach(&mut root, 5); - - assert!(!tree.contains(5, root)); - assert!(tree.contains(3, root)); - } - } - - #[test] - fn test_large_sequential_insert() { - // Tests inserting many nodes in ascending order - let mut tree = TestTree::new(50); - let mut root: usize = 0; - - unsafe { - for i in 1..=20 { - tree.attach(&mut root, i); - } - - for i in 1..=20 { - assert!(tree.contains(i, root), "Node {i} missing"); - } - assert_eq!(tree.get_size(root), 20); - } - } - - #[test] - fn test_large_reverse_insert() { - // Tests inserting many nodes in descending order - let mut tree = TestTree::new(50); - let mut root: usize = 0; - - unsafe { - for i in (1..=20).rev() { - tree.attach(&mut root, i); - } - - for i in 1..=20 { - assert!(tree.contains(i, root), "Node {i} missing"); - } - assert_eq!(tree.get_size(root), 20); - } - } - - #[test] - fn test_alternating_insert() { - // Tests inserting nodes in alternating pattern - let mut tree = TestTree::new(50); - let mut root: usize = 0; - - unsafe { - // Insert 10, 1, 20, 2, 19, 3, 18, etc. - let mut low = 1; - let mut high = 20; - for i in 0..20 { - if i % 2 == 0 { - tree.attach(&mut root, high); - high -= 1; - } else { - tree.attach(&mut root, low); - low += 1; - } - } - - for i in 1..=20 { - assert!(tree.contains(i, root), "Node {i} missing"); - } - } - } - - #[test] - fn test_detach_from_large_tree() { - // Tests various detach patterns on large tree - let mut tree = TestTree::new(100); - let mut root: usize = 0; - - unsafe { - // Build tree with 50 nodes - for i in 1..=50 { - tree.attach(&mut root, i); - } - - // Detach every other node - for i in (1..=50).step_by(2) { - tree.detach(&mut root, i); - } - - // Check remaining nodes - for i in (2..=50).step_by(2) { - assert!(tree.contains(i, root), "Node {i} should be present"); - } - for i in (1..=49).step_by(2) { - assert!(!tree.contains(i, root), "Node {i} should be absent"); - } - } - } - - #[test] - fn test_detach_node_with_right_child_only() { - // Tests the path where we set *root = *right (right_size > 0, left_size == 0) - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - tree.attach(&mut root, 17); - - // Remove 15, which should have only right child (17) after tree balancing - tree.detach(&mut root, 15); - - assert!(!tree.contains(15, root)); - assert!(tree.contains(17, root)); - } - } - - #[test] - fn test_attach_right_special_case_empty() { - // Tests the special case in attach_core where right_left_size == 0 and left_size == 0 - let mut tree = TestTree::new(20); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 20); - // Insert 15 which is left of 20 but right of 10 - // With just 2 nodes, this triggers the right-side special case - tree.attach(&mut root, 15); - - assert!(tree.contains(10, root)); - assert!(tree.contains(15, root)); - assert!(tree.contains(20, root)); - } - } - - #[test] - fn test_detach_replacement_from_right_subtree() { - // Tests detach where replacement comes from right subtree (left_size <= right_size) - let mut tree = TestTree::new(30); - let mut root: usize = 0; - - unsafe { - tree.attach(&mut root, 10); - tree.attach(&mut root, 5); - tree.attach(&mut root, 15); - tree.attach(&mut root, 12); - tree.attach(&mut root, 17); - tree.attach(&mut root, 16); - tree.attach(&mut root, 18); - - // Detach 15 which has both children; right subtree is larger - tree.detach(&mut root, 15); - - assert!(!tree.contains(15, root)); - assert!(tree.contains(12, root)); - assert!(tree.contains(17, root)); - assert!(tree.contains(16, root)); - assert!(tree.contains(18, root)); - } - } - - #[test] - fn test_get_rightest_single_node() { - // Tests get_rightest with a single node (no right child) - let mut tree = TestTree::new(10); - unsafe { - tree.set_size(1, 1); - assert_eq!(tree.get_rightest(1), 1); - } - } - - #[test] - fn test_get_leftest_single_node() { - // Tests get_leftest with a single node (no left child) - let mut tree = TestTree::new(10); - unsafe { - tree.set_size(1, 1); - assert_eq!(tree.get_leftest(1), 1); - } - } - - #[test] - fn test_get_left_size_no_left_child() { - // Tests get_left_size when node has no left child - let mut tree = TestTree::new(10); - unsafe { - tree.set_size(1, 1); - assert_eq!(tree.get_left_size(1), 0); - } - } - - #[test] - fn test_get_right_size_no_right_child() { - // Tests get_right_size when node has no right child - let mut tree = TestTree::new(10); - unsafe { - tree.set_size(1, 1); - assert_eq!(tree.get_right_size(1), 0); - } - } - - #[test] - fn test_fix_size_leaf_node() { - // Tests fix_size on a leaf node (no children) - let mut tree = TestTree::new(10); - unsafe { - tree.fix_size(1); - assert_eq!(tree.get_size(1), 1); // 0 + 0 + 1 - } - } - - #[test] - fn test_detach_reinsert_cycle() { - // Tests detaching all nodes and reinserting them - let mut tree = TestTree::new(30); - let mut root: usize = 0; - - unsafe { - let nodes = [15, 10, 20, 5, 12, 17, 25]; - for &i in &nodes { - tree.attach(&mut root, i); - } - - // Detach all - for &i in &nodes { - tree.detach(&mut root, i); - } - assert_eq!(root, 0); - - // Reinsert all - for &i in &nodes { - tree.attach(&mut root, i); - } - - for &i in &nodes { - assert!( - tree.contains(i, root), - "Node {i} should be present after reinsert" - ); - } - assert_eq!(tree.get_size(root), 7); - } - } - - #[test] - fn test_contains_deep_tree() { - // Tests contains on a deep tree traversal - let mut tree = TestTree::new(30); - let mut root: usize = 0; - - unsafe { - // Build a larger tree - for i in [15, 8, 22, 4, 12, 18, 26, 2, 6, 10, 14, 16, 20, 24, 28] { - tree.attach(&mut root, i); - } - - // Search for nodes at various depths - assert!(tree.contains(2, root)); - assert!(tree.contains(28, root)); - assert!(tree.contains(15, root)); - assert!(!tree.contains(1, root)); - assert!(!tree.contains(30, root)); - assert!(!tree.contains(13, root)); - } - } -} From 7a63995f087dea52d8ae073abc0e46bc0a1f720f Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 29 Mar 2026 19:42:08 +0000 Subject: [PATCH 5/5] Revert "Initial commit with task details" This reverts commit 25950fa116bbc93a8dd0041c092d615648f0303f. --- .gitkeep | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitkeep b/.gitkeep index 29bcf74..cfedde0 100644 --- a/.gitkeep +++ b/.gitkeep @@ -1,2 +1 @@ -# .gitkeep file auto-generated at 2026-03-24T09:13:49.526Z for PR creation at branch issue-8-c1619bfb477b for issue https://github.com/linksplatform/trees-rs/issues/8 -# Updated: 2026-03-29T19:20:23.179Z \ No newline at end of file +# .gitkeep file auto-generated at 2026-03-24T09:13:49.526Z for PR creation at branch issue-8-c1619bfb477b for issue https://github.com/linksplatform/trees-rs/issues/8 \ No newline at end of file