Skip to content

Commit a9d9fae

Browse files
evanlinjinclaude
andcommitted
test(core): add comprehensive skiplist tests
Test index tracking, skip pointer placement, get/floor_at/range performance, and insert operation with index maintenance. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a2a8630 commit a9d9fae

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
use bdk_core::CheckPoint;
2+
use bitcoin::BlockHash;
3+
use bitcoin::hashes::Hash;
4+
5+
#[test]
6+
fn test_skiplist_indices() {
7+
// Create a long chain to test skiplist
8+
let mut cp = CheckPoint::new(0, BlockHash::all_zeros());
9+
assert_eq!(cp.index(), 0);
10+
11+
for height in 1..=500 {
12+
let hash = BlockHash::from_byte_array([height as u8; 32]);
13+
cp = cp.push(height, hash).unwrap();
14+
assert_eq!(cp.index(), height);
15+
}
16+
17+
// Test that skip pointers are set correctly
18+
// At index 100, 200, 300, 400, 500 we should have skip pointers
19+
assert_eq!(cp.index(), 500);
20+
21+
// Navigate to index 400 and check skip pointer
22+
let mut current = cp.clone();
23+
for _ in 0..100 {
24+
current = current.prev().unwrap();
25+
}
26+
assert_eq!(current.index(), 400);
27+
28+
// Check that skip pointer exists at index 400
29+
if let Some(skip) = current.skip() {
30+
assert_eq!(skip.index(), 300);
31+
} else {
32+
panic!("Expected skip pointer at index 400");
33+
}
34+
35+
// Navigate to index 300 and check skip pointer
36+
for _ in 0..100 {
37+
current = current.prev().unwrap();
38+
}
39+
assert_eq!(current.index(), 300);
40+
41+
if let Some(skip) = current.skip() {
42+
assert_eq!(skip.index(), 200);
43+
} else {
44+
panic!("Expected skip pointer at index 300");
45+
}
46+
47+
// Navigate to index 100 and check skip pointer
48+
for _ in 0..200 {
49+
current = current.prev().unwrap();
50+
}
51+
assert_eq!(current.index(), 100);
52+
53+
if let Some(skip) = current.skip() {
54+
assert_eq!(skip.index(), 0);
55+
} else {
56+
panic!("Expected skip pointer at index 100");
57+
}
58+
}
59+
60+
#[test]
61+
fn test_skiplist_get_performance() {
62+
// Create a very long chain
63+
let mut cp = CheckPoint::new(0, BlockHash::all_zeros());
64+
65+
for height in 1..=1000 {
66+
let hash = BlockHash::from_byte_array([(height % 256) as u8; 32]);
67+
cp = cp.push(height, hash).unwrap();
68+
}
69+
70+
// Test that get() can find checkpoints efficiently
71+
// This should use skip pointers to navigate quickly
72+
73+
// Verify the chain was built correctly
74+
assert_eq!(cp.height(), 1000);
75+
assert_eq!(cp.index(), 1000);
76+
77+
// Find checkpoint near the beginning
78+
if let Some(found) = cp.get(50) {
79+
assert_eq!(found.height(), 50);
80+
assert_eq!(found.index(), 50);
81+
} else {
82+
// Debug: print the first few checkpoints
83+
let mut current = cp.clone();
84+
println!("First 10 checkpoints:");
85+
for _ in 0..10 {
86+
println!("Height: {}, Index: {}", current.height(), current.index());
87+
if let Some(prev) = current.prev() {
88+
current = prev;
89+
} else {
90+
break;
91+
}
92+
}
93+
panic!("Could not find checkpoint at height 50");
94+
}
95+
96+
// Find checkpoint in the middle
97+
if let Some(found) = cp.get(500) {
98+
assert_eq!(found.height(), 500);
99+
assert_eq!(found.index(), 500);
100+
} else {
101+
panic!("Could not find checkpoint at height 500");
102+
}
103+
104+
// Find checkpoint near the end
105+
if let Some(found) = cp.get(950) {
106+
assert_eq!(found.height(), 950);
107+
assert_eq!(found.index(), 950);
108+
} else {
109+
panic!("Could not find checkpoint at height 950");
110+
}
111+
112+
// Test non-existent checkpoint
113+
assert!(cp.get(1001).is_none());
114+
}
115+
116+
#[test]
117+
fn test_skiplist_floor_at() {
118+
let mut cp = CheckPoint::new(0, BlockHash::all_zeros());
119+
120+
// Create sparse chain with gaps
121+
for height in [10, 50, 100, 150, 200, 300, 400, 500] {
122+
let hash = BlockHash::from_byte_array([height as u8; 32]);
123+
cp = cp.push(height, hash).unwrap();
124+
}
125+
126+
// Test floor_at with skip pointers
127+
let floor = cp.floor_at(250).unwrap();
128+
assert_eq!(floor.height(), 200);
129+
130+
let floor = cp.floor_at(99).unwrap();
131+
assert_eq!(floor.height(), 50);
132+
133+
let floor = cp.floor_at(500).unwrap();
134+
assert_eq!(floor.height(), 500);
135+
136+
let floor = cp.floor_at(600).unwrap();
137+
assert_eq!(floor.height(), 500);
138+
}
139+
140+
#[test]
141+
fn test_skiplist_insert_maintains_indices() {
142+
let mut cp = CheckPoint::new(0, BlockHash::all_zeros());
143+
144+
// Build initial chain
145+
for height in [10, 20, 30, 40, 50] {
146+
let hash = BlockHash::from_byte_array([height as u8; 32]);
147+
cp = cp.push(height, hash).unwrap();
148+
}
149+
150+
// Insert a block in the middle
151+
let hash = BlockHash::from_byte_array([25; 32]);
152+
cp = cp.insert(25, hash);
153+
154+
// Check that indices are maintained correctly
155+
let check = cp.get(50).unwrap();
156+
assert_eq!(check.index(), 6); // 0, 10, 20, 25, 30, 40, 50
157+
158+
let check = cp.get(25).unwrap();
159+
assert_eq!(check.index(), 3);
160+
161+
// Check the full chain has correct indices
162+
let mut current = cp.clone();
163+
let expected_heights = vec![50, 40, 30, 25, 20, 10, 0];
164+
let expected_indices = vec![6, 5, 4, 3, 2, 1, 0];
165+
166+
for (expected_height, expected_index) in expected_heights.iter().zip(expected_indices.iter()) {
167+
assert_eq!(current.height(), *expected_height);
168+
assert_eq!(current.index(), *expected_index);
169+
if *expected_height > 0 {
170+
current = current.prev().unwrap();
171+
}
172+
}
173+
}
174+
175+
#[test]
176+
fn test_skiplist_range_uses_skip_pointers() {
177+
let mut cp = CheckPoint::new(0, BlockHash::all_zeros());
178+
179+
// Create a chain with 500 checkpoints
180+
for height in 1..=500 {
181+
let hash = BlockHash::from_byte_array([(height % 256) as u8; 32]);
182+
cp = cp.push(height, hash).unwrap();
183+
}
184+
185+
// Test range iteration
186+
let range_items: Vec<_> = cp.range(100..=200).collect();
187+
assert_eq!(range_items.len(), 101);
188+
assert_eq!(range_items.first().unwrap().height(), 200);
189+
assert_eq!(range_items.last().unwrap().height(), 100);
190+
191+
// Test open range
192+
let range_items: Vec<_> = cp.range(450..).collect();
193+
assert_eq!(range_items.len(), 51);
194+
assert_eq!(range_items.first().unwrap().height(), 500);
195+
assert_eq!(range_items.last().unwrap().height(), 450);
196+
}

0 commit comments

Comments
 (0)