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

Add assert_str_eq macro for comparing "raw" strings #88

Merged
merged 1 commit into from Feb 1, 2022
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
115 changes: 115 additions & 0 deletions pretty_assertions/src/lib.rs
Expand Up @@ -141,6 +141,76 @@ where
}
}

/// A comparison of two strings.
///
/// In contrast to [`Comparison`], which uses the [`core::fmt::Debug`] representation,
/// `StrComparison` uses the string values directly, resulting in multi-line output for multiline strings.
///
/// ```
/// use pretty_assertions::StrComparison;
///
/// print!("{}", StrComparison::new("foo\nbar", "foo\nbaz"));
/// ```
///
/// ## Value type bounds
///
/// Any value that can be referenced as a [`str`] via [`AsRef`] may be used:
///
/// ```
/// use pretty_assertions::StrComparison;
///
/// #[derive(PartialEq)]
/// struct MyString(String);
///
/// impl AsRef<str> for MyString {
/// fn as_ref(&self) -> &str {
/// &self.0
/// }
/// }
///
/// print!(
/// "{}",
/// StrComparison::new(
/// &MyString("foo\nbar".to_owned()),
/// &MyString("foo\nbaz".to_owned()),
/// ),
/// );
/// ```
///
/// The values may have different types, although in practice they are usually the same.
pub struct StrComparison<'a, TLeft, TRight>
where
TLeft: ?Sized,
TRight: ?Sized,
{
left: &'a TLeft,
right: &'a TRight,
}

impl<'a, TLeft, TRight> StrComparison<'a, TLeft, TRight>
where
TLeft: AsRef<str> + ?Sized,
TRight: AsRef<str> + ?Sized,
{
/// Store two values to be compared in future.
///
/// Expensive diffing is deferred until calling `Debug::fmt`.
pub fn new(left: &'a TLeft, right: &'a TRight) -> StrComparison<'a, TLeft, TRight> {
StrComparison { left, right }
}
}

impl<'a, TLeft, TRight> Display for StrComparison<'a, TLeft, TRight>
where
TLeft: AsRef<str> + ?Sized,
TRight: AsRef<str> + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
printer::write_header(f)?;
printer::write_lines(f, self.left.as_ref(), self.right.as_ref())
}
}

/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
///
/// On panic, this macro will print a diff derived from [`Debug`] representation of
Expand Down Expand Up @@ -186,6 +256,51 @@ macro_rules! assert_eq {
});
}

/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
///
/// On panic, this macro will print a diff derived from each value's [`str`] representation.
/// See [`StrComparison`] for further details.
///
/// This is a drop in replacement for [`core::assert_eq!`].
/// You can provide a custom panic message if desired.
///
/// # Examples
///
/// ```
/// use pretty_assertions::assert_str_eq;
///
/// let a = "foo\nbar";
/// let b = ["foo", "bar"].join("\n");
/// assert_str_eq!(a, b);
///
/// assert_str_eq!(a, b, "we are testing concatenation with {} and {}", a, b);
/// ```
#[macro_export]
macro_rules! assert_str_eq {
($left:expr, $right:expr$(,)?) => ({
$crate::assert_str_eq!(@ $left, $right, "", "");
});
($left:expr, $right:expr, $($arg:tt)*) => ({
$crate::assert_str_eq!(@ $left, $right, ": ", $($arg)+);
});
(@ $left:expr, $right:expr, $maybe_semicolon:expr, $($arg:tt)*) => ({
match (&($left), &($right)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
::core::panic!("assertion failed: `(left == right)`{}{}\
\n\
\n{}\
\n",
$maybe_semicolon,
format_args!($($arg)*),
$crate::StrComparison::new(left_val, right_val)
)
}
}
}
});
}

/// Asserts that two expressions are not equal to each other (using [`PartialEq`]).
///
/// On panic, this macro will print the values of the expressions with their
Expand Down
64 changes: 64 additions & 0 deletions pretty_assertions/tests/macros.rs
Expand Up @@ -4,6 +4,70 @@
#[cfg(feature = "alloc")]
extern crate alloc;

#[allow(clippy::eq_op)]
mod assert_str_eq {
use ::core::{cmp::PartialEq, convert::AsRef};

#[cfg(feature = "alloc")]
use ::alloc::string::{String, ToString};
#[cfg(feature = "std")]
use ::std::string::{String, ToString};

#[test]
fn passes_str() {
let a = "some value";
::pretty_assertions::assert_str_eq!(a, a);
}

#[test]
fn passes_string() {
let a: String = "some value".to_string();
::pretty_assertions::assert_str_eq!(a, a);
}

#[test]
fn passes_comparable_types() {
let s0: &'static str = "foo";
let s1: String = "foo".to_string();
::pretty_assertions::assert_str_eq!(s0, s1);
}

#[test]
fn passes_as_ref_types() {
#[derive(PartialEq)]
struct MyString(String);

impl AsRef<str> for MyString {
fn as_ref(&self) -> &str {
&self.0
}
}

impl PartialEq<String> for MyString {
fn eq(&self, other: &String) -> bool {
&self.0 == other
}
}

let s0 = MyString("foo".to_string());
let s1 = "foo".to_string();
::pretty_assertions::assert_str_eq!(s0, s1);
}

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

Diff < left / right > :
foo
<bar
>baz

"#)]
fn fails_foo() {
::pretty_assertions::assert_str_eq!("foo\nbar", "foo\nbaz");
}
}

#[allow(clippy::eq_op)]
mod assert_eq {
#[cfg(feature = "alloc")]
Expand Down