1use crate::err::{Action, ErrorSource, Resource};
4use crate::{Error, LockStatus, Result, StateMgr};
5use serde::{de::DeserializeOwned, Serialize};
6use std::collections::HashMap;
7use std::sync::{Arc, Mutex};
8
9#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
14#[derive(Clone, Debug)]
15pub struct TestingStateMgr {
16 inner: Arc<Mutex<TestingStateMgrInner>>,
18}
19
20#[derive(Debug)]
22struct TestingStateMgrInner {
23 lock_held: bool,
26 storage: Arc<Mutex<TestingStateMgrStorage>>,
28}
29
30impl TestingStateMgrInner {
31 fn unlock(&mut self) {
33 if self.lock_held {
34 self.lock_held = false;
35 let mut storage = self.storage.lock().expect("Lock poisoned");
36 storage.lock_available = true;
37 }
38 }
39}
40
41#[derive(Debug)]
45struct TestingStateMgrStorage {
46 lock_available: bool,
48 entries: HashMap<String, String>,
54}
55
56impl Default for TestingStateMgr {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl TestingStateMgr {
63 pub fn new() -> Self {
65 let storage = TestingStateMgrStorage {
66 lock_available: true,
67 entries: HashMap::new(),
68 };
69 let inner = TestingStateMgrInner {
70 lock_held: false,
71 storage: Arc::new(Mutex::new(storage)),
72 };
73 TestingStateMgr {
74 inner: Arc::new(Mutex::new(inner)),
75 }
76 }
77
78 #[must_use]
81 pub fn new_manager(&self) -> Self {
82 let inner = self.inner.lock().expect("Lock poisoned.");
83 let new_inner = TestingStateMgrInner {
84 lock_held: false,
85 storage: Arc::clone(&inner.storage),
86 };
87 TestingStateMgr {
88 inner: Arc::new(Mutex::new(new_inner)),
89 }
90 }
91
92 fn err_resource(&self, key: &str) -> Resource {
94 Resource::Temporary {
95 key: key.to_string(),
96 }
97 }
98}
99
100impl StateMgr for TestingStateMgr {
101 fn load<D>(&self, key: &str) -> Result<Option<D>>
102 where
103 D: DeserializeOwned,
104 {
105 let inner = self.inner.lock().expect("Lock poisoned.");
106 let storage = inner.storage.lock().expect("Lock poisoned.");
107 let content = storage.entries.get(key);
108 match content {
109 Some(value) => {
110 Ok(Some(serde_json::from_str(value).map_err(|e| {
111 Error::new(e, Action::Loading, self.err_resource(key))
112 })?))
113 }
114 None => Ok(None),
115 }
116 }
117
118 fn store<S>(&self, key: &str, val: &S) -> Result<()>
119 where
120 S: Serialize,
121 {
122 let inner = self.inner.lock().expect("Lock poisoned.");
123 if !inner.lock_held {
124 return Err(Error::new(
125 ErrorSource::NoLock,
126 Action::Storing,
127 Resource::Manager,
128 ));
129 }
130 let mut storage = inner.storage.lock().expect("Lock poisoned.");
131
132 let val = serde_json::to_string_pretty(val)
133 .map_err(|e| Error::new(e, Action::Storing, self.err_resource(key)))?;
134
135 storage.entries.insert(key.to_string(), val);
136 Ok(())
137 }
138
139 fn can_store(&self) -> bool {
140 let inner = self.inner.lock().expect("Lock poisoned.");
141
142 inner.lock_held
143 }
144
145 fn try_lock(&self) -> Result<LockStatus> {
146 let mut inner = self.inner.lock().expect("Lock poisoned.");
147 if inner.lock_held {
148 return Ok(LockStatus::AlreadyHeld);
149 }
150
151 let mut storage = inner.storage.lock().expect("Lock poisoned");
152 if storage.lock_available {
153 storage.lock_available = false;
154 drop(storage); inner.lock_held = true;
156 Ok(LockStatus::NewlyAcquired)
157 } else {
158 Ok(LockStatus::NoLock)
159 }
160 }
161
162 fn unlock(&self) -> Result<()> {
163 let mut inner = self.inner.lock().expect("Lock poisoned.");
164 inner.unlock();
165 Ok(())
166 }
167}
168
169impl Drop for TestingStateMgrInner {
170 fn drop(&mut self) {
171 self.unlock();
172 }
173}
174
175#[cfg(test)]
176mod test {
177 #![allow(clippy::bool_assert_comparison)]
179 #![allow(clippy::clone_on_copy)]
180 #![allow(clippy::dbg_macro)]
181 #![allow(clippy::mixed_attributes_style)]
182 #![allow(clippy::print_stderr)]
183 #![allow(clippy::print_stdout)]
184 #![allow(clippy::single_char_pattern)]
185 #![allow(clippy::unwrap_used)]
186 #![allow(clippy::unchecked_duration_subtraction)]
187 #![allow(clippy::useless_vec)]
188 #![allow(clippy::needless_pass_by_value)]
189 use super::*;
191 use serde::{Deserialize, Serialize};
192
193 #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
194 struct Ex1 {
195 v1: u32,
196 v2: u64,
197 }
198 #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
199 struct Ex2 {
200 s1: String,
201 s2: String,
202 }
203 #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
204 enum OldEnum {
205 Variant1,
206 }
207 #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
208 enum NewEnum {
209 Variant1,
210 Variant2,
211 }
212
213 #[test]
214 fn basic_tests() {
215 let mgr = TestingStateMgr::new();
216 let v1 = Ex1 { v1: 8, v2: 99 };
217 let s1 = Ex2 {
218 s1: "Hello".into(),
219 s2: "World".into(),
220 };
221
222 assert_eq!(mgr.load::<Ex1>("item1").unwrap(), None);
223 assert!(matches!(
224 mgr.store("item1", &v1).unwrap_err().source(),
225 ErrorSource::NoLock
226 ));
227
228 assert!(!mgr.can_store());
229 assert_eq!(mgr.try_lock().unwrap(), LockStatus::NewlyAcquired);
230 assert!(mgr.can_store());
231
232 assert!(mgr.store("item1", &v1).is_ok());
233 assert_eq!(mgr.load::<Ex1>("item1").unwrap(), Some(v1));
234 assert!(mgr.load::<Ex2>("item1").is_err());
235
236 assert!(mgr.store("item2", &s1).is_ok());
237 assert_eq!(mgr.load::<Ex2>("item2").unwrap(), Some(s1));
238 assert!(mgr.load::<Ex1>("item2").is_err());
239
240 let v2 = Ex1 { v1: 10, v2: 12 };
241 assert!(mgr.store("item1", &v2).is_ok());
242 assert_eq!(mgr.load::<Ex1>("item1").unwrap(), Some(v2));
243 }
244
245 #[test]
246 fn lock_blocking() {
247 let mgr = TestingStateMgr::new();
248
249 assert!(!mgr.can_store());
250
251 let mgr2 = mgr.new_manager();
252
253 assert_eq!(mgr.try_lock().unwrap(), LockStatus::NewlyAcquired);
254 assert_eq!(mgr.try_lock().unwrap(), LockStatus::AlreadyHeld);
255 assert!(mgr.can_store());
256
257 assert!(!mgr2.can_store());
258 assert_eq!(mgr2.try_lock().unwrap(), LockStatus::NoLock);
259 assert!(!mgr2.can_store());
260
261 drop(mgr);
262 assert_eq!(mgr2.try_lock().unwrap(), LockStatus::NewlyAcquired);
263 assert!(mgr2.can_store());
264 }
265
266 #[test]
267 fn typesafe_handles() {
268 use crate::DynStorageHandle;
269 let mgr = TestingStateMgr::new();
270
271 let h1: DynStorageHandle<Ex1> = mgr.clone().create_handle("foo");
272 let h2: DynStorageHandle<Ex2> = mgr.clone().create_handle("bar");
273 let h3: DynStorageHandle<Ex2> = mgr.clone().create_handle("baz");
274
275 let v1 = Ex1 { v1: 1, v2: 2 };
276 let s1 = Ex2 {
277 s1: "aaa".into(),
278 s2: "bbb".into(),
279 };
280 let s2 = Ex2 {
281 s1: "jj".into(),
282 s2: "yrfmstbyes".into(),
283 };
284
285 assert!(matches!(
286 h1.store(&v1).unwrap_err().source(),
287 ErrorSource::NoLock
288 ));
289 assert!(mgr.try_lock().unwrap().held());
290 assert!(h1.can_store());
291 assert!(h1.store(&v1).is_ok());
292
293 assert!(h2.can_store());
294 assert!(h2.store(&s1).is_ok());
295 assert!(h3.load().unwrap().is_none());
296 assert!(h3.store(&s2).is_ok());
297
298 assert_eq!(h1.load().unwrap(), Some(v1));
299 assert_eq!(h2.load().unwrap(), Some(s1));
300 assert_eq!(h3.load().unwrap(), Some(s2));
301 }
302
303 #[test]
304 fn futureproof() {
305 use crate::Futureproof;
306
307 let v1 = Ex1 { v1: 8, v2: 99 };
308
309 let v1_ser = serde_json::to_string(&v1).unwrap();
310
311 let v1_as_ex1: Futureproof<Ex1> = serde_json::from_str(&v1_ser).unwrap();
312 let v1_as_ex2: Futureproof<Ex2> = serde_json::from_str(&v1_ser).unwrap();
313 assert!(v1_as_ex1.clone().into_option().is_some());
314 assert!(v1_as_ex2.into_option().is_none());
315
316 assert_eq!(serde_json::to_string(&v1_as_ex1).unwrap(), v1_ser);
317 }
318
319 #[test]
320 fn futureproof_enums() {
321 use crate::Futureproof;
322
323 let new1 = NewEnum::Variant1;
324 let new2 = NewEnum::Variant2;
325
326 let new1_ser = serde_json::to_string(&new1).unwrap();
327 let new2_ser = serde_json::to_string(&new2).unwrap();
328
329 let old1: Futureproof<OldEnum> = serde_json::from_str(&new1_ser).unwrap();
330 let old2: Futureproof<OldEnum> = serde_json::from_str(&new2_ser).unwrap();
331
332 assert!(old1.into_option().is_some());
333 assert!(old2.into_option().is_none());
334 }
335}