blob: 3703dc3ef48be170b72c511b030f785c0e80b453 [file] [log] [blame]
//-
// Copyright 2017 Jason Lingle
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::fmt;
use core::marker::PhantomData;
use crate::std_facade::Arc;
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<S, F> {
pub(super) source: S,
pub(super) fun: Arc<F>,
}
impl<S : fmt::Debug, F> fmt::Debug for Map<S, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Map")
.field("source", &self.source)
.field("fun", &"<function>")
.finish()
}
}
impl<S : Clone, F> Clone for Map<S, F> {
fn clone(&self) -> Self {
Map {
source: self.source.clone(),
fun: Arc::clone(&self.fun),
}
}
}
impl<S : Strategy, O : fmt::Debug,
F : Fn (S::Value) -> O>
Strategy for Map<S, F> {
type Tree = Map<S::Tree, F>;
type Value = O;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
self.source.new_tree(runner).map(
|v| Map { source: v, fun: Arc::clone(&self.fun) })
}
}
impl<S : ValueTree, O : fmt::Debug, F : Fn (S::Value) -> O>
ValueTree for Map<S, F> {
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<S, O> {
pub(super) source: S,
pub(super) output: PhantomData<O>,
}
impl<S, O> MapInto<S, O> {
/// 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<S : fmt::Debug, O> fmt::Debug for MapInto<S, O> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MapInto").field("source", &self.source).finish()
}
}
impl<S : Clone, O> Clone for MapInto<S, O> {
fn clone(&self) -> Self {
Self::new(self.source.clone())
}
}
impl<S : Strategy, O : fmt::Debug> Strategy for MapInto<S, O>
where
S::Value : Into<O>
{
type Tree = MapInto<S::Tree, O>;
type Value = O;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
self.source.new_tree(runner).map(MapInto::new)
}
}
impl<S : ValueTree, O : fmt::Debug> ValueTree for MapInto<S, O>
where
S::Value: Into<O>
{
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<S, F> {
pub(super) source: S,
pub(super) fun: Arc<F>,
}
impl<S : fmt::Debug, F> fmt::Debug for Perturb<S, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Perturb")
.field("source", &self.source)
.field("fun", &"<function>")
.finish()
}
}
impl<S : Clone, F> Clone for Perturb<S, F> {
fn clone(&self) -> Self {
Perturb {
source: self.source.clone(),
fun: Arc::clone(&self.fun),
}
}
}
impl<S : Strategy, O : fmt::Debug,
F : Fn (S::Value, TestRng) -> O>
Strategy for Perturb<S, F> {
type Tree = PerturbValueTree<S::Tree, F>;
type Value = O;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
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<S, F> {
source: S,
fun: Arc<F>,
rng: TestRng,
}
impl<S : fmt::Debug, F> fmt::Debug for PerturbValueTree<S, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PerturbValueTree")
.field("source", &self.source)
.field("fun", &"<function>")
.field("rng", &self.rng)
.finish()
}
}
impl<S : Clone, F> Clone for PerturbValueTree<S, F> {
fn clone(&self) -> Self {
PerturbValueTree {
source: self.source.clone(),
fun: Arc::clone(&self.fun),
rng: self.rng.clone(),
}
}
}
impl<S : ValueTree, O : fmt::Debug, F : Fn (S::Value, TestRng) -> O>
ValueTree for PerturbValueTree<S, F> {
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 crate::strategy::just::Just;
use super::*;
#[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::<usize>(), |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());
}
}