use super::*;
use proxy_wasm_stub::types::Status;
use std::cell::RefCell;
use std::collections::HashMap;
#[test]
fn expires_with_time() {
let clock = Rc::new(ManualClock::default());
let shared_data = Rc::new(MapSharedData::new());
let lock = TryLock::new(
"key".to_string(),
Duration::from_secs(10),
Rc::clone(&clock) as Rc<dyn Clock>,
shared_data,
);
let lock1 = lock.try_lock();
let lock2 = lock.try_lock();
assert!(lock1.is_some());
assert!(lock2.is_none());
clock.move_forward(Duration::from_millis(10001));
let lock3 = lock.try_lock();
assert!(lock3.is_some());
}
#[test]
fn release_after_acquire() {
let clock = Rc::new(ManualClock::default());
let shared_data = Rc::new(MapSharedData::new());
let lock = TryLock::new(
"key".to_string(),
Duration::from_secs(10),
Rc::clone(&clock) as Rc<dyn Clock>,
shared_data,
);
let lock1 = lock.try_lock();
assert!(lock1.is_some());
drop(lock1);
let lock1 = lock.try_lock();
assert!(lock1.is_some());
clock.move_forward(Duration::from_millis(5000));
drop(lock1);
let lock1 = lock.try_lock();
assert!(lock1.is_some());
clock.move_forward(Duration::from_millis(20000));
drop(lock1);
}
#[test]
fn refresh_lock() {
let clock = Rc::new(ManualClock::default());
let shared_data = Rc::new(MapSharedData::new());
let lock = TryLock::new(
"key".to_string(),
Duration::from_secs(10),
Rc::clone(&clock) as Rc<dyn Clock>,
shared_data,
);
if let Some(lock1) = lock.try_lock() {
assert!(lock.try_lock().is_none());
clock.move_forward(Duration::from_secs(5));
assert!(lock1.refresh_lock());
assert!(lock.try_lock().is_none());
clock.move_forward(Duration::from_secs(6));
assert!(lock.try_lock().is_none());
clock.move_forward(Duration::from_secs(6));
let lock2 = lock.try_lock();
assert!(lock2.is_some());
assert!(!lock1.refresh_lock());
};
}
pub struct ManualClock {
current: RefCell<SystemTime>,
}
impl ManualClock {
fn new() -> Self {
Self {
current: RefCell::new(SystemTime::now()),
}
}
}
impl Default for ManualClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for ManualClock {
fn get_current_time(&self) -> SystemTime {
*self.current.borrow()
}
}
impl ManualClock {
pub fn move_forward(&self, duration: Duration) {
self.current.replace(self.get_current_time().add(duration));
}
}
#[derive(Default)]
pub struct MapSharedData {
map: RefCell<HashMap<String, MapSharedDataEntry>>,
}
struct MapSharedDataEntry {
data: Option<Vec<u8>>,
lock: u32,
}
impl MapSharedData {
pub fn new() -> Self {
Self {
map: RefCell::new(HashMap::new()),
}
}
fn do_store(&self, key: &str, value: &[u8], version: u32) {
self.map.borrow_mut().insert(
key.to_string(),
MapSharedDataEntry {
data: Some(value.to_vec()),
lock: version,
},
);
}
fn next_cas(cas: Option<u32>) -> u32 {
match cas {
Some(u32::MAX) | None => 1,
Some(cas) => cas + 1,
}
}
}
impl SharedData for MapSharedData {
fn shared_data_get(&self, key: &str) -> (Option<Vec<u8>>, Option<u32>) {
let key = key.to_string();
self.map
.borrow()
.get(&key)
.map(|entry| (entry.data.clone(), Some(entry.lock)))
.unwrap_or_else(|| (None, None))
}
fn shared_data_set(&self, key: &str, value: &[u8], version: Option<u32>) -> Result<(), Status> {
let (_, lock) = self.shared_data_get(key);
if version.is_none() || lock.is_none() || version.unwrap() == lock.unwrap() {
self.do_store(key, value, Self::next_cas(version));
Ok(())
} else {
Err(Status::CasMismatch)
}
}
fn shared_data_remove(
&self,
key: &str,
version: Option<u32>,
) -> Result<Option<Vec<u8>>, Status> {
let (val, _) = self.shared_data_get(key);
self.shared_data_set(key, &[], version)?;
Ok(val)
}
fn shared_data_keys(&self) -> Vec<String> {
self.map.borrow().keys().cloned().collect()
}
}