#[cfg(feature = "metrics")]
use crate::metrics::metrics_impl::CoreOnlyMetrics;
#[cfg(feature = "metrics")]
use crate::metrics::snapshot::CoreOnlyMetricsSnapshot;
#[cfg(feature = "metrics")]
use crate::metrics::traits::{CoreMetricsRecorder, MetricsSnapshotProvider};
use crate::traits::Cache;
use rustc_hash::FxHashMap;
use std::hash::Hash;
pub struct RandomCore<K, V> {
map: FxHashMap<K, (usize, V)>,
keys: Vec<K>,
capacity: usize,
rng_state: u64,
#[cfg(feature = "metrics")]
metrics: CoreOnlyMetrics,
}
impl<K, V> RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
#[inline]
pub fn new(capacity: usize) -> Self {
Self {
map: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
keys: Vec::with_capacity(capacity),
capacity,
rng_state: capacity as u64 + 0x9e3779b97f4a7c15,
#[cfg(feature = "metrics")]
metrics: CoreOnlyMetrics::default(),
}
}
#[inline]
pub fn get(&mut self, key: &K) -> Option<&V> {
#[cfg(feature = "metrics")]
if self.map.contains_key(key) {
self.metrics.record_get_hit();
} else {
self.metrics.record_get_miss();
}
self.map.get(key).map(|(_, v)| v)
}
#[inline]
pub fn peek(&self, key: &K) -> Option<&V> {
self.map.get(key).map(|(_, v)| v)
}
#[inline]
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
#[cfg(feature = "metrics")]
self.metrics.record_insert_call();
if self.capacity == 0 {
return None;
}
if let Some((_, v)) = self.map.get_mut(&key) {
#[cfg(feature = "metrics")]
self.metrics.record_insert_update();
return Some(std::mem::replace(v, value));
}
#[cfg(feature = "metrics")]
self.metrics.record_insert_new();
self.evict_if_needed();
let idx = self.keys.len();
self.keys.push(key.clone());
self.map.insert(key, (idx, value));
None
}
#[inline]
fn evict_if_needed(&mut self) {
#[cfg(feature = "metrics")]
if self.len() >= self.capacity && self.capacity > 0 && !self.keys.is_empty() {
self.metrics.record_evict_call();
}
while self.len() >= self.capacity && self.capacity > 0 && !self.keys.is_empty() {
self.evict_random();
#[cfg(feature = "metrics")]
self.metrics.record_evicted_entry();
}
#[cfg(debug_assertions)]
self.validate_invariants();
}
#[inline]
fn evict_random(&mut self) {
if self.keys.is_empty() {
return;
}
let len = self.keys.len();
let mut x = self.rng_state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.rng_state = x;
let random_idx = (x as usize) % len;
let victim_key = self.keys[random_idx].clone();
let last_idx = len - 1;
if random_idx != last_idx {
self.keys.swap(random_idx, last_idx);
let swapped_key = &self.keys[random_idx];
if let Some((idx, _)) = self.map.get_mut(swapped_key) {
*idx = random_idx;
}
}
self.keys.pop();
self.map.remove(&victim_key);
}
#[inline]
pub fn len(&self) -> usize {
self.map.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn contains(&self, key: &K) -> bool {
self.map.contains_key(key)
}
pub fn clear(&mut self) {
#[cfg(feature = "metrics")]
self.metrics.record_clear();
self.map.clear();
self.keys.clear();
#[cfg(debug_assertions)]
self.validate_invariants();
}
pub fn remove(&mut self, key: &K) -> Option<V> {
let (idx, value) = self.map.remove(key)?;
let last_idx = self.keys.len() - 1;
if idx != last_idx {
self.keys.swap(idx, last_idx);
let swapped_key = &self.keys[idx];
if let Some((stored_idx, _)) = self.map.get_mut(swapped_key) {
*stored_idx = idx;
}
}
self.keys.pop();
#[cfg(debug_assertions)]
self.validate_invariants();
Some(value)
}
pub fn iter(&self) -> Iter<'_, K, V> {
Iter {
inner: self.map.iter(),
}
}
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
IterMut {
inner: self.map.iter_mut(),
}
}
#[cfg(debug_assertions)]
fn validate_invariants(&self) {
debug_assert_eq!(
self.map.len(),
self.keys.len(),
"Map and keys vector have different sizes"
);
for (key, &(idx, _)) in &self.map {
debug_assert!(idx < self.keys.len(), "Index out of bounds");
debug_assert!(
&self.keys[idx] == key,
"Index mismatch: map points to wrong position"
);
}
for (i, key) in self.keys.iter().enumerate() {
debug_assert!(self.map.contains_key(key), "Key in vector not found in map");
if let Some(&(idx, _)) = self.map.get(key) {
debug_assert!(idx == i, "Vector position doesn't match map index");
}
}
let unique_count = self
.keys
.iter()
.collect::<std::collections::HashSet<_>>()
.len();
debug_assert!(unique_count == self.keys.len(), "Duplicate keys in vector");
}
}
impl<K: std::fmt::Debug, V> std::fmt::Debug for RandomCore<K, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RandomCore")
.field("capacity", &self.capacity)
.field("len", &self.map.len())
.finish_non_exhaustive()
}
}
impl<K, V> Cache<K, V> for RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
#[inline]
fn contains(&self, key: &K) -> bool {
self.map.contains_key(key)
}
#[inline]
fn len(&self) -> usize {
self.map.len()
}
#[inline]
fn capacity(&self) -> usize {
self.capacity
}
#[inline]
fn peek(&self, key: &K) -> Option<&V> {
self.map.get(key).map(|(_, v)| v)
}
#[inline]
fn get(&mut self, key: &K) -> Option<&V> {
RandomCore::get(self, key)
}
#[inline]
fn insert(&mut self, key: K, value: V) -> Option<V> {
RandomCore::insert(self, key, value)
}
fn remove(&mut self, key: &K) -> Option<V> {
RandomCore::remove(self, key)
}
fn clear(&mut self) {
RandomCore::clear(self);
}
}
#[cfg(feature = "metrics")]
impl<K, V> RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
pub fn metrics_snapshot(&self) -> CoreOnlyMetricsSnapshot {
CoreOnlyMetricsSnapshot {
get_calls: self.metrics.get_calls,
get_hits: self.metrics.get_hits,
get_misses: self.metrics.get_misses,
insert_calls: self.metrics.insert_calls,
insert_updates: self.metrics.insert_updates,
insert_new: self.metrics.insert_new,
evict_calls: self.metrics.evict_calls,
evicted_entries: self.metrics.evicted_entries,
cache_len: self.len(),
capacity: self.capacity,
}
}
}
#[cfg(feature = "metrics")]
impl<K, V> MetricsSnapshotProvider<CoreOnlyMetricsSnapshot> for RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
fn snapshot(&self) -> CoreOnlyMetricsSnapshot {
self.metrics_snapshot()
}
}
impl<K, V> Clone for RandomCore<K, V>
where
K: Clone + Eq + Hash,
V: Clone,
{
fn clone(&self) -> Self {
Self {
map: self.map.clone(),
keys: self.keys.clone(),
capacity: self.capacity,
rng_state: self.rng_state,
#[cfg(feature = "metrics")]
metrics: self.metrics,
}
}
}
impl<K, V> Default for RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
fn default() -> Self {
Self::new(16)
}
}
pub struct Iter<'a, K, V> {
inner: std::collections::hash_map::Iter<'a, K, (usize, V)>,
}
impl<'a, K, V> Iterator for Iter<'a, K, V> {
type Item = (&'a K, &'a V);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, (_, v))| (k, v))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<K, V> ExactSizeIterator for Iter<'_, K, V> {}
impl<K, V> std::iter::FusedIterator for Iter<'_, K, V> {}
pub struct IterMut<'a, K, V> {
inner: std::collections::hash_map::IterMut<'a, K, (usize, V)>,
}
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
type Item = (&'a K, &'a mut V);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, (_, v))| (k, v))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<K, V> ExactSizeIterator for IterMut<'_, K, V> {}
impl<K, V> std::iter::FusedIterator for IterMut<'_, K, V> {}
pub struct IntoIter<K, V> {
inner: std::collections::hash_map::IntoIter<K, (usize, V)>,
}
impl<K, V> Iterator for IntoIter<K, V> {
type Item = (K, V);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, (_, v))| (k, v))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<K, V> ExactSizeIterator for IntoIter<K, V> {}
impl<K, V> std::iter::FusedIterator for IntoIter<K, V> {}
impl<K, V> IntoIterator for RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
IntoIter {
inner: self.map.into_iter(),
}
}
}
impl<'a, K, V> IntoIterator for &'a RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
type Item = (&'a K, &'a V);
type IntoIter = Iter<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, K, V> IntoIterator for &'a mut RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
type Item = (&'a K, &'a mut V);
type IntoIter = IterMut<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<K, V> Extend<(K, V)> for RandomCore<K, V>
where
K: Clone + Eq + Hash,
{
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
for (k, v) in iter {
self.insert(k, v);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
mod basic_operations {
use super::*;
#[test]
fn new_cache_is_empty() {
let cache: RandomCore<&str, i32> = RandomCore::new(100);
assert!(cache.is_empty());
assert_eq!(cache.len(), 0);
assert_eq!(cache.capacity(), 100);
}
#[test]
fn insert_and_get() {
let mut cache = RandomCore::new(100);
cache.insert("key1", "value1");
assert_eq!(cache.len(), 1);
assert_eq!(cache.get(&"key1"), Some(&"value1"));
}
#[test]
fn insert_multiple_items() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
assert_eq!(cache.len(), 3);
assert_eq!(cache.get(&"a"), Some(&1));
assert_eq!(cache.get(&"b"), Some(&2));
assert_eq!(cache.get(&"c"), Some(&3));
}
#[test]
fn get_missing_key_returns_none() {
let mut cache: RandomCore<&str, i32> = RandomCore::new(100);
assert_eq!(cache.get(&"missing"), None);
}
#[test]
fn update_existing_key() {
let mut cache = RandomCore::new(100);
cache.insert("key", "initial");
cache.insert("key", "updated");
assert_eq!(cache.len(), 1);
assert_eq!(cache.get(&"key"), Some(&"updated"));
}
#[test]
fn contains_returns_correct_result() {
let mut cache = RandomCore::new(100);
cache.insert("exists", 1);
assert!(cache.contains(&"exists"));
assert!(!cache.contains(&"missing"));
}
#[test]
fn clear_removes_all_entries() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
cache.clear();
assert!(cache.is_empty());
assert_eq!(cache.len(), 0);
assert!(!cache.contains(&"a"));
assert!(!cache.contains(&"b"));
}
#[test]
fn capacity_returns_correct_value() {
let cache: RandomCore<i32, i32> = RandomCore::new(500);
assert_eq!(cache.capacity(), 500);
}
}
mod eviction_behavior {
use super::*;
#[test]
fn eviction_occurs_when_over_capacity() {
let mut cache = RandomCore::new(5);
for i in 0..10 {
cache.insert(i, i * 10);
}
assert_eq!(cache.len(), 5);
}
#[test]
fn eviction_maintains_capacity() {
let mut cache = RandomCore::new(3);
cache.insert(1, 10);
cache.insert(2, 20);
cache.insert(3, 30);
assert_eq!(cache.len(), 3);
cache.insert(4, 40);
assert_eq!(cache.len(), 3);
cache.insert(5, 50);
assert_eq!(cache.len(), 3);
}
#[test]
fn eviction_removes_from_index() {
let mut cache = RandomCore::new(3);
cache.insert(1, 10);
cache.insert(2, 20);
cache.insert(3, 30);
assert_eq!(cache.len(), 3);
for i in 4..15 {
cache.insert(i, i * 10);
}
assert_eq!(cache.len(), 3);
}
#[test]
fn random_eviction_is_unpredictable() {
let mut cache = RandomCore::new(5);
for i in 0..5 {
cache.insert(i, i * 10);
}
assert_eq!(cache.len(), 5);
for i in 5..15 {
cache.insert(i, i * 10);
}
assert_eq!(cache.len(), 5);
let original_remaining = (0..5).filter(|i| cache.contains(i)).count();
assert!(original_remaining <= 5);
}
}
mod get_behavior {
use super::*;
#[test]
fn get_does_not_change_eviction_probability() {
let mut cache = RandomCore::new(5);
for i in 0..5 {
cache.insert(i, i * 10);
}
for _ in 0..100 {
cache.get(&0);
}
assert!(cache.contains(&0));
assert_eq!(cache.len(), 5);
}
}
mod edge_cases {
use super::*;
#[test]
fn single_capacity_cache() {
let mut cache = RandomCore::new(1);
cache.insert("a", 1);
assert_eq!(cache.get(&"a"), Some(&1));
cache.insert("b", 2);
assert_eq!(cache.len(), 1);
}
#[test]
fn zero_capacity_cache() {
let mut cache = RandomCore::new(0);
cache.insert("a", 1);
assert_eq!(cache.len(), 0);
assert!(!cache.contains(&"a"));
}
#[test]
fn get_after_update() {
let mut cache = RandomCore::new(100);
cache.insert("key", "v1");
assert_eq!(cache.get(&"key"), Some(&"v1"));
cache.insert("key", "v2");
assert_eq!(cache.get(&"key"), Some(&"v2"));
cache.insert("key", "v3");
cache.insert("key", "v4");
assert_eq!(cache.get(&"key"), Some(&"v4"));
}
#[test]
fn large_capacity() {
let mut cache = RandomCore::new(10000);
for i in 0..10000 {
cache.insert(i, i * 2);
}
assert_eq!(cache.len(), 10000);
assert_eq!(cache.get(&5000), Some(&10000));
assert_eq!(cache.get(&9999), Some(&19998));
}
#[test]
fn empty_cache_operations() {
let mut cache: RandomCore<i32, i32> = RandomCore::new(100);
assert!(cache.is_empty());
assert_eq!(cache.get(&1), None);
assert!(!cache.contains(&1));
}
#[test]
fn string_keys_and_values() {
let mut cache = RandomCore::new(100);
cache.insert(String::from("hello"), String::from("world"));
cache.insert(String::from("foo"), String::from("bar"));
assert_eq!(
cache.get(&String::from("hello")),
Some(&String::from("world"))
);
assert_eq!(cache.get(&String::from("foo")), Some(&String::from("bar")));
}
#[test]
fn internal_consistency_after_evictions() {
let mut cache = RandomCore::new(10);
for i in 0..100 {
cache.insert(i, i * 10);
}
assert_eq!(cache.len(), 10);
assert_eq!(cache.keys.len(), 10);
assert_eq!(cache.map.len(), 10);
for (idx, key) in cache.keys.iter().enumerate() {
assert!(cache.map.contains_key(key));
let (stored_idx, _) = cache.map.get(key).unwrap();
assert_eq!(*stored_idx, idx);
}
}
}
mod baseline_properties {
use super::*;
#[test]
fn no_access_pattern_tracking() {
let mut cache = RandomCore::new(10);
for i in 0..10 {
cache.insert(i, i * 10);
}
for _ in 0..100 {
cache.get(&0);
cache.get(&1);
}
assert_eq!(cache.len(), 10);
assert!(cache.contains(&0));
assert!(cache.contains(&1));
}
#[test]
fn works_as_baseline_policy() {
let mut cache = RandomCore::new(5);
for i in 0..10 {
cache.insert(i, i);
}
assert_eq!(cache.len(), 5);
let count = (0..10).filter(|i| cache.contains(i)).count();
assert_eq!(count, 5);
}
}
#[test]
#[cfg(debug_assertions)]
fn validate_invariants_after_operations() {
let mut cache = RandomCore::new(10);
for i in 1..=10 {
cache.insert(i, i * 100);
}
cache.validate_invariants();
for _ in 0..5 {
cache.get(&5);
}
cache.validate_invariants();
cache.insert(11, 1100);
cache.validate_invariants();
cache.insert(12, 1200);
cache.validate_invariants();
cache.clear();
cache.validate_invariants();
assert_eq!(cache.len(), 0);
assert_eq!(cache.keys.len(), 0);
}
#[test]
#[cfg(debug_assertions)]
fn validate_invariants_with_index_consistency() {
let mut cache = RandomCore::new(5);
cache.insert(1, 100);
cache.insert(2, 200);
cache.insert(3, 300);
cache.validate_invariants();
for i in 4..=10 {
cache.insert(i, i * 100);
cache.validate_invariants();
}
assert_eq!(cache.len(), 5);
assert_eq!(cache.keys.len(), 5);
for (key, &(idx, _)) in &cache.map {
assert_eq!(&cache.keys[idx], key);
}
}
mod insert_return_value {
use super::*;
#[test]
fn insert_new_returns_none() {
let mut cache = RandomCore::new(100);
assert_eq!(cache.insert("key", 1), None);
}
#[test]
fn insert_update_returns_old_value() {
let mut cache = RandomCore::new(100);
cache.insert("key", 1);
assert_eq!(cache.insert("key", 2), Some(1));
assert_eq!(cache.insert("key", 3), Some(2));
}
#[test]
fn insert_zero_capacity_returns_none() {
let mut cache = RandomCore::new(0);
assert_eq!(cache.insert("key", 1), None);
}
#[test]
fn trait_insert_matches_inherent() {
use crate::traits::Cache;
let mut cache = RandomCore::new(100);
assert_eq!(Cache::insert(&mut cache, "a", 1), None);
assert_eq!(Cache::insert(&mut cache, "a", 2), Some(1));
}
}
mod peek_tests {
use super::*;
#[test]
fn peek_returns_value() {
let mut cache = RandomCore::new(100);
cache.insert("key", 42);
assert_eq!(cache.peek(&"key"), Some(&42));
}
#[test]
fn peek_missing_returns_none() {
let cache: RandomCore<&str, i32> = RandomCore::new(100);
assert_eq!(cache.peek(&"missing"), None);
}
#[test]
fn peek_does_not_require_mut() {
let mut cache = RandomCore::new(100);
cache.insert("key", 42);
let cache_ref: &RandomCore<&str, i32> = &cache;
assert_eq!(cache_ref.peek(&"key"), Some(&42));
}
}
mod remove_tests {
use super::*;
#[test]
fn remove_existing_key() {
let mut cache = RandomCore::new(100);
cache.insert("key", 42);
assert_eq!(cache.remove(&"key"), Some(42));
assert!(!cache.contains(&"key"));
assert_eq!(cache.len(), 0);
}
#[test]
fn remove_missing_key() {
let mut cache: RandomCore<&str, i32> = RandomCore::new(100);
assert_eq!(cache.remove(&"missing"), None);
}
#[test]
fn remove_maintains_consistency() {
let mut cache = RandomCore::new(100);
for i in 0..10 {
cache.insert(i, i * 10);
}
cache.remove(&5);
assert_eq!(cache.len(), 9);
assert!(!cache.contains(&5));
for i in 0..10 {
if i != 5 {
assert_eq!(cache.peek(&i), Some(&(i * 10)));
}
}
}
#[test]
fn remove_via_mutable_cache_trait() {
use crate::traits::Cache;
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
assert_eq!(Cache::remove(&mut cache, &"a"), Some(1));
assert!(!cache.contains(&"a"));
assert!(cache.contains(&"b"));
}
#[test]
#[cfg(debug_assertions)]
fn remove_validates_invariants() {
let mut cache = RandomCore::new(10);
for i in 0..10 {
cache.insert(i, i);
}
for i in (0..10).rev() {
cache.remove(&i);
cache.validate_invariants();
}
assert!(cache.is_empty());
}
}
mod clone_default_tests {
use super::*;
#[test]
fn clone_preserves_contents() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
let cloned = cache.clone();
assert_eq!(cloned.len(), 2);
assert_eq!(cloned.peek(&"a"), Some(&1));
assert_eq!(cloned.peek(&"b"), Some(&2));
assert_eq!(cloned.capacity(), 100);
}
#[test]
fn clone_is_independent() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
let mut cloned = cache.clone();
cloned.insert("b", 2);
assert_eq!(cache.len(), 1);
assert_eq!(cloned.len(), 2);
}
#[test]
fn default_creates_cache() {
let cache: RandomCore<String, i32> = RandomCore::default();
assert_eq!(cache.capacity(), 16);
assert!(cache.is_empty());
}
}
mod iterator_tests {
use super::*;
#[test]
fn iter_visits_all_entries() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
let mut pairs: Vec<_> = cache.iter().collect();
pairs.sort_by_key(|&(k, _)| *k);
assert_eq!(pairs, vec![(&"a", &1), (&"b", &2), (&"c", &3)]);
}
#[test]
fn iter_mut_modifies_values() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
for (_, v) in cache.iter_mut() {
*v *= 10;
}
assert_eq!(cache.peek(&"a"), Some(&10));
assert_eq!(cache.peek(&"b"), Some(&20));
}
#[test]
fn iter_exact_size() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
let iter = cache.iter();
assert_eq!(iter.len(), 2);
}
#[test]
fn into_iter_owned() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
let mut pairs: Vec<_> = cache.into_iter().collect();
pairs.sort_by_key(|&(k, _)| k);
assert_eq!(pairs, vec![("a", 1), ("b", 2)]);
}
#[test]
fn into_iter_ref() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
let mut pairs: Vec<_> = (&cache).into_iter().collect();
pairs.sort_by_key(|&(k, _)| *k);
assert_eq!(pairs, vec![(&"a", &1), (&"b", &2)]);
}
#[test]
fn into_iter_mut() {
let mut cache = RandomCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
for (_, v) in &mut cache {
*v += 100;
}
assert_eq!(cache.peek(&"a"), Some(&101));
assert_eq!(cache.peek(&"b"), Some(&102));
}
#[test]
fn empty_iter() {
let cache: RandomCore<&str, i32> = RandomCore::new(100);
assert_eq!(cache.iter().count(), 0);
}
}
mod extend_tests {
use super::*;
#[test]
fn extend_inserts_all() {
let mut cache = RandomCore::new(100);
cache.extend(vec![("a", 1), ("b", 2), ("c", 3)]);
assert_eq!(cache.len(), 3);
assert_eq!(cache.peek(&"a"), Some(&1));
assert_eq!(cache.peek(&"b"), Some(&2));
assert_eq!(cache.peek(&"c"), Some(&3));
}
#[test]
fn extend_respects_capacity() {
let mut cache = RandomCore::new(3);
cache.extend(vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50)]);
assert_eq!(cache.len(), 3);
}
}
}