use std::ops::Bound;
#[cfg(feature = "var-collections")]
use armdb::VarTree;
use armdb::{Config, ConstTree};
use tempfile::tempdir;
fn populate_const(tree: &ConstTree<[u8; 8], 8>, n: u64) {
for i in 0..n {
let key = i.to_be_bytes();
let value = (i * 10).to_be_bytes();
tree.put(&key, &value).unwrap();
}
}
#[cfg(feature = "var-collections")]
fn populate_var(tree: &VarTree<[u8; 8]>, n: u64) {
for i in 0..n {
let key = i.to_be_bytes();
let value = format!("val_{i}");
tree.put(&key, value.as_bytes()).unwrap();
}
}
#[test]
fn test_const_iter_all() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree.iter().collect();
assert_eq!(entries.len(), 20);
for i in 0..20u64 {
let expected = 19 - i;
assert_eq!(entries[i as usize].0, expected.to_be_bytes());
assert_eq!(entries[i as usize].1, (expected * 10).to_be_bytes());
}
}
#[test]
fn test_const_iter_empty() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
assert_eq!(tree.iter().count(), 0);
assert!(tree.iter().next().is_none());
assert!(tree.iter().next_back().is_none());
}
#[test]
fn test_const_iter_single_element() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
tree.put(&1u64.to_be_bytes(), &10u64.to_be_bytes()).unwrap();
let mut iter = tree.iter();
assert!(iter.next().is_some());
assert!(iter.next().is_none());
let mut iter = tree.iter();
assert!(iter.next_back().is_some());
assert!(iter.next_back().is_none());
let mut iter = tree.iter();
let item = iter.next().unwrap();
assert_eq!(item.0, 1u64.to_be_bytes());
assert!(iter.next_back().is_none()); }
#[test]
fn test_const_range() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let start = 5u64.to_be_bytes();
let end = 10u64.to_be_bytes();
let entries: Vec<_> = tree.range(&start, &end).collect();
assert_eq!(entries.len(), 5);
for (i, (key, _)) in entries.iter().enumerate() {
assert_eq!(*key, (9 - i as u64).to_be_bytes());
}
}
#[test]
fn test_const_range_empty() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 10);
let key = 5u64.to_be_bytes();
assert_eq!(tree.range(&key, &key).count(), 0);
let start = 100u64.to_be_bytes();
let end = 200u64.to_be_bytes();
assert_eq!(tree.range(&start, &end).count(), 0);
}
#[test]
fn test_const_prefix_iter() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
for (group, id) in [(1u32, 1u32), (1, 2), (1, 3), (2, 1), (2, 2)] {
let mut key = [0u8; 8];
key[..4].copy_from_slice(&group.to_be_bytes());
key[4..].copy_from_slice(&id.to_be_bytes());
tree.put(&key, &[0u8; 8]).unwrap();
}
let group1: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).collect();
assert_eq!(group1.len(), 3);
let ids: Vec<u32> = group1
.iter()
.map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
.collect();
assert_eq!(ids, vec![3, 2, 1]);
let group2: Vec<_> = tree.prefix_iter(&2u32.to_be_bytes()).collect();
assert_eq!(group2.len(), 2);
let group3: Vec<_> = tree.prefix_iter(&3u32.to_be_bytes()).collect();
assert_eq!(group3.len(), 0);
}
#[test]
fn test_const_prefix_take() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
for id in 1..=100u32 {
let mut key = [0u8; 8];
key[..4].copy_from_slice(&1u32.to_be_bytes());
key[4..].copy_from_slice(&id.to_be_bytes());
tree.put(&key, &[0u8; 8]).unwrap();
}
let latest: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).take(5).collect();
assert_eq!(latest.len(), 5);
let ids: Vec<u32> = latest
.iter()
.map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
.collect();
assert_eq!(ids, vec![100, 99, 98, 97, 96]);
}
#[test]
fn test_const_iter_rev() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 10);
let entries: Vec<_> = tree.iter().rev().collect();
assert_eq!(entries.len(), 10);
for i in 0..10u64 {
assert_eq!(entries[i as usize].0, i.to_be_bytes());
}
}
#[test]
fn test_const_iter_next_back() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 5);
let mut iter = tree.iter();
let (k, _) = iter.next().unwrap();
assert_eq!(k, 4u64.to_be_bytes());
let (k, _) = iter.next_back().unwrap();
assert_eq!(k, 0u64.to_be_bytes());
let (k, _) = iter.next().unwrap();
assert_eq!(k, 3u64.to_be_bytes());
let (k, _) = iter.next_back().unwrap();
assert_eq!(k, 1u64.to_be_bytes());
let (k, _) = iter.next().unwrap();
assert_eq!(k, 2u64.to_be_bytes());
assert!(iter.next().is_none());
assert!(iter.next_back().is_none());
}
#[test]
fn test_const_range_rev() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let start = 5u64.to_be_bytes();
let end = 10u64.to_be_bytes();
let entries: Vec<_> = tree.range(&start, &end).rev().collect();
assert_eq!(entries.len(), 5);
for (i, (key, _)) in entries.iter().enumerate() {
assert_eq!(*key, (i as u64 + 5).to_be_bytes());
}
}
#[test]
fn test_const_prefix_iter_rev() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
for (group, id) in [(1u32, 1u32), (1, 2), (1, 3)] {
let mut key = [0u8; 8];
key[..4].copy_from_slice(&group.to_be_bytes());
key[4..].copy_from_slice(&id.to_be_bytes());
let value = id.to_be_bytes();
let mut val8 = [0u8; 8];
val8[4..].copy_from_slice(&value);
tree.put(&key, &val8).unwrap();
}
let entries: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).rev().collect();
assert_eq!(entries.len(), 3);
let ids: Vec<u32> = entries
.iter()
.map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
.collect();
assert_eq!(ids, vec![1, 2, 3]);
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_iter_all() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
populate_var(&tree, 20);
let entries: Vec<_> = tree.iter().collect();
assert_eq!(entries.len(), 20);
for i in 0..20u64 {
let expected = 19 - i;
assert_eq!(entries[i as usize].0, expected.to_be_bytes());
assert_eq!(
entries[i as usize].1.as_bytes(),
format!("val_{expected}").as_bytes()
);
}
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_iter_empty() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
assert_eq!(tree.iter().count(), 0);
assert!(tree.iter().next_back().is_none());
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_range() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
populate_var(&tree, 20);
let start = 5u64.to_be_bytes();
let end = 10u64.to_be_bytes();
let entries: Vec<_> = tree.range(&start, &end).collect();
assert_eq!(entries.len(), 5);
for (i, (key, _)) in entries.iter().enumerate() {
assert_eq!(*key, (9 - i as u64).to_be_bytes());
}
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_prefix_take() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
for id in 1..=50u32 {
let mut key = [0u8; 8];
key[..4].copy_from_slice(&1u32.to_be_bytes());
key[4..].copy_from_slice(&id.to_be_bytes());
tree.put(&key, format!("msg_{id}").as_bytes()).unwrap();
}
let latest: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).take(5).collect();
assert_eq!(latest.len(), 5);
let ids: Vec<u32> = latest
.iter()
.map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
.collect();
assert_eq!(ids, vec![50, 49, 48, 47, 46]);
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_iter_rev() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
populate_var(&tree, 10);
let entries: Vec<_> = tree.iter().rev().collect();
assert_eq!(entries.len(), 10);
for i in 0..10u64 {
assert_eq!(entries[i as usize].0, i.to_be_bytes());
}
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_iter_next_back() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
populate_var(&tree, 5);
let mut iter = tree.iter();
let (k, _) = iter.next().unwrap();
assert_eq!(k, 4u64.to_be_bytes());
let (k, _) = iter.next_back().unwrap();
assert_eq!(k, 0u64.to_be_bytes());
let (k, _) = iter.next().unwrap();
assert_eq!(k, 3u64.to_be_bytes());
let (k, _) = iter.next_back().unwrap();
assert_eq!(k, 1u64.to_be_bytes());
let (k, _) = iter.next().unwrap();
assert_eq!(k, 2u64.to_be_bytes());
assert!(iter.next().is_none());
assert!(iter.next_back().is_none());
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_range_rev() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
populate_var(&tree, 20);
let start = 5u64.to_be_bytes();
let end = 10u64.to_be_bytes();
let entries: Vec<_> = tree.range(&start, &end).rev().collect();
assert_eq!(entries.len(), 5);
for (i, (key, _)) in entries.iter().enumerate() {
assert_eq!(*key, (i as u64 + 5).to_be_bytes());
}
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_prefix_iter_rev() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
for (group, id) in [(1u32, 1u32), (1, 2), (1, 3)] {
let mut key = [0u8; 8];
key[..4].copy_from_slice(&group.to_be_bytes());
key[4..].copy_from_slice(&id.to_be_bytes());
tree.put(&key, format!("msg_{id}").as_bytes()).unwrap();
}
let entries: Vec<_> = tree.prefix_iter(&1u32.to_be_bytes()).rev().collect();
assert_eq!(entries.len(), 3);
let ids: Vec<u32> = entries
.iter()
.map(|(k, _)| u32::from_be_bytes(k[4..].try_into().unwrap()))
.collect();
assert_eq!(ids, vec![1, 2, 3]);
}
fn ascending() -> Config {
let mut c = Config::test();
c.reversed = false;
c
}
#[test]
fn test_const_ascending_iter() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
populate_const(&tree, 10);
let entries: Vec<_> = tree.iter().collect();
assert_eq!(entries.len(), 10);
for i in 0..10u64 {
assert_eq!(entries[i as usize].0, i.to_be_bytes());
}
let rev: Vec<_> = tree.iter().rev().collect();
assert_eq!(rev[0].0, 9u64.to_be_bytes());
assert_eq!(rev[9].0, 0u64.to_be_bytes());
}
#[test]
fn test_const_ascending_range() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
populate_const(&tree, 20);
let start = 5u64.to_be_bytes();
let end = 10u64.to_be_bytes();
let entries: Vec<_> = tree.range(&start, &end).collect();
assert_eq!(entries.len(), 5);
for (i, (key, _)) in entries.iter().enumerate() {
assert_eq!(*key, (i as u64 + 5).to_be_bytes());
}
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_ascending_iter() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), ascending()).unwrap();
populate_var(&tree, 10);
let entries: Vec<_> = tree.iter().collect();
assert_eq!(entries.len(), 10);
for i in 0..10u64 {
assert_eq!(entries[i as usize].0, i.to_be_bytes());
}
let rev: Vec<_> = tree.iter().rev().collect();
assert_eq!(rev[0].0, 9u64.to_be_bytes());
assert_eq!(rev[9].0, 0u64.to_be_bytes());
}
fn key(v: u64) -> [u8; 8] {
v.to_be_bytes()
}
fn keys_from(entries: &[([u8; 8], [u8; 8])]) -> Vec<u64> {
entries
.iter()
.map(|(k, _)| u64::from_be_bytes(*k))
.collect()
}
#[test]
fn test_const_range_bounds_included_included() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Included(&key(5)), Bound::Included(&key(10)))
.collect();
assert_eq!(keys_from(&entries), vec![10, 9, 8, 7, 6, 5]);
}
#[test]
fn test_const_range_bounds_excluded_excluded() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Excluded(&key(5)), Bound::Excluded(&key(10)))
.collect();
assert_eq!(keys_from(&entries), vec![9, 8, 7, 6]);
}
#[test]
fn test_const_range_bounds_excluded_included() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Excluded(&key(5)), Bound::Included(&key(10)))
.collect();
assert_eq!(keys_from(&entries), vec![10, 9, 8, 7, 6]);
}
#[test]
fn test_const_range_bounds_included_excluded() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Included(&key(5)), Bound::Excluded(&key(10)))
.collect();
let via_range: Vec<_> = tree.range(&key(5), &key(10)).collect();
assert_eq!(keys_from(&entries), vec![9, 8, 7, 6, 5]);
assert_eq!(entries, via_range);
}
#[test]
fn test_const_range_bounds_unbounded_start() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Unbounded, Bound::Excluded(&key(5)))
.collect();
assert_eq!(keys_from(&entries), vec![4, 3, 2, 1, 0]);
}
#[test]
fn test_const_range_bounds_unbounded_end() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Included(&key(15)), Bound::Unbounded)
.collect();
assert_eq!(keys_from(&entries), vec![19, 18, 17, 16, 15]);
}
#[test]
fn test_const_range_bounds_both_unbounded() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let via_bounds: Vec<_> = tree
.range_bounds(Bound::Unbounded, Bound::Unbounded)
.collect();
let via_iter: Vec<_> = tree.iter().collect();
assert_eq!(via_bounds, via_iter);
assert_eq!(via_bounds.len(), 20);
}
#[test]
fn test_const_range_bounds_rev() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Included(&key(5)), Bound::Included(&key(10)))
.rev()
.collect();
assert_eq!(keys_from(&entries), vec![5, 6, 7, 8, 9, 10]);
}
#[test]
fn test_const_range_bounds_single_element() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), Config::test()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Included(&key(5)), Bound::Included(&key(5)))
.collect();
assert_eq!(keys_from(&entries), vec![5]);
let entries: Vec<_> = tree
.range_bounds(Bound::Excluded(&key(5)), Bound::Excluded(&key(5)))
.collect();
assert!(entries.is_empty());
}
#[test]
fn test_const_ascending_range_bounds_included_included() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Included(&key(5)), Bound::Included(&key(10)))
.collect();
assert_eq!(keys_from(&entries), vec![5, 6, 7, 8, 9, 10]);
}
#[test]
fn test_const_ascending_range_bounds_excluded_excluded() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Excluded(&key(5)), Bound::Excluded(&key(10)))
.collect();
assert_eq!(keys_from(&entries), vec![6, 7, 8, 9]);
}
#[test]
fn test_const_ascending_range_bounds_unbounded() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
populate_const(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Unbounded, Bound::Included(&key(3)))
.collect();
assert_eq!(keys_from(&entries), vec![0, 1, 2, 3]);
let entries: Vec<_> = tree
.range_bounds(Bound::Excluded(&key(16)), Bound::Unbounded)
.collect();
assert_eq!(keys_from(&entries), vec![17, 18, 19]);
}
#[test]
fn test_const_ascending_range_bounds_matches_range() {
let dir = tempdir().unwrap();
let tree = ConstTree::<[u8; 8], 8>::open(dir.path(), ascending()).unwrap();
populate_const(&tree, 20);
let via_bounds: Vec<_> = tree
.range_bounds(Bound::Included(&key(5)), Bound::Excluded(&key(10)))
.collect();
let via_range: Vec<_> = tree.range(&key(5), &key(10)).collect();
assert_eq!(via_bounds, via_range);
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_range_bounds_included_included() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), Config::test()).unwrap();
populate_var(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Included(&key(5)), Bound::Included(&key(10)))
.collect();
let keys: Vec<u64> = entries
.iter()
.map(|(k, _)| u64::from_be_bytes(*k))
.collect();
assert_eq!(keys, vec![10, 9, 8, 7, 6, 5]);
}
#[cfg(feature = "var-collections")]
#[test]
fn test_var_ascending_range_bounds_excluded_excluded() {
let dir = tempdir().unwrap();
let tree = VarTree::<[u8; 8]>::open(dir.path(), ascending()).unwrap();
populate_var(&tree, 20);
let entries: Vec<_> = tree
.range_bounds(Bound::Excluded(&key(5)), Bound::Excluded(&key(10)))
.collect();
let keys: Vec<u64> = entries
.iter()
.map(|(k, _)| u64::from_be_bytes(*k))
.collect();
assert_eq!(keys, vec![6, 7, 8, 9]);
}