1extern crate permutohedron;
95
96pub extern crate proptest as pt;
97#[doc(hidden)]
98pub use pt::*;
99
100pub struct Shared<T>(*mut T);
101
102impl<T> Clone for Shared<T> {
103    fn clone(&self) -> Shared<T> {
104        *self
105    }
106}
107
108impl<T> Copy for Shared<T> {}
109
110unsafe impl<T> Sync for Shared<T> {}
111
112unsafe impl<T> Send for Shared<T> {}
113
114impl<T> std::ops::Deref for Shared<T> {
115    type Target = T;
116
117    fn deref(&self) -> &T {
118        unsafe { &*self.0 }
119    }
120}
121
122impl<T> Shared<T> {
123    pub fn new(inner: T) -> Shared<T> {
124        Shared(Box::into_raw(Box::new(inner)))
125    }
126}
127
128#[macro_export]
129macro_rules! model {
130    (
131        Model => $model:stmt,
132        Implementation => $implementation:stmt,
133        $($op:ident ($($type:ty),*) ($parm:pat in $strategy:expr) => $body:expr),*
134    ) => {
135        model! {
136            Config => $crate::default_config(file!()),
137            Model => $model,
138            Implementation => $implementation,
139            $($op ($($type),*) ($parm in $strategy) => $body),*
140        }
141    };
142    (
143        Config => $config:expr,
144        Model => $model:stmt,
145        Implementation => $implementation:stmt,
146        $($op:ident ($($type:ty),*) ($parm:pat in $strategy:expr) => $body:expr),*
147    ) => {
148        use $crate::pt::collection::vec as prop_vec;
149        use $crate::pt::prelude::*;
150        use $crate::pt::test_runner::TestRunner;
151
152        #[derive(Debug)]
153        enum Op {
154            $(
155                $op($($type),*)
156            ),*
157        }
158
159        fn arb() -> BoxedStrategy<Vec<Op>> {
160            prop_vec(
161                prop_oneof![
162                    $(
163                        $strategy.prop_map(Op::$op)
164                    ),*
165                ],
166                0..40,
167            ).boxed()
168        }
169
170        let config = $config;
171        let mut runner = TestRunner::new(config);
172
173        match runner.run(&arb(), |ops| {
174            $model;
175            $implementation;
176
177            for op in ops {
178                match op {
179                    $(Op::$op($parm) => $body),*
180                };
181            };
182            Ok(())
183        }) {
184            Ok(_) => {}
185            Err(e) =>  panic!("{}\n{}", e, runner),
186        }
187    }
188}
189
190#[macro_export]
191macro_rules! linearizable {
192    (
193        Implementation => $implementation:stmt,
194        $($op:ident ($($type:ty),*) ($parm:pat in $strategy:expr) -> $ret:ty $body:block),*
195    ) => {
196        linearizable! {
197            Config => $crate::default_config(file!()),
198            Implementation => $implementation,
199            $($op ($($type),*) ($parm in $strategy) -> $ret $body),*
200        }
201    };
202    (
203        Config => $config:expr,
204        Implementation => $implementation:stmt,
205        $($op:ident ($($type:ty),*) ($parm:pat in $strategy:expr) -> $ret:ty $body:block),*
206    ) => {
207        use $crate::pt::collection::vec as prop_vec;
208        use $crate::pt::prelude::*;
209        use $crate::pt::test_runner::TestRunner;
210
211        use std::ops::Deref as StdDeref;
212
213        #[derive(Debug, Clone)]
214        enum Op {
215            $(
216                $op($($type),*)
217            ),*
218        }
219
220        #[derive(Debug, PartialEq)]
221        enum Ret {
222            $(
223                $op($ret)
224            ),*
225        }
226
227        fn arb() -> BoxedStrategy<(usize, Vec<Op>)> {
228            prop_vec(
229                prop_oneof![
230                    $(
231                        $strategy.prop_map(Op::$op)
232                    ),*
233                ],
234                1..4,
235            )
236                .prop_flat_map(|ops| (0..ops.len(), Just(ops)))
237                .boxed()
238        }
239
240        let config = $config;
241        let mut runner = TestRunner::new(config);
242
243        match runner.run(&arb(), |(split_point, ref ops)| {
244            $implementation;
245
246            let ops1 = ops[0..split_point].to_vec();
247            let ops2 = ops[split_point..].to_vec();
248
249            let t1 = std::thread::spawn(move || {
250                    let mut ret = vec![];
251                    for op in ops1 {
252                        ret.push(match op {
253                            $(
254                                Op::$op($parm) => Ret::$op($body)
255                            ),*
256                        });
257                    }
258                    ret
259                });
260
261            let t2 = std::thread::spawn(move || {
262                    let mut ret = vec![];
263                    for op in ops2 {
264                        ret.push(match op {
265                            $(
266                                Op::$op($parm) => Ret::$op($body)
267                            ),*
268                        });
269                    }
270                    ret
271                });
272
273            let r1 = t1.join().expect("thread should not panic");
274            let r2 = t2.join().expect("thread should not panic");
275
276            let o1 = ops[0..split_point].to_vec();
278            let o2 = ops[split_point..].to_vec();
279
280            let calls1: Vec<(Op, Ret)> = o1.into_iter().zip(r1.into_iter()).collect();
281            let calls2: Vec<(Op, Ret)> = o2.into_iter().zip(r2.into_iter()).collect();
282            let mut indexes = vec![0; calls1.len()];
283            indexes.resize(calls1.len() + calls2.len(), 1);
284            let calls = vec![calls1, calls2];
285
286            let mut linearizable = false;
287
288            let call_permutations = $crate::permutohedron_heap(&mut indexes);
289
290            'outer: for walk in call_permutations {
291                $implementation;
292
293                let mut indexes = vec![0, 0];
294                for idx in walk {
297                    let (ref op, ref expected) = calls[idx][indexes[idx]];
298                    indexes[idx] += 1;
299
300                    match *op {
301                        $(
302                            Op::$op($parm) => {
303                                let ret = Ret::$op($body);
304                                if ret != *expected {
305                                    continue 'outer;
306                                }
307                            }
308                        ),*
309                    }
310                }
311
312                linearizable = true;
313                break;
314            }
315
316            assert!(linearizable, "operations are not linearizable: {:?}", calls);
317
318            Ok(())
319        }) {
320            Ok(_) => {}
321            Err(e) =>  panic!("{}\n{}", e, runner),
322        }
323    };
324}
325
326pub fn permutohedron_heap<'a, Data, T>(
327    orig: &'a mut Data,
328) -> permutohedron::Heap<'a, Data, T>
329where
330    Data: 'a + Sized + AsMut<[T]>,
331    T: 'a,
332{
333    permutohedron::Heap::new(orig)
334}
335
336use pt::test_runner::Config;
338
339pub fn default_config(file: &'static str) -> Config {
340    let cases = std::env::var("PROPTEST_CASES")
341        .ok()
342        .and_then(|val| val.parse().ok())
343        .unwrap_or(1000);
344    Config::with_cases(cases).clone_with_source_file(file)
345}