std.unittests for (final?) review

Jonathan M Davis jmdavisProg at gmx.com
Wed Jan 5 18:39:40 PST 2011


On Wednesday, January 05, 2011 16:09:17 Andrei Alexandrescu wrote:
> On 1/2/11 10:44 PM, Jonathan M Davis wrote:
> > Okay. As some of you are aware, I created a module with unit testing
> > functions when I wrote std.datetime, and it really helped with writing
> > the unit tests for std.datetime. It has been at least somewhat reviewed
> > here previously, and that has definitely improved it. However, it has
> > not yet been decided whether it's going to go into Phobos along with
> > std.datetime. I'd like it to, but it hasn't been voted on, and I'm not
> > sure that it's been reviewed as heavily as it should be. So, I'm posting
> > the most up-to-date version in the hopes of getting any necessary
> > changes found and made so that it can be voted on. Andrei has yet to
> > weigh in on std.unittests at all, so I don't know if we're actually
> > going to get a vote on it, but I'm presenting it for (final?) review so
> > that it can be voted in if that's what he wants to do.
> > 
> > The code: http://is.gd/jZEVl
> 
> Honest I feared I'll see a monster with a lot of useless crap, so the
> surprise was nice. I appreciate that the module is small and to the
> point. It contains functions that I can easily imagine needing while
> unittesting while at the same time are tedious enough to emulate with
> inline code.

Well, most of the functions were created out of need when developing 
std.datetime rather than just because they seemed useful. If I had simply been 
trying to create a unit testing module to create a unit testing module, it might 
have been much larger.  Then again, I've tried to come up with more functions 
which might be useful, and I couldn't think of many. As it is, several of the 
functions that are there are there because of suggestions in previous threads.

> Suggestions:
> 
> * Documentation should contain meaningful, working examples that
> exercise the most expected usage patterns of the functions.

They should for the most part, but several of them explicitly test overloaded 
operators, which requires a struct or class with the appropriate operator, which 
would have made the examples much larger, since there aren't really any in 
druntime or Phobos. So, in those cases, the examples don't work. Perhaps that 
was a poor decision though, and I should have just added minimal type definitions 
to the examples.

> * assertEqual and assertNotEqual are misnomers. They should be
> consolidated into one function called assertPredicate:
> 
> assertPredicate(1 + 1, 2); // pass, default predicate is a == b
> assertPredicate!"a < b"(1 + 1, 3); // pass, 1 + 1 < 3
> 
> * To emphasize: since we have the awesome short lambdas, there's no need
> to for a negation function - we can embed the negation inside the lambda.

Valid point. assertEqual didn't even take a predicate originally, so it didn't 
occur to me to combine assertEqual and assertNotEqual. I added the predicate 
after someone suggested that it be more like std.algorithm.equal.

> * Remove assertCmp and fold it into assertPredicate. With trivial
> metaprogramming you can distinguish an operator from an actual expression.
> 
> assertPredicate!"<"(1 + 1, 2.1);
> 
> * The explanation and the examples do not clarify for me what value
> assertOpCmp adds over assertCmp.

The key difference is that assertOpCmp explicitly calls the opCmp() while 
assertCmp just uses the given operator. This makes it possible to check that 
opCmp() returns 0 when it's supposed to rather than just having opEquals() 
checked when checking ==. Now, with a more general predicate, you could feed it 
opCmp and get around that problem, though it would be slightly more tedious. I 
fully understand the confusion between the two though. I originally just had 
assertOpCmp() and added assertCmp() after realizing that assertOpCmp() wouldn't 
work for primitive types and the like, and people are obviously going to want to 
be able to test primitives with comparisons other than equality. As long as I 
can get assertPred to test both the operators and the actual overloaded operator 
functions, then combining that way would certainly be ideal.

> * I'd replace assertOpBinary with a ternary predicate and fold that into
> assertPredicate, too. That is a bit more difficult but worth looking into.

That may take some doing, but it could certainly be worth doing. I'll look into 
it.

> * I think assertOpOpAssign is too rarely necessary to warrant inclusion.

I think that I actually am using it in std.datetime. I don't think that it's 
something that I would have come up with otherwise. But maybe I'm just more 
thorough about that sort of testing than most people. You can replace it with 
two tests though, so getting rid of it isn't necessarily a big deal, just 
somewhat less efficient.

> * Wow, assertPred was there at the bottom the whole time :o). Rename my
> assertPredicate into assertPred, fold the existing assertPred into it
> and at the end we have a useful library with a one-stop-shop function.

Well, it was the last one I came up with (based on a suggestion; I haven't 
actually used it in std.datetime), so it ended up at the bottom. But if 
assertPred can indeed be made powerful enough to absorb the functionality of 
most of the other functions, then that would definitely be worth doing. It'll 
take a bit of work though.

> * getMsg() should be renamed as collectExceptionMsg() because we already
> have collectException() in std.exception.

Fine with me. I was looking for the functionality. The name isn't terribly 
important as long as its clear.

> If some or all of the suggestions above are implemented (which I'd
> love), the library becomes lean and mean enough to be integrated within
> an existing module. I think that would be awesome.

I'll try and do most of them. It'll certainly be a challeng to implement such a 
powerful assertPred though. I'm just glad that D templates are so powerful. I'm 
not good enough with templates in C++ to even dream of doing some of the stuff 
that I do with D templates, and some if it just isn't possible. I'm going to be 
porting a portion of std.datetime to C++ for use at work, and some of the stuff 
in there (like the nice convert function) just can't be done the same way in 
C++. After using D templates, C++ templates seem really unwieldy.

If I'm able to combine most of the functions into a single assertPred as you 
suggest, then it does definitely look like a whole module is overkill. However, 
we're then going to have to decide where to put it. std.exception would probably 
be the best place, though it does strike me as somewhat of an odd place to put 
them. Of course, almost all of them _do_ throw AssertError, and the one 
exception to that (getMsg) operates on an exception (well, Throwable), so they 
do at least sort of fit.

- Jonathan M Davis


More information about the Digitalmars-d mailing list