Trait Strategy
trait Strategy: fmt::Debug
A strategy for producing arbitrary values of a given type.
fmt::Debug is a hard requirement for all strategies currently due to
prop_flat_map(). This constraint will be removed when specialisation
becomes stable.
Associated Types
type Tree: TraitBound { trait_: Path { path: "ValueTree", id: Id(1390), args: Some(AngleBracketed { args: [], constraints: [AssocItemConstraint { name: "Value", args: None, binding: Equality(Type(QualifiedPath { name: "Value", args: None, self_type: Generic("Self"), trait_: Some(Path { path: "", id: Id(39), args: None }) })) }] }) }, generic_params: [], modifier: None }The value tree generated by this
Strategy.type Value: TraitBound { trait_: Path { path: "fmt::Debug", id: Id(41), args: None }, generic_params: [], modifier: None }The type of value used by functions under test generated by this Strategy.
This corresponds to the same type as the associated type
ValueinSelf::Tree. It is provided here to simplify usage particularly in conjunction with-> impl Strategy<Value = MyType>.
Required Methods
fn new_tree(self: &Self, runner: &mut TestRunner) -> NewTree<Self>Generate a new value tree from the given runner.
This may fail if there are constraints on the generated value and the generator is unable to produce anything that satisfies them. Any failure is wrapped in
TestError::Abort.This method is generally expected to be deterministic. That is, given a
TestRunnerwith its RNG in a particular state, this should produce an identicalValueTreeevery time. Non-deterministic strategies do not cause problems during normal operation, but they do break failure persistence since it is implemented by simply saving the seed used to generate the test case.
Provided Methods
fn prop_map<O: fmt::Debug, F: Fn(<Self as >::Value) -> O>(self: Self, fun: F) -> Map<Self, F> where Self: SizedReturns a strategy which produces values transformed by the function
fun.There is no need (or possibility, for that matter) to define how the output is to be shrunken. Shrinking continues to take place in terms of the source value.
funshould be a deterministic function. That is, for a given input value, it should produce an equivalent output value on every call. Proptest assumes that it can call the function as many times as needed to generate as many identical values as needed. For this reason,FisFnrather thanFnMut.fn prop_map_into<O: fmt::Debug>(self: Self) -> MapInto<Self, O> where Self: Sized, <Self as >::Value: Into<O>Returns a strategy which produces values of type
Oby transformingSelfwithInto<O>.You should always prefer this operation instead of
prop_mapwhen you can as it is both clearer and also currently more efficient.There is no need (or possibility, for that matter) to define how the output is to be shrunken. Shrinking continues to take place in terms of the source value.
fn prop_perturb<O: fmt::Debug, F: Fn(<Self as >::Value, TestRng) -> O>(self: Self, fun: F) -> Perturb<Self, F> where Self: SizedReturns a strategy which produces values transformed by the function
fun, which is additionally given a random number generator.This is exactly like
prop_map()except for the addition of the second argument to the function. This allows introducing chaotic variations to generated values that are not easily expressed otherwise while allowing shrinking to proceed reasonably.During shrinking,
funis always called with an identical random number generator, so if it is a pure function it will always perform the same perturbation.Example
// The prelude also gets us the `Rng` trait. use *; proptest! #fn prop_flat_map<S: Strategy, F: Fn(<Self as >::Value) -> S>(self: Self, fun: F) -> Flatten<Map<Self, F>> where Self: SizedMaps values produced by this strategy into new strategies and picks values from those strategies.
funis used to transform the values produced by this strategy into other strategies. Values are then chosen from the derived strategies. Shrinking proceeds by shrinking individual values as well as shrinking the input used to generate the internal strategies.Shrinking
In the case of test failure, shrinking will not only shrink the output from the combinator itself, but also the input, i.e., the strategy used to generate the output itself. Doing this requires searching the new derived strategy for a new failing input. The combinator will generate up to
Config::casesvalues for this search.As a result, nested
prop_flat_map/Flattencombinators risk exponential run time on this search for new failing values. To ensure that test failures occur within a reasonable amount of time, all of these combinators share a single "flat map regen" counter, and will stop generating new values if it exceedsConfig::max_flat_map_regens.Example
Generate two integers, where the second is always less than the first, without using filtering:
use *; proptest! # #Choosing the right flat-map
Strategyhas three "flat-map" combinators. They look very similar at first, and can be used to produce superficially identical test results. For example, the following three expressions all produce inputs which are 2-tuples(a,b)where thebcomponent is less thana.# #![allow(unused_variables)] use proptest::prelude::*; let flat_map = (1..10).prop_flat_map(|a| (Just(a), 0..a)); let ind_flat_map = (1..10).prop_ind_flat_map(|a| (Just(a), 0..a)); let ind_flat_map2 = (1..10).prop_ind_flat_map2(|a| 0..a);The three do differ however in terms of how they shrink.
For
flat_map, bothaandbwill shrink, and the invariant thatb < ais maintained. This is a "dependent" or "higher-order" strategy in that it remembers that the strategy for choosingbis dependent on the value chosen fora.For
ind_flat_map, the invariantb < ais maintained, but only becauseadoes not shrink. This is due to the fact that the dependency between the strategies is not tracked;ais simply seen as a constant.Finally, for
ind_flat_map2, the invariantb < ais not maintained, becauseacan shrink independently ofb, again because the dependency between the two variables is not tracked, but in this case the derivation ofais still exposed to the shrinking system.The use-cases for the independent flat-map variants is pretty narrow. For the majority of cases where invariants need to be maintained and you want all components to shrink,
prop_flat_mapis the way to go.prop_ind_flat_mapmakes the most sense when the input to the map function is not exposed in the output and shrinking across strategies is not expected to be useful.prop_ind_flat_map2is useful for using related values as starting points while not constraining them to that relation.fn prop_ind_flat_map<S: Strategy, F: Fn(<Self as >::Value) -> S>(self: Self, fun: F) -> IndFlatten<Map<Self, F>> where Self: SizedMaps values produced by this strategy into new strategies and picks values from those strategies while considering the new strategies to be independent.
This is very similar to
prop_flat_map(), but shrinking will not attempt to shrink the input that produces the derived strategies. This is appropriate for when the derived strategies already fully shrink in the desired way.In most cases, you want
prop_flat_map().See
prop_flat_map()for a more detailed explanation on how the three flat-map combinators differ.fn prop_ind_flat_map2<S: Strategy, F: Fn(<Self as >::Value) -> S>(self: Self, fun: F) -> IndFlattenMap<Self, F> where Self: SizedSimilar to
prop_ind_flat_map(), but produces 2-tuples with the input generated fromselfin slot 0 and the derived strategy in slot 1.See
prop_flat_map()for a more detailed explanation on how the three flat-map combinators differ.fn prop_filter<R: Into<Reason>, F: Fn(&<Self as >::Value) -> bool>(self: Self, whence: R, fun: F) -> Filter<Self, F> where Self: SizedReturns a strategy which only produces values accepted by
fun.This results in a very naïve form of rejection sampling and should only be used if (a) relatively few values will actually be rejected; (b) it isn't easy to express what you want by using another strategy and/or
map().There are a lot of downsides to this form of filtering. It slows testing down, since values must be generated but then discarded. Proptest only allows a limited number of rejects this way (across the entire
TestRunner). Rejection can interfere with shrinking; particularly, complex filters may largely or entirely prevent shrinking from substantially altering the original value.Local rejection sampling is still preferable to rejecting the entire input to a test (via
TestCaseError::Reject), however, and the default number of local rejections allowed is much higher than the number of whole-input rejections.whenceis used to record where and why the rejection occurred.fn prop_filter_map<F: Fn(<Self as >::Value) -> Option<O>, O: fmt::Debug, impl Into<Reason>: Into<Reason>>(self: Self, whence: impl Into<Reason>, fun: F) -> FilterMap<Self, F> where Self: SizedReturns a strategy which only produces transformed values where
funreturnsSome(value)and rejects those wherefunreturnsNone.Using this method is preferable to using
.prop_map(..).prop_filter(..).This results in a very naïve form of rejection sampling and should only be used if (a) relatively few values will actually be rejected; (b) it isn't easy to express what you want by using another strategy and/or
map().There are a lot of downsides to this form of filtering. It slows testing down, since values must be generated but then discarded. Proptest only allows a limited number of rejects this way (across the entire
TestRunner). Rejection can interfere with shrinking; particularly, complex filters may largely or entirely prevent shrinking from substantially altering the original value.Local rejection sampling is still preferable to rejecting the entire input to a test (via
TestCaseError::Reject), however, and the default number of local rejections allowed is much higher than the number of whole-input rejections.whenceis used to record where and why the rejection occurred.fn prop_union(self: Self, other: Self) -> Union<Self> where Self: SizedReturns a strategy which picks uniformly from
selfandother.When shrinking, if a value from
otherwas originally chosen but that value can be shrunken no further, it switches to a value fromselfand starts shrinking that.Be aware that chaining
prop_unioncalls will result in a very right-skewed distribution. If this is not what you want, you can call the.or()method on theUnionto add more values to the same union, or directly callUnion::new().Both
selfandothermust be of the same type. To combine heterogeneous strategies, call theboxed()method on bothselfandotherto erase the type differences before callingprop_union().fn prop_recursive<R: Strategy<Value = <Self as >::Value> + 'static, F: Fn(BoxedStrategy<<Self as >::Value>) -> R>(self: Self, depth: u32, desired_size: u32, expected_branch_size: u32, recurse: F) -> Recursive<<Self as >::Value, F> where Self: Sized + 'staticGenerate a recursive structure with
selfitems as leaves.recurseis applied to various strategies that produce the same type asselfwith nesting depth n to create a strategy that produces the same type with nesting depth n+1. Generated structures will have a depth between 0 anddepthand will usually have up todesired_sizetotal elements, though they may have more.expected_branch_sizegives the expected maximum size for any collection which may contain recursive elements and is used to control branch probability to achieve the desired size. Passing a too small value can result in trees vastly larger than desired.Note that
depthonly counts branches; i.e.,depth = 0is a single leaf, anddepth = 1is a leaf or a branch containing only leaves.In practise, generated values usually have a lower depth than
depth(butdepthis a hard limit) and almost always underexpected_branch_size(though it is not a hard limit) since the underlying code underestimates probabilities.Shrinking shrinks both the inner values and attempts switching from recursive to non-recursive cases.
Example
# use HashMap; use *; /// Define our own JSON AST type #fn prop_shuffle(self: Self) -> Shuffle<Self> where Self: Sized, <Self as >::Value: ShuffleableShuffle the contents of the values produced by this strategy.
That is, this modifies a strategy producing a
Vec, slice, etc, to shuffle the contents of thatVec/slice/etc.Initially, the value is fully shuffled. During shrinking, the input value will initially be unchanged while the result will gradually be restored to its original order. Once de-shuffling either completes or is cancelled by calls to
complicate()pinning it to a particular permutation, the inner value will be simplified.Example
use *; static VALUES: &'static = &; proptest! # #fn boxed(self: Self) -> BoxedStrategy<<Self as >::Value> where Self: Sized + 'staticErases the type of this
Strategyso it can be passed around as a simple trait object.See also
sboxed()if thisStrategyisSendandSyncand you want to preserve that information.Strategies of this type afford cheap shallow cloning via reference counting by using an
Arcinternally.fn sboxed(self: Self) -> SBoxedStrategy<<Self as >::Value> where Self: Sized + Send + Sync + 'staticErases the type of this
Strategyso it can be passed around as a simple trait object.Unlike
boxed(), this conversion retains theSendandSynctraits on the output.Strategies of this type afford cheap shallow cloning via reference counting by using an
Arcinternally.fn no_shrink(self: Self) -> NoShrink<Self> where Self: SizedWraps this strategy to prevent values from being subject to shrinking.
Suppressing shrinking is useful when testing things like linear approximation functions. Ordinarily, proptest will tend to shrink the input to the function until the result is just barely outside the acceptable range whereas the original input may have produced a result far outside of it. Since this makes it harder to see what the actual problem is, making the input
NoShrinkallows learning about inputs that produce more incorrect results.
Implementors
impl Strategy for RangeTo<usize>impl<T: fmt::Debug> Strategy for fn() -> Timpl Strategy for RangeTo<i8>impl<S: Strategy> Strategy for IndFlatten<S>impl Strategy for Range<i64>impl<S: Strategy, O: fmt::Debug> Strategy for MapInto<S, O>impl Strategy for RangeFrom<isize>impl Strategy for RangeToInclusive<u16>impl<T: fmt::Debug + 'static, R: Strategy<Value = T> + 'static, F: Fn(BoxedStrategy<T>) -> R> Strategy for Recursive<T, F>impl<A: Strategy, B: Strategy, C: Strategy> Strategy for (A, B, C)impl<A: Strategy, B: Strategy<Value = <A as >::Value>, C: Strategy<Value = <A as >::Value>, D: Strategy<Value = <A as >::Value>, E: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>, WA<C>, WA<D>, WA<E>)>impl Strategy for Anyimpl Strategy for RangeInclusive<u128>impl<T: Clone + fmt::Debug> Strategy for Just<T>impl Strategy for RangeTo<f32>impl<S: Strategy + ?Sized> Strategy for Rc<S>impl<S: Strategy, F: Fn(<S as >::Value) -> Option<O>, O: fmt::Debug> Strategy for FilterMap<S, F>impl Strategy for RangeTo<i16>impl Strategy for Range<i128>impl<T: Strategy> Strategy for Fuse<T>impl Strategy for RangeFrom<u8>impl<T> Strategy for Select<T>impl Strategy for RangeToInclusive<u32>impl<A: Strategy, B: Strategy, C: Strategy, D: Strategy, E: Strategy, F: Strategy, G: Strategy, H: Strategy> Strategy for (A, B, C, D, E, F, G, H)impl Strategy for Anyimpl<T> Strategy for HashSetStrategy<T>impl<A: Strategy, B: Strategy<Value = <A as >::Value>, C: Strategy<Value = <A as >::Value>, D: Strategy<Value = <A as >::Value>, E: Strategy<Value = <A as >::Value>, F: Strategy<Value = <A as >::Value>, G: Strategy<Value = <A as >::Value>, H: Strategy<Value = <A as >::Value>, I: Strategy<Value = <A as >::Value>, J: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>, WA<C>, WA<D>, WA<E>, WA<F>, WA<G>, WA<H>, WA<I>, WA<J>)>impl Strategy for RangeInclusive<usize>impl<T: fmt::Debug> Strategy for SBoxedStrategy<T>impl Strategy for RangeTo<f64>impl<S: Strategy, F: FilterFn<<S as >::Value> + Clone> Strategy for Filter<S, F>impl Strategy for RangeInclusive<i8>impl<T: Strategy> Strategy for Union<T>impl Strategy for RangeTo<i32>impl Strategy for Range<isize>impl<A: Strategy, B: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>)>impl Strategy for RangeFrom<u16>impl<A: Strategy> Strategy for (A)impl<A: Strategy, B: Strategy<Value = <A as >::Value>, C: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>, WA<C>)>impl Strategy for RangeToInclusive<u64>impl Strategy for RangeInclusive<f32>impl<'a, S: Strategy + ?Sized> Strategy for &'a Simpl Strategy for RangeInclusive<i16>impl Strategy for Anyimpl Strategy for RangeTo<i64>impl Strategy for Range<u8>impl Strategy for RangeFrom<u32>impl<A: Strategy, B: Strategy, C: Strategy, D: Strategy, E: Strategy, F: Strategy> Strategy for (A, B, C, D, E, F)impl Strategy for Anyimpl<A: Strategy, B: Strategy<Value = <A as >::Value>, C: Strategy<Value = <A as >::Value>, D: Strategy<Value = <A as >::Value>, E: Strategy<Value = <A as >::Value>, F: Strategy<Value = <A as >::Value>, G: Strategy<Value = <A as >::Value>, H: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>, WA<C>, WA<D>, WA<E>, WA<F>, WA<G>, WA<H>)>impl Strategy for RangeToInclusive<u128>impl Strategy for RangeInclusive<f64>impl<T: Strategy> Strategy for Vec<T>impl Strategy for RangeInclusive<i32>impl Strategy for Anyimpl Strategy for RangeTo<i128>impl Strategy for Range<u16>impl<T> Strategy for BTreeSetStrategy<T>impl Strategy for RangeFrom<u64>impl Strategy for Anyimpl<A: Strategy, B: Strategy, C: Strategy, D: Strategy, E: Strategy, F: Strategy, G: Strategy, H: Strategy, I: Strategy, J: Strategy, K: Strategy> Strategy for (A, B, C, D, E, F, G, H, I, J, K)impl Strategy for RangeToInclusive<usize>impl<T> Strategy for VecDequeStrategy<T>impl<T: Strategy> Strategy for NoShrink<T>impl Strategy for RangeToInclusive<i8>impl Strategy for RangeInclusive<i64>impl Strategy for RangeTo<isize>impl Strategy for Anyimpl Strategy for Range<u32>impl<A: Strategy, B: Strategy, C: Strategy, D: Strategy> Strategy for (A, B, C, D)impl<A: Strategy, B: Strategy<Value = <A as >::Value>, C: Strategy<Value = <A as >::Value>, D: Strategy<Value = <A as >::Value>, E: Strategy<Value = <A as >::Value>, F: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>, WA<C>, WA<D>, WA<E>, WA<F>)>impl Strategy for RangeFrom<u128>impl<T: Strategy> Strategy for VecStrategy<T>impl Strategy for RangeToInclusive<f32>impl<T: fmt::Debug + Clone + 'static> Strategy for Subsequence<T>impl<S: Strategy + ?Sized> Strategy for Arc<S>impl Strategy for RangeToInclusive<i16>impl<T: BitSetLike> Strategy for BitSetStrategy<T>impl Strategy for Anyimpl Strategy for RangeInclusive<i128>impl<T: fmt::Debug> Strategy for BoxedStrategy<T>impl Strategy for RangeTo<u8>impl<S: Strategy, R: Strategy, F: Fn(<S as >::Value) -> R> Strategy for IndFlattenMap<S, F>impl<'a> Strategy for CharStrategy<'a>impl Strategy for Range<u64>impl<A: Strategy, B: Strategy, C: Strategy, D: Strategy, E: Strategy, F: Strategy, G: Strategy, H: Strategy, I: Strategy> Strategy for (A, B, C, D, E, F, G, H, I)impl Strategy for RangeFrom<usize>impl<S: Strategy, O: fmt::Debug, F: Fn(<S as >::Value, TestRng) -> O> Strategy for Perturb<S, F>impl<S: Strategy> Strategy for Shuffle<S>impl Strategy for RangeToInclusive<f64>impl Strategy for RangeFrom<i8>impl<T: fmt::Debug, F: Clone + Fn() -> T> Strategy for LazyJust<T, F>impl Strategy for RangeToInclusive<i32>impl Strategy for RangeInclusive<isize>impl Strategy for RangeTo<u16>impl<K, V> Strategy for HashMapStrategy<K, V>impl<A: Strategy, B: Strategy> Strategy for (A, B)impl<A: Strategy, B: Strategy<Value = <A as >::Value>, C: Strategy<Value = <A as >::Value>, D: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>, WA<C>, WA<D>)>impl Strategy for Range<u128>impl<T> Strategy for LinkedListStrategy<T>impl<T: BitSetLike> Strategy for SampledBitSetStrategy<T>impl Strategy for RangeFrom<f32>impl Strategy for Weightedimpl<'a, S: Strategy + ?Sized> Strategy for &'a mut Simpl Strategy for RangeFrom<i16>impl<S: Strategy, O: fmt::Debug, F: Fn(<S as >::Value) -> O> Strategy for Map<S, F>impl Strategy for RangeToInclusive<i64>impl Strategy for RangeInclusive<u8>impl Strategy for RangeTo<u32>impl<A: Strategy, B: Strategy, C: Strategy, D: Strategy, E: Strategy, F: Strategy, G: Strategy> Strategy for (A, B, C, D, E, F, G)impl<S: Strategy, F: Fn(&<S as >::Value) -> bool> Strategy for Filter<S, F>impl<A: Strategy, B: Strategy<Value = <A as >::Value>, C: Strategy<Value = <A as >::Value>, D: Strategy<Value = <A as >::Value>, E: Strategy<Value = <A as >::Value>, F: Strategy<Value = <A as >::Value>, G: Strategy<Value = <A as >::Value>, H: Strategy<Value = <A as >::Value>, I: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>, WA<C>, WA<D>, WA<E>, WA<F>, WA<G>, WA<H>, WA<I>)>impl Strategy for Range<usize>impl Strategy for Anyimpl Strategy for RangeFrom<f64>impl Strategy for Range<i8>impl Strategy for IndexStrategyimpl Strategy for RangeFrom<i32>impl Strategy for SelectorStrategyimpl Strategy for RangeToInclusive<i128>impl Strategy for Anyimpl Strategy for RangeInclusive<u16>impl<S: Strategy, F: Clone + MapFn<<S as >::Value>> Strategy for Map<S, F>impl<T> Strategy for RegexGeneratorStrategy<T>impl Strategy for RangeTo<u64>impl<A: Strategy, B: Strategy, C: Strategy, D: Strategy, E: Strategy, F: Strategy, G: Strategy, H: Strategy, I: Strategy, J: Strategy, K: Strategy, L: Strategy> Strategy for (A, B, C, D, E, F, G, H, I, J, K, L)impl Strategy for Anyimpl Strategy for Range<f32>impl Strategy for strimpl<T> Strategy for OptionStrategy<T>impl Strategy for Anyimpl<S: Strategy + ?Sized> Strategy for Box<S>impl Strategy for Range<i16>impl<S: Strategy, N: usize> Strategy for UniformArrayStrategy<S, [<S as >::Value; N]>impl<T, E> Strategy for MaybeOk<T, E>impl Strategy for RangeFrom<i64>impl Strategy for RangeToInclusive<isize>impl<K, V> Strategy for BTreeMapStrategy<K, V>impl<T, E> Strategy for MaybeErr<T, E>impl Strategy for Anyimpl Strategy for RangeInclusive<u32>impl<A: Strategy, B: Strategy, C: Strategy, D: Strategy, E: Strategy> Strategy for (A, B, C, D, E)impl<A: Strategy, B: Strategy<Value = <A as >::Value>, C: Strategy<Value = <A as >::Value>, D: Strategy<Value = <A as >::Value>, E: Strategy<Value = <A as >::Value>, F: Strategy<Value = <A as >::Value>, G: Strategy<Value = <A as >::Value>> Strategy for TupleUnion<(WA<A>, WA<B>, WA<C>, WA<D>, WA<E>, WA<F>, WA<G>)>impl<T> Strategy for BinaryHeapStrategy<T>impl Strategy for RangeTo<u128>impl Strategy for Anyimpl Strategy for Range<f64>impl<S: Strategy, N: usize> Strategy for [S; N]impl Strategy for Range<i32>impl Strategy for RangeFrom<i128>impl Strategy for RangeToInclusive<u8>impl<S: Strategy> Strategy for Flatten<S>impl Strategy for RangeInclusive<u64>impl<A: Strategy, B: Strategy, C: Strategy, D: Strategy, E: Strategy, F: Strategy, G: Strategy, H: Strategy, I: Strategy, J: Strategy> Strategy for (A, B, C, D, E, F, G, H, I, J)impl Strategy for Any