diem_proptest_helpers/lib.rs
1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8#![forbid(unsafe_code)]
9
10#[cfg(test)]
11mod unit_tests;
12
13mod growing_subset;
14mod repeat_vec;
15mod value_generator;
16
17pub use crate::{
18 growing_subset::GrowingSubset, repeat_vec::RepeatVec,
19 value_generator::ValueGenerator,
20};
21
22use crossbeam::thread;
23use proptest::sample::Index as PropIndex;
24use proptest_derive::Arbitrary;
25use std::{
26 any::Any,
27 collections::BTreeSet,
28 ops::{Deref, Index as OpsIndex},
29};
30
31/// Creates a new thread with a larger stack size.
32///
33/// Generating some proptest values can overflow the stack. This allows test
34/// authors to work around this limitation.
35///
36/// This is expected to be used with closure-style proptest invocations:
37///
38/// ```
39/// use proptest::prelude::*;
40/// use diem_proptest_helpers::with_stack_size;
41///
42/// with_stack_size(4 * 1024 * 1024, || proptest!(|(x in 0usize..128)| {
43/// // assertions go here
44/// prop_assert!(x >= 0 && x < 128);
45/// }));
46/// ```
47pub fn with_stack_size<'a, F, T>(
48 size: usize, f: F,
49) -> Result<T, Box<dyn Any + 'static + Send>>
50where
51 F: FnOnce() -> T + Send + 'a,
52 T: Send + 'a,
53{
54 thread::scope(|s| {
55 let handle =
56 s.builder().stack_size(size).spawn(|_| f()).map_err(|err| {
57 let any: Box<dyn Any + 'static + Send> = Box::new(err);
58 any
59 })?;
60 handle.join()
61 })?
62}
63
64/// Given a maximum value `max` and a list of [`Index`](proptest::sample::Index)
65/// instances, picks integers in the range `[0, max)` uniformly randomly and
66/// without duplication.
67///
68/// If `indexes_len` is greater than `max`, all indexes will be returned.
69///
70/// This function implements [Robert Floyd's F2
71/// algorithm](https://blog.acolyer.org/2018/01/30/a-sample-of-brilliance/) for sampling without
72/// replacement.
73pub fn pick_idxs<T, P>(
74 max: usize, indexes: &T, indexes_len: usize,
75) -> Vec<usize>
76where
77 T: OpsIndex<usize, Output = P> + ?Sized,
78 P: AsRef<PropIndex>,
79{
80 // See https://blog.acolyer.org/2018/01/30/a-sample-of-brilliance/ (the F2 algorithm)
81 // for a longer explanation. This is a variant that works with
82 // zero-indexing.
83 let mut selected = BTreeSet::new();
84 let to_select = indexes_len.min(max);
85 for (iter_idx, choice) in ((max - to_select)..max).enumerate() {
86 // "RandInt(1, J)" in the original algorithm means a number between 1
87 // and choice, both inclusive. `PropIndex::index` picks a number between
88 // 0 and whatever's passed in, with the latter exclusive. Pass
89 // in "+1" to ensure the same range of values is picked from.
90 // (This also ensures that if choice is 0 then `index`
91 // doesn't panic.
92 let idx = indexes[iter_idx].as_ref().index(choice + 1);
93 if !selected.insert(idx) {
94 selected.insert(choice);
95 }
96 }
97 selected.into_iter().collect()
98}
99
100/// Given a maximum value `max` and a slice of
101/// [`Index`](proptest::sample::Index) instances, picks integers in the range
102/// `[0, max)` uniformly randomly and without duplication.
103///
104/// If the number of `Index` instances is greater than `max`, all indexes will
105/// be returned.
106///
107/// This function implements [Robert Floyd's F2
108/// algorithm](https://blog.acolyer.org/2018/01/30/a-sample-of-brilliance/) for sampling without
109/// replacement.
110#[inline]
111pub fn pick_slice_idxs(
112 max: usize, indexes: &[impl AsRef<PropIndex>],
113) -> Vec<usize> {
114 pick_idxs(max, indexes, indexes.len())
115}
116
117/// Wrapper for `proptest`'s [`Index`][proptest::sample::Index] that allows
118/// `AsRef` to work.
119///
120/// There is no blanket `impl<T> AsRef<T> for T`, so `&[PropIndex]` doesn't work
121/// with `&[impl AsRef<PropIndex>]` (unless an impl gets added upstream).
122/// `Index` does.
123#[derive(Arbitrary, Clone, Copy, Debug)]
124pub struct Index(PropIndex);
125
126impl AsRef<PropIndex> for Index {
127 fn as_ref(&self) -> &PropIndex { &self.0 }
128}
129
130impl Deref for Index {
131 type Target = PropIndex;
132
133 fn deref(&self) -> &PropIndex { &self.0 }
134}