//- // 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. use crate::std_facade::Arc; use core::fmt; use core::marker::PhantomData; use crate::strategy::traits::*; use crate::test_runner::*; //============================================================================== // Map //============================================================================== /// `Strategy` and `ValueTree` map adaptor. /// /// See `Strategy::prop_map()`. #[must_use = "strategies do nothing unless used"] pub struct Map { pub(super) source: S, pub(super) fun: Arc, } 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 Clone for Map { fn clone(&self) -> Self { Map { source: self.source.clone(), fun: Arc::clone(&self.fun), } } } impl O> Strategy for Map { type Tree = Map; type Value = O; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { self.source.new_tree(runner).map(|v| Map { source: v, fun: Arc::clone(&self.fun), }) } } impl O> ValueTree for Map { type Value = O; fn current(&self) -> O { (self.fun)(self.source.current()) } fn simplify(&mut self) -> bool { self.source.simplify() } fn complicate(&mut self) -> bool { self.source.complicate() } } //============================================================================== // MapInto //============================================================================== // NOTE: Since this is external stable API, // we avoid relying on the Map in `statics`. /// `Strategy` and `ValueTree` map into adaptor. /// /// See `Strategy::prop_map_into()`. #[must_use = "strategies do nothing unless used"] pub struct MapInto { pub(super) source: S, pub(super) output: PhantomData, } impl MapInto { /// Construct a `MapInto` mapper from an `S` strategy into a strategy /// producing `O`s. pub(super) fn new(source: S) -> Self { Self { source, output: PhantomData, } } } impl fmt::Debug for MapInto { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("MapInto") .field("source", &self.source) .finish() } } impl Clone for MapInto { fn clone(&self) -> Self { Self::new(self.source.clone()) } } impl Strategy for MapInto where S::Value: Into, { type Tree = MapInto; type Value = O; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { self.source.new_tree(runner).map(MapInto::new) } } impl ValueTree for MapInto where S::Value: Into, { type Value = O; fn current(&self) -> O { self.source.current().into() } fn simplify(&mut self) -> bool { self.source.simplify() } fn complicate(&mut self) -> bool { self.source.complicate() } } //============================================================================== // Perturb //============================================================================== /// `Strategy` perturbation adaptor. /// /// See `Strategy::prop_perturb()`. #[must_use = "strategies do nothing unless used"] pub struct Perturb { pub(super) source: S, pub(super) fun: Arc, } impl fmt::Debug for Perturb { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Perturb") .field("source", &self.source) .field("fun", &"") .finish() } } impl Clone for Perturb { fn clone(&self) -> Self { Perturb { source: self.source.clone(), fun: Arc::clone(&self.fun), } } } impl O> Strategy for Perturb { type Tree = PerturbValueTree; type Value = O; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let rng = runner.new_rng(); self.source.new_tree(runner).map(|source| PerturbValueTree { source, rng, fun: Arc::clone(&self.fun), }) } } /// `ValueTree` perturbation adaptor. /// /// See `Strategy::prop_perturb()`. pub struct PerturbValueTree { source: S, fun: Arc, rng: TestRng, } impl fmt::Debug for PerturbValueTree { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("PerturbValueTree") .field("source", &self.source) .field("fun", &"") .field("rng", &self.rng) .finish() } } impl Clone for PerturbValueTree { fn clone(&self) -> Self { PerturbValueTree { source: self.source.clone(), fun: Arc::clone(&self.fun), rng: self.rng.clone(), } } } impl O> ValueTree for PerturbValueTree { type Value = O; fn current(&self) -> O { (self.fun)(self.source.current(), self.rng.clone()) } fn simplify(&mut self) -> bool { self.source.simplify() } fn complicate(&mut self) -> bool { self.source.complicate() } } //============================================================================== // Tests //============================================================================== #[cfg(test)] mod test { use std::collections::HashSet; use rand::RngCore; use super::*; use crate::strategy::just::Just; #[test] fn test_map() { TestRunner::default() .run(&(0..10).prop_map(|v| v * 2), |v| { assert!(0 == v % 2); Ok(()) }) .unwrap(); } #[test] fn test_map_into() { TestRunner::default() .run(&(0..10u8).prop_map_into::(), |v| { assert!(v < 10); Ok(()) }) .unwrap(); } #[test] fn perturb_uses_same_rng_every_time() { let mut runner = TestRunner::default(); let input = Just(1).prop_perturb(|v, mut rng| v + rng.next_u32()); for _ in 0..16 { let value = input.new_tree(&mut runner).unwrap(); assert_eq!(value.current(), value.current()); } } #[test] fn perturb_uses_varying_random_seeds() { let mut runner = TestRunner::default(); let input = Just(1).prop_perturb(|v, mut rng| v + rng.next_u32()); let mut seen = HashSet::new(); for _ in 0..64 { seen.insert(input.new_tree(&mut runner).unwrap().current()); } assert_eq!(64, seen.len()); } }