D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 5547 - Improve assert to give information on values given to it when it fails
Summary: Improve assert to give information on values given to it when it fails
Status: RESOLVED WONTFIX
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P2 enhancement
Assignee: No Owner
URL:
Keywords: pull
Depends on:
Blocks:
 
Reported: 2011-02-08 07:47 UTC by Jonathan M Davis
Modified: 2016-06-10 17:05 UTC (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Jonathan M Davis 2011-02-08 07:47:45 UTC
Right now, assert gives no useful information beyond the file and line number where the failure occurred. It really should give information on what the failure was. The recent attempt at getting assertPred in Phobos was to fix this, but it was decided that it would be better to fix assert to do it. So, essentially, when asserts like

assert(a == b);
assert(a.canFind(b));

fails, it should print out what the arguments to the expression were. For instance, take this from the assertPred docs:

assert(collectExceptionMsg!AssertError(
            assertPred!"=="("hello", "goodbye")) ==
       `assertPred!"==" failed:` ~ "\n" ~
       `[hello] (lhs)` ~ "\n" ~
       `[goodbye] (rhs).`);

On failure assertPred would print out that == failed and that the values given to it were hello and goodbye. Ideally, assert("hello" == "goodbye"); would print something similar.

Another example would be

assert(collectExceptionMsg!AssertError(
            assertPred!((int a, int b, int c, float d){return a * b < c * d;})
                        (22, 4, 5, 1.7)) ==
       `assertPred failed: arguments: [22], [4], [5], [1.7].`);

Something similar to this - such as

assert((int a, int b, int c, float d){return a * b < c * d;}(22, 4, 5, 1.7)); or
assert(22 * 4 < 5 * 1.7);

- should print something similar to what assertPred printed.

Exactly what the best format should be and exactly what values will be best for assert to print for more complicated expressions, I don't know - that will depend on what is reasonable to implement - but assert should at least make a valiant attempt at printing what the values in the expression were that failed so that the programmer has sufficient debugging information not to have to go and add a debug statement to print out the arguments, because the assert didn't give enough information to properly debug the failure.

Also, it was pointed out that assertPred should print values such that they line up on separate lines so that they're easier to compare (which it does in the == case, though not in the case of the more general predicate - perhas it should have). So, it would probably be a good idea for assert to do something similar.

These improvements need to be made for assert to be properly usable for unit tests. Otherwise, we're really going to need to add something like assertPred to Phobos.
Comment 1 Don 2011-02-08 10:49:00 UTC
There's one difficult issue in this: this assert behaviour is not always desirable, especially outside of unittests.
You have to save a copy of the parameters to assert, and that can be quite expensive -- extremely expensive, in some cases.
And, you also need to be able to print them out, which creates the risk of recursion, and potentially links in a huge amount of code.

The basic idea of making assert() be syntax sugar for an internal function which behaves like your assertPred() does not seem too difficult. But knowing *when* to do it is difficult.
Comment 2 Steven Schveighoffer 2011-02-08 10:57:27 UTC
Is it too difficult for this to apply only inside unit tests?  I mean literally within unit test blocks.  This means asserts triggered in non-unit test blocks will not report the extra info, even if called from a unit test block.  But I believe this is equivalent to the original proposal of assertPred (which would only be used inside unittest blocks).
Comment 4 kennytm 2012-02-16 15:10:22 UTC
Updated pull requests for 2.058. (The IDs are still the same).
Comment 5 github-bugzilla 2012-07-08 13:46:35 UTC
Commits pushed to master at https://github.com/D-Programming-Language/druntime

https://github.com/D-Programming-Language/druntime/commit/17db389925581804a3d6d8a10191772077b9713c
Bug 5547: assertPred (the druntime part)

https://github.com/D-Programming-Language/druntime/commit/ce783fff516d21c253edfecb40982c833add3e4b
Merge pull request #41 from kennytm/bug5547_assertPred

Bug 5547: assertPred (the druntime part)
Comment 6 Walter Bright 2012-09-05 15:35:51 UTC
As I commented on the pull request:

I have two problems with this:
 1.
Currently, there are so many unittests in Phobos that the compiler sometimes runs out of memory. This will add a lot more and may push a lot more over the edge.

 2.
I am not too happy about the dependencies on specific library names and functionality.



On further reflection, I think that this is more properly the domain of a library template, such as

testEqual(arg1, arg2);

std.math.approxEqual can easily be extended to print its args on failure.
Comment 7 Jonathan M Davis 2012-09-05 15:45:23 UTC
> On further reflection, I think that this is more properly the domain of a
library template

I'm fine with having a helper function that does this, but the whole reason that this enhancement was created in the first place was because enough folks in the newsgroup thought that that it should be built into assert rather than creating a new function to do it.

Though it should be noted that the library function does tend to cause memory issues as well, since you end up with a lot of different instantiations for it.
Comment 8 bearophile_hugs 2012-09-05 16:07:28 UTC
(In reply to comment #6)

> Currently, there are so many unittests in Phobos that the compiler sometimes
> runs out of memory. This will add a lot more and may push a lot more over the
> edge.

A large program written by D users risks having the same amount of unit-tests, so this is a general problem, not just of Phobos.

So maybe the right solution is not to keep assert() dumb, but to find ways to compile unit-tests using much less memory.

I think this idea also goes well with compiling unit-tests more independently from each other, to give the user a simple summary of what tests have failed.