use crate::config::TestConfig;
use crate::error::PropertyError;
use crate::error::PropertyResult;
use crate::execution::check_with_config;
use crate::generator::Generator;
use crate::property::Property;
pub trait PropertyClosure<T> {
type Output;
fn call(&self, input: T) -> Result<Self::Output, PropertyError>;
}
impl<F, T> PropertyClosure<T> for F
where
F: Fn(T) -> bool,
{
type Output = ();
fn call(&self, input: T) -> Result<Self::Output, PropertyError> {
if self(input) {
Ok(())
} else {
Err(PropertyError::property_failed("Property returned false"))
}
}
}
pub struct ClosureProperty<F> {
closure: F,
}
impl<F> ClosureProperty<F> {
pub fn new(closure: F) -> Self {
Self { closure }
}
}
impl<F, T> Property<T> for ClosureProperty<F>
where
F: PropertyClosure<T>,
{
type Output = F::Output;
fn test(&self, input: T) -> Result<Self::Output, PropertyError> {
self.closure.call(input)
}
}
pub fn check_with_closure<T, G, F>(generator: G, closure: F) -> PropertyResult<T>
where
T: Clone + std::fmt::Debug + PartialEq + 'static,
G: Generator<T> + 'static,
F: PropertyClosure<T>,
{
let property = ClosureProperty::new(closure);
check_with_config(generator, property, TestConfig::default())
}
pub fn check_with_closure_config<T, G, F>(
generator: G,
closure: F,
config: TestConfig,
) -> PropertyResult<T>
where
T: Clone + std::fmt::Debug + PartialEq + 'static,
G: Generator<T> + 'static,
F: PropertyClosure<T>,
{
let property = ClosureProperty::new(closure);
check_with_config(generator, property, config)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::primitives::IntGenerator;
#[test]
fn test_closure_property_bool_return() {
let result = check_with_closure(IntGenerator::new(1, 10), |x: i32| x > 0);
assert!(result.is_ok());
}
#[test]
fn test_closure_property_with_operations() {
let result = check_with_closure(IntGenerator::new(-100, 100), |x: i32| x.abs() >= 0);
assert!(result.is_ok());
}
#[test]
fn test_closure_property_failing_case() {
let result = check_with_closure(
IntGenerator::new(1, 100),
|x: i32| x < 50, );
assert!(result.is_err());
}
#[test]
fn test_closure_property_with_config() {
let config = TestConfig {
iterations: 10,
seed: Some(42),
..TestConfig::default()
};
let result = check_with_closure_config(IntGenerator::new(1, 10), |x: i32| x > 0, config);
assert!(result.is_ok());
}
}