| //- |
| // Copyright 2017, 2018 The proptest developers |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<usize>); |
| |
| /// Creates a `SizeRange` from some value that is convertible into it. |
| pub fn size_range(from: impl Into<SizeRange>) -> 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<usize>`. |
| pub fn new(range: RangeInclusive<usize>) -> 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<X>(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<X: Default>(self) -> product_type![Self, X] { |
| self.with(Default::default()) |
| } |
| |
| pub(crate) fn start(&self) -> usize { |
| self.0.start |
| } |
| |
| /// Extract the ends `[low, high]` of a `SizeRange`. |
| pub(crate) fn start_end_incl(&self) -> (usize, usize) { |
| (self.start(), self.end_incl()) |
| } |
| |
| pub(crate) fn end_incl(&self) -> usize { |
| self.0.end - 1 |
| } |
| |
| pub(crate) fn end_excl(&self) -> usize { |
| self.0.end |
| } |
| |
| pub(crate) fn iter(&self) -> impl Iterator<Item = usize> { |
| 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<usize> for SizeRange { |
| fn from(exact: usize) -> Self { |
| size_range(exact..=exact) |
| } |
| } |
| |
| /// Given `..high`, then a size range `[0, high)` is the result. |
| impl From<RangeTo<usize>> for SizeRange { |
| fn from(high: RangeTo<usize>) -> Self { |
| size_range(0..high.end) |
| } |
| } |
| |
| /// Given `low .. high`, then a size range `[low, high)` is the result. |
| impl From<Range<usize>> for SizeRange { |
| fn from(r: Range<usize>) -> Self { |
| SizeRange(r) |
| } |
| } |
| |
| /// Given `low ..= high`, then a size range `[low, high]` is the result. |
| impl From<RangeInclusive<usize>> for SizeRange { |
| fn from(r: RangeInclusive<usize>) -> Self { |
| size_range(*r.start()..r.end().saturating_add(1)) |
| } |
| } |
| |
| /// Given `..=high`, then a size range `[0, high]` is the result. |
| impl From<RangeToInclusive<usize>> for SizeRange { |
| fn from(high: RangeToInclusive<usize>) -> Self { |
| size_range(0..=high.end) |
| } |
| } |
| |
| #[cfg(feature = "frunk")] |
| impl Generic for SizeRange { |
| type Repr = RangeInclusive<usize>; |
| |
| /// Converts the `SizeRange` into `Range<usize>`. |
| fn into(self) -> Self::Repr { |
| self.0 |
| } |
| |
| /// Converts `RangeInclusive<usize>` 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<usize> 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<T: Strategy> { |
| 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<T: Strategy>( |
| element: T, |
| size: impl Into<SizeRange>, |
| ) -> VecStrategy<T> { |
| let size = size.into(); |
| size.assert_nonempty(); |
| VecStrategy { element, size } |
| } |
| |
| mapfn! { |
| [] fn VecToDeque[<T : fmt::Debug>](vec: Vec<T>) -> VecDeque<T> { |
| 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[<T>][where T : Strategy]( |
| statics::Map<VecStrategy<T>, VecToDeque>) |
| -> VecDequeValueTree<T::Tree>; |
| /// `ValueTree` corresponding to `VecDequeStrategy`. |
| #[derive(Clone, Debug)] |
| pub struct VecDequeValueTree[<T>][where T : ValueTree]( |
| statics::Map<VecValueTree<T>, VecToDeque>) |
| -> VecDeque<T::Value>; |
| } |
| |
| /// Create a strategy to generate `VecDeque`s containing elements drawn from |
| /// `element` and with a size range given by `size`. |
| pub fn vec_deque<T: Strategy>( |
| element: T, |
| size: impl Into<SizeRange>, |
| ) -> VecDequeStrategy<T> { |
| VecDequeStrategy(statics::Map::new(vec(element, size), VecToDeque)) |
| } |
| |
| mapfn! { |
| [] fn VecToLl[<T : fmt::Debug>](vec: Vec<T>) -> LinkedList<T> { |
| 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[<T>][where T : Strategy]( |
| statics::Map<VecStrategy<T>, VecToLl>) |
| -> LinkedListValueTree<T::Tree>; |
| /// `ValueTree` corresponding to `LinkedListStrategy`. |
| #[derive(Clone, Debug)] |
| pub struct LinkedListValueTree[<T>][where T : ValueTree]( |
| statics::Map<VecValueTree<T>, VecToLl>) |
| -> LinkedList<T::Value>; |
| } |
| |
| /// Create a strategy to generate `LinkedList`s containing elements drawn from |
| /// `element` and with a size range given by `size`. |
| pub fn linked_list<T: Strategy>( |
| element: T, |
| size: impl Into<SizeRange>, |
| ) -> LinkedListStrategy<T> { |
| LinkedListStrategy(statics::Map::new(vec(element, size), VecToLl)) |
| } |
| |
| mapfn! { |
| [] fn VecToBinHeap[<T : fmt::Debug + Ord>](vec: Vec<T>) -> BinaryHeap<T> { |
| 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[<T>][where T : Strategy, T::Value : Ord]( |
| statics::Map<VecStrategy<T>, VecToBinHeap>) |
| -> BinaryHeapValueTree<T::Tree>; |
| /// `ValueTree` corresponding to `BinaryHeapStrategy`. |
| #[derive(Clone, Debug)] |
| pub struct BinaryHeapValueTree[<T>][where T : ValueTree, T::Value : Ord]( |
| statics::Map<VecValueTree<T>, VecToBinHeap>) |
| -> BinaryHeap<T::Value>; |
| } |
| |
| /// Create a strategy to generate `BinaryHeap`s containing elements drawn from |
| /// `element` and with a size range given by `size`. |
| pub fn binary_heap<T: Strategy>( |
| element: T, |
| size: impl Into<SizeRange>, |
| ) -> BinaryHeapStrategy<T> |
| where |
| T::Value: Ord, |
| { |
| BinaryHeapStrategy(statics::Map::new(vec(element, size), VecToBinHeap)) |
| } |
| |
| mapfn! { |
| {#[cfg(feature = "std")]} |
| [] fn VecToHashSet[<T : fmt::Debug + Hash + Eq>](vec: Vec<T>) |
| -> HashSet<T> { |
| vec.into_iter().collect() |
| } |
| } |
| |
| #[derive(Debug, Clone, Copy)] |
| struct MinSize(usize); |
| |
| #[cfg(feature = "std")] |
| impl<T: Eq + Hash> statics::FilterFn<HashSet<T>> for MinSize { |
| fn apply(&self, set: &HashSet<T>) -> bool { |
| set.len() >= self.0 |
| } |
| } |
| |
| opaque_strategy_wrapper! { |
| {#[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[<T>][where T : Strategy, T::Value : Hash + Eq]( |
| statics::Filter<statics::Map<VecStrategy<T>, VecToHashSet>, MinSize>) |
| -> HashSetValueTree<T::Tree>; |
| /// `ValueTree` corresponding to `HashSetStrategy`. |
| #[derive(Clone, Debug)] |
| pub struct HashSetValueTree[<T>][where T : ValueTree, T::Value : Hash + Eq]( |
| statics::Filter<statics::Map<VecValueTree<T>, VecToHashSet>, MinSize>) |
| -> HashSet<T::Value>; |
| } |
| |
| /// 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")] |
| pub fn hash_set<T: Strategy>( |
| element: T, |
| size: impl Into<SizeRange>, |
| ) -> HashSetStrategy<T> |
| 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[<T : fmt::Debug + Ord>](vec: Vec<T>) |
| -> BTreeSet<T> { |
| vec.into_iter().collect() |
| } |
| } |
| |
| impl<T: Ord> statics::FilterFn<BTreeSet<T>> for MinSize { |
| fn apply(&self, set: &BTreeSet<T>) -> 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[<T>][where T : Strategy, T::Value : Ord]( |
| statics::Filter<statics::Map<VecStrategy<T>, VecToBTreeSet>, MinSize>) |
| -> BTreeSetValueTree<T::Tree>; |
| /// `ValueTree` corresponding to `BTreeSetStrategy`. |
| #[derive(Clone, Debug)] |
| pub struct BTreeSetValueTree[<T>][where T : ValueTree, T::Value : Ord]( |
| statics::Filter<statics::Map<VecValueTree<T>, VecToBTreeSet>, MinSize>) |
| -> BTreeSet<T::Value>; |
| } |
| |
| /// 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<T: Strategy>( |
| element: T, |
| size: impl Into<SizeRange>, |
| ) -> BTreeSetStrategy<T> |
| 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[<K : fmt::Debug + Hash + Eq, V : fmt::Debug>] |
| (vec: Vec<(K, V)>) -> HashMap<K, V> |
| { |
| vec.into_iter().collect() |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl<K: Hash + Eq, V> statics::FilterFn<HashMap<K, V>> for MinSize { |
| fn apply(&self, map: &HashMap<K, V>) -> bool { |
| map.len() >= self.0 |
| } |
| } |
| |
| opaque_strategy_wrapper! { |
| {#[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[<K, V>] |
| [where K : Strategy, V : Strategy, K::Value : Hash + Eq]( |
| statics::Filter<statics::Map<VecStrategy<(K,V)>, |
| VecToHashMap>, MinSize>) |
| -> HashMapValueTree<K::Tree, V::Tree>; |
| /// `ValueTree` corresponding to `HashMapStrategy`. |
| #[derive(Clone, Debug)] |
| pub struct HashMapValueTree[<K, V>] |
| [where K : ValueTree, V : ValueTree, K::Value : Hash + Eq]( |
| statics::Filter<statics::Map<VecValueTree<TupleValueTree<(K, V)>>, |
| VecToHashMap>, MinSize>) |
| -> HashMap<K::Value, V::Value>; |
| } |
| |
| /// 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")] |
| pub fn hash_map<K: Strategy, V: Strategy>( |
| key: K, |
| value: V, |
| size: impl Into<SizeRange>, |
| ) -> HashMapStrategy<K, V> |
| 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[<K : fmt::Debug + Ord, V : fmt::Debug>] |
| (vec: Vec<(K, V)>) -> BTreeMap<K, V> |
| { |
| vec.into_iter().collect() |
| } |
| } |
| |
| impl<K: Ord, V> statics::FilterFn<BTreeMap<K, V>> for MinSize { |
| fn apply(&self, map: &BTreeMap<K, V>) -> 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[<K, V>] |
| [where K : Strategy, V : Strategy, K::Value : Ord]( |
| statics::Filter<statics::Map<VecStrategy<(K,V)>, |
| VecToBTreeMap>, MinSize>) |
| -> BTreeMapValueTree<K::Tree, V::Tree>; |
| /// `ValueTree` corresponding to `BTreeMapStrategy`. |
| #[derive(Clone, Debug)] |
| pub struct BTreeMapValueTree[<K, V>] |
| [where K : ValueTree, V : ValueTree, K::Value : Ord]( |
| statics::Filter<statics::Map<VecValueTree<TupleValueTree<(K, V)>>, |
| VecToBTreeMap>, MinSize>) |
| -> BTreeMap<K::Value, V::Value>; |
| } |
| |
| /// 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<K: Strategy, V: Strategy>( |
| key: K, |
| value: V, |
| size: impl Into<SizeRange>, |
| ) -> BTreeMapStrategy<K, V> |
| 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<T: ValueTree> { |
| elements: Vec<T>, |
| included_elements: VarBitSet, |
| min_size: usize, |
| shrink: Shrink, |
| prev_shrink: Option<Shrink>, |
| } |
| |
| impl<T: Strategy> Strategy for VecStrategy<T> { |
| type Tree = VecValueTree<T::Tree>; |
| type Value = Vec<T::Value>; |
| |
| fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> { |
| 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<T: Strategy> Strategy for Vec<T> { |
| type Tree = VecValueTree<T::Tree>; |
| type Value = Vec<T::Value>; |
| |
| fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> { |
| let len = self.len(); |
| let elements = self |
| .iter() |
| .map(|t| t.new_tree(runner)) |
| .collect::<Result<Vec<_>, Reason>>()?; |
| |
| Ok(VecValueTree { |
| elements, |
| included_elements: VarBitSet::saturated(len), |
| min_size: len, |
| shrink: Shrink::ShrinkElement(0), |
| prev_shrink: None, |
| }) |
| } |
| } |
| |
| impl<T: ValueTree> ValueTree for VecValueTree<T> { |
| type Value = Vec<T::Value>; |
| |
| fn current(&self) -> Vec<T::Value> { |
| 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::<VarBitSet>().len() >= 2); |
| |
| let result = runner.run_one(case, |v| { |
| prop_assert!( |
| v.iter().map(|&v| v).sum::<usize>() < 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::<usize>() == 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()); |
| } |
| } |
| } |