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

            
3
use crate::err::{Action, ErrorSource, Resource};
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
#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
14
#[derive(Clone, Debug)]
15
pub struct TestingStateMgr {
16
    /// Inner reference-counted storage.
17
    inner: Arc<Mutex<TestingStateMgrInner>>,
18
}
19

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

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

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

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

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

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

            
92
    /// Return an error Resource corresponding to a given `key`.
93
4
    fn err_resource(&self, key: &str) -> Resource {
94
4
        Resource::Temporary {
95
4
            key: key.to_string(),
96
4
        }
97
4
    }
98
}
99

            
100
impl StateMgr for TestingStateMgr {
101
3676
    fn load<D>(&self, key: &str) -> Result<Option<D>>
102
3676
    where
103
3676
        D: DeserializeOwned,
104
3676
    {
105
3676
        let inner = self.inner.lock().expect("Lock poisoned.");
106
3676
        let storage = inner.storage.lock().expect("Lock poisoned.");
107
3676
        let content = storage.entries.get(key);
108
3676
        match content {
109
46
            Some(value) => {
110
46
                Ok(Some(serde_json::from_str(value).map_err(|e| {
111
4
                    Error::new(e, Action::Loading, self.err_resource(key))
112
46
                })?))
113
            }
114
3630
            None => Ok(None),
115
        }
116
3676
    }
117

            
118
1356
    fn store<S>(&self, key: &str, val: &S) -> Result<()>
119
1356
    where
120
1356
        S: Serialize,
121
1356
    {
122
1356
        let inner = self.inner.lock().expect("Lock poisoned.");
123
1356
        if !inner.lock_held {
124
4
            return Err(Error::new(
125
4
                ErrorSource::NoLock,
126
4
                Action::Storing,
127
4
                Resource::Manager,
128
4
            ));
129
1352
        }
130
1352
        let mut storage = inner.storage.lock().expect("Lock poisoned.");
131

            
132
1352
        let val = serde_json::to_string_pretty(val)
133
1352
            .map_err(|e| Error::new(e, Action::Storing, self.err_resource(key)))?;
134

            
135
1352
        storage.entries.insert(key.to_string(), val);
136
1352
        Ok(())
137
1356
    }
138

            
139
873
    fn can_store(&self) -> bool {
140
873
        let inner = self.inner.lock().expect("Lock poisoned.");
141
873

            
142
873
        inner.lock_held
143
873
    }
144

            
145
2667
    fn try_lock(&self) -> Result<LockStatus> {
146
2667
        let mut inner = self.inner.lock().expect("Lock poisoned.");
147
2667
        if inner.lock_held {
148
2
            return Ok(LockStatus::AlreadyHeld);
149
2665
        }
150
2665

            
151
2665
        let mut storage = inner.storage.lock().expect("Lock poisoned");
152
2665
        if storage.lock_available {
153
2618
            storage.lock_available = false;
154
2618
            drop(storage); // release borrow
155
2618
            inner.lock_held = true;
156
2618
            Ok(LockStatus::NewlyAcquired)
157
        } else {
158
47
            Ok(LockStatus::NoLock)
159
        }
160
2667
    }
161

            
162
    fn unlock(&self) -> Result<()> {
163
        let mut inner = self.inner.lock().expect("Lock poisoned.");
164
        inner.unlock();
165
        Ok(())
166
    }
167
}
168

            
169
impl Drop for TestingStateMgrInner {
170
13598
    fn drop(&mut self) {
171
13598
        self.unlock();
172
13598
    }
173
}
174

            
175
#[cfg(test)]
176
mod test {
177
    // @@ begin test lint list maintained by maint/add_warning @@
178
    #![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
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
190
    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
}