aba-cache 0.1.1

In-process in-memory cache library
Documentation
use super::*;
#[cfg(feature = "asynchronous")]
use crate::LruAsyncCache;
use crate::LruCache;
use serde_json::{self, Value};
#[cfg(feature = "asynchronous")]
use std::sync::Arc;
use std::{rc::Rc, thread, time::Duration};
#[cfg(feature = "asynchronous")]
use tokio::time::delay_for;

#[test]
#[should_panic]
fn test_create_cache_with_cap_0() {
    LruCache::<usize, ()>::new(0, 60);
}

#[test]
fn test_get_on_empty_cache() {
    let mut cache = LruCache::<(), usize>::new(1, 60);

    assert!(cache.is_empty());
    assert_eq!(cache.get(&()), None);
}

#[test]
fn test_get_uncached_key() {
    let mut cache = LruCache::<usize, usize>::new(1, 60);

    cache.put(1, 1);

    assert_eq!(cache.get(&2), None);
    let mut iter = cache.storage.iter();
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.prev().is_null()
            && item.next().is_null()
    } else {
        false
    });
    assert!(iter.next().is_none());
}

#[test]
fn test_reuse_single_entry() {
    let mut cache = LruCache::<&str, Rc<Value>>::new(1, 60);

    let val_1: Rc<Value> = Rc::new(serde_json::from_str(r#"{"id":1}"#).unwrap());
    let val_2: Rc<Value> = Rc::new(serde_json::from_str(r#"{"id":2}"#).unwrap());

    let old_value = cache.put("1", val_1.clone());
    assert_eq!(old_value, None);

    let old_value = cache.put("1", val_2.clone());
    assert!(if let Some(value) = old_value {
        value == val_1
    } else {
        false
    });
    assert_eq!(cache.len(), 1);
    let mut iter = cache.storage.iter();
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.prev().is_null()
            && item.next().is_null()
    } else {
        false
    });
    assert!(iter.next().is_none());
}

#[test]
fn test_reuse_expire_entry() {
    let mut cache = LruCache::<usize, Rc<Value>>::new(2, 1);

    let val_1: Rc<Value> = Rc::new(serde_json::from_str(r#"{"id":1}"#).unwrap());
    let val_2: Rc<Value> = Rc::new(serde_json::from_str(r#"{"id":2}"#).unwrap());

    let old_value = cache.put(1, val_1.clone());
    assert_eq!(old_value, None);

    thread::sleep(Duration::from_secs(1));
    let old_value = cache.put(2, val_2.clone());
    assert!(if let Some(value) = old_value {
        value == val_1
    } else {
        false
    });
    assert_eq!(cache.len(), 1);
    let mut iter = cache.storage.iter();
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.prev().is_null()
            && item.next().is_null()
    } else {
        false
    });
    assert!(iter.next().is_none());
}

#[test]
fn test_reuse_last_expire_entry() {
    let mut cache = LruCache::<usize, Rc<Value>>::new(2, 1);

    let val_1: Rc<Value> = Rc::new(serde_json::from_str(r#"{"id":1}"#).unwrap());
    let val_2: Rc<Value> = Rc::new(serde_json::from_str(r#"{"id":2}"#).unwrap());
    let val_3: Rc<Value> = Rc::new(serde_json::from_str(r#"{"id":3}"#).unwrap());

    let old_value = cache.put(1, val_1.clone());
    assert_eq!(old_value, None);

    let old_value = cache.put(2, val_2.clone());
    assert_eq!(old_value, None);

    thread::sleep(Duration::from_secs(1));
    let old_value = cache.put(3, val_3.clone());
    assert!(if let Some(value) = old_value {
        value == val_1
    } else {
        false
    });
    assert_eq!(cache.len(), 2);
    let mut iter = cache.storage.iter();
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.prev().is_null()
            && item.next() == Pointer::InternalPointer { slab: 0, pos: 1 }
    } else {
        false
    });
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 1 }
            && item.prev() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.next().is_null()
    } else {
        false
    });
    assert!(iter.next().is_none());
}

#[test]
fn test_get_head_entry() {
    let mut cache = LruCache::<usize, &str>::new(2, 60);

    cache.put(1, "one");
    cache.put(2, "two");

    let cache_head = cache.get(&2);
    assert_eq!(cache_head, Some(&"two"));
    let mut iter = cache.storage.iter();
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 1 }
            && item.prev().is_null()
            && item.next() == Pointer::InternalPointer { slab: 0, pos: 0 }
    } else {
        false
    });
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.prev() == Pointer::InternalPointer { slab: 0, pos: 1 }
            && item.next().is_null()
    } else {
        false
    });
    assert!(iter.next().is_none());
}

