1
//! Testing-only StateMgr that stores values in a hash table.
2

            
3
use crate::{load_error, store_error};
4
use crate::{Error, LockStatus, Result, StateMgr};
5
use serde::{de::DeserializeOwned, Serialize};
6
use std::collections::HashMap;
7
use std::sync::{Arc, Mutex};
8

            
9
/// A state manager for testing support, that allows simulating persistence
10
/// without having to store anything to disk.
11
///
12
/// Only available when this crate is built with the `testing` feature.
13
21
#[derive(Clone, Debug)]
14
pub struct TestingStateMgr {
15
    /// Inner reference-counted storage.
16
    inner: Arc<Mutex<TestingStateMgrInner>>,
17
}
18

            
19
/// The inner state of a TestingStateMgr.
20
#[derive(Debug)]
21
struct TestingStateMgrInner {
22
    /// True if this manager, and all references to it, hold the lock on
23
    /// the storage.
24
    lock_held: bool,
25
    /// The underlying shared storage object.
26
    storage: Arc<Mutex<TestingStateMgrStorage>>,
27
}
28

            
29
impl TestingStateMgrInner {
30
    /// Release the lock, if we hold it. Otherwise, do nothing.
31
356
    fn unlock(&mut self) {
32
356
        if self.lock_held {
33
228
            self.lock_held = false;
34
228
            let mut storage = self.storage.lock().expect("Lock poisoned");
35
228
            storage.lock_available = true;
36
228
        }
37
356
    }
38
}
39

            
40
/// Implementation type for [`TestingStateMgr`]: represents an underlying
41
/// storage system that can be shared by multiple TestingStateMgr instances
42
/// at a time, only one of which can hold the lock.
43
#[derive(Debug)]
44
struct TestingStateMgrStorage {
45
    /// True if nobody currently holds the lock for this storage.
46
    lock_available: bool,
47
    /// Map from key to JSON-encoded values.
48
    ///
49
    /// We serialize our values here for convenience (so that we don't
50
    /// have to use `Any`) and to try to detect any
51
    /// serialization-related bugs.
52
    entries: HashMap<String, String>,
53
}
54

            
55
impl Default for TestingStateMgr {
56
    fn default() -> Self {
57
        Self::new()
58
    }
59
}
60

            
61
impl TestingStateMgr {
62
    /// Create a new empty unlocked [`TestingStateMgr`].
63
339
    pub fn new() -> Self {
64
339
        let storage = TestingStateMgrStorage {
65
339
            lock_available: true,
66
339
            entries: HashMap::new(),
67
339
        };
68
339
        let inner = TestingStateMgrInner {
69
339
            lock_held: false,
70
339
            storage: Arc::new(Mutex::new(storage)),
71
339
        };
72
339
        TestingStateMgr {
73
339
            inner: Arc::new(Mutex::new(inner)),
74
339
        }
75
339
    }
76

            
77
    /// Create a new unlocked [`TestingStateMgr`] that shares the same
78
    /// underlying storage with this one.
79
    #[must_use]
80
17
    pub fn new_manager(&self) -> Self {
81
17
        let inner = self.inner.lock().expect("Lock poisoned.");
82
17
        let new_inner = TestingStateMgrInner {
83
17
            lock_held: false,
84
17
            storage: Arc::clone(&inner.storage),
85
17
        };
86
17
        TestingStateMgr {
87
17
            inner: Arc::new(Mutex::new(new_inner)),
88
17
        }
89
17
    }
90
}
91

            
92
impl StateMgr for TestingStateMgr {
93
38
    fn load<D>(&self, key: &str) -> Result<Option<D>>
94
38
    where
95
38
        D: DeserializeOwned,
96
38
    {
97
38
        let inner = self.inner.lock().expect("Lock poisoned.");
98
38
        let storage = inner.storage.lock().expect("Lock poisoned.");
99
38
        match storage.entries.get(key) {
100
15
            Some(value) => Ok(Some(serde_json::from_str(value).map_err(load_error)?)),
101
23
            None => Ok(None),
102
        }
103
38
    }
104

            
105
14
    fn store<S>(&self, key: &str, val: &S) -> Result<()>
106
14
    where
107
14
        S: Serialize,
108
14
    {
109
14
        let inner = self.inner.lock().expect("Lock poisoned.");
110
14
        if !inner.lock_held {
111
2
            return Err(Error::NoLock);
112
12
        }
113
12
        let mut storage = inner.storage.lock().expect("Lock poisoned.");
114

            
115
12
        let val = serde_json::to_string_pretty(val).map_err(store_error)?;
116

            
117
11
        storage.entries.insert(key.to_string(), val);
118
11
        Ok(())
119
13
    }
120

            
121
57
    fn can_store(&self) -> bool {
122
57
        let inner = self.inner.lock().expect("Lock poisoned.");
123
57

            
124
57
        inner.lock_held
125
57
    }
126

            
127
246
    fn try_lock(&self) -> Result<LockStatus> {
128
246
        let mut inner = self.inner.lock().expect("Lock poisoned.");
129
246
        if inner.lock_held {
130
1
            return Ok(LockStatus::AlreadyHeld);
131
245
        }
132
245

            
133
245
        let mut storage = inner.storage.lock().expect("Lock poisoned");
134
245
        if storage.lock_available {
135
228
            storage.lock_available = false;
136
228
            drop(storage); // release borrow
137
228
            inner.lock_held = true;
138
228
            Ok(LockStatus::NewlyAcquired)
139
        } else {
140
17
            Ok(LockStatus::NoLock)
141
        }
142
246
    }
143

            
144
    fn unlock(&self) -> Result<()> {
145
        let mut inner = self.inner.lock().expect("Lock poisoned.");
146
        inner.unlock();
147
        Ok(())
148
    }
149
}
150

            
151
impl Drop for TestingStateMgrInner {
152
356
    fn drop(&mut self) {
153
356
        self.unlock();
154
356
    }
155
}
156

            
157
#[cfg(test)]
158
mod test {
159
    #![allow(clippy::unwrap_used)]
160
    use super::*;
161
    use serde::{Deserialize, Serialize};
162

            
163
    #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
164
    struct Ex1 {
165
        v1: u32,
166
        v2: u64,
167
    }
168
    #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
169
    struct Ex2 {
170
        s1: String,
171
        s2: String,
172
    }
173
    #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
174
    enum OldEnum {
175
        Variant1,
176
    }
177
    #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
178
    enum NewEnum {
179
        Variant1,
180
        Variant2,
181
    }
182

            
183
    #[test]
184
    fn basic_tests() {
185
        let mgr = TestingStateMgr::new();
186
        let v1 = Ex1 { v1: 8, v2: 99 };
187
        let s1 = Ex2 {
188
            s1: "Hello".into(),
189
            s2: "World".into(),
190
        };
191

            
192
        assert_eq!(mgr.load::<Ex1>("item1").unwrap(), None);
193
        assert!(matches!(mgr.store("item1", &v1), Err(Error::NoLock)));
194

            
195
        assert!(!mgr.can_store());
196
        assert_eq!(mgr.try_lock().unwrap(), LockStatus::NewlyAcquired);
197
        assert!(mgr.can_store());
198

            
199
        assert!(mgr.store("item1", &v1).is_ok());
200
        assert_eq!(mgr.load::<Ex1>("item1").unwrap(), Some(v1));
201
        assert!(mgr.load::<Ex2>("item1").is_err());
202

            
203
        assert!(mgr.store("item2", &s1).is_ok());
204
        assert_eq!(mgr.load::<Ex2>("item2").unwrap(), Some(s1));
205
        assert!(mgr.load::<Ex1>("item2").is_err());
206

            
207
        let v2 = Ex1 { v1: 10, v2: 12 };
208
        assert!(mgr.store("item1", &v2).is_ok());
209
        assert_eq!(mgr.load::<Ex1>("item1").unwrap(), Some(v2));
210
    }
211

            
212
    #[test]
213
    fn lock_blocking() {
214
        let mgr = TestingStateMgr::new();
215

            
216
        assert!(!mgr.can_store());
217

            
218
        let mgr2 = mgr.new_manager();
219

            
220
        assert_eq!(mgr.try_lock().unwrap(), LockStatus::NewlyAcquired);
221
        assert_eq!(mgr.try_lock().unwrap(), LockStatus::AlreadyHeld);
222
        assert!(mgr.can_store());
223

            
224
        assert!(!mgr2.can_store());
225
        assert_eq!(mgr2.try_lock().unwrap(), LockStatus::NoLock);
226
        assert!(!mgr2.can_store());
227

            
228
        drop(mgr);
229
        assert_eq!(mgr2.try_lock().unwrap(), LockStatus::NewlyAcquired);
230
        assert!(mgr2.can_store());
231
    }
232

            
233
    #[test]
234
    fn typesafe_handles() {
235
        use crate::DynStorageHandle;
236
        let mgr = TestingStateMgr::new();
237

            
238
        let h1: DynStorageHandle<Ex1> = mgr.clone().create_handle("foo");
239
        let h2: DynStorageHandle<Ex2> = mgr.clone().create_handle("bar");
240
        let h3: DynStorageHandle<Ex2> = mgr.clone().create_handle("baz");
241

            
242
        let v1 = Ex1 { v1: 1, v2: 2 };
243
        let s1 = Ex2 {
244
            s1: "aaa".into(),
245
            s2: "bbb".into(),
246
        };
247
        let s2 = Ex2 {
248
            s1: "jj".into(),
249
            s2: "yrfmstbyes".into(),
250
        };
251

            
252
        assert!(matches!(h1.store(&v1), Err(Error::NoLock)));
253
        assert!(mgr.try_lock().unwrap().held());
254
        assert!(h1.can_store());
255
        assert!(h1.store(&v1).is_ok());
256

            
257
        assert!(h2.can_store());
258
        assert!(h2.store(&s1).is_ok());
259
        assert!(h3.load().unwrap().is_none());
260
        assert!(h3.store(&s2).is_ok());
261

            
262
        assert_eq!(h1.load().unwrap(), Some(v1));
263
        assert_eq!(h2.load().unwrap(), Some(s1));
264
        assert_eq!(h3.load().unwrap(), Some(s2));
265
    }
266

            
267
    #[test]
268
    fn futureproof() {
269
        use crate::Futureproof;
270

            
271
        let v1 = Ex1 { v1: 8, v2: 99 };
272

            
273
        let v1_ser = serde_json::to_string(&v1).unwrap();
274

            
275
        let v1_as_ex1: Futureproof<Ex1> = serde_json::from_str(&v1_ser).unwrap();
276
        let v1_as_ex2: Futureproof<Ex2> = serde_json::from_str(&v1_ser).unwrap();
277
        assert!(v1_as_ex1.clone().into_option().is_some());
278
        assert!(v1_as_ex2.into_option().is_none());
279

            
280
        assert_eq!(serde_json::to_string(&v1_as_ex1).unwrap(), v1_ser);
281
    }
282

            
283
    #[test]
284
    fn futureproof_enums() {
285
        use crate::Futureproof;
286

            
287
        let new1 = NewEnum::Variant1;
288
        let new2 = NewEnum::Variant2;
289

            
290
        let new1_ser = serde_json::to_string(&new1).unwrap();
291
        let new2_ser = serde_json::to_string(&new2).unwrap();
292

            
293
        let old1: Futureproof<OldEnum> = serde_json::from_str(&new1_ser).unwrap();
294
        let old2: Futureproof<OldEnum> = serde_json::from_str(&new2_ser).unwrap();
295

            
296
        assert!(old1.into_option().is_some());
297
        assert!(old2.into_option().is_none());
298
    }
299
}