Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion proptest/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
pub use crate::arbitrary::{any, any_with, Arbitrary};
pub use crate::strategy::{BoxedStrategy, Just, SBoxedStrategy, Strategy};
pub use crate::test_runner::Config as ProptestConfig;
pub use crate::test_runner::TestCaseError;
pub use crate::test_runner::{TestCaseError, ResultExt};
pub use crate::{
prop_assert, prop_assert_eq, prop_assert_ne, prop_assume, prop_compose,
prop_oneof, proptest,
Expand Down
45 changes: 45 additions & 0 deletions proptest/src/test_runner/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,48 @@ impl<T: fmt::Debug> ::std::error::Error for TestError<T> {
}
}
}

/// Extension trait for `Result<T, E>` to provide additional functionality
/// specifically for prop test cases.
pub trait ResultExt<T, E> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a nice ergonomic addition, but ResultExt is pretty common. can we name this ProptestResultExt instead?

also seems like a good case for a sealed trait
e.g. from blog post

mod private {
    pub trait Sealed {}
}

pub trait SealedTrait : private::Sealed {
    fn method(&self);
}

/// Converts a `Result<T, E>` into a `Result<T, TestCaseError>`, where the
/// `Err` case is transformed into a `TestCaseError::Reject`.
///
/// This is intended to be used like the [`prop_assume!`] macro, but for
/// fallible computations. If the result is `Err`, the test input is rejected
/// and a new input will be generated.
///
/// ## Example
///
/// ```
/// use proptest::prelude::*;
/// proptest! {
/// #[test]
/// fn test_that_only_works_with_positive_integers(a in -10i32..10i32) {
/// // Reject the case if `a` cannot be converted to u8 (e.g., negative values)
/// let _unsigned: u8 = a.try_into().assume_ok()?;
/// // ...rest of test...
/// }
/// }
/// #
/// # fn main() { test_signed_to_unsigned(); }
/// ```
///
/// [`prop_assume!`]: crate::prop_assume
fn assume_ok(self) -> Result<T, TestCaseError>
where
E: fmt::Debug;
}

impl<T, E> ResultExt<T, E> for Result<T, E> {
#[track_caller]
fn assume_ok(self) -> Result<T, TestCaseError>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about naming prop_assume_ok?

where
E: fmt::Debug,
{
let location = core::panic::Location::caller();
self.map_err(|err| {
TestCaseError::reject(format!("{location}: {err:?}"))
})
}
}