//- // 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. //! Modified versions of the normal strategy combinators which take specialised //! traits instead of normal functions. //! //! This entire module is strictly a workaround until //! and //! are available in stable. It //! allows naming types built on the combinators without resorting to dynamic //! dispatch or causing `Arc` to allocate space for a function pointer. //! //! External code is discouraged from using this module directly. It is //! deliberately not exposed in a convenient way (i.e., via the `Strategy` //! trait itself), but is nonetheless exposed since external trait implementors //! may face the same issues. //! //! **This module is subject to removal at some point after the language //! features linked above become stable.** use crate::std_facade::fmt; use crate::strategy::traits::*; use crate::test_runner::*; //============================================================================== // Filter //============================================================================== /// Essentially `Fn (&T) -> bool`. pub trait FilterFn { /// Test whether `t` passes the filter. fn apply(&self, t: &T) -> bool; } /// Static version of `strategy::Filter`. #[derive(Clone)] #[must_use = "strategies do nothing unless used"] pub struct Filter { source: S, whence: Reason, fun: F, } impl Filter { /// Adapt strategy `source` to reject values which do not pass `filter`, /// using `whence` as the reported reason/location. pub fn new(source: S, whence: Reason, filter: F) -> Self { // NOTE: We don't use universal quantification R: Into // since the module is not conveniently exposed. Filter { source, whence, fun: filter, } } } impl fmt::Debug for Filter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Filter") .field("source", &self.source) .field("whence", &self.whence) .field("fun", &"") .finish() } } impl + Clone> Strategy for Filter { type Tree = Filter; type Value = S::Value; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { loop { let val = self.source.new_tree(runner)?; if !self.fun.apply(&val.current()) { runner.reject_local(self.whence.clone())?; } else { return Ok(Filter { source: val, whence: "unused".into(), fun: self.fun.clone(), }); } } } } impl> Filter { fn ensure_acceptable(&mut self) { while !self.fun.apply(&self.source.current()) { if !self.source.complicate() { panic!( "Unable to complicate filtered strategy \ back into acceptable value" ); } } } } impl> ValueTree for Filter { type Value = S::Value; fn current(&self) -> S::Value { self.source.current() } fn simplify(&mut self) -> bool { if self.source.simplify() { self.ensure_acceptable(); true } else { false } } fn complicate(&mut self) -> bool { if self.source.complicate() { self.ensure_acceptable(); true } else { false } } } //============================================================================== // Map //============================================================================== /// Essentially `Fn (T) -> Output`. pub trait MapFn { #[allow(missing_docs)] type Output: fmt::Debug; /// Map `T` to `Output`. fn apply(&self, t: T) -> Self::Output; } /// Static version of `strategy::Map`. #[derive(Clone)] #[must_use = "strategies do nothing unless used"] pub struct Map { source: S, fun: F, } impl Map { /// Adapt strategy `source` by applying `fun` to values it produces. pub fn new(source: S, fun: F) -> Self { Map { source, fun } } } impl fmt::Debug for Map { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Map") .field("source", &self.source) .field("fun", &"") .finish() } } impl> Strategy for Map { type Tree = Map; type Value = F::Output; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { self.source.new_tree(runner).map(|v| Map { source: v, fun: self.fun.clone(), }) } } impl> ValueTree for Map { type Value = F::Output; fn current(&self) -> F::Output { self.fun.apply(self.source.current()) } fn simplify(&mut self) -> bool { self.source.simplify() } fn complicate(&mut self) -> bool { self.source.complicate() } } impl MapFn for fn(I) -> O { type Output = O; fn apply(&self, x: I) -> Self::Output { self(x) } } pub(crate) fn static_map( strat: S, fun: fn(S::Value) -> O, ) -> Map O> { Map::new(strat, fun) } //============================================================================== // Tests //============================================================================== #[cfg(test)] mod test { use super::*; #[test] fn test_static_filter() { #[derive(Clone, Copy, Debug)] struct MyFilter; impl FilterFn for MyFilter { fn apply(&self, &v: &i32) -> bool { 0 == v % 3 } } let input = Filter::new(0..256, "%3".into(), MyFilter); for _ in 0..256 { let mut runner = TestRunner::default(); let mut case = input.new_tree(&mut runner).unwrap(); assert!(0 == case.current() % 3); while case.simplify() { assert!(0 == case.current() % 3); } assert!(0 == case.current() % 3); } } #[test] fn test_static_map() { #[derive(Clone, Copy, Debug)] struct MyMap; impl MapFn for MyMap { type Output = i32; fn apply(&self, v: i32) -> i32 { v * 2 } } let input = Map::new(0..10, MyMap); TestRunner::default() .run(&input, |v| { assert!(0 == v % 2); Ok(()) }) .unwrap(); } }