#[test]
fn test_get_least_entry() {
    let mut cache = LruCache::<usize, &str>::new(3, 60);

    cache.put(1, "one");
    cache.put(2, "two");
    cache.put(3, "three");

    let cache_head = cache.get(&1);
    assert_eq!(cache_head, Some(&"one"));
    let mut iter = cache.storage.iter();
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.prev().is_null()
            && item.next() == Pointer::InternalPointer { slab: 0, pos: 2 }
    } else {
        false
    });
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 2 }
            && item.prev() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.next() == Pointer::InternalPointer { slab: 0, pos: 1 }
    } else {
        false
    });
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 1 }
            && item.prev() == Pointer::InternalPointer { slab: 0, pos: 2 }
            && item.next().is_null()
    } else {
        false
    });
    assert!(iter.next().is_none());
}

#[test]
fn test_get_middle_entry() {
    let mut cache = LruCache::<usize, &str>::new(3, 60);

    cache.put(1, "one");
    cache.put(2, "two");
    cache.put(3, "three");

    let cache_head = cache.get(&2);
    assert_eq!(cache_head, Some(&"two"));
    let mut iter = cache.storage.iter();
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 1 }
            && item.prev().is_null()
            && item.next() == Pointer::InternalPointer { slab: 0, pos: 2 }
    } else {
        false
    });
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 2 }
            && item.prev() == Pointer::InternalPointer { slab: 0, pos: 1 }
            && item.next() == Pointer::InternalPointer { slab: 0, pos: 0 }
    } else {
        false
    });
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.prev() == Pointer::InternalPointer { slab: 0, pos: 2 }
            && item.next().is_null()
    } else {
        false
    });
    assert!(iter.next().is_none());
}

#[test]
fn test_get_expire_entry() {
    let mut cache = LruCache::<usize, &str>::new(2, 1);

    cache.put(1, "one");
    cache.put(2, "two");
    cache.put(3, "three");

    let cache_head = cache.get(&2);
    assert_eq!(cache_head, Some(&"two"));

    thread::sleep(Duration::from_secs(1));
    assert_eq!(cache.get(&2), None);
    let mut iter = cache.storage.iter();
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 1, pos: 0 }
            && item.prev().is_null()
            && item.next() == Pointer::InternalPointer { slab: 0, pos: 0 }
    } else {
        false
    });
    assert!(if let Some(item) = iter.next() {
        item.ptr() == Pointer::InternalPointer { slab: 0, pos: 0 }
            && item.prev() == Pointer::InternalPointer { slab: 1, pos: 0 }
            && item.next().is_null()
    } else {
        false
    });
    assert!(iter.next().is_none());
    assert_eq!(cache.len(), 2);
    assert_eq!(cache.capacity(), 4);
}

#[cfg(feature = "asynchronous")]
#[tokio::test]
async fn test_get_expire_entry_async() {
    let cache = LruAsyncCache::<usize, Arc<Value>>::new(2, 1);

    let val_1: Arc<Value> = Arc::new(serde_json::from_str(r#"{"id":1}"#).unwrap());
    let val_2: Arc<Value> = Arc::new(serde_json::from_str(r#"{"id":2}"#).unwrap());
    let val_3: Arc<Value> = Arc::new(serde_json::from_str(r#"{"id":3}"#).unwrap());

    cache.put(1, val_1.clone()).await;
    cache.put(2, val_2.clone()).await;
    cache.put(3, val_3.clone()).await;

    assert!(if let Some(value) = cache.get(&2).await {
        value == val_2
    } else {
        false
    });

    delay_for(Duration::from_millis(1500)).await;
    assert_eq!(cache.len().await, 0);
    assert_eq!(cache.capacity().await, 0);
}