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::{Serialize, de::DeserializeOwned};
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
15236
    fn unlock(&mut self) {
33
15236
        if self.lock_held {
34
1324
            self.lock_held = false;
35
1324
            let mut storage = self.storage.lock().expect("Lock poisoned");
36
1324
            storage.lock_available = true;
37
13920
        }
38
15236
    }
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
18007
    pub fn new() -> Self {
65
18007
        let storage = TestingStateMgrStorage {
66
18007
            lock_available: true,
67
18007
            entries: HashMap::new(),
68
18007
        };
69
18007
        let inner = TestingStateMgrInner {
70
18007
            lock_held: false,
71
18007
            storage: Arc::new(Mutex::new(storage)),
72
18007
        };
73
18007
        TestingStateMgr {
74
18007
            inner: Arc::new(Mutex::new(inner)),
75
18007
        }
76
18007
    }
77

            
78
    /// Create a new unlocked [`TestingStateMgr`] that shares the same
79
    /// underlying storage with this one.
80
    #[must_use]
81
49
    pub fn new_manager(&self) -> Self {
82
49
        let inner = self.inner.lock().expect("Lock poisoned.");
83
49
        let new_inner = TestingStateMgrInner {
84
49
            lock_held: false,
85
49
            storage: Arc::clone(&inner.storage),
86
49
        };
87
49
        TestingStateMgr {
88
49
            inner: Arc::new(Mutex::new(new_inner)),
89
49
        }
90
49
    }
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
4192
    fn load<D>(&self, key: &str) -> Result<Option<D>>
102
4192
    where
103
4192
        D: DeserializeOwned,
104
    {
105
4192
        let inner = self.inner.lock().expect("Lock poisoned.");
106
4192
        let storage = inner.storage.lock().expect("Lock poisoned.");
107
4192
        let content = storage.entries.get(key);
108
4192
        match content {
109
50
            Some(value) => {
110
50
                Ok(Some(serde_json::from_str(value).map_err(|e| {
111
4
                    Error::new(e, Action::Loading, self.err_resource(key))
112
4
                })?))
113
            }
114
4142
            None => Ok(None),
115
        }
116
4192
    }
117

            
118
1488
    fn store<S>(&self, key: &str, val: &S) -> Result<()>
119
1488
    where
120
1488
        S: Serialize,
121
    {
122
1488
        let inner = self.inner.lock().expect("Lock poisoned.");
123
1488
        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
1484
        }
130
1484
        let mut storage = inner.storage.lock().expect("Lock poisoned.");
131

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

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

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

            
142
911
        inner.lock_held
143
911
    }
144

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

            
151
3159
        let mut storage = inner.storage.lock().expect("Lock poisoned");
152
3159
        if storage.lock_available {
153
3110
            storage.lock_available = false;
154
3110
            drop(storage); // release borrow
155
3110
            inner.lock_held = true;
156
3110
            Ok(LockStatus::NewlyAcquired)
157
        } else {
158
49
            Ok(LockStatus::NoLock)
159
        }
160
3161
    }
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
15236
    fn drop(&mut self) {
171
15236
        self.unlock();
172
15236
    }
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_time_subtraction)]
187
    #![allow(clippy::useless_vec)]
188
    #![allow(clippy::needless_pass_by_value)]
189
    #![allow(clippy::string_slice)] // See arti#2571
190
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
191
    use super::*;
192
    use serde::{Deserialize, Serialize};
193

            
194
    #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
195
    struct Ex1 {
196
        v1: u32,
197
        v2: u64,
198
    }
199
    #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
200
    struct Ex2 {
201
        s1: String,
202
        s2: String,
203
    }
204
    #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
205
    enum OldEnum {
206
        Variant1,
207
    }
208
    #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
209
    enum NewEnum {
210
        Variant1,
211
        Variant2,
212
    }
213

            
214
    #[test]
215
    fn basic_tests() {
216
        let mgr = TestingStateMgr::new();
217
        let v1 = Ex1 { v1: 8, v2: 99 };
218
        let s1 = Ex2 {
219
            s1: "Hello".into(),
220
            s2: "World".into(),
221
        };
222

            
223
        assert_eq!(mgr.load::<Ex1>("item1").unwrap(), None);
224
        assert!(matches!(
225
            mgr.store("item1", &v1).unwrap_err().source(),
226
            ErrorSource::NoLock
227
        ));
228

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

            
233
        assert!(mgr.store("item1", &v1).is_ok());
234
        assert_eq!(mgr.load::<Ex1>("item1").unwrap(), Some(v1));
235
        assert!(mgr.load::<Ex2>("item1").is_err());
236

            
237
        assert!(mgr.store("item2", &s1).is_ok());
238
        assert_eq!(mgr.load::<Ex2>("item2").unwrap(), Some(s1));
239
        assert!(mgr.load::<Ex1>("item2").is_err());
240

            
241
        let v2 = Ex1 { v1: 10, v2: 12 };
242
        assert!(mgr.store("item1", &v2).is_ok());
243
        assert_eq!(mgr.load::<Ex1>("item1").unwrap(), Some(v2));
244
    }
