diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f09c84d..bc8abf1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -25,6 +25,11 @@ jobs: with: toolchain: stable override: true + - name: Install toolchain (nightly) + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: false - name: Install additional test dependencies env: CARGO_TARGET_DIR: cargo_target diff --git a/CHANGELOG.md b/CHANGELOG.md index a5c81b7..23744e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +- Adds the `unstable` feature to the `pretty_assertions` crate, for use with nightly rustc ([#81](https://github.com/colin-kiegel/rust-pretty-assertions/pull/81), [@tommilligan](https://github.com/tommilligan)) +- Add a drop in replacement for the unstable stdlib `assert_matches` macro, behind the `unstable` flag - thanks [@gilescope](https://github.com/gilescope) for the suggestion! ([#81](https://github.com/colin-kiegel/rust-pretty-assertions/issues/81), [@tommilligan](https://github.com/tommilligan)) + # v0.7.2 - Fix macro hygiene for expansion in a `no_implicit_prelude` context ([#70](https://github.com/colin-kiegel/rust-pretty-assertions/issues/70), [@tommilligan](https://github.com/tommilligan)) diff --git a/pretty_assertions/Cargo.toml b/pretty_assertions/Cargo.toml index 90b343e..0b2084f 100644 --- a/pretty_assertions/Cargo.toml +++ b/pretty_assertions/Cargo.toml @@ -17,8 +17,11 @@ categories = ["development-tools"] keywords = ["assert", "diff", "pretty", "color"] readme = "README.md" -[badges] -travis-ci = { repository = "colin-kiegel/rust-pretty-assertions" } +[features] +default = [] + +# Enable unstable features requiring nightly rustc +unstable = [] [dependencies] ansi_term = "0.12.1" diff --git a/pretty_assertions/src/lib.rs b/pretty_assertions/src/lib.rs index caf7be9..34c5f2a 100644 --- a/pretty_assertions/src/lib.rs +++ b/pretty_assertions/src/lib.rs @@ -62,6 +62,13 @@ //! you include. //! * `assert_ne` is also switched to multi-line presentation, but does _not_ show //! a diff. +//! +//! ## Features +//! +//! Features provided by the crate are as follows: +//! +//! - `unstable`: opt-in to unstable features that may not follow Semantic Versioning. +//! Implmenetion behind this feature is subject to change without warning between patch versions. #![deny(clippy::all, missing_docs, unsafe_code)] @@ -238,3 +245,85 @@ macro_rules! assert_ne { } }); } + +/// Asserts that a value matches a pattern. +/// +/// On panic, this macro will print a diff derived from [`Debug`] representation of +/// the value, and a string representation of the pattern. +/// +/// This is a drop in replacement for [`std::assert_matches::assert_matches!`]. +/// You can provide a custom panic message if desired. +/// +/// # Examples +/// +/// ``` +/// use pretty_assertions::assert_matches; +/// +/// let a = Some(3); +/// assert_matches!(a, Some(_)); +/// +/// assert_matches!(a, Some(value) if value > 2, "we are testing {:?} with a pattern", a); +/// ``` +/// +/// # Features +/// +/// Requires the `unstable` feature to be enabled. +/// +/// **Please note:** implementation under the `unstable` feature may be changed between +/// patch versions without warning. +#[cfg(feature = "unstable")] +#[macro_export] +macro_rules! assert_matches { + ($left:expr, $( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => ({ + match $left { + $( $pattern )|+ $( if $guard )? => {} + ref left_val => { + $crate::assert_matches!( + @ + left_val, + ::std::stringify!($($pattern)|+ $(if $guard)?), + "", + "" + ); + } + } + }); + ($left:expr, $( $pattern:pat )|+ $( if $guard: expr )?, $($arg:tt)+) => ({ + match $left { + $( $pattern )|+ $( if $guard )? => {} + ref left_val => { + $crate::assert_matches!( + @ + left_val, + ::std::stringify!($($pattern)|+ $(if $guard)?), + ": ", + $($arg)+ + ); + } + } + + }); + (@ $left:expr, $right:expr, $maybe_semicolon:expr, $($arg:tt)*) => ({ + match (&($left), &($right)) { + (left_val, right_val) => { + // Use the Display implementation to display the pattern, + // as using Debug would add another layer of quotes to the output. + struct Pattern<'a>(&'a str); + impl ::std::fmt::Debug for Pattern<'_> { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::std::fmt::Display::fmt(self.0, f) + } + } + + ::std::panic!("assertion failed: `(left matches right)`{}{}\ + \n\ + \n{}\ + \n", + $maybe_semicolon, + format_args!($($arg)*), + $crate::Comparison::new(left_val, &Pattern(right_val)) + ) + } + } + }); +} diff --git a/pretty_assertions/tests/macros.rs b/pretty_assertions/tests/macros.rs index 28fcfaf..56da56c 100644 --- a/pretty_assertions/tests/macros.rs +++ b/pretty_assertions/tests/macros.rs @@ -220,3 +220,89 @@ mod assert_ne { not_zero(0); } } + +#[cfg(feature = "unstable")] +mod assert_matches { + use ::std::option::Option::{None, Some}; + + #[test] + fn passes() { + let a = Some("some value"); + ::pretty_assertions::assert_matches!(a, Some(_)); + } + + #[test] + fn passes_unsized() { + let a: &[u8] = b"e"; + ::pretty_assertions::assert_matches!(*a, _); + } + + #[test] + #[should_panic(expected = r#"assertion failed: `(left matches right)` + +Diff < left / right > : +<None +>Some(_) + +"#)] + fn fails() { + ::pretty_assertions::assert_matches!(None::, Some(_)); + } + + #[test] + #[should_panic(expected = r#"assertion failed: `(left matches right)` + +Diff < left / right > : +Some(3) if 0 > 0 + +"#)] + fn fails_guard() { + ::pretty_assertions::assert_matches!(Some(3), Some(3) if 0 > 0,); + } + + #[test] + #[should_panic(expected = r#"assertion failed: `(left matches right)` + +Diff < left / right > : +<[ +< 101, +<] +>ref b if b == b"ee" + +"#)] + fn fails_unsized() { + let a: &[u8] = b"e"; + ::pretty_assertions::assert_matches!(*a, ref b if b == b"ee"); + } + + #[test] + #[should_panic( + expected = r#"assertion failed: `(left matches right)`: custom panic message + +Diff < left / right > : +<666 +>999 + +"# + )] + fn fails_custom() { + ::pretty_assertions::assert_matches!(666, 999, "custom panic message"); + } + + #[test] + #[should_panic( + expected = r#"assertion failed: `(left matches right)`: custom panic message + +Diff < left / right > : +<666 +>999 + +"# + )] + fn fails_custom_trailing_comma() { + ::pretty_assertions::assert_matches!(666, 999, "custom panic message",); + } +} diff --git a/pretty_assertions_bench/Cargo.toml b/pretty_assertions_bench/Cargo.toml index a44f32b..011f803 100644 --- a/pretty_assertions_bench/Cargo.toml +++ b/pretty_assertions_bench/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Tom Milligan "] edition = "2018" [dependencies] -criterion = { version = "0.3.4", features = ["html_reports"] } +criterion = { version = "0.3.5", features = ["html_reports"] } pretty_assertions = { path = "../pretty_assertions" } [lib] diff --git a/scripts/check b/scripts/check index 0ee0598..8167328 100755 --- a/scripts/check +++ b/scripts/check @@ -18,6 +18,9 @@ cargo clippy --all-targets -- -D warnings eprintln "Running unit tests" cargo test +eprintln "Running unit tests (unstable)" +cargo +nightly test --manifest-path pretty_assertions/Cargo.toml --all --features unstable + eprintln "Building documentation" cargo doc --no-deps