//- // Copyright 2017 Jason Lingle // // 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. //! Support for strategies producing fixed-length arrays. //! //! An array of strategies (but only length 1 to 32 for now) is itself a //! strategy which generates arrays of that size drawing elements from the //! corresponding input strategies. //! //! See also [`UniformArrayStrategy`](struct.UniformArrayStrategy.html) for //! easily making a strategy for an array drawn from one strategy. //! //! General implementations are available for sizes 1 through 32. use core::marker::PhantomData; use crate::strategy::*; use crate::test_runner::*; /// A `Strategy` which generates fixed-size arrays containing values drawn from /// an inner strategy. /// /// `T` must be an array type of length 1 to 32 whose values are produced by /// strategy `S`. Instances of this type are normally created by the various /// `uniformXX` functions in this module. /// /// This is mainly useful when the inner strategy is not `Copy`, precluding /// expressing the strategy as `[myStrategy; 32]`, for example. /// /// ## Example /// /// ``` /// use proptest::prelude::*; /// /// proptest! { /// #[test] /// fn test_something(a in prop::array::uniform32(1u32..)) { /// let unexpected = [0u32;32]; /// // `a` is also a [u32;32], so we can compare them directly /// assert_ne!(unexpected, a); /// } /// } /// # fn main() { } /// ``` #[must_use = "strategies do nothing unless used"] #[derive(Clone, Copy, Debug)] pub struct UniformArrayStrategy { strategy: S, _marker: PhantomData, } impl UniformArrayStrategy { /// Directly create a `UniformArrayStrategy`. /// /// This is only intended for advanced use, since the only way to specify /// the array size is with the turbofish operator and explicitly naming the /// type of the values in the array and the strategy itself. /// /// Prefer the `uniformXX` functions at module-level unless something /// precludes their use. pub fn new(strategy: S) -> Self { UniformArrayStrategy { strategy, _marker: PhantomData, } } } /// A `ValueTree` operating over a fixed-size array. #[derive(Clone, Copy, Debug)] pub struct ArrayValueTree { tree: T, shrinker: usize, last_shrinker: Option, } /// Create a strategy to generate fixed-length arrays. /// /// All values within the new strategy are generated using the given /// strategy. /// /// See [`UniformArrayStrategy`](struct.UniformArrayStrategy.html) for /// example usage. pub fn uniform( strategy: S, ) -> UniformArrayStrategy { UniformArrayStrategy { strategy, _marker: PhantomData, } } macro_rules! small_array { ($n:tt $uni:ident) => { /// Create a strategy to generate fixed-length arrays. /// /// All values within the new strategy are generated using the given /// strategy. The length of the array corresponds to the suffix of the /// name of this function. /// /// See [`UniformArrayStrategy`](struct.UniformArrayStrategy.html) for /// example usage. pub fn $uni( strategy: S, ) -> UniformArrayStrategy { UniformArrayStrategy { strategy, _marker: PhantomData, } } }; } impl Strategy for [S; N] { type Tree = ArrayValueTree<[S::Tree; N]>; type Value = [S::Value; N]; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { Ok(ArrayValueTree { tree: unarray::build_array_result(|i| self[i].new_tree(runner))?, shrinker: 0, last_shrinker: None, }) } } impl Strategy for UniformArrayStrategy { type Tree = ArrayValueTree<[S::Tree; N]>; type Value = [S::Value; N]; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { Ok(ArrayValueTree { tree: unarray::build_array_result(|_| { self.strategy.new_tree(runner) })?, shrinker: 0, last_shrinker: None, }) } } impl ValueTree for ArrayValueTree<[T; N]> { type Value = [T::Value; N]; fn current(&self) -> [T::Value; N] { unarray::build_array(|i| self.tree[i].current()) } fn simplify(&mut self) -> bool { while self.shrinker < N { if self.tree[self.shrinker].simplify() { self.last_shrinker = Some(self.shrinker); return true; } else { self.shrinker += 1; } } false } fn complicate(&mut self) -> bool { if let Some(shrinker) = self.last_shrinker { self.shrinker = shrinker; if self.tree[shrinker].complicate() { true } else { self.last_shrinker = None; false } } else { false } } } small_array!(1 uniform1); small_array!(2 uniform2); small_array!(3 uniform3); small_array!(4 uniform4); small_array!(5 uniform5); small_array!(6 uniform6); small_array!(7 uniform7); small_array!(8 uniform8); small_array!(9 uniform9); small_array!(10 uniform10); small_array!(11 uniform11); small_array!(12 uniform12); small_array!(13 uniform13); small_array!(14 uniform14); small_array!(15 uniform15); small_array!(16 uniform16); small_array!(17 uniform17); small_array!(18 uniform18); small_array!(19 uniform19); small_array!(20 uniform20); small_array!(21 uniform21); small_array!(22 uniform22); small_array!(23 uniform23); small_array!(24 uniform24); small_array!(25 uniform25); small_array!(26 uniform26); small_array!(27 uniform27); small_array!(28 uniform28); small_array!(29 uniform29); small_array!(30 uniform30); small_array!(31 uniform31); small_array!(32 uniform32); #[cfg(test)] mod test { use super::*; #[test] fn shrinks_fully_ltr() { fn pass(a: [i32; 2]) -> bool { a[0] * a[1] <= 9 } let input = [0..32, 0..32]; let mut runner = TestRunner::deterministic(); let mut cases_tested = 0; for _ in 0..256 { // Find a failing test case let mut case = input.new_tree(&mut runner).unwrap(); if pass(case.current()) { continue; } loop { if pass(case.current()) { if !case.complicate() { break; } } else { if !case.simplify() { break; } } } let last = case.current(); assert!(!pass(last)); // Maximally shrunken assert!(pass([last[0] - 1, last[1]])); assert!(pass([last[0], last[1] - 1])); cases_tested += 1; } assert!(cases_tested > 32, "Didn't find enough test cases"); } #[test] fn test_sanity() { check_strategy_sanity([(0i32..1000), (1i32..1000)], None); } }