use std::fmt;
#[derive(Debug, Clone)]
pub struct GenError(proptest::test_runner::Reason);
impl fmt::Display for GenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "strategy could not generate a value: {}", self.0)
}
}
impl std::error::Error for GenError {}
pub struct Runner {
inner: proptest::test_runner::TestRunner,
}
impl Runner {
#[must_use]
pub fn deterministic() -> Self {
Self {
inner: proptest::test_runner::TestRunner::deterministic(),
}
}
#[must_use]
pub fn randomized() -> Self {
Self {
inner: proptest::test_runner::TestRunner::default(),
}
}
fn backend(&mut self) -> &mut proptest::test_runner::TestRunner {
&mut self.inner
}
}
impl Default for Runner {
fn default() -> Self {
Self::deterministic()
}
}
pub trait Strategy<T> {
type Tree: ValueTree<T>;
fn new_tree(&self, runner: &mut Runner) -> Result<Self::Tree, GenError>;
}
pub trait ValueTree<T> {
fn current(&self) -> T;
fn simplify(&mut self) -> bool;
fn complicate(&mut self) -> bool;
}
pub struct ProptestTree<VT>(VT);
impl<VT, T> ValueTree<T> for ProptestTree<VT>
where
VT: proptest::strategy::ValueTree<Value = T>,
{
fn current(&self) -> T {
self.0.current()
}
fn simplify(&mut self) -> bool {
self.0.simplify()
}
fn complicate(&mut self) -> bool {
self.0.complicate()
}
}
impl<S, T> Strategy<T> for S
where
S: proptest::strategy::Strategy<Value = T>,
{
type Tree = ProptestTree<S::Tree>;
fn new_tree(&self, runner: &mut Runner) -> Result<Self::Tree, GenError> {
proptest::strategy::Strategy::new_tree(self, runner.backend())
.map(ProptestTree)
.map_err(GenError)
}
}
#[must_use]
pub fn any<T>() -> impl Strategy<T>
where
T: proptest::arbitrary::Arbitrary,
{
proptest::arbitrary::any::<T>()
}
#[cfg(test)]
mod tests {
use super::*;
use test_better_core::{OrFail, TestResult};
use test_better_matchers::{check, ge, is_true};
#[test]
fn a_proptest_strategy_is_a_seam_strategy() -> TestResult {
let mut runner = Runner::deterministic();
let tree = (0u32..10).new_tree(&mut runner).or_fail()?;
check!(tree.current() < 10).satisfies(is_true())
}
#[test]
fn simplify_shrinks_the_current_value_toward_its_origin() -> TestResult {
let mut runner = Runner::deterministic();
let mut tree = (5u32..1_000).new_tree(&mut runner).or_fail()?;
let start = tree.current();
while tree.simplify() {}
check!(start).satisfies(ge(tree.current()))
}
}