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