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}