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 custom message parameter to assert_eq! #33976

Merged
merged 1 commit into from
Jun 22, 2016

Conversation

komamitsu
Copy link
Contributor

assert! macro accepts a custom message parameter and it's sometimes useful. But assert_eq! doesn't have it and users need to use assert! instead of assert_eq! when they want to output a custom message even if the assertion just compares two values. This pull request will resolve those cases.

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @brson (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@GuillaumeGomez
Copy link
Member

Nice! Squash your commits please.

@komamitsu
Copy link
Contributor Author

@GuillaumeGomez Thanks. Squashed them.

@GuillaumeGomez
Copy link
Member

It's good for me but I'd prefer to let someone from @rust-lang/core team to review it as well.

@steveklabnik
Copy link
Member

Yes, @rust-lang/lang and @rust-lang/libs should probably sign off on something like this.

@oli-obk
Copy link
Contributor

oli-obk commented May 31, 2016

related rfc issue: rust-lang/rfcs#1604

@komamitsu
Copy link
Contributor Author

Oops. I should've checked the issue...

@ollie27
Copy link
Member

ollie27 commented May 31, 2016

Is there any reason to use:

assert_eq!("b", xs[i], "xs[{}] should be \"b\" but {}", i, xs[i]);

rather than:

assert!("b" == xs[i], "xs[{}] should be \"b\" but {}", i, xs[i]);

?

@brson brson added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label May 31, 2016
@brson
Copy link
Contributor

brson commented May 31, 2016

@ollie27 Not that I can think of, since the reason assert_eq exists is to provide a better error message.

@komamitsu
Copy link
Contributor Author

komamitsu commented May 31, 2016

@ollie27
This question sounds to be similar to "Is there any reason to use assert_eq!(a, b) rather than assert!(a == b)?"
Just kidding :)

I prefer assert_eq! to just assert! since assert_eq! seems more explicit to me than assert! when I see test codes.

In major unit test libraries (e.g. JUnit in Java, OUnit in OCaml and Test::Unit in Ruby) in other languages, there are assert_equals which support custom message. So, when I noticed that assert_eq! doesn't accept custom message while assert! supports it, I thought it's just lack of feature or something not design decision.

@nrc
Copy link
Member

nrc commented Jun 1, 2016

+1

@komamitsu
Copy link
Contributor Author

@brson @GuillaumeGomez Well, should I close this PR...?

@aturon
Copy link
Member

aturon commented Jun 6, 2016

I agree with @brson, it's simplest to just use assert for a custom message, since assert_eq basically exists just to give a better default message.

@nikomatsakis
Copy link
Contributor

I think that @brson and @aturon's logic makes sense, but I confess that I have been regularly surprised that assert_eq doesn't accept a custom message -- and rewriting to use assert never occurred to me.

@nikomatsakis
Copy link
Contributor

Like @komamitsu, I have mostly used assert_eq because it seemed more declarative (with a nicer message to boot).

@nikomatsakis
Copy link
Contributor

Well, maybe another way to put it is that I got in the habit of using assert_eq long ago, and so now I just do it out of habit whenever there is an equality comparison involved.

@brson
Copy link
Contributor

brson commented Jun 10, 2016

@nikomatsakis good point that having the error message reduces the surprise factor

@nrc
Copy link
Member

nrc commented Jun 10, 2016

to clarify, +1 to the PR itself - while I understand the argument that assert_eq should do its own thing for the error message, I don't see that as the point of the macro - aiui, assert_eq just saves you writing == (and is implicitly self-documenting), so it makes sense to have an optional message in the same way assert has. I must admit, I've never really been a fan of assert_eq at all - perhaps this mental model is why I don't see the benefit.

@SimonSapin
Copy link
Contributor

As I wrote in rust-lang/rfcs#1604, I think the panic message given to assert_eq! should be in addition (concatenated) to the default one. If the default one is replaced entirely instead, that’s the same as assert!(a == b, "message").

IMO the point of assert_eq! over assert! is that it prints the two values for you, so you don’t have to. It can be desirable to have that, and also provide more information/context in the panic message.

@aturon
Copy link
Member

aturon commented Jun 10, 2016

OK, @nikomatsakis makes a convincing case that it's worth adding something here. @SimonSapin's suggestion makes sense for added value.

cc @rust-lang/libs

@alexcrichton
Copy link
Member

I feel that as-is this may not add too much value as it's shorter even to use assert! and you'd get the same message, but if we were to append the message to the {left} != {right} then it seems reasonable to me.

@komamitsu
Copy link
Contributor Author

komamitsu commented Jun 13, 2016

Thanks for the feedbacks. I agree to appending a custom message to the default one.

I have two ideas to do that.

macro_rules! assert_eq {
    ($left:expr , $right:expr) => ({
        match (&($left), &($right)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!("assertion failed: `(left == right)` \
                           (left: `{:?}`, right: `{:?}`)", left_val, right_val)
                }
            }
        }
    });
    ($left:expr , $right:expr, $msg:expr) => ({
        match (&($left), &($right)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!("assertion failed: `(left == right)` \
                            (left: `{:?}`, right: `{:?}`): {}", left_val, right_val, $msg)
                }
            }
        }
    });
    ($left:expr , $right:expr, $fmt:expr, $($arg:tt)*) => ({
        match (&($left), &($right)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!(concat!("assertion failed: `(left == right)` \
                                   (left: `{:?}`, right: `{:?}`): ", $fmt),
                           left_val, right_val, $($arg)*)
                }
            }
        }
    });
}

