use priority_lfu::{Cache, CacheKey, DeepSizeOf};
use proptest::prelude::*;
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
struct TestKey(u64, u64);
impl CacheKey for TestKey {
type Value = TestValue;
fn policy(&self) -> priority_lfu::CachePolicy {
match self.1 % 3 {
0 => priority_lfu::CachePolicy::Critical,
1 => priority_lfu::CachePolicy::Standard,
_ => priority_lfu::CachePolicy::Volatile,
}
}
}
#[derive(Clone, Debug, PartialEq, DeepSizeOf)]
struct TestValue {
size: usize,
}
proptest! {
#[test]
fn test_insert_get_consistency(keys in prop::collection::vec(0u64..100, 1..50)) {
let cache = Cache::new(1024 * 1024);
for key in &keys {
let value = TestValue { size: 100 };
cache.insert(TestKey(*key, 50), value.clone());
}
for key in &keys {
let result = cache.get_clone(&TestKey(*key, 50));
prop_assert!(result.is_some());
}
}
#[test]
fn test_size_accounting(operations in prop::collection::vec((0u64..20, 10usize..200, 10u64..100), 1..30)) {
let max_size = 5000;
let cache = Cache::new(max_size);
for (key, size, weight) in operations {
cache.insert(TestKey(key, weight), TestValue { size });
}
prop_assert!(cache.size() <= max_size);
}
#[test]
fn test_remove_decreases_size(
inserts in prop::collection::vec((0u64..50, 50usize..150, 10u64..100), 10..20),
remove_indices in prop::collection::vec(0usize..10, 1..5)
) {
let cache = Cache::new(1024 * 1024); let mut inserted_keys = Vec::new();
for (key, size, weight) in inserts {
cache.insert(TestKey(key, weight), TestValue { size });
inserted_keys.push(TestKey(key, weight));
}
let size_before = cache.size();
for &idx in &remove_indices {
if idx < inserted_keys.len() {
cache.remove(&inserted_keys[idx]);
}
}
let size_after = cache.size();
prop_assert!(size_after <= size_before);
}
#[test]
fn test_clear_empties_cache(operations in prop::collection::vec((0u64..100, 10usize..100, 10u64..100), 1..50)) {
let cache = Cache::new(1024 * 1024);
for (key, size, weight) in operations {
cache.insert(TestKey(key, weight), TestValue { size });
}
cache.clear();
prop_assert_eq!(cache.len(), 0);
prop_assert_eq!(cache.size(), 0);
prop_assert!(cache.is_empty());
}
#[test]
fn test_update_existing_key(key in 0u64..100, values in prop::collection::vec((10usize..200, 10u64..100), 2..10)) {
let cache = Cache::new(1024 * 1024);
let weight = 50u64;
for (size, _) in values {
cache.insert(TestKey(key, weight), TestValue { size });
}
prop_assert!(cache.contains(&TestKey(key, weight)));
let result = cache.get_clone(&TestKey(key, weight));
prop_assert!(result.is_some());
}
#[test]
fn test_contains_after_insert(keys in prop::collection::vec(0u64..100, 1..50)) {
let cache = Cache::new(1024 * 1024);
for key in &keys {
cache.insert(TestKey(*key, 50), TestValue { size: 100 });
}
for key in &keys {
prop_assert!(cache.contains(&TestKey(*key, 50)));
}
}
#[test]
fn test_not_contains_after_remove(keys in prop::collection::vec(0u64..50, 5..20)) {
let cache = Cache::new(1024 * 1024);
for key in &keys {
cache.insert(TestKey(*key, 50), TestValue { size: 100 });
}
for key in &keys {
cache.remove(&TestKey(*key, 50));
prop_assert!(!cache.contains(&TestKey(*key, 50)));
}
}
}
#[test]
fn test_no_panics_on_empty_operations() {
let cache = Cache::new(1024);
assert!(cache.get_clone(&TestKey(1, 50)).is_none());
assert!(cache.remove(&TestKey(1, 50)).is_none());
assert!(!cache.contains(&TestKey(1, 50)));
assert_eq!(cache.len(), 0);
assert_eq!(cache.size(), 0);
cache.clear(); }
#[test]
fn test_duplicate_insertions() {
let cache = Cache::new(10240);
let key = TestKey(1, 50);
for _ in 0..100 {
cache.insert(
key.clone(),
TestValue {
size: 50,
},
);
}
assert_eq!(cache.len(), 1);
}