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(feature="cargo-clippy", allow(implicit_hasher))]
13 
14 //==============================================================================
15 // Imports:
16 //==============================================================================
17 
18 use 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 };
22 use core::hash::Hash;
23 use core::ops::{Bound, RangeInclusive};
24 
25 #[cfg(feature = "std")]
26 use crate::std_facade::{hash_map, hash_set, HashMap, HashSet};
27 
28 use crate::arbitrary::*;
29 use crate::collection::*;
30 use crate::strategy::statics::static_map;
31 use crate::strategy::*;
32 
33 //==============================================================================
34 // Macros:
35 //==============================================================================
36 
37 /// Parameters for configuring the generation of `StrategyFor<...<A>>`.
38 type RangedParams1<A> = product_type![SizeRange, A];
39 
40 /// Parameters for configuring the generation of `StrategyFor<...<A, B>>`.
41 type RangedParams2<A, B> = product_type![SizeRange, A, B];
42 
43 macro_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 
57 arbitrary!(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 
65 macro_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 
75 impl_1!(Vec, VecStrategy, => vec);
76 dst_wrapped!(Box, Rc, Arc);
77 impl_1!(VecDeque, VecDequeStrategy, => vec_deque);
78 impl_1!(LinkedList, LinkedListStrategy, => linked_list);
79 impl_1!(BTreeSet, BTreeSetStrategy, Ord => btree_set);
80 impl_1!(BinaryHeap, BinaryHeapStrategy, Ord => binary_heap);
81 #[cfg(feature = "std")]
82 impl_1!(HashSet, HashSetStrategy, Hash, Eq => hash_set);
83 
84 //==============================================================================
85 // IntoIterator:
86 //==============================================================================
87 
88 macro_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 
102 into_iter_1!(vec, Vec);
103 into_iter_1!(vec_deque, VecDeque);
104 into_iter_1!(linked_list, LinkedList);
105 into_iter_1!(btree_set, BTreeSet, Ord);
106 into_iter_1!(binary_heap, BinaryHeap, Ord);
107 #[cfg(feature = "std")]
108 into_iter_1!(hash_set, HashSet, Hash, Eq);
109 
110 //==============================================================================
111 // HashMap:
112 //==============================================================================
113 
114 #[cfg(feature = "std")]
115 arbitrary!([A: Arbitrary + Hash + Eq, B: Arbitrary] HashMap<A, B>,
116 HashMapStrategy<A::Strategy, B::Strategy>,
117 RangedParams2<A::Parameters, B::Parameters>;
118 args => {
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")]
124 arbitrary!([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")]
130 lift1!([, 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")]
139 lift1!(['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")]
148 impl<A: fmt::Debug + Eq + Hash, B: fmt::Debug> functor::ArbitraryF2<A, B>
149     for HashMap<A, B>
150 {
151     type Parameters = SizeRange;
152 
lift2_with<AS, BS>( fst: AS, snd: BS, args: Self::Parameters, ) -> BoxedStrategy<Self> where AS: Strategy<Value = A> + 'static, BS: Strategy<Value = B> + 'static,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")]
167 impl<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 
lift2_with<AS, BS>( fst: AS, snd: BS, args: Self::Parameters, ) -> BoxedStrategy<Self> where AS: Strategy<Value = A> + 'static, BS: Strategy<Value = B> + 'static,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 
189 arbitrary!([A: Arbitrary + Ord, B: Arbitrary] BTreeMap<A, B>,
190 BTreeMapStrategy<A::Strategy, B::Strategy>,
191 RangedParams2<A::Parameters, B::Parameters>;
192 args => {
193     let product_unpack![range, a, b] = args;
194     btree_map(any_with::<A>(a), any_with::<B>(b), range)
195 });
196 
197 lift1!([, 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 
205 impl<A: fmt::Debug + Ord, B: fmt::Debug> functor::ArbitraryF2<A, B>
206     for BTreeMap<A, B>
207 {
208     type Parameters = SizeRange;
lift2_with<AS, BS>( fst: AS, snd: BS, args: Self::Parameters, ) -> BoxedStrategy<Self> where AS: Strategy<Value = A> + 'static, BS: Strategy<Value = B> + 'static,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 
222 arbitrary!([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 
227 impl<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 
lift2_with<AS, BS>( fst: AS, snd: BS, args: Self::Parameters, ) -> BoxedStrategy<Self> where AS: Strategy<Value = A> + 'static, BS: Strategy<Value = B> + 'static,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 
249 arbitrary!([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 
266 lift1!(['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)]
276 mod 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 }
305