#![cfg(feature = "std")]
use std::time::Duration;
use cache_mod::{Cache, LfuCache, LruCache, SizedCache, TinyLfuCache, TtlCache};
use proptest::collection::vec;
use proptest::prelude::*;
#[derive(Debug, Clone)]
enum Op {
Insert(u8, u8),
Get(u8),
Remove(u8),
ContainsKey(u8),
}
fn op_strategy() -> impl Strategy<Value = Op> {
prop_oneof![
(any::<u8>(), any::<u8>()).prop_map(|(k, v)| Op::Insert(k, v)),
any::<u8>().prop_map(Op::Get),
any::<u8>().prop_map(Op::Remove),
any::<u8>().prop_map(Op::ContainsKey),
]
}
fn ops_strategy() -> impl Strategy<Value = Vec<Op>> {
vec(op_strategy(), 0..200)
}
fn drive<C>(cache: &C, ops: &[Op], capacity_bound: usize)
where
C: Cache<u8, u8>,
{
for op in ops {
match op {
Op::Insert(k, v) => {
let _ = cache.insert(*k, *v);
}
Op::Get(k) => {
let _ = cache.get(k);
}
Op::Remove(k) => {
let _ = cache.remove(k);
}
Op::ContainsKey(k) => {
let _ = cache.contains_key(k);
}
}
assert!(
cache.len() <= capacity_bound,
"len {} exceeded capacity bound {}",
cache.len(),
capacity_bound,
);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(96))]
#[test]
fn lru_never_exceeds_capacity(ops in ops_strategy()) {
let cache: LruCache<u8, u8> = LruCache::new(8).expect("capacity > 0");
drive(&cache, &ops, 8);
cache.clear();
prop_assert_eq!(cache.len(), 0);
prop_assert!(cache.is_empty());
}
#[test]
fn lfu_never_exceeds_capacity(ops in ops_strategy()) {
let cache: LfuCache<u8, u8> = LfuCache::new(8).expect("capacity > 0");
drive(&cache, &ops, 8);
cache.clear();
prop_assert_eq!(cache.len(), 0);
}
#[test]
fn ttl_never_exceeds_capacity(ops in ops_strategy()) {
let cache: TtlCache<u8, u8> = TtlCache::new(8, Duration::from_secs(60)).expect("capacity > 0");
drive(&cache, &ops, 8);
cache.clear();
prop_assert_eq!(cache.len(), 0);
}
#[test]
fn tinylfu_never_exceeds_capacity(ops in ops_strategy()) {
let cache: TinyLfuCache<u8, u8> = TinyLfuCache::new(8).expect("capacity > 0");
drive(&cache, &ops, 8);
cache.clear();
prop_assert_eq!(cache.len(), 0);
}
#[test]
fn sized_never_exceeds_max_weight(ops in ops_strategy()) {
fn unit_weight(_: &u8) -> usize { 1 }
let cache: SizedCache<u8, u8> = SizedCache::new(8, unit_weight).expect("max_weight > 0");
for op in &ops {
match op {
Op::Insert(k, v) => { let _ = cache.insert(*k, *v); }
Op::Get(k) => { let _ = cache.get(k); }
Op::Remove(k) => { let _ = cache.remove(k); }
Op::ContainsKey(k) => { let _ = cache.contains_key(k); }
}
prop_assert!(cache.total_weight() <= cache.max_weight());
prop_assert!(cache.len() <= cache.max_weight());
}
cache.clear();
prop_assert_eq!(cache.total_weight(), 0);
prop_assert_eq!(cache.len(), 0);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(64))]
#[test]
fn lru_insert_then_get_round_trips(k in any::<u8>(), v in any::<u8>()) {
let cache: LruCache<u8, u8> = LruCache::new(16).expect("capacity > 0");
let _ = cache.insert(k, v);
prop_assert_eq!(cache.get(&k), Some(v));
}
#[test]
fn lfu_insert_then_get_round_trips(k in any::<u8>(), v in any::<u8>()) {
let cache: LfuCache<u8, u8> = LfuCache::new(16).expect("capacity > 0");
let _ = cache.insert(k, v);
prop_assert_eq!(cache.get(&k), Some(v));
}
#[test]
fn ttl_insert_then_get_round_trips(k in any::<u8>(), v in any::<u8>()) {
let cache: TtlCache<u8, u8> = TtlCache::new(16, Duration::from_secs(60)).expect("capacity > 0");
let _ = cache.insert(k, v);
prop_assert_eq!(cache.get(&k), Some(v));
}
#[test]
fn sized_insert_then_get_round_trips(k in any::<u8>(), v in any::<u8>()) {
fn unit_weight(_: &u8) -> usize { 1 }
let cache: SizedCache<u8, u8> = SizedCache::new(16, unit_weight).expect("max_weight > 0");
let _ = cache.insert(k, v);
prop_assert_eq!(cache.get(&k), Some(v));
}
}