proptest/strategy/
statics.rs

1//-
2// Copyright 2017 Jason Lingle
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Modified versions of the normal strategy combinators which take specialised
11//! traits instead of normal functions.
12//!
13//! This entire module is strictly a workaround until
14//! <https://github.com/rust-lang/rfcs/pull/1522> and
15//! <https://github.com/rust-lang/rfcs/pull/2071> are available in stable. It
16//! allows naming types built on the combinators without resorting to dynamic
17//! dispatch or causing `Arc` to allocate space for a function pointer.
18//!
19//! External code is discouraged from using this module directly. It is
20//! deliberately not exposed in a convenient way (i.e., via the `Strategy`
21//! trait itself), but is nonetheless exposed since external trait implementors
22//! may face the same issues.
23//!
24//! **This module is subject to removal at some point after the language
25//! features linked above become stable.**
26
27use crate::std_facade::fmt;
28
29use crate::strategy::traits::*;
30use crate::test_runner::*;
31
32//==============================================================================
33// Filter
34//==============================================================================
35
36/// Essentially `Fn (&T) -> bool`.
37pub trait FilterFn<T> {
38    /// Test whether `t` passes the filter.
39    fn apply(&self, t: &T) -> bool;
40}
41
42/// Static version of `strategy::Filter`.
43#[derive(Clone)]
44#[must_use = "strategies do nothing unless used"]
45pub struct Filter<S, F> {
46    source: S,
47    whence: Reason,
48    fun: F,
49}
50
51impl<S, F> Filter<S, F> {
52    /// Adapt strategy `source` to reject values which do not pass `filter`,
53    /// using `whence` as the reported reason/location.
54    pub fn new(source: S, whence: Reason, filter: F) -> Self {
55        // NOTE: We don't use universal quantification R: Into<Reason>
56        // since the module is not conveniently exposed.
57        Filter {
58            source,
59            whence,
60            fun: filter,
61        }
62    }
63}
64
65impl<S: fmt::Debug, F> fmt::Debug for Filter<S, F> {
66    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67        f.debug_struct("Filter")
68            .field("source", &self.source)
69            .field("whence", &self.whence)
70            .field("fun", &"<function>")
71            .finish()
72    }
73}
74
75impl<S: Strategy, F: FilterFn<S::Value> + Clone> Strategy for Filter<S, F> {
76    type Tree = Filter<S::Tree, F>;
77    type Value = S::Value;
78
79    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
80        loop {
81            let val = self.source.new_tree(runner)?;
82            if !self.fun.apply(&val.current()) {
83                runner.reject_local(self.whence.clone())?;
84            } else {
85                return Ok(Filter {
86                    source: val,
87                    whence: "unused".into(),
88                    fun: self.fun.clone(),
89                });
90            }
91        }
92    }
93}
94
95impl<S: ValueTree, F: FilterFn<S::Value>> Filter<S, F> {
96    fn ensure_acceptable(&mut self) {
97        while !self.fun.apply(&self.source.current()) {
98            if !self.source.complicate() {
99                panic!(
100                    "Unable to complicate filtered strategy \
101                     back into acceptable value"
102                );
103            }
104        }
105    }
106}
107
108impl<S: ValueTree, F: FilterFn<S::Value>> ValueTree for Filter<S, F> {
109    type Value = S::Value;
110
111    fn current(&self) -> S::Value {
112        self.source.current()
113    }
114
115    fn simplify(&mut self) -> bool {
116        if self.source.simplify() {
117            self.ensure_acceptable();
118            true
119        } else {
120            false
121        }
122    }
123
124    fn complicate(&mut self) -> bool {
125        if self.source.complicate() {
126            self.ensure_acceptable();
127            true
128        } else {
129            false
130        }
131    }
132}
133
134//==============================================================================
135// Map
136//==============================================================================
137
138/// Essentially `Fn (T) -> Output`.
139pub trait MapFn<T> {
140    #[allow(missing_docs)]
141    type Output: fmt::Debug;
142
143    /// Map `T` to `Output`.
144    fn apply(&self, t: T) -> Self::Output;
145}
146
147/// Static version of `strategy::Map`.
148#[derive(Clone)]
149#[must_use = "strategies do nothing unless used"]
150pub struct Map<S, F> {
151    source: S,
152    fun: F,
153}
154
155impl<S, F> Map<S, F> {
156    /// Adapt strategy `source` by applying `fun` to values it produces.
157    pub fn new(source: S, fun: F) -> Self {
158        Map { source, fun }
159    }
160}
161
162impl<S: fmt::Debug, F> fmt::Debug for Map<S, F> {
163    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164        f.debug_struct("Map")
165            .field("source", &self.source)
166            .field("fun", &"<function>")
167            .finish()
168    }
169}
170
171impl<S: Strategy, F: Clone + MapFn<S::Value>> Strategy for Map<S, F> {
172    type Tree = Map<S::Tree, F>;
173    type Value = F::Output;
174
175    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
176        self.source.new_tree(runner).map(|v| Map {
177            source: v,
178            fun: self.fun.clone(),
179        })
180    }
181}
182
183impl<S: ValueTree, F: MapFn<S::Value>> ValueTree for Map<S, F> {
184    type Value = F::Output;
185
186    fn current(&self) -> F::Output {
187        self.fun.apply(self.source.current())
188    }
189
190    fn simplify(&mut self) -> bool {
191        self.source.simplify()
192    }
193
194    fn complicate(&mut self) -> bool {
195        self.source.complicate()
196    }
197}
198
199impl<I, O: fmt::Debug> MapFn<I> for fn(I) -> O {
200    type Output = O;
201    fn apply(&self, x: I) -> Self::Output {
202        self(x)
203    }
204}
205
206pub(crate) fn static_map<S: Strategy, O: fmt::Debug>(
207    strat: S,
208    fun: fn(S::Value) -> O,
209) -> Map<S, fn(S::Value) -> O> {
210    Map::new(strat, fun)
211}
212
213//==============================================================================
214// Tests
215//==============================================================================
216
217#[cfg(test)]
218mod test {
219    use super::*;
220
221    #[test]
222    fn test_static_filter() {
223        #[derive(Clone, Copy, Debug)]
224        struct MyFilter;
225        impl FilterFn<i32> for MyFilter {
226            fn apply(&self, &v: &i32) -> bool {
227                0 == v % 3
228            }
229        }
230
231        let input = Filter::new(0..256, "%3".into(), MyFilter);
232
233        for _ in 0..256 {
234            let mut runner = TestRunner::default();
235            let mut case = input.new_tree(&mut runner).unwrap();
236
237            assert!(0 == case.current() % 3);
238
239            while case.simplify() {
240                assert!(0 == case.current() % 3);
241            }
242            assert!(0 == case.current() % 3);
243        }
244    }
245
246    #[test]
247    fn test_static_map() {
248        #[derive(Clone, Copy, Debug)]
249        struct MyMap;
250        impl MapFn<i32> for MyMap {
251            type Output = i32;
252            fn apply(&self, v: i32) -> i32 {
253                v * 2
254            }
255        }
256
257        let input = Map::new(0..10, MyMap);
258
259        TestRunner::default()
260            .run(&input, |v| {
261                assert!(0 == v % 2);
262                Ok(())
263            })
264            .unwrap();
265    }
266}