use super::*;
#[test]
fn direct_ifds_bounded_cache_evicts_least_recently_used_entry() {
let mut batch = super::DirectResidentIfdsBatch::<u64>::with_max_entries(2);
let dispatch = FreeOnlyDispatch {
freed: RefCell::new(Vec::new()),
};
let cold = key(1);
let hot = key(2);
batch.entries.insert(
cold.clone(),
DirectIfdsEntry {
retained_bytes: 24,
last_seen: 0,
graph: resident(10),
},
);
batch.entries.insert(
hot.clone(),
DirectIfdsEntry {
retained_bytes: 24,
last_seen: 0,
graph: resident(20),
},
);
batch.retained_bytes = 48;
batch.touch_key(&cold).expect("cold touch should record");
batch.touch_key(&hot).expect("hot touch should record");
batch
.touch_key(&cold)
.expect("second cold touch should record");
batch
.evict_until_room(&dispatch, 24)
.expect("bounded direct IFDS cache must evict one resident entry");
assert!(
batch.entries.contains_key(&cold),
"recently touched direct IFDS graph must remain resident"
);
assert!(
!batch.entries.contains_key(&hot),
"least recently used direct IFDS graph must be evicted"
);
assert_eq!(
dispatch.freed.borrow().as_slice(),
&[20, 21, 22, 23, 24],
"direct IFDS eviction must free every resident CSR buffer for the LRU graph"
);
assert_eq!(batch.evictions, 1);
assert_eq!(batch.stats().retained_bytes, 24);
assert_eq!(batch.stats().direct_prepare_bytes, 0);
}
#[test]
fn direct_ifds_lru_heap_compacts_hot_touches() {
let mut batch = super::DirectResidentIfdsBatch::<u64>::with_max_entries(2);
let dispatch = FreeOnlyDispatch {
freed: RefCell::new(Vec::new()),
};
let cached = key(7);
batch.entries.insert(
cached.clone(),
DirectIfdsEntry {
retained_bytes: 24,
last_seen: 0,
graph: resident(70),
},
);
batch.retained_bytes = 24;
for _ in 0..160 {
batch
.touch_key(&cached)
.expect("cached touch should record and compact");
}
assert_eq!(batch.entries.len(), 1);
let stale_limit = batch
.entries
.len()
.checked_mul(4)
.and_then(|value| value.checked_add(32))
.expect("test direct IFDS stale LRU limit must fit usize");
assert!(
batch.lru_len_for_tests() <= stale_limit,
"Fix: direct resident IFDS LRU metadata must compact stale hot-hit records instead of growing with every access."
);
batch
.free_all(&dispatch)
.expect("direct resident IFDS cache must free retained CSR resources");
assert_eq!(batch.lru_len_for_tests(), 0);
}
#[test]
fn direct_ifds_cache_accounting_uses_checked_arithmetic() {
let source = include_str!("../cache.rs");
for forbidden in [
"retained_bytes.saturating_add",
"retained_bytes.saturating_sub",
"direct_prepare_bytes\n .saturating_add",
"self.lru.len() <= self.entries.len().saturating_mul(4).saturating_add(32)",
"BinaryHeap::with_capacity(self.entries.len())",
] {
assert!(
!source.contains(forbidden),
"Fix: direct resident IFDS cache accounting must use checked arithmetic, not saturating math that hides budget/accounting corruption: {forbidden}"
);
}
}