diff --git a/Cargo.toml b/Cargo.toml index 60412d819..dc40e6b0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "assertables" -version = "8.3.0" +version = "8.4.0" authors = ["Joel Parker Henderson "] edition = "2021" description = "Assertables: Rust crate of macros `assert…!` for better tests, quality assurance, debug testing, and runtime reliability." diff --git a/README.md b/README.md index 7ed332b8b..4ab0030f5 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ Examples: 8.5: Add `assert_option_some_eq`, `assert_option_some_ne` (planned) -8.4: Add `assert_result_ok_eq`, `assert_result_ok_ne` (planned) +8.4: Add `assert_result_ok_eq`, `assert_result_ok_ne` 8.3: Add `assert_poll_ready`, `assert_poll_pending`. @@ -229,6 +229,6 @@ Examples: * Package: assertables-rust-crate * Version: 8.4.0 * Created: 2021-03-30T15:47:49Z -* Updated: 2024-09-07T22:22:42Z +* Updated: 2024-09-11T02:11:16Z * License: MIT or Apache-2.0 or GPL-2.0 or GPL-3.0 or contact us for more * Contact: Joel Parker Henderson (joel@sixarm.com) diff --git a/help/comparisons/claims/index.md b/help/comparisons/claims/index.md index cf05df860..96dddb4f7 100644 --- a/help/comparisons/claims/index.md +++ b/help/comparisons/claims/index.md @@ -29,8 +29,8 @@ If there's an assertion from any of those crates that you would like us to add t | Contains | [`assert_contains`](https://docs.rs/assertables/latest/assertables/assert_contains)
[`assert_not_contains`](https://docs.rs/assertables/latest/assertables/assert_not_contains) | -
- | | Starts/Ends | [`assert_starts_with`](https://docs.rs/assertables/latest/assertables/assert_starts_with)
[`assert_not_starts_with`](https://docs.rs/assertables/latest/assertables/assert_not_starts_with)
[`assert_ends_with`](https://docs.rs/assertables/latest/assertables/assert_ends_with)
[`assert_not_ends_with`](https://docs.rs/assertables/latest/assertables/assert_not_ends_with) | -
-
-
- | | Result | [`assert_result_ok`](https://docs.rs/assertables/latest/assertables/assert_result/assert_result_ok)
[`assert_result_ok_eq`](https://docs.rs/assertables/latest/assertables/assert_result/assert_result_ok_eq)
[`assert_result_err`](https://docs.rs/assertables/latest/assertables/assert_result/assert_result_err) | `assert_result_ok`
`assert_result_ok_eq`
`assert_result_err` | -| Option | [`assert_option_some`](https://docs.rs/assertables/latest/assertables/assert_option/assert_option_some)
[`assert_option_some_eq`](https://docs.rs/assertables/latest/assertables/assert_option/assert_option_some_eq)
[`assert_option_none`](https://docs.rs/assertables/latest/assertables/assert_option/assert_option_none) | `assert_option_some`
`assert_option_some_eq`
`assert_option_none` | -| Poll | [`assert_poll_ready`](https://docs.rs/assertables/latest/assertables/assert_poll/assert_poll_ready)
[`assert_poll_ready_eq`](https://docs.rs/assertables/latest/assertables/assert_poll/assert_poll_ready_eq)
`todo`
`todo`
[`assert_poll_pending`](https://docs.rs/assertables/latest/assertables/assert_poll/assert_poll_pending) | `assert_ready`
`assert_ready_eq`
`assert_ready_ok`
`assert_ready_err`
`assert_pending` | +| Option | [`assert_option_some`](https://docs.rs/assertables/latest/assertables/assert_option/assert_option_some)
[`assert_option_some_eq`](https://docs.rs/assertables/latest/assertables/assert_option/assert_option_some_eq)
[`assert_option_none`](https://docs.rs/assertables/latest/assertables/assert_option/assert_option_none) | `assert_option_some`
`assert_option_some_eq` (planned)
`assert_option_none` | +| Poll | [`assert_poll_ready`](https://docs.rs/assertables/latest/assertables/assert_poll/assert_poll_ready)
[`assert_poll_ready_eq`](https://docs.rs/assertables/latest/assertables/assert_poll/assert_poll_ready_eq)
`todo`
`todo`
[`assert_poll_pending`](https://docs.rs/assertables/latest/assertables/assert_poll/assert_poll_pending) | `assert_ready`
`assert_ready_eq` (planned)
`assert_ready_ok` (planned)
`assert_ready_err` (planned)
`assert_pending` | | Readers | [`assert_fs_read_to_string_*`](https://docs.rs/assertables/latest/assertables/assert_fs_read_to_string)
[`assert_io_read_to_string_*`](https://docs.rs/assertables/latest/assertables/assert_io_read_to_string) | -
- | | Commands | [`assert_command_*`](https://docs.rs/assertables/latest/assertables/assert_command)
[`assert_program_args_*`](https://docs.rs/assertables/latest/assertables/assert_program_args) | -
- | | Collections | [`assert_set_*`](https://docs.rs/assertables/latest/assertables/assert_set)
[`assert_bag_*`](https://docs.rs/assertables/latest/assertables/assert_bag) | -
- | diff --git a/src/assert_result/assert_result_ok_eq.rs b/src/assert_result/assert_result_ok_eq.rs new file mode 100644 index 000000000..cfc7232bb --- /dev/null +++ b/src/assert_result/assert_result_ok_eq.rs @@ -0,0 +1,245 @@ +//! Assert expression is ok, and its value is equal to another. +//! +//! # Example +//! +//! ```rust +//! # #[macro_use] extern crate assertables; +//! # fn main() { +//! let a: Result = Result::Ok(1); +//! let b: Result = Result::Ok(1); +//! assert_result_ok_eq!(a, b); +//! # } +//! ``` +//! +//! # Module macros +//! +//! * [`assert_result_ok_eq`](macro@crate::assert_result_ok_eq) +//! * [`assert_result_ok_eq_as_result`](macro@crate::assert_result_ok_eq_as_result) +//! * [`debug_assert_result_ok_eq`](macro@crate::debug_assert_result_ok_eq) + +/// Assert expression is ok, and its value is equal to another. +/// +/// * If true, return Result `Ok(())`. +/// +/// * Otherwise, return Result `Err` with a diagnostic message. +/// +/// This macro provides the same statements as [`assert_result_ok_eq`](macro.assert_result_ok_eq.html), +/// except this macro returns a Result, rather than doing a panic. +/// +/// This macro is useful for runtime checks, such as checking parameters, +/// or sanitizing inputs, or handling different results in different ways. +/// +/// # Module macros +/// +/// * [`assert_result_ok_eq`](macro@crate::assert_result_ok_eq) +/// * [`assert_result_ok_eq_as_result`](macro@crate::assert_result_ok_eq_as_result) +/// * [`debug_assert_result_ok_eq`](macro@crate::debug_assert_result_ok_eq) +/// +#[macro_export] +macro_rules! assert_result_ok_eq_as_result { + ($a_result:expr, $b_result:expr $(,)?) => {{ + match (&$a_result, &$b_result) { + (a_result, b_result) => { + if a_result.is_err() || b_result.is_err() { + Err(format!( + concat!( + "assertion failed: `assert_result_ok_eq!(a, b)`\n", + " a label: `{}`,\n", + " a debug: `{:?}`,\n", + " b label: `{}`,\n", + " b debug: `{:?}`", + ), + stringify!($a_result), + $a_result, + stringify!($b_result), + $b_result, + )) + } else { + let a_ok = a_result.unwrap(); + let b_ok = b_result.unwrap(); + if a_ok == b_ok { + Ok(()) + } else { + Err(format!( + concat!( + "assertion failed: `assert_result_ok_eq!(a, b)`\n", + " a label: `{}`,\n", + " a debug: `{:?}`,\n", + " b label: `{}`,\n", + " b debug: `{:?}`,\n", + " a ok: `{:?}`,\n", + " b ok: `{:?}`" + ), + stringify!($a_result), + $a_result, + stringify!($b_result), + $b_result, + a_ok, + b_ok + )) + } + } + } + } + }}; +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_assert_result_ok_eq_as_result_x_success() { + let a: Result = Result::Ok(1); + let b: Result = Result::Ok(1); + let result = assert_result_ok_eq_as_result!(a, b); + assert_eq!(result, Ok(())); + } + + #[test] + fn test_assert_result_ok_eq_as_result_x_failure_because_ok_ne() { + let a: Result = Result::Ok(1); + let b: Result = Result::Ok(2); + let result = assert_result_ok_eq_as_result!(a, b); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + concat!( + "assertion failed: `assert_result_ok_eq!(a, b)`\n", + " a label: `a`,\n", + " a debug: `Ok(1)`,\n", + " b label: `b`,\n", + " b debug: `Ok(2)`,\n", + " a ok: `1`,\n", + " b ok: `2`", + ) + ); + } + + #[test] + fn test_assert_result_ok_eq_as_result_x_failure_because_err() { + let a: Result = Result::Ok(1); + let b: Result = Result::Err(1); + let result = assert_result_ok_eq_as_result!(a, b); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + concat!( + "assertion failed: `assert_result_ok_eq!(a, b)`\n", + " a label: `a`,\n", + " a debug: `Ok(1)`,\n", + " b label: `b`,\n", + " b debug: `Err(1)`", + ) + ); + } + +} + +/// Assert expression is ok, and its value is equal to another. +/// +/// * If true, return `()`. +/// +/// * Otherwise, call [`panic!`] with a message and the values of the +/// expressions with their debug representations. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] extern crate assertables; +/// # use std::panic; +/// # fn main() { +/// let a: Result = Result::Ok(1); +/// let b: Result = Result::Ok(1); +/// assert_result_ok_eq!(a, b); +/// //-> () +/// +/// // Panic with error message +/// let a: Result = Result::Ok(1); +/// let b: Result = Result::Ok(2); +/// let result = panic::catch_unwind(|| { +/// assert_result_ok_eq!(a, b); +/// //-> panic! +/// }); +/// assert!(result.is_err()); +/// let actual = result.unwrap_err().downcast::().unwrap().to_string(); +/// let expect = concat!( +/// "assertion failed: `assert_result_ok_eq!(a, b)`\n", +/// " a label: `a`,\n", +/// " a debug: `Ok(1)`,\n", +/// " b label: `b`,\n", +/// " b debug: `Ok(2)`,\n", +/// " a ok: `1`,\n", +/// " b ok: `2`", +/// ); +/// assert_eq!(actual, expect); +/// +/// // Panic with error message +/// let result = panic::catch_unwind(|| { +/// assert_result_ok_eq!(a, b, "message"); +/// //-> panic! +/// }); +/// assert!(result.is_err()); +/// let actual = result.unwrap_err().downcast::().unwrap().to_string(); +/// let expect = "message"; +/// assert_eq!(actual, expect); +/// # } +/// ``` +/// +/// # Module macros +/// +/// * [`assert_result_ok_eq`](macro@crate::assert_result_ok_eq) +/// * [`assert_result_ok_eq_as_result`](macro@crate::assert_result_ok_eq_as_result) +/// * [`debug_assert_result_ok_eq`](macro@crate::debug_assert_result_ok_eq) +/// +#[macro_export] +macro_rules! assert_result_ok_eq { + ($a:expr, $b:expr $(,)?) => ({ + match assert_result_ok_eq_as_result!($a, $b) { + Ok(()) => (), + Err(err) => panic!("{}", err), + } + }); + ($a:expr, $b:expr, $($message:tt)+) => ({ + match assert_result_ok_eq_as_result!($a, $b) { + Ok(()) => (), + Err(_err) => panic!("{}", $($message)+), + } + }); +} + +/// Assert expression is ok, and its value is equal to another. +/// +/// This macro provides the same statements as [`assert_result_ok_eq`](macro.assert_result_ok_eq.html), +/// except this macro's statements are only enabled in non-optimized +/// builds by default. An optimized build will not execute this macro's +/// statements unless `-C debug-assertions` is passed to the compiler. +/// +/// This macro is useful for checks that are too expensive to be present +/// in a release build but may be helpful during development. +/// +/// The result of expanding this macro is always type checked. +/// +/// An unchecked assertion allows a program in an inconsistent state to +/// keep running, which might have unexpected consequences but does not +/// introduce unsafety as long as this only happens in safe code. The +/// performance cost of assertions, however, is not measurable in general. +/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged +/// after thorough profiling, and more importantly, only in safe code! +/// +/// This macro is intended to work in a similar way to +/// [`std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html). +/// +/// # Module macros +/// +/// * [`assert_result_ok_eq`](macro@crate::assert_result_ok_eq) +/// * [`assert_result_ok_eq`](macro@crate::assert_result_ok_eq) +/// * [`debug_assert_result_ok_eq`](macro@crate::debug_assert_result_ok_eq) +/// +#[macro_export] +macro_rules! debug_assert_result_ok_eq { + ($($arg:tt)*) => { + if $crate::cfg!(debug_assertions) { + $crate::assert_result_ok_eq!($($arg)*); + } + }; +} diff --git a/src/assert_result/assert_result_ok_ne.rs b/src/assert_result/assert_result_ok_ne.rs new file mode 100644 index 000000000..85247f568 --- /dev/null +++ b/src/assert_result/assert_result_ok_ne.rs @@ -0,0 +1,245 @@ +//! Assert expression is ok, and its value is equal to another. +//! +//! # Example +//! +//! ```rust +//! # #[macro_use] extern crate assertables; +//! # fn main() { +//! let a: Result = Result::Ok(1); +//! let b: Result = Result::Ok(2); +//! assert_result_ok_ne!(a, b); +//! # } +//! ``` +//! +//! # Module macros +//! +//! * [`assert_result_ok_ne`](macro@crate::assert_result_ok_ne) +//! * [`assert_result_ok_ne_as_result`](macro@crate::assert_result_ok_ne_as_result) +//! * [`debug_assert_result_ok_ne`](macro@crate::debug_assert_result_ok_ne) + +/// Assert expression is ok, and its value is equal to another. +/// +/// * If true, return Result `Ok(())`. +/// +/// * Otherwise, return Result `Err` with a diagnostic message. +/// +/// This macro provides the same statements as [`assert_result_ok_ne`](macro.assert_result_ok_ne.html), +/// except this macro returns a Result, rather than doing a panic. +/// +/// This macro is useful for runtime checks, such as checking parameters, +/// or sanitizing inputs, or handling different results in different ways. +/// +/// # Module macros +/// +/// * [`assert_result_ok_ne`](macro@crate::assert_result_ok_ne) +/// * [`assert_result_ok_ne_as_result`](macro@crate::assert_result_ok_ne_as_result) +/// * [`debug_assert_result_ok_ne`](macro@crate::debug_assert_result_ok_ne) +/// +#[macro_export] +macro_rules! assert_result_ok_ne_as_result { + ($a:expr, $b:expr $(,)?) => {{ + match (&$a, &$b) { + (a_result, b_result) => { + if a_result.is_err() || b_result.is_err() { + Err(format!( + concat!( + "assertion failed: `assert_result_ok_ne!(a, b)`\n", + " a label: `{}`,\n", + " a debug: `{:?}`,\n", + " b label: `{}`,\n", + " b debug: `{:?}`", + ), + stringify!($a), + $a, + stringify!($b), + $b, + )) + } else { + let a_ok = a_result.unwrap(); + let b_ok = b_result.unwrap(); + if a_ok != b_ok { + Ok(()) + } else { + Err(format!( + concat!( + "assertion failed: `assert_result_ok_ne!(a, b)`\n", + " a label: `{}`,\n", + " a debug: `{:?}`,\n", + " b label: `{}`,\n", + " b debug: `{:?}`,\n", + " a ok: `{:?}`,\n", + " b ok: `{:?}`" + ), + stringify!($a), + $a, + stringify!($b), + $b, + a_ok, + b_ok + )) + } + } + } + } + }}; +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_assert_result_ok_ne_as_result_x_success() { + let a: Result = Result::Ok(1); + let b: Result = Result::Ok(2); + let result = assert_result_ok_ne_as_result!(a, b); + assert_eq!(result, Ok(())); + } + + #[test] + fn test_assert_result_ok_ne_as_result_x_failure_because_ok_eq() { + let a: Result = Result::Ok(1); + let b: Result = Result::Ok(1); + let result = assert_result_ok_ne_as_result!(a, b); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + concat!( + "assertion failed: `assert_result_ok_ne!(a, b)`\n", + " a label: `a`,\n", + " a debug: `Ok(1)`,\n", + " b label: `b`,\n", + " b debug: `Ok(1)`,\n", + " a ok: `1`,\n", + " b ok: `1`", + ) + ); + } + + #[test] + fn test_assert_result_ok_ne_as_result_x_failure_because_err() { + let a: Result = Result::Ok(1); + let b: Result = Result::Err(1); + let result = assert_result_ok_ne_as_result!(a, b); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + concat!( + "assertion failed: `assert_result_ok_ne!(a, b)`\n", + " a label: `a`,\n", + " a debug: `Ok(1)`,\n", + " b label: `b`,\n", + " b debug: `Err(1)`", + ) + ); + } + +} + +/// Assert expression is ok, and its value is equal to another. +/// +/// * If true, return `()`. +/// +/// * Otherwise, call [`panic!`] with a message and the values of the +/// expressions with their debug representations. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] extern crate assertables; +/// # use std::panic; +/// # fn main() { +/// let a: Result = Result::Ok(1); +/// let b: Result = Result::Ok(2); +/// assert_result_ok_ne!(a, b); +/// //-> () +/// +/// // Panic with error message +/// let a: Result = Result::Ok(1); +/// let b: Result = Result::Ok(1); +/// let result = panic::catch_unwind(|| { +/// assert_result_ok_ne!(a, b); +/// //-> panic! +/// }); +/// assert!(result.is_err()); +/// let actual = result.unwrap_err().downcast::().unwrap().to_string(); +/// let expect = concat!( +/// "assertion failed: `assert_result_ok_ne!(a, b)`\n", +/// " a label: `a`,\n", +/// " a debug: `Ok(1)`,\n", +/// " b label: `b`,\n", +/// " b debug: `Ok(1)`,\n", +/// " a ok: `1`,\n", +/// " b ok: `1`", +/// ); +/// assert_eq!(actual, expect); +/// +/// // Panic with error message +/// let result = panic::catch_unwind(|| { +/// assert_result_ok_ne!(a, b, "message"); +/// //-> panic! +/// }); +/// assert!(result.is_err()); +/// let actual = result.unwrap_err().downcast::().unwrap().to_string(); +/// let expect = "message"; +/// assert_eq!(actual, expect); +/// # } +/// ``` +/// +/// # Module macros +/// +/// * [`assert_result_ok_ne`](macro@crate::assert_result_ok_ne) +/// * [`assert_result_ok_ne_as_result`](macro@crate::assert_result_ok_ne_as_result) +/// * [`debug_assert_result_ok_ne`](macro@crate::debug_assert_result_ok_ne) +/// +#[macro_export] +macro_rules! assert_result_ok_ne { + ($a:expr, $b:expr $(,)?) => ({ + match assert_result_ok_ne_as_result!($a, $b) { + Ok(()) => (), + Err(err) => panic!("{}", err), + } + }); + ($a:expr, $b:expr, $($message:tt)+) => ({ + match assert_result_ok_ne_as_result!($a, $b) { + Ok(()) => (), + Err(_err) => panic!("{}", $($message)+), + } + }); +} + +/// Assert expression is ok, and its value is equal to another. +/// +/// This macro provides the same statements as [`assert_result_ok_ne`](macro.assert_result_ok_ne.html), +/// except this macro's statements are only enabled in non-optimized +/// builds by default. An optimized build will not execute this macro's +/// statements unless `-C debug-assertions` is passed to the compiler. +/// +/// This macro is useful for checks that are too expensive to be present +/// in a release build but may be helpful during development. +/// +/// The result of expanding this macro is always type checked. +/// +/// An unchecked assertion allows a program in an inconsistent state to +/// keep running, which might have unexpected consequences but does not +/// introduce unsafety as long as this only happens in safe code. The +/// performance cost of assertions, however, is not measurable in general. +/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged +/// after thorough profiling, and more importantly, only in safe code! +/// +/// This macro is intended to work in a similar way to +/// [`std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html). +/// +/// # Module macros +/// +/// * [`assert_result_ok_ne`](macro@crate::assert_result_ok_ne) +/// * [`assert_result_ok_ne`](macro@crate::assert_result_ok_ne) +/// * [`debug_assert_result_ok_ne`](macro@crate::debug_assert_result_ok_ne) +/// +#[macro_export] +macro_rules! debug_assert_result_ok_ne { + ($($arg:tt)*) => { + if $crate::cfg!(debug_assertions) { + $crate::assert_result_ok_ne!($($arg)*); + } + }; +} diff --git a/src/assert_result/mod.rs b/src/assert_result/mod.rs index 0e5613b4b..e7a3c76a7 100644 --- a/src/assert_result/mod.rs +++ b/src/assert_result/mod.rs @@ -30,7 +30,7 @@ // Assert for Result pub mod assert_result_ok; -// pub mod assert_result_ok_eq; //TODO -// pub mod assert_result_ok_eq_expr; //TODO +pub mod assert_result_ok_eq; +pub mod assert_result_ok_ne; pub mod assert_result_err; diff --git a/src/lib.rs b/src/lib.rs index 090832252..29d0f6e31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,7 +231,7 @@ //! * Package: assertables-rust-crate //! * Version: 8.4.0 //! * Created: 2021-03-30T15:47:49Z -//! * Updated: 2024-09-07T22:22:42Z +//! * Updated: 2024-09-11T02:11:16Z //! * License: MIT or Apache-2.0 or GPL-2.0 or GPL-3.0 or contact us for more //! * Contact: Joel Parker Henderson (joel@sixarm.com)