245

            
246
    #[test]
247
    fn lock_blocking() {
248
        let mgr = TestingStateMgr::new();
249

            
250
        assert!(!mgr.can_store());
251

            
252
        let mgr2 = mgr.new_manager();
253

            
254
        assert_eq!(mgr.try_lock().unwrap(), LockStatus::NewlyAcquired);
255
        assert_eq!(mgr.try_lock().unwrap(), LockStatus::AlreadyHeld);
256
        assert!(mgr.can_store());
257

            
258
        assert!(!mgr2.can_store());
259
        assert_eq!(mgr2.try_lock().unwrap(), LockStatus::NoLock);
260
        assert!(!mgr2.can_store());
261

            
262
        drop(mgr);
263
        assert_eq!(mgr2.try_lock().unwrap(), LockStatus::NewlyAcquired);
264
        assert!(mgr2.can_store());
265
    }
266

            
267
    #[test]
268
    fn typesafe_handles() {
269
        use crate::DynStorageHandle;
270
        let mgr = TestingStateMgr::new();
271

            
272
        let h1: DynStorageHandle<Ex1> = mgr.clone().create_handle("foo");
273
        let h2: DynStorageHandle<Ex2> = mgr.clone().create_handle("bar");
274
        let h3: DynStorageHandle<Ex2> = mgr.clone().create_handle("baz");
275

            
276
        let v1 = Ex1 { v1: 1, v2: 2 };
277
        let s1 = Ex2 {
278
            s1: "aaa".into(),
279
            s2: "bbb".into(),
280
        };
281
        let s2 = Ex2 {
282
            s1: "jj".into(),
283
            s2: "yrfmstbyes".into(),
284
        };
285

            
286
        assert!(matches!(
287
            h1.store(&v1).unwrap_err().source(),
288
            ErrorSource::NoLock
289
        ));
290
        assert!(mgr.try_lock().unwrap().held());
291
        assert!(h1.can_store());
292
        assert!(h1.store(&v1).is_ok());
293

            
294
        assert!(h2.can_store());
295
        assert!(h2.store(&s1).is_ok());
296
        assert!(h3.load().unwrap().is_none());
297
        assert!(h3.store(&s2).is_ok());
298

            
299
        assert_eq!(h1.load().unwrap(), Some(v1));
300
        assert_eq!(h2.load().unwrap(), Some(s1));
301
        assert_eq!(h3.load().unwrap(), Some(s2));
302
    }
303

            
304
    #[test]
305
    fn futureproof() {
306
        use crate::Futureproof;
307

            
308
        let v1 = Ex1 { v1: 8, v2: 99 };
309

            
310
        let v1_ser = serde_json::to_string(&v1).unwrap();
311

            
312
        let v1_as_ex1: Futureproof<Ex1> = serde_json::from_str(&v1_ser).unwrap();
313
        let v1_as_ex2: Futureproof<Ex2> = serde_json::from_str(&v1_ser).unwrap();
314
        assert!(v1_as_ex1.clone().into_option().is_some());
315
        assert!(v1_as_ex2.into_option().is_none());
316

            
317
        assert_eq!(serde_json::to_string(&v1_as_ex1).unwrap(), v1_ser);
318
    }
319

            
320
    #[test]
321
    fn futureproof_enums() {
322
        use crate::Futureproof;
323

            
324
        let new1 = NewEnum::Variant1;
325
        let new2 = NewEnum::Variant2;
326

            
327
        let new1_ser = serde_json::to_string(&new1).unwrap();
328
        let new2_ser = serde_json::to_string(&new2).unwrap();
329

            
330
        let old1: Futureproof<OldEnum> = serde_json::from_str(&new1_ser).unwrap();
331
        let old2: Futureproof<OldEnum> = serde_json::from_str(&new2_ser).unwrap();
332

            
333
        assert!(old1.into_option().is_some());
334
        assert!(old2.into_option().is_none());
335
    }
336
}