blob: e29b938781e95f07e4c60954bd8f574262be95c1 [file] [log] [blame]
//-
// 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, RangeTo, RangeInclusive, RangeToInclusive};
use core::usize;
use crate::std_facade::{fmt, Vec, VecDeque, BinaryHeap, BTreeMap, BTreeSet, LinkedList};
#[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::tuple::TupleValueTree;
use crate::test_runner::*;
//==============================================================================
// 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 impelementations 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
/// impelementations 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());
}
}
}