use super::BoundedLruCache;
fn cache<K, V>(cap: usize) -> BoundedLruCache<K, V>
where
K: Clone + Eq + std::hash::Hash,
{
BoundedLruCache::with_capacity_at_least_one(cap)
}
#[test]
fn clamped_constructor_uses_minimum_nonzero_capacity() {
let mut cache = BoundedLruCache::with_capacity_at_least_one(0);
assert_eq!(cache.cap().get(), 1);
assert_eq!(cache.push("a", 1), None);
assert_eq!(cache.push("b", 2), Some(("a", 1)));
assert_eq!(cache.get(&"b"), Some(&2));
}
#[test]
fn get_promotes_entry_and_push_evicts_lru() {
let mut cache = cache(2);
assert_eq!(cache.push("a", 1), None);
assert_eq!(cache.push("b", 2), None);
assert_eq!(cache.get(&"a"), Some(&1));
assert_eq!(cache.push("c", 3), Some(("b", 2)));
assert!(cache.contains(&"a"));
assert!(cache.contains(&"c"));
assert!(!cache.contains(&"b"));
}
#[test]
fn push_existing_replaces_value_and_keeps_capacity() {
let mut cache = cache(2);
cache.push("a", 1);
cache.push("b", 2);
assert_eq!(cache.push("a", 3), Some(("a", 1)));
assert_eq!(cache.len(), 2);
assert_eq!(cache.get(&"a"), Some(&3));
}
#[test]
fn pop_removes_requested_entry_and_preserves_lru_order() {
let mut cache = cache(3);
cache.push("a", 1);
cache.push("b", 2);
cache.push("c", 3);
assert_eq!(cache.pop(&"b"), Some(2));
assert_eq!(cache.get(&"a"), Some(&1));
assert_eq!(cache.pop_lru(), Some(("c", 3)));
assert_eq!(cache.len(), 1);
}
#[test]
fn a_full_cache_that_only_misses_keeps_evicting_in_order() {
let mut cache = cache(4);
for step in 0..4 {
assert_eq!(cache.push(step, step * 10), None);
}
for step in 4..64 {
let evicted = cache.push(step, step * 10);
assert_eq!(
evicted,
Some((step - 4, (step - 4) * 10)),
"insert {step} must evict the oldest entry"
);
assert_eq!(cache.len(), 4);
}
let live: Vec<_> = cache.iter().map(|(key, value)| (*key, *value)).collect();
assert_eq!(live, vec![(63, 630), (62, 620), (61, 610), (60, 600)]);
}
#[test]
fn reused_slots_do_not_resurrect_the_entries_that_vacated_them() {
let mut cache = cache(3);
cache.push("a", 1);
cache.push("b", 2);
cache.push("c", 3);
assert_eq!(cache.pop(&"b"), Some(2));
assert_eq!(cache.push("d", 4), None);
assert!(!cache.contains(&"b"));
assert_eq!(cache.peek(&"d"), Some(&4));
assert_eq!(cache.len(), 3);
assert_eq!(cache.pop_lru(), Some(("a", 1)));
assert_eq!(cache.pop_lru(), Some(("c", 3)));
assert_eq!(cache.pop_lru(), Some(("d", 4)));
assert_eq!(cache.pop_lru(), None);
assert!(cache.is_empty());
}
#[test]
fn a_promoted_entry_survives_the_next_eviction() {
let mut cache = cache(3);
cache.push("a", 1);
cache.push("b", 2);
cache.push("c", 3);
assert_eq!(cache.get(&"a"), Some(&1));
assert_eq!(cache.get_mut(&"b").map(|value| *value), Some(2));
assert_eq!(cache.push("d", 4), Some(("c", 3)));
assert!(cache.contains(&"a"));
assert!(cache.contains(&"b"));
}
#[test]
fn peek_reads_without_promoting_entry() {
let mut cache = cache(2);
cache.push("a", 1);
cache.push("b", 2);
assert_eq!(cache.peek(&"a"), Some(&1));
assert_eq!(cache.push("c", 3), Some(("a", 1)));
}
#[test]
fn iter_reports_mru_to_lru_entries() {
let mut cache = cache(3);
cache.push("a", 1);
cache.push("b", 2);
cache.get(&"a");
let entries: Vec<_> = cache.iter().map(|(key, value)| (*key, *value)).collect();
assert_eq!(entries, vec![("a", 1), ("b", 2)]);
}
#[test]
fn a_cache_reserves_room_only_for_the_entries_it_holds() {
let mut cache: BoundedLruCache<u64, [u8; 64]> =
BoundedLruCache::with_capacity_at_least_one(8192);
assert_eq!(
(cache.index.capacity(), cache.slots.capacity()),
(0, 0),
"a cache no text reaches must not hold room for its whole bound"
);
for key in 0..10 {
cache.put(key, [0; 64]);
}
assert!(cache.slots.capacity() < 64, "{}", cache.slots.capacity());
assert_eq!(cache.cap().get(), 8192);
}
#[test]
fn peek_lru_reads_the_oldest_entry_without_refreshing_it() {
let mut cache = BoundedLruCache::with_capacity_at_least_one(3);
cache.put(1, "one");
cache.put(2, "two");
cache.put(3, "three");
assert_eq!(cache.peek_lru(), Some((&1, &"one")));
assert_eq!(
cache.peek_lru(),
Some((&1, &"one")),
"peeking keeps it oldest"
);
assert_eq!(cache.get(&1), Some(&"one"));
assert_eq!(cache.peek_lru(), Some((&2, &"two")));
assert_eq!(
BoundedLruCache::<u32, u32>::with_capacity_at_least_one(1).peek_lru(),
None
);
}