proptest/arbitrary/_alloc/
collections.rs

1//-
2// Copyright 2017, 2018 The proptest developers
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Arbitrary implementations for `std::collections`.
11
12//#![cfg_attr(clippy, allow(implicit_hasher))]
13
14//==============================================================================
15// Imports:
16//==============================================================================
17
18use crate::std_facade::{
19    binary_heap, btree_map, btree_set, fmt, linked_list, vec, vec_deque, Arc,
20    BTreeMap, BTreeSet, BinaryHeap, Box, LinkedList, Rc, Vec, VecDeque,
21};
22use core::hash::Hash;
23use core::ops::{Bound, RangeInclusive};
24
25#[cfg(feature = "std")]
26use crate::std_facade::{hash_map, hash_set, HashMap, HashSet};
27
28use crate::arbitrary::*;
29use crate::collection::*;
30use crate::strategy::statics::static_map;
31use crate::strategy::*;
32
33//==============================================================================
34// Macros:
35//==============================================================================
36
37/// Parameters for configuring the generation of `StrategyFor<...<A>>`.
38type RangedParams1<A> = product_type![SizeRange, A];
39
40/// Parameters for configuring the generation of `StrategyFor<...<A, B>>`.
41type RangedParams2<A, B> = product_type![SizeRange, A, B];
42
43macro_rules! impl_1 {
44    ($typ: ident, $strat: ident, $($bound : path),* => $fun: ident) => {
45        arbitrary!([A: Arbitrary $(+ $bound)*] $typ<A>,
46            $strat<A::Strategy>, RangedParams1<A::Parameters>;
47            args => {
48                let product_unpack![range, a] = args;
49                $fun(any_with::<A>(a), range)
50            });
51
52        lift1!([$($bound+)*] $typ<A>, SizeRange;
53            base, args => $fun(base, args));
54    };
55}
56
57arbitrary!(SizeRange, MapInto<StrategyFor<RangeInclusive<usize>>, Self>;
58    any::<RangeInclusive<usize>>().prop_map_into()
59);
60
61//==============================================================================
62// Vec, VecDeque, LinkedList, BTreeSet, BinaryHeap, HashSet, HashMap:
63//==============================================================================
64
65macro_rules! dst_wrapped {
66    ($($w: ident),*) => {
67        $(arbitrary!([A: Arbitrary] $w<[A]>,
68            MapInto<StrategyFor<Vec<A>>, Self>,
69            <Vec<A> as Arbitrary>::Parameters;
70            a => any_with::<Vec<A>>(a).prop_map_into()
71        );)*
72    };
73}
74
75impl_1!(Vec, VecStrategy, => vec);
76dst_wrapped!(Box, Rc, Arc);
77impl_1!(VecDeque, VecDequeStrategy, => vec_deque);
78impl_1!(LinkedList, LinkedListStrategy, => linked_list);
79impl_1!(BTreeSet, BTreeSetStrategy, Ord => btree_set);
80impl_1!(BinaryHeap, BinaryHeapStrategy, Ord => binary_heap);
81#[cfg(feature = "std")]
82impl_1!(HashSet, HashSetStrategy, Hash, Eq => hash_set);
83
84//==============================================================================
85// IntoIterator:
86//==============================================================================
87
88macro_rules! into_iter_1 {
89    ($module: ident, $type: ident $(, $bound : path)*) => {
90        arbitrary!([A: Arbitrary $(+ $bound)*]
91            $module::IntoIter<A>,
92            SMapped<$type<A>, Self>,
93            <$type<A> as Arbitrary>::Parameters;
94            args => static_map(any_with::<$type<A>>(args), $type::into_iter));
95
96        lift1!(['static + $($bound+)*] $module::IntoIter<A>, SizeRange;
97            base, args =>
98                $module(base, args).prop_map($type::into_iter));
99    };
100}
101
102into_iter_1!(vec, Vec);
103into_iter_1!(vec_deque, VecDeque);
104into_iter_1!(linked_list, LinkedList);
105into_iter_1!(btree_set, BTreeSet, Ord);
106into_iter_1!(binary_heap, BinaryHeap, Ord);
107#[cfg(feature = "std")]
108into_iter_1!(hash_set, HashSet, Hash, Eq);
109
110//==============================================================================
111// HashMap:
112//==============================================================================
113
114#[cfg(feature = "std")]
115arbitrary!([A: Arbitrary + Hash + Eq, B: Arbitrary] HashMap<A, B>,
116HashMapStrategy<A::Strategy, B::Strategy>,
117RangedParams2<A::Parameters, B::Parameters>;
118args => {
119    let product_unpack![range, a, b] = args;
120    hash_map(any_with::<A>(a), any_with::<B>(b), range)
121});
122
123#[cfg(feature = "std")]
124arbitrary!([A: Arbitrary + Hash + Eq, B: Arbitrary] hash_map::IntoIter<A, B>,
125    SMapped<HashMap<A, B>, Self>,
126    <HashMap<A, B> as Arbitrary>::Parameters;
127    args => static_map(any_with::<HashMap<A, B>>(args), HashMap::into_iter));
128
129#[cfg(feature = "std")]
130lift1!([, K: Hash + Eq + Arbitrary + 'static] HashMap<K, A>,
131    RangedParams1<K::Parameters>;
132    base, args => {
133        let product_unpack![range, k] = args;
134        hash_map(any_with::<K>(k), base, range)
135    }
136);
137
138#[cfg(feature = "std")]
139lift1!(['static, K: Hash + Eq + Arbitrary + 'static] hash_map::IntoIter<K, A>,
140    RangedParams1<K::Parameters>;
141    base, args => {
142        let product_unpack![range, k] = args;
143        static_map(hash_map(any_with::<K>(k), base, range), HashMap::into_iter)
144    }
145);
146
147#[cfg(feature = "std")]
148impl<A: fmt::Debug + Eq + Hash, B: fmt::Debug> functor::ArbitraryF2<A, B>
149    for HashMap<A, B>
150{
151    type Parameters = SizeRange;
152
153    fn lift2_with<AS, BS>(
154        fst: AS,
155        snd: BS,
156        args: Self::Parameters,
157    ) -> BoxedStrategy<Self>
158    where
159        AS: Strategy<Value = A> + 'static,
160        BS: Strategy<Value = B> + 'static,
161    {
162        hash_map(fst, snd, args).boxed()
163    }
164}
165
166#[cfg(feature = "std")]
167impl<A: fmt::Debug + Eq + Hash + 'static, B: fmt::Debug + 'static>
168    functor::ArbitraryF2<A, B> for hash_map::IntoIter<A, B>
169{
170    type Parameters = SizeRange;
171
172    fn lift2_with<AS, BS>(
173        fst: AS,
174        snd: BS,
175        args: Self::Parameters,
176    ) -> BoxedStrategy<Self>
177    where
178        AS: Strategy<Value = A> + 'static,
179        BS: Strategy<Value = B> + 'static,
180    {
181        static_map(hash_map(fst, snd, args), HashMap::into_iter).boxed()
182    }
183}
184
185//==============================================================================
186// BTreeMap:
187//==============================================================================
188
189arbitrary!([A: Arbitrary + Ord, B: Arbitrary] BTreeMap<A, B>,
190BTreeMapStrategy<A::Strategy, B::Strategy>,
191RangedParams2<A::Parameters, B::Parameters>;
192args => {
193    let product_unpack![range, a, b] = args;
194    btree_map(any_with::<A>(a), any_with::<B>(b), range)
195});
196
197lift1!([, K: Ord + Arbitrary + 'static] BTreeMap<K, A>,
198    RangedParams1<K::Parameters>;
199    base, args => {
200        let product_unpack![range, k] = args;
201        btree_map(any_with::<K>(k), base, range)
202    }
203);
204
205impl<A: fmt::Debug + Ord, B: fmt::Debug> functor::ArbitraryF2<A, B>
206    for BTreeMap<A, B>
207{
208    type Parameters = SizeRange;
209    fn lift2_with<AS, BS>(
210        fst: AS,
211        snd: BS,
212        args: Self::Parameters,
213    ) -> BoxedStrategy<Self>
214    where
215        AS: Strategy<Value = A> + 'static,
216        BS: Strategy<Value = B> + 'static,
217    {
218        btree_map(fst, snd, args).boxed()
219    }
220}
221
222arbitrary!([A: Arbitrary + Ord, B: Arbitrary] btree_map::IntoIter<A, B>,
223    SMapped<BTreeMap<A, B>, Self>,
224    <BTreeMap<A, B> as Arbitrary>::Parameters;
225    args => static_map(any_with::<BTreeMap<A, B>>(args), BTreeMap::into_iter));
226
227impl<A: fmt::Debug + Ord + 'static, B: fmt::Debug + 'static>
228    functor::ArbitraryF2<A, B> for btree_map::IntoIter<A, B>
229{
230    type Parameters = SizeRange;
231
232    fn lift2_with<AS, BS>(
233        fst: AS,
234        snd: BS,
235        args: Self::Parameters,
236    ) -> BoxedStrategy<Self>
237    where
238        AS: Strategy<Value = A> + 'static,
239        BS: Strategy<Value = B> + 'static,
240    {
241        static_map(btree_map(fst, snd, args), BTreeMap::into_iter).boxed()
242    }
243}
244
245//==============================================================================
246// Bound:
247//==============================================================================
248
249arbitrary!([A: Arbitrary] Bound<A>,
250    TupleUnion<(
251        WA<SFnPtrMap<Arc<A::Strategy>, Self>>,
252        WA<SFnPtrMap<Arc<A::Strategy>, Self>>,
253        WA<LazyJustFn<Self>>
254    )>,
255    A::Parameters;
256    args => {
257        let base = Arc::new(any_with::<A>(args));
258        prop_oneof![
259            2 => static_map(base.clone(), Bound::Included),
260            2 => static_map(base, Bound::Excluded),
261            1 => LazyJust::new(|| Bound::Unbounded),
262        ]
263    }
264);
265
266lift1!(['static] Bound<A>; base => {
267    let base = Rc::new(base);
268    prop_oneof![
269        2 => base.clone().prop_map(Bound::Included),
270        2 => base.prop_map(Bound::Excluded),
271        1 => LazyJustFn::new(|| Bound::Unbounded),
272    ]
273});
274
275#[cfg(test)]
276mod test {
277    no_panic_test!(
278        size_bounds => SizeRange,
279        vec => Vec<u8>,
280        box_slice => Box<[u8]>,
281        rc_slice  => Rc<[u8]>,
282        arc_slice  => Arc<[u8]>,
283        vec_deque => VecDeque<u8>,
284        linked_list => LinkedList<u8>,
285        btree_set => BTreeSet<u8>,
286        btree_map => BTreeMap<u8, u8>,
287        bound => Bound<u8>,
288        binary_heap => BinaryHeap<u8>,
289        into_iter_vec => vec::IntoIter<u8>,
290        into_iter_vec_deque => vec_deque::IntoIter<u8>,
291        into_iter_linked_list => linked_list::IntoIter<u8>,
292        into_iter_binary_heap => binary_heap::IntoIter<u8>,
293        into_iter_btree_set => btree_set::IntoIter<u8>,
294        into_iter_btree_map => btree_map::IntoIter<u8, u8>
295    );
296
297    #[cfg(feature = "std")]
298    no_panic_test!(
299        hash_set => HashSet<u8>,
300        hash_map => HashMap<u8, u8>,
301        into_iter_hash_set => hash_set::IntoIter<u8>,
302        into_iter_hash_map => hash_map::IntoIter<u8, u8>
303    );
304}