//- // Copyright 2017, 2018 The proptest developers // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Strategies for generating `std::collections` of values. use core::cmp::Ord; use core::hash::Hash; use core::ops::{Add, Range, RangeInclusive, RangeTo, RangeToInclusive}; use core::usize; use crate::std_facade::{ fmt, BTreeMap, BTreeSet, BinaryHeap, LinkedList, Vec, VecDeque, }; #[cfg(feature = "std")] use crate::std_facade::{HashMap, HashSet}; use crate::bits::{BitSetLike, VarBitSet}; use crate::num::sample_uniform_incl; use crate::strategy::*; use crate::test_runner::*; use crate::tuple::TupleValueTree; //============================================================================== // SizeRange //============================================================================== /// The minimum and maximum range/bounds on the size of a collection. /// The interval must form a subset of `[0, std::usize::MAX)`. /// /// A value like `0..=std::usize::MAX` will still be accepted but will silently /// truncate the maximum to `std::usize::MAX - 1`. /// /// The `Default` is `0..100`. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct SizeRange(Range); /// Creates a `SizeRange` from some value that is convertible into it. pub fn size_range(from: impl Into) -> SizeRange { from.into() } impl Default for SizeRange { /// Constructs a `SizeRange` equivalent to `size_range(0..100)`. fn default() -> Self { size_range(0..100) } } impl SizeRange { /// Creates a `SizeBounds` from a `RangeInclusive`. pub fn new(range: RangeInclusive) -> Self { range.into() } // Don't rely on these existing internally: /// Merges self together with some other argument producing a product /// type expected by some implementations of `A: Arbitrary` in /// `A::Parameters`. This can be more ergonomic to work with and may /// help type inference. pub fn with(self, and: X) -> product_type![Self, X] { product_pack![self, and] } /// Merges self together with some other argument generated with a /// default value producing a product type expected by some /// implementations of `A: Arbitrary` in `A::Parameters`. /// This can be more ergonomic to work with and may help type inference. pub fn lift(self) -> product_type![Self, X] { self.with(Default::default()) } /// The lower bound of the range (inclusive). pub fn start(&self) -> usize { self.0.start } /// Extract the ends `[low, high]` of a `SizeRange`. pub fn start_end_incl(&self) -> (usize, usize) { (self.start(), self.end_incl()) } /// The upper bound of the range (inclusive). pub fn end_incl(&self) -> usize { self.0.end - 1 } /// The upper bound of the range (exclusive). pub fn end_excl(&self) -> usize { self.0.end } pub(crate) fn iter(&self) -> impl Iterator { self.0.clone().into_iter() } pub(crate) fn is_empty(&self) -> bool { self.start() == self.end_excl() } pub(crate) fn assert_nonempty(&self) { if self.is_empty() { panic!( "Invalid use of empty size range. (hint: did you \ accidentally write {}..{} where you meant {}..={} \ somewhere?)", self.start(), self.end_excl(), self.start(), self.end_excl() ); } } } /// Given `(low: usize, high: usize)`, /// then a size range of `[low..high)` is the result. impl From<(usize, usize)> for SizeRange { fn from((low, high): (usize, usize)) -> Self { size_range(low..high) } } /// Given `exact`, then a size range of `[exact, exact]` is the result. impl From for SizeRange { fn from(exact: usize) -> Self { size_range(exact..=exact) } } /// Given `..high`, then a size range `[0, high)` is the result. impl From> for SizeRange { fn from(high: RangeTo) -> Self { size_range(0..high.end) } } /// Given `low .. high`, then a size range `[low, high)` is the result. impl From> for SizeRange { fn from(r: Range) -> Self { SizeRange(r) } } /// Given `low ..= high`, then a size range `[low, high]` is the result. impl From> for SizeRange { fn from(r: RangeInclusive) -> Self { size_range(*r.start()..r.end().saturating_add(1)) } } /// Given `..=high`, then a size range `[0, high]` is the result. impl From> for SizeRange { fn from(high: RangeToInclusive) -> Self { size_range(0..=high.end) } } #[cfg(feature = "frunk")] impl Generic for SizeRange { type Repr = RangeInclusive; /// Converts the `SizeRange` into `Range`. fn into(self) -> Self::Repr { self.0 } /// Converts `RangeInclusive` into `SizeRange`. fn from(r: Self::Repr) -> Self { r.into() } } /// Adds `usize` to both start and end of the bounds. /// /// Panics if adding to either end overflows `usize`. impl Add for SizeRange { type Output = SizeRange; fn add(self, rhs: usize) -> Self::Output { let (start, end) = self.start_end_incl(); size_range((start + rhs)..=(end + rhs)) } } //============================================================================== // Strategies //============================================================================== /// Strategy to create `Vec`s with a length in a certain range. /// /// Created by the `vec()` function in the same module. #[must_use = "strategies do nothing unless used"] #[derive(Clone, Debug)] pub struct VecStrategy { element: T, size: SizeRange, } /// Create a strategy to generate `Vec`s containing elements drawn from /// `element` and with a size range given by `size`. /// /// To make a `Vec` with a fixed number of elements, each with its own /// strategy, you can instead make a `Vec` of strategies (boxed if necessary). pub fn vec( element: T, size: impl Into, ) -> VecStrategy { let size = size.into(); size.assert_nonempty(); VecStrategy { element, size } } mapfn! { [] fn VecToDeque[](vec: Vec) -> VecDeque { vec.into() } } opaque_strategy_wrapper! { /// Strategy to create `VecDeque`s with a length in a certain range. /// /// Created by the `vec_deque()` function in the same module. #[derive(Clone, Debug)] pub struct VecDequeStrategy[][where T : Strategy]( statics::Map, VecToDeque>) -> VecDequeValueTree; /// `ValueTree` corresponding to `VecDequeStrategy`. #[derive(Clone, Debug)] pub struct VecDequeValueTree[][where T : ValueTree]( statics::Map, VecToDeque>) -> VecDeque; } /// Create a strategy to generate `VecDeque`s containing elements drawn from /// `element` and with a size range given by `size`. pub fn vec_deque( element: T, size: impl Into, ) -> VecDequeStrategy { VecDequeStrategy(statics::Map::new(vec(element, size), VecToDeque)) } mapfn! { [] fn VecToLl[](vec: Vec) -> LinkedList { vec.into_iter().collect() } } opaque_strategy_wrapper! { /// Strategy to create `LinkedList`s with a length in a certain range. /// /// Created by the `linkedlist()` function in the same module. #[derive(Clone, Debug)] pub struct LinkedListStrategy[][where T : Strategy]( statics::Map, VecToLl>) -> LinkedListValueTree; /// `ValueTree` corresponding to `LinkedListStrategy`. #[derive(Clone, Debug)] pub struct LinkedListValueTree[][where T : ValueTree]( statics::Map, VecToLl>) -> LinkedList; } /// Create a strategy to generate `LinkedList`s containing elements drawn from /// `element` and with a size range given by `size`. pub fn linked_list( element: T, size: impl Into, ) -> LinkedListStrategy { LinkedListStrategy(statics::Map::new(vec(element, size), VecToLl)) } mapfn! { [] fn VecToBinHeap[](vec: Vec) -> BinaryHeap { vec.into() } } opaque_strategy_wrapper! { /// Strategy to create `BinaryHeap`s with a length in a certain range. /// /// Created by the `binary_heap()` function in the same module. #[derive(Clone, Debug)] pub struct BinaryHeapStrategy[][where T : Strategy, T::Value : Ord]( statics::Map, VecToBinHeap>) -> BinaryHeapValueTree; /// `ValueTree` corresponding to `BinaryHeapStrategy`. #[derive(Clone, Debug)] pub struct BinaryHeapValueTree[][where T : ValueTree, T::Value : Ord]( statics::Map, VecToBinHeap>) -> BinaryHeap; } /// Create a strategy to generate `BinaryHeap`s containing elements drawn from /// `element` and with a size range given by `size`. pub fn binary_heap( element: T, size: impl Into, ) -> BinaryHeapStrategy where T::Value: Ord, { BinaryHeapStrategy(statics::Map::new(vec(element, size), VecToBinHeap)) } mapfn! { {#[cfg(feature = "std")]} [] fn VecToHashSet[](vec: Vec) -> HashSet { vec.into_iter().collect() } } #[derive(Debug, Clone, Copy)] struct MinSize(usize); #[cfg(feature = "std")] impl statics::FilterFn> for MinSize { fn apply(&self, set: &HashSet) -> bool { set.len() >= self.0 } } opaque_strategy_wrapper! { {#[cfg(feature = "std")]} {#[cfg_attr(docsrs, doc(cfg(feature = "std")))]} /// Strategy to create `HashSet`s with a length in a certain range. /// /// Created by the `hash_set()` function in the same module. #[derive(Clone, Debug)] pub struct HashSetStrategy[][where T : Strategy, T::Value : Hash + Eq]( statics::Filter, VecToHashSet>, MinSize>) -> HashSetValueTree; /// `ValueTree` corresponding to `HashSetStrategy`. #[derive(Clone, Debug)] pub struct HashSetValueTree[][where T : ValueTree, T::Value : Hash + Eq]( statics::Filter, VecToHashSet>, MinSize>) -> HashSet; } /// Create a strategy to generate `HashSet`s containing elements drawn from /// `element` and with a size range given by `size`. /// /// This strategy will implicitly do local rejects to ensure that the `HashSet` /// has at least the minimum number of elements, in case `element` should /// produce duplicate values. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn hash_set( element: T, size: impl Into, ) -> HashSetStrategy where T::Value: Hash + Eq, { let size = size.into(); HashSetStrategy(statics::Filter::new( statics::Map::new(vec(element, size.clone()), VecToHashSet), "HashSet minimum size".into(), MinSize(size.start()), )) } mapfn! { [] fn VecToBTreeSet[](vec: Vec) -> BTreeSet { vec.into_iter().collect() } } impl statics::FilterFn> for MinSize { fn apply(&self, set: &BTreeSet) -> bool { set.len() >= self.0 } } opaque_strategy_wrapper! { /// Strategy to create `BTreeSet`s with a length in a certain range. /// /// Created by the `btree_set()` function in the same module. #[derive(Clone, Debug)] pub struct BTreeSetStrategy[][where T : Strategy, T::Value : Ord]( statics::Filter, VecToBTreeSet>, MinSize>) -> BTreeSetValueTree; /// `ValueTree` corresponding to `BTreeSetStrategy`. #[derive(Clone, Debug)] pub struct BTreeSetValueTree[][where T : ValueTree, T::Value : Ord]( statics::Filter, VecToBTreeSet>, MinSize>) -> BTreeSet; } /// Create a strategy to generate `BTreeSet`s containing elements drawn from /// `element` and with a size range given by `size`. /// /// This strategy will implicitly do local rejects to ensure that the /// `BTreeSet` has at least the minimum number of elements, in case `element` /// should produce duplicate values. pub fn btree_set( element: T, size: impl Into, ) -> BTreeSetStrategy where T::Value: Ord, { let size = size.into(); BTreeSetStrategy(statics::Filter::new( statics::Map::new(vec(element, size.clone()), VecToBTreeSet), "BTreeSet minimum size".into(), MinSize(size.start()), )) } mapfn! { {#[cfg(feature = "std")]} [] fn VecToHashMap[] (vec: Vec<(K, V)>) -> HashMap { vec.into_iter().collect() } } #[cfg(feature = "std")] impl statics::FilterFn> for MinSize { fn apply(&self, map: &HashMap) -> bool { map.len() >= self.0 } } opaque_strategy_wrapper! { {#[cfg(feature = "std")]} {#[cfg_attr(docsrs, doc(cfg(feature = "std")))]} /// Strategy to create `HashMap`s with a length in a certain range. /// /// Created by the `hash_map()` function in the same module. #[derive(Clone, Debug)] pub struct HashMapStrategy[] [where K : Strategy, V : Strategy, K::Value : Hash + Eq]( statics::Filter, VecToHashMap>, MinSize>) -> HashMapValueTree; /// `ValueTree` corresponding to `HashMapStrategy`. #[derive(Clone, Debug)] pub struct HashMapValueTree[] [where K : ValueTree, V : ValueTree, K::Value : Hash + Eq]( statics::Filter>, VecToHashMap>, MinSize>) -> HashMap; } /// Create a strategy to generate `HashMap`s containing keys and values drawn /// from `key` and `value` respectively, and with a size within the given /// range. /// /// This strategy will implicitly do local rejects to ensure that the `HashMap` /// has at least the minimum number of elements, in case `key` should produce /// duplicate values. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn hash_map( key: K, value: V, size: impl Into, ) -> HashMapStrategy where K::Value: Hash + Eq, { let size = size.into(); HashMapStrategy(statics::Filter::new( statics::Map::new(vec((key, value), size.clone()), VecToHashMap), "HashMap minimum size".into(), MinSize(size.start()), )) } mapfn! { [] fn VecToBTreeMap[] (vec: Vec<(K, V)>) -> BTreeMap { vec.into_iter().collect() } } impl statics::FilterFn> for MinSize { fn apply(&self, map: &BTreeMap) -> bool { map.len() >= self.0 } } opaque_strategy_wrapper! { /// Strategy to create `BTreeMap`s with a length in a certain range. /// /// Created by the `btree_map()` function in the same module. #[derive(Clone, Debug)] pub struct BTreeMapStrategy[] [where K : Strategy, V : Strategy, K::Value : Ord]( statics::Filter, VecToBTreeMap>, MinSize>) -> BTreeMapValueTree; /// `ValueTree` corresponding to `BTreeMapStrategy`. #[derive(Clone, Debug)] pub struct BTreeMapValueTree[] [where K : ValueTree, V : ValueTree, K::Value : Ord]( statics::Filter>, VecToBTreeMap>, MinSize>) -> BTreeMap; } /// Create a strategy to generate `BTreeMap`s containing keys and values drawn /// from `key` and `value` respectively, and with a size within the given /// range. /// /// This strategy will implicitly do local rejects to ensure that the /// `BTreeMap` has at least the minimum number of elements, in case `key` /// should produce duplicate values. pub fn btree_map( key: K, value: V, size: impl Into, ) -> BTreeMapStrategy where K::Value: Ord, { let size = size.into(); BTreeMapStrategy(statics::Filter::new( statics::Map::new(vec((key, value), size.clone()), VecToBTreeMap), "BTreeMap minimum size".into(), MinSize(size.start()), )) } #[derive(Clone, Copy, Debug)] enum Shrink { DeleteElement(usize), ShrinkElement(usize), } /// `ValueTree` corresponding to `VecStrategy`. #[derive(Clone, Debug)] pub struct VecValueTree { elements: Vec, included_elements: VarBitSet, min_size: usize, shrink: Shrink, prev_shrink: Option, } impl Strategy for VecStrategy { type Tree = VecValueTree; type Value = Vec; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let (start, end) = self.size.start_end_incl(); let max_size = sample_uniform_incl(runner, start, end); let mut elements = Vec::with_capacity(max_size); while elements.len() < max_size { elements.push(self.element.new_tree(runner)?); } Ok(VecValueTree { elements, included_elements: VarBitSet::saturated(max_size), min_size: start, shrink: Shrink::DeleteElement(0), prev_shrink: None, }) } } impl Strategy for Vec { type Tree = VecValueTree; type Value = Vec; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let len = self.len(); let elements = self .iter() .map(|t| t.new_tree(runner)) .collect::, Reason>>()?; Ok(VecValueTree { elements, included_elements: VarBitSet::saturated(len), min_size: len, shrink: Shrink::ShrinkElement(0), prev_shrink: None, }) } } impl ValueTree for VecValueTree { type Value = Vec; fn current(&self) -> Vec { self.elements .iter() .enumerate() .filter(|&(ix, _)| self.included_elements.test(ix)) .map(|(_, element)| element.current()) .collect() } fn simplify(&mut self) -> bool { // The overall strategy here is to iteratively delete elements from the // list until we can do so no further, then to shrink each remaining // element in sequence. // // For `complicate()`, we simply undo the last shrink operation, if // there was any. if let Shrink::DeleteElement(ix) = self.shrink { // Can't delete an element if beyond the end of the vec or if it // would put us under the minimum length. if ix >= self.elements.len() || self.included_elements.count() == self.min_size { self.shrink = Shrink::ShrinkElement(0); } else { self.included_elements.clear(ix); self.prev_shrink = Some(self.shrink); self.shrink = Shrink::DeleteElement(ix + 1); return true; } } while let Shrink::ShrinkElement(ix) = self.shrink { if ix >= self.elements.len() { // Nothing more we can do return false; } if !self.included_elements.test(ix) { // No use shrinking something we're not including. self.shrink = Shrink::ShrinkElement(ix + 1); continue; } if !self.elements[ix].simplify() { // Move on to the next element self.shrink = Shrink::ShrinkElement(ix + 1); } else { self.prev_shrink = Some(self.shrink); return true; } } panic!("Unexpected shrink state"); } fn complicate(&mut self) -> bool { match self.prev_shrink { None => false, Some(Shrink::DeleteElement(ix)) => { // Undo the last item we deleted. Can't complicate any further, // so unset prev_shrink. self.included_elements.set(ix); self.prev_shrink = None; true } Some(Shrink::ShrinkElement(ix)) => { if self.elements[ix].complicate() { // Don't unset prev_shrink; we may be able to complicate // again. true } else { // Can't complicate the last element any further. self.prev_shrink = None; false } } } } } //============================================================================== // Tests //============================================================================== #[cfg(test)] mod test { use super::*; use crate::bits; #[test] fn test_vec() { let input = vec(1usize..20usize, 5..20); let mut num_successes = 0; let mut runner = TestRunner::deterministic(); for _ in 0..256 { let case = input.new_tree(&mut runner).unwrap(); let start = case.current(); // Has correct length assert!(start.len() >= 5 && start.len() < 20); // Has at least 2 distinct values assert!(start.iter().map(|&v| v).collect::().len() >= 2); let result = runner.run_one(case, |v| { prop_assert!( v.iter().map(|&v| v).sum::() < 9, "greater than 8" ); Ok(()) }); match result { Ok(true) => num_successes += 1, Err(TestError::Fail(_, value)) => { // The minimal case always has between 5 (due to min // length) and 9 (min element value = 1) elements, and // always sums to exactly 9. assert!( value.len() >= 5 && value.len() <= 9 && value.iter().map(|&v| v).sum::() == 9, "Unexpected minimal value: {:?}", value ); } e => panic!("Unexpected result: {:?}", e), } } assert!(num_successes < 256); } #[test] fn test_vec_sanity() { check_strategy_sanity(vec(0i32..1000, 5..10), None); } #[test] fn test_parallel_vec() { let input = vec![(1u32..10).boxed(), bits::u32::masked(0xF0u32).boxed()]; for _ in 0..256 { let mut runner = TestRunner::default(); let mut case = input.new_tree(&mut runner).unwrap(); loop { let current = case.current(); assert_eq!(2, current.len()); assert!(current[0] >= 1 && current[0] <= 10); assert_eq!(0, (current[1] & !0xF0)); if !case.simplify() { break; } } } } #[cfg(feature = "std")] #[test] fn test_map() { // Only 8 possible keys let input = hash_map("[ab]{3}", "a", 2..3); let mut runner = TestRunner::deterministic(); for _ in 0..256 { let v = input.new_tree(&mut runner).unwrap().current(); assert_eq!(2, v.len()); } } #[cfg(feature = "std")] #[test] fn test_set() { // Only 8 possible values let input = hash_set("[ab]{3}", 2..3); let mut runner = TestRunner::deterministic(); for _ in 0..256 { let v = input.new_tree(&mut runner).unwrap().current(); assert_eq!(2, v.len()); } } }