use fibre_cache::{Cache, CacheBuilder, policy::*};
use std::{
hash::{BuildHasher, Hash},
time::Duration,
};
mod lru {
use std::time::Duration;
use super::*;
use fibre_cache::policy::lru::LruPolicy;
#[test]
fn test_lru_eviction_logic() {
let cache = CacheBuilder::default()
.capacity(3)
.shards(1)
.cache_policy_factory(|| Box::new(LruPolicy::new()))
.janitor_tick_interval(Duration::from_millis(10))
.maintenance_chance(1)
.build()
.unwrap();
cache.insert(1, "a", 1);
cache.insert(2, "b", 1);
cache.insert(3, "c", 1);
assert_eq!(cache.metrics().current_cost, 3);
cache.fetch(&1);
cache.insert(4, "d", 1);
cache.run_maintenance();
assert_eq!(cache.metrics().current_cost, 3);
assert!(cache.fetch(&2).is_none(), "Key 2 should have been evicted");
assert!(cache.fetch(&1).is_some());
assert!(cache.fetch(&3).is_some());
assert!(cache.fetch(&4).is_some());
}
}
mod fifo {
use std::time::Duration;
use super::*;
use fibre_cache::policy::fifo::Fifo;
#[test]
fn test_fifo_eviction_logic() {
let cache = CacheBuilder::default()
.capacity(3)
.shards(1)
.cache_policy_factory(|| Box::new(Fifo::new()))
.janitor_tick_interval(Duration::from_millis(10))
.maintenance_chance(1)
.build()
.unwrap();
cache.insert(1, "a", 1);
cache.insert(2, "b", 1);
cache.insert(3, "c", 1);
cache.fetch(&1);
cache.insert(4, "d", 1);
cache.run_maintenance();
assert_eq!(cache.metrics().current_cost, 3);
assert!(cache.fetch(&1).is_none(), "Key 1 should have been evicted");
assert!(cache.fetch(&2).is_some());
assert!(cache.fetch(&3).is_some());
assert!(cache.fetch(&4).is_some());
}
}
mod sieve {
use std::time::Duration;
use super::*;
use fibre_cache::policy::sieve::SievePolicy;
#[test]
fn test_sieve_eviction_logic() {
let cache = CacheBuilder::default()
.capacity(3)
.shards(1)
.cache_policy_factory(|| Box::new(SievePolicy::new()))
.janitor_tick_interval(Duration::from_millis(10))
.build()
.unwrap();
cache.insert(1, "a", 1);
cache.insert(2, "b", 1);
cache.insert(3, "c", 1);
cache.fetch(&2);
cache.insert(4, "d", 1);
cache.run_maintenance();
assert_eq!(cache.metrics().current_cost, 3);
assert!(cache.fetch(&1).is_none(), "Key 1 should be evicted");
assert!(cache.fetch(&2).is_some(), "Key 2 should be retained");
assert!(cache.fetch(&3).is_some());
assert!(cache.fetch(&4).is_some());
}
}
mod tinylfu {
use std::time::Duration;
use super::*;
use fibre_cache::policy::tinylfu::TinyLfuPolicy;
fn build_test_cache(capacity: u64) -> fibre_cache::Cache<i32, String> {
CacheBuilder::default()
.capacity(capacity)
.shards(1)
.cache_policy_factory(move || Box::new(TinyLfuPolicy::new(capacity)))
.janitor_tick_interval(Duration::from_millis(10))
.maintenance_chance(1)
.build()
.unwrap()
}
#[test]
fn store_and_retrieve_items() {
let cache = build_test_cache(10);
cache.insert(1, "one".to_string(), 1);
cache.insert(2, "two".to_string(), 1);
assert_eq!(*cache.fetch(&1).unwrap(), "one");
assert_eq!(*cache.fetch(&2).unwrap(), "two");
}
#[test]
fn store_retrieve_and_invalidate_items() {
let cache = build_test_cache(10);
cache.insert(1, "one".to_string(), 1);
cache.insert(2, "two".to_string(), 1);
assert_eq!(*cache.fetch(&1).unwrap(), "one");
assert_eq!(*cache.fetch(&2).unwrap(), "two");
cache.invalidate(&1);
assert_eq!(cache.fetch(&1), None);
assert_eq!(*cache.fetch(&2).unwrap(), "two");
}
#[test]
fn check_if_cap_and_cost_are_correct() {
let cache = build_test_cache(3);
cache.insert(1, "one".to_string(), 1);
cache.insert(2, "two".to_string(), 1);
assert_eq!(cache.metrics().current_cost, 2);
cache.fetch(&1);
cache.fetch(&2);
assert_eq!(cache.metrics().current_cost, 2);
cache.insert(3, "three".to_string(), 1);
assert_eq!(cache.metrics().current_cost, 3);
cache.insert(4, "four".to_string(), 1);
cache.run_maintenance();
assert_eq!(
cache.metrics().current_cost,
3,
"run_maintenance should bring cost back down to capacity"
);
}
#[test]
fn clear_cache() {
let cache = build_test_cache(10);
cache.insert(1, "one".to_string(), 1);
cache.insert(2, "two".to_string(), 1);
assert_eq!(cache.metrics().current_cost, 2);
cache.clear();
assert_eq!(cache.fetch(&1), None);
assert_eq!(cache.fetch(&2), None);
assert_eq!(cache.metrics().current_cost, 0);
}
#[test]
fn test_admission_logic_rejects_infrequent_item() {
let cache = build_test_cache(10);
for i in 1..=10 {
cache.insert(i, i.to_string(), 1);
}
for i in 1..=9 {
cache.fetch(&i);
}
assert_eq!(cache.metrics().current_cost, 10);
cache.run_maintenance();
cache.insert(100, "trigger".to_string(), 1);
cache.run_maintenance();
let final_cost = cache.metrics().current_cost;
assert!(
final_cost <= 10,
"Janitor should bring cost to at or below capacity. Final cost: {}",
final_cost
);
assert!(
cache.fetch(&10).is_none(),
"Infrequent item (10) should be evicted"
);
assert!(
cache.fetch(&1).is_some(),
"Hot item (1) should have been protected"
);
assert!(
cache.fetch(&100).is_some(),
"New trigger item (100) should now be in the cache"
);
}
#[test]
fn test_admission_logic_admits_frequent_item() {
let cache = build_test_cache(10);
for i in 1..=10 {
cache.insert(i, i.to_string(), 1);
}
assert_eq!(cache.metrics().current_cost, 10);
for _ in 0..10 {
cache.fetch(&5);
}
cache.run_maintenance();
cache.insert(11, "new".to_string(), 1);
cache.run_maintenance();
assert_eq!(
cache.metrics().current_cost,
10,
"Janitor should bring cost back to capacity"
);
assert!(
cache.fetch(&5).is_some(),
"Hot item 5 should have been protected"
);
assert!(
cache.fetch(&1).is_none(),
"Weak, cold victim (1) should be evicted by policy"
);
assert!(
cache.fetch(&11).is_some(),
"New item 11 should be in the cache"
);
}
}
mod slru {
use std::time::Duration;
use super::*;
use fibre_cache::policy::slru::SlruPolicy;
#[test]
fn test_slru_promotion_and_eviction() {
let cache = CacheBuilder::default()
.capacity(4)
.shards(1)
.cache_policy_factory(|| Box::new(SlruPolicy::new(4)))
.janitor_tick_interval(Duration::from_millis(10))
.build()
.unwrap();
cache.insert(1, "a".to_string(), 1);
cache.insert(2, "b".to_string(), 1);
cache.insert(3, "c".to_string(), 1);
cache.insert(4, "d".to_string(), 1);
assert_eq!(cache.metrics().current_cost, 4);
cache.fetch(&2);
cache.insert(5, "e".to_string(), 1);
assert_eq!(
cache.metrics().current_cost,
5,
"Cache should be temporarily over capacity"
);
cache.run_maintenance();
assert_eq!(
cache.metrics().current_cost,
4,
"Cache cost should be back to capacity after janitor runs"
);
assert!(
cache.fetch(&1).is_none(),
"LRU of probationary (1) should be evicted"
);
assert!(
cache.fetch(&2).is_some(),
"Promoted item (2) should be safe"
);
assert!(cache.fetch(&3).is_some());
assert!(cache.fetch(&4).is_some());
assert!(cache.fetch(&5).is_some(), "New item (5) should be present");
}
}