use super::*;
#[test]
fn small_entries_fit_under_capacity() {
let mut c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(100, |v: &Vec<u8>| v.len());
let ev1 = c.put(1, vec![0u8; 10]);
let ev2 = c.put(2, vec![0u8; 20]);
assert!(ev1.is_empty());
assert!(ev2.is_empty());
assert_eq!(c.used_bytes(), 30);
assert_eq!(c.len(), 2);
}
#[test]
fn evicts_to_make_room() {
let mut c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(50, |v: &Vec<u8>| v.len());
c.put(1, vec![0u8; 30]);
c.put(2, vec![0u8; 10]);
let ev = c.put(3, vec![0u8; 30]);
assert!(
!ev.is_empty(),
"should have evicted to fit 30B + 10B + 30B into 50B"
);
assert!(c.used_bytes() <= 50, "used={}", c.used_bytes());
}
#[test]
fn entry_larger_than_capacity_is_rejected() {
let mut c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(10, |v: &Vec<u8>| v.len());
let ev = c.put(1, vec![0u8; 100]);
assert_eq!(ev.len(), 1);
assert_eq!(ev[0].0, 1);
assert_eq!(c.used_bytes(), 0, "rejected entry must not be counted");
assert!(c.get(&1).is_none());
}
#[test]
fn update_in_place_adjusts_used_bytes() {
let mut c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(100, |v: &Vec<u8>| v.len());
c.put(1, vec![0u8; 10]);
c.put(1, vec![0u8; 40]);
assert_eq!(c.used_bytes(), 40);
assert_eq!(c.len(), 1);
assert_eq!(c.get(&1).unwrap().len(), 40);
}
#[test]
fn update_bloating_evicts_others() {
let mut c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(60, |v: &Vec<u8>| v.len());
c.put(1, vec![0u8; 10]);
c.put(2, vec![0u8; 10]);
c.put(3, vec![0u8; 10]);
let ev = c.put(1, vec![0u8; 50]);
assert!(!ev.is_empty());
assert!(c.used_bytes() <= 60);
assert!(c.get(&1).is_some());
}
#[test]
fn touched_entry_survives_sweep() {
let mut c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(40, |v: &Vec<u8>| v.len());
c.put(1, vec![0u8; 10]);
c.put(2, vec![0u8; 10]);
c.put(3, vec![0u8; 10]);
c.put(4, vec![0u8; 10]);
let _ = c.put(5, vec![0u8; 10]); if c.get(&2).is_some() {
let _ = c.put(6, vec![0u8; 10]);
assert!(
c.get(&2).is_some(),
"touched key 2 should survive the next sweep"
);
}
}
#[test]
fn capacity_bytes_floor_is_one() {
let c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(0, |v: &Vec<u8>| v.len());
assert_eq!(c.capacity_bytes(), 1);
}
#[test]
fn is_empty_reflects_contents() {
let mut c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(100, |v: &Vec<u8>| v.len());
assert!(c.is_empty());
c.put(1, vec![0u8; 10]);
assert!(!c.is_empty());
}
#[test]
fn eviction_then_insert_reuses_freed_slot() {
let mut c: WeightedCache<u32, Vec<u8>> =
WeightedCache::with_capacity_bytes(20, |v: &Vec<u8>| v.len());
c.put(1, vec![0u8; 10]);
c.put(2, vec![0u8; 10]);
let ev = c.put(3, vec![0u8; 10]);
assert!(!ev.is_empty(), "third insert should have evicted to fit");
assert!(c.used_bytes() <= 20);
let _ = c.put(4, vec![0u8; 10]);
assert!(c.used_bytes() <= 20);
assert!(c.len() <= 2);
}
#[test]
fn sweep_evict_excluding_edge_cases() {
let mut c: WeightedCache<u32, u32> = WeightedCache::with_capacity_bytes(100, |_v: &u32| 10);
assert!(c.sweep_evict_excluding(NIL).is_none());
c.put(1, 1);
c.put(2, 2);
c.put(3, 3);
let id1 = *c.index.get(&1).unwrap();
let first = c.sweep_evict_excluding(NIL);
assert!(first.is_some(), "an unreferenced resident should evict");
let second = c.sweep_evict_excluding(NIL);
assert!(second.is_some());
while c.len() > 1 {
let _ = c.sweep_evict_excluding(NIL);
}
if !c.is_empty() {
let only = *c.index.keys().next().unwrap();
let only_id = *c.index.get(&only).unwrap();
let _ = id1;
assert!(
c.sweep_evict_excluding(only_id).is_none(),
"excluding the sole resident leaves nothing to evict"
);
}
}