1 //! Let it torture the implementation with some randomized operations.
2 
3 use std::mem;
4 use std::sync::Arc;
5 
6 use arc_swap::{ArcSwapAny, DefaultStrategy, IndependentStrategy};
7 use once_cell::sync::Lazy;
8 use proptest::prelude::*;
9 
10 #[derive(Copy, Clone, Debug)]
11 enum OpsInstruction {
12     Store(usize),
13     Swap(usize),
14     LoadFull,
15     Load,
16 }
17 
18 impl OpsInstruction {
random() -> impl Strategy<Value = Self>19     fn random() -> impl Strategy<Value = Self> {
20         prop_oneof![
21             any::<usize>().prop_map(Self::Store),
22             any::<usize>().prop_map(Self::Swap),
23             Just(Self::LoadFull),
24             Just(Self::Load),
25         ]
26     }
27 }
28 
29 proptest! {}
30 
31 const LIMIT: usize = 5;
32 #[cfg(not(miri))]
33 const SIZE: usize = 100;
34 #[cfg(miri)]
35 const SIZE: usize = 10;
36 static ARCS: Lazy<Vec<Arc<usize>>> = Lazy::new(|| (0..LIMIT).map(Arc::new).collect());
37 
38 #[derive(Copy, Clone, Debug)]
39 enum SelInstruction {
40     Swap(usize),
41     Cas(usize, usize),
42 }
43 
44 impl SelInstruction {
random() -> impl Strategy<Value = Self>45     fn random() -> impl Strategy<Value = Self> {
46         prop_oneof![
47             (0..LIMIT).prop_map(Self::Swap),
48             (0..LIMIT, 0..LIMIT).prop_map(|(cur, new)| Self::Cas(cur, new)),
49         ]
50     }
51 }
52 
53 // Generate the same tests for bunch of strategies (one module for one strategy)
54 macro_rules! t {
55     (@full => $name: ident, $strategy: ty) => {
56         t!(@compose => $name, $strategy,
57             #[test]
58             fn selection(
59                 instructions in proptest::collection::vec(SelInstruction::random(), 1..SIZE),
60             ) {
61                 let mut bare = Arc::clone(&ARCS[0]);
62                 #[allow(deprecated)] // We use "deprecated" testing strategies in here.
63                 let a = ArcSwapAny::<_, $strategy>::from(Arc::clone(&ARCS[0]));
64                 for ins in instructions {
65                     match ins {
66                         SelInstruction::Swap(idx) => {
67                             let expected = mem::replace(&mut bare, Arc::clone(&ARCS[idx]));
68                             let actual = a.swap(Arc::clone(&ARCS[idx]));
69                             assert!(Arc::ptr_eq(&expected, &actual));
70                         }
71                         SelInstruction::Cas(cur, new) => {
72                             let expected = Arc::clone(&bare);
73                             if bare == ARCS[cur] {
74                                 bare = Arc::clone(&ARCS[new]);
75                             }
76                             let actual = a.compare_and_swap(&ARCS[cur], Arc::clone(&ARCS[new]));
77                             assert!(Arc::ptr_eq(&expected, &actual));
78                         }
79                     }
80                 }
81             }
82         );
83     };
84     (@nocas => $name: ident, $strategy: ty) => {
85         t!(@compose => $name, $strategy, );
86     };
87     (@compose => $name: ident, $strategy: ty, $($extra: tt)*) => {
88         mod $name {
89             use super::*;
90 
91             proptest! {
92                 $($extra)*
93 
94                 #[test]
95                 fn ops(
96                     instructions in proptest::collection::vec(OpsInstruction::random(), 1..SIZE),
97                 ) {
98                     use crate::OpsInstruction::*;
99                     let mut m = 0;
100                     #[allow(deprecated)] // We use "deprecated" testing strategies in here.
101                     let a = ArcSwapAny::<_, $strategy>::new(Arc::new(0usize));
102                     for ins in instructions {
103                         match ins {
104                             Store(v) => {
105                                 m = v;
106                                 a.store(Arc::new(v));
107                             }
108                             Swap(v) => {
109                                 let old = mem::replace(&mut m, v);
110                                 assert_eq!(old, *a.swap(Arc::new(v)));
111                             }
112                             Load => assert_eq!(m, **a.load()),
113                             LoadFull => assert_eq!(m, *a.load_full()),
114                         }
115                     }
116                 }
117             }
118         }
119     };
120 }
121 
122 t!(@full => default, DefaultStrategy);
123 t!(@full => independent, IndependentStrategy);
124 #[cfg(feature = "internal-test-strategies")]
125 t!(@full => full_slots, arc_swap::strategy::test_strategies::FillFastSlots);
126