// assert_eq!(1, 2);     >>> assertion failed: `(left == right)` (left: `1`, right: `2`)
// assert_eq!(1, 2, "Type is u32");
//                       >>> assertion failed: `(left == right)` (left: `1`, right: `2`): Type is u32
// assert_eq!(1, 2, "concurrency = {concurrency}, counter = {counter}",
//                   concurrency = c, counter = i);
//                       >>> assertion failed: `(left == right)` (left: `1`, right: `2`): concurrency = 8, counter = 65536
macro_rules! assert_eq {
    ($left:expr , $right:expr) => ({
        match (&($left), &($right)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!("assertion failed: `(left == right)` \
                           (left: `{:?}`, right: `{:?}`)", left_val, right_val)
                }
            }
        }
    });
    ($left:expr , $right:expr, $msg:expr) => ({
        match (&($left), &($right)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!("assertion failed: `(left == right)` \
                            (left: `{:?}`, right: `{:?}`): {}", left_val, right_val, $msg)
                }
            }
        }
    });
}

// assert_eq!(1, 2);     >>> assertion failed: `(left == right)` (left: `1`, right: `2`)
// assert_eq!(1, 2, "Type is u32"); 
//                       >>> assertion failed: `(left == right)` (left: `1`, right: `2`): Type is u32
// assert_eq!(1, 2, format!("concurrency = {concurrency}, counter = {counter}",
//                             concurrency = c, counter = i));
//                       >>>  assertion failed: `(left == right)` (left: `1`, right: `2`): concurrency = 8, counter = 65536

The first one can format strings by itself, but it's a bit verbose. The implementation of the second one is simpler, but it doesn't support formatting. I'm wondering which one is better...

@alexcrichton
Copy link
Member

Ah yeah I'd vote for the first where the message itself can have more arguments and such like assert! can

@komamitsu
Copy link
Contributor Author

Updated the PR

(left_val, right_val) => {
if !(*left_val == *right_val) {
panic!("assertion failed: `(left == right)` \
(left: `{:?}`, right: `{:?}`): {}", left_val, right_val, $msg)
Copy link
Member

Choose a reason for hiding this comment

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

It may be worth using concat! here as well to ensure that a $msg which looks like "foo {}" gives an error saying that it expected an argument rather than being accepted

Copy link
Member

Choose a reason for hiding this comment

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

Maybe this was trying to match unreachable! which can also be given a single argument which implements Display and use that. If we don't want that behaviour and only accept valid format strings I think we can use a single extra rule like:

($left:expr , $right:expr, $($arg:tt)*) => ({
    match (&($left), &($right)) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                panic!("assertion failed: `(left == right)` \
                       (left: `{:?}`, right: `{:?}`): {}", left_val, right_val, format_args!($($arg)*))
            }
        }
    }
});

This will also accept things like assert_eq!(1, 2, "{0}", 3) which I think we do want to accept.

(also we definitely don't want to use concat! as we'll just run into #30143 again)

Copy link
Member

Choose a reason for hiding this comment

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

That definition looks good to me!

@alexcrichton
Copy link
Member

The libs team got a chance to discuss this PR during triage today and the conclusion was to merge. @komamitsu if you want to update with @ollie27's suggestion, I'll r+!

@komamitsu
Copy link
Contributor Author

komamitsu commented Jun 21, 2016

@ollie27 Thanks. That's what I wanted to do.

@alexcrichton Okay. Updated the PR

@alexcrichton alexcrichton added the relnotes Marks issues that should be documented in the release notes of the next release. label Jun 21, 2016
@alexcrichton
Copy link
Member

@bors: r+ 45a63d3

Thanks!

@alexcrichton
Copy link
Member

@bors: retry force clean

  • appears... stuck

@bors
Copy link
Contributor

bors commented Jun 21, 2016

💣 Buildbot returned an error: <span>exceptions.KeyError</span>: <span>'auto-linux-32cross-opt'</span>

@bors
Copy link
Contributor

bors commented Jun 21, 2016

⌛ Testing commit 45a63d3 with merge a32aaea...

@alexcrichton
Copy link
Member

@bors: retry force clean

  • bots disappeared?

@bors
Copy link
Contributor

bors commented Jun 22, 2016

⌛ Testing commit 45a63d3 with merge 3ee3267...

bors added a commit that referenced this pull request Jun 22, 2016
Add custom message parameter to `assert_eq!`

`assert!` macro accepts a custom message parameter and it's sometimes useful. But `assert_eq!` doesn't have it and users need to use `assert!` instead of `assert_eq!` when they want to output a custom message even if the assertion just compares two values. This pull request will resolve those cases.
@bors bors merged commit 45a63d3 into rust-lang:master Jun 22, 2016
@komamitsu
Copy link
Contributor Author

@alexcrichton Thanks!

@donaldpipowitch
Copy link

Is this mentioned somewhere in the docs? I looked here and thought it would not be possible to append a custom message. I'm glad it is possible.

@GuillaumeGomez
Copy link
Member

@donaldpipowitch: You must look into the nightly docs: https://doc.rust-lang.org/nightly/std/macro.assert_eq.html

@donaldpipowitch
Copy link

Okay, Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
relnotes Marks issues that should be documented in the release notes of the next release. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.