Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add assert_matches as unstable feature #81

Merged
merged 2 commits into from Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/check.yml
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions 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))
Expand Down
7 changes: 5 additions & 2 deletions pretty_assertions/Cargo.toml
Expand Up @@ -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"
Expand Down
89 changes: 89 additions & 0 deletions pretty_assertions/src/lib.rs
Expand Up @@ -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)]

Expand Down Expand Up @@ -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))
)
}
}
});
}
86 changes: 86 additions & 0 deletions pretty_assertions/tests/macros.rs
Expand Up @@ -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::<usize>, Some(_));
}

#[test]
#[should_panic(expected = r#"assertion failed: `(left matches right)`

Diff < left / right > :
<Some(
< 3,
<)
>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",);
}
}
2 changes: 1 addition & 1 deletion pretty_assertions_bench/Cargo.toml
Expand Up @@ -5,7 +5,7 @@ authors = ["Tom Milligan <code@tommilligan.net>"]
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]
Expand Down
3 changes: 3 additions & 0 deletions scripts/check
Expand Up @@ -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

Expand Down