use super::super::*;
use super::*;
use captains_log::{log_println, logfn};
use rstest::rstest;
use std::fmt::Debug;
use std::println;
use std::vec::Vec;
fn _test_delete_all_seq<S: Stats, F>(
mut map: BTree<CounterI32, CounterI32, S>, count: u32, height: u8, print_f: F,
) where
F: Fn(&BTree<CounterI32, CounterI32, S>),
{
reset_alive_count();
assert_eq!(alive_count(), 0);
for i in 0..count {
map.insert((i as i32).into(), (i as i32 * 10).into());
map.validate();
}
print_f(&map);
assert_eq!(height, map.height());
let alive_after_insert = alive_count();
println!("alive_after_insert: {}", alive_after_insert);
#[cfg(feature = "trace_log")]
map.inner.print_trigger_flags();
for i in 0..count {
let v = map.remove(&(i as i32));
assert!(v.is_some(), "failed to remove {}", i);
assert_eq!(*v.unwrap(), i as i32 * 10); map.validate();
}
assert_eq!(map.height(), 1);
let alive_after_remove = alive_count();
println!("alive_after_remove: {}", alive_after_remove);
assert_eq!(alive_after_remove, 0); #[cfg(feature = "trace_log")]
map.inner.print_trigger_flags();
drop(map);
assert_eq!(alive_count(), 0);
}
#[logfn]
#[rstest]
#[case(100, 2)]
#[case(1000, 3)]
#[case(10000, 3)]
fn test_large_delete_all_seq(setup_log: (), #[case] count: u32, #[case] height: u8) {
#[cfg(miri)]
{
if count > 100 {
println!("skip big test for miri");
return;
}
}
let map = crate::large::BTreeMap::<CounterI32, CounterI32>::new();
_test_delete_all_seq(map, count, height, |_map| {
println!("leaf_count: {}", _map.leaf_count());
println!("fill_ratio: {:.2}", _map.get_fill_ratio());
println!("height: {}", _map.height());
});
}
#[logfn]
#[rstest]
#[case(100, 2)]
#[case(1000, 3)]
#[case(10000, 3)]
fn test_compact_delete_all_seq(setup_log: (), #[case] count: u32, #[case] height: u8) {
#[cfg(miri)]
{
if count > 100 {
println!("skip big test for miri");
return;
}
}
let map = crate::compact::BTreeMap::<CounterI32, CounterI32>::new();
_test_delete_all_seq(map, count, height, |_map| {
println!("height: {}", _map.height());
});
}
#[logfn]
#[cfg(not(miri))]
#[rstest]
#[case(100, 10, true)]
#[case(100, 10, false)]
#[case(500, 5, true)]
#[case(500, 5, false)]
#[case(1000, 10, true)]
#[case(1000, 10, false)]
#[case(10000, 3, true)]
#[case(10000, 3, false)]
fn test_large_mixed_random_batch_insert_delete(
setup_log: (), #[case] batch_size: usize, #[case] iterations: usize, #[case] use_entry: bool,
) {
reset_alive_count();
assert_eq!(alive_count(), 0);
let gen_data = |_rng: &mut fastrand::Rng| {
let k = _rng.i32(..);
let v = k.wrapping_mul(10);
(CounterI32::from(k), CounterI32::from(v))
};
{
println!("test large");
let mut map = crate::large::BTreeMap::<CounterI32, CounterI32>::new();
_test_large_mixed_random_batch_insert_delete(
batch_size,
iterations,
&mut map,
|_map: &crate::large::BTreeMap<CounterI32, CounterI32>| {
println!("len: {}", _map.len());
println!("leaf_count: {}", _map.leaf_count());
println!("fill_ratio: {:.2}", _map.get_fill_ratio());
println!("height: {}", _map.height());
},
&gen_data,
use_entry,
);
assert_eq!(map.len(), 0, "Map should be empty after deleting all elements");
assert_eq!(map.height(), 1, "Height should be 1 for empty tree");
assert_eq!(map.leaf_count(), 1);
}
assert_eq!(alive_count(), 0, "All CounterI32 should be dropped");
{
println!("test compact");
let mut map = crate::compact::BTreeMap::<CounterI32, CounterI32>::new();
_test_large_mixed_random_batch_insert_delete(
batch_size,
iterations,
&mut map,
|_map: &crate::compact::BTreeMap<CounterI32, CounterI32>| {
println!("len: {}", _map.len());
println!("height: {}", _map.height());
},
&gen_data,
use_entry,
);
assert_eq!(map.len(), 0, "Map should be empty after deleting all elements");
assert_eq!(map.height(), 1, "Height should be 1 for empty tree");
}
assert_eq!(alive_count(), 0, "All CounterI32 should be dropped");
}
fn _test_large_mixed_random_batch_insert_delete<K: Key + Debug, V: Key + Debug, S: Stats, F, FR>(
batch_size: usize, iterations: usize, map: &mut BTree<K, V, S>, print_func: F, randf: FR,
use_entry: bool,
) where
F: Fn(&BTree<K, V, S>),
FR: Fn(&mut fastrand::Rng) -> (K, V),
{
reset_alive_count();
assert_eq!(alive_count(), 0);
let seed: u64 = match std::env::var("TEST_SEED") {
Ok(val) => val.parse().expect("TEST_SEED must be a valid u64"),
Err(_) => fastrand::u64(..),
};
println!(
"=== Test Parameters === seed: {}, batch_size: {}, iterations: {} ===",
seed, batch_size, iterations
);
macro_rules! insert {
($archive: expr, $k: expr, $v: expr) => {
crate::trace_log!("check contain {:?}", $k);
if map.contains_key(&$k) {
continue;
} else {
crate::trace_log!("insert {:?} {}th", $k, map.len());
$archive.push(($k.clone(), $v.clone()));
if use_entry {
map.entry($k).or_insert($v);
} else {
map.insert($k, $v);
}
}
};
}
macro_rules! remove {
($k: expr, $v: expr) => {
let v = if !use_entry {
if let Entry::Occupied(ent) = map.entry($k.clone()) {
Some(ent.remove())
} else {
None
}
} else {
map.remove($k)
};
map.validate();
assert!(v.is_some(), "Key {:?} from prev_batch should exist", $k);
assert_eq!(&v.unwrap(), $v);
};
}
let mut rng = fastrand::Rng::with_seed(seed);
let mut prev_batch: Vec<(K, V)> = Vec::with_capacity(batch_size);
while prev_batch.len() < batch_size {
let (key, value) = randf(&mut rng);
insert!(prev_batch, key, value);
}
println!("---");
print_func(map);
map.validate();
println!("After first batch: height={}, len={}", map.height(), map.len());
for iter in 1..iterations {
let mut current_batch: Vec<(K, V)> = Vec::with_capacity(batch_size);
while current_batch.len() < batch_size {
let (key, value) = randf(&mut rng);
insert!(current_batch, key, value);
}
map.validate();
println!("---iteration {iter}: insert ---");
print_func(map);
for (_i, (key, _val)) in prev_batch.iter().enumerate() {
crate::trace_log!("remove {key:?} {_i}th");
remove!(key, _val);
}
map.validate();
println!("---iteration {iter}: removed ---");
print_func(map);
#[cfg(feature = "trace_log")]
map.inner.print_trigger_flags();
prev_batch = current_batch;
}
let mut height = map.height();
for (key, _) in &prev_batch {
if !map.contains_key(key) {
map.dump();
map.validate();
panic!("error: Remaining key {key:?} should be accessible");
}
}
for (key, val) in &prev_batch {
remove!(key, val);
if height != map.height() {
height = map.height();
#[cfg(feature = "trace_log")]
map.inner.print_trigger_flags();
println!("tree height dec to {}, len {}", height, map.len());
}
}
map.validate();
assert_eq!(map.len(), 0, "Map should be empty after deleting all elements");
assert_eq!(map.height(), 1, "Height should be 1 for empty tree");
}
#[cfg(not(miri))]
#[logfn]
#[rstest]
#[case(500, 20)]
#[case(1000, 20)]
#[case(10000, 5)]
fn test_large_mix_remove_range_random(
setup_log: (), #[case] count: usize, #[case] iterations: usize,
) {
let seed: u64 = match std::env::var("TEST_SEED") {
Ok(val) => val.parse().expect("TEST_SEED must be a valid u64"),
Err(_) => fastrand::u64(..),
};
fn run_test<S: Stats>(
mut map: BTree<CounterI32, CounterI32, S>, count: usize, iterations: usize, seed: u64,
) {
reset_alive_count();
println!("=== test_mix_remove_range_random seed: {} ===", seed);
let mut rng = fastrand::Rng::with_seed(seed);
for i in 0..iterations {
let mut inserted = 0;
for _ in 0..count {
let k = rng.i32(0..20000);
if !map.contains_key(&k) {
trace_log!("insert {k:?}");
map.insert(k.into(), (k * 10).into());
inserted += 1;
}
}
map.validate();
log_println!(
"Iter {}: Inserted {} elements, len: {}, height: {}",
i,
inserted,
map.len(),
map.height()
);
if map.len() > 0 {
let mut k1 = rng.i32(0..20000);
let mut k2 = rng.i32(0..20000);
if k1 > k2 {
std::mem::swap(&mut k1, &mut k2);
}
let range = CounterI32::from(k1)..=CounterI32::from(k2);
log_println!("Removing range [{}..={}]", k1, k2);
map.remove_range(range);
map.validate();
for k in k1..=k2 {
assert!(!map.contains_key(&k), "Key {} should be removed", k);
}
}
}
println!("Final clear all, current len: {}", map.len());
map.remove_range(..);
map.validate();
assert_eq!(map.len(), 0);
assert_eq!(map.height(), 1);
assert_eq!(alive_count(), 0, "Memory leak detected after remove_range");
}
println!("-- test large ---");
let map = crate::large::BTreeMap::<CounterI32, CounterI32>::new();
run_test(map, count, iterations, seed);
println!("-- test compact ---");
let map = crate::compact::BTreeMap::<CounterI32, CounterI32>::new();
run_test(map, count, iterations, seed);
}