www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Review: A new stab at a potential std.unittests

reply Jonathan M Davis <jmdavisProg gmx.com> writes:
Updated code: http://is.gd/hqPb2

Okay. As mentioned before, I have helper unit test functions which I use
heavily 
in std.datetime and which are pretty much going to have to either end up as 
private helper functions in std.datetime or actually get added in a separate 
module for everyone to use (I'd prefer the latter). My last post on it seems to 
have met with positive feedback overall for the basic idea but not necessarily 
the execution.

In particular, needing to pass LineInfo() to assertExcThrown!() to know the
file 
and line number was disliked (though it was by far the best solution that I'd 
been able to come up with). Overall, it's worked quite well, but periodically 
(for reasons that I _still_ don't understand) passing the function as an alias 
didn't work properly (sometimes the template constraint which checked that 
calling the function - exactly how it was called in the code - compiled failed, 
whereas the template would compile if the constraint was removed, which makes
no 
sense at all to me). So, there were enough problems in using it and enough 
dislike for how it worked, that I mucked around with it a bit to find a better 
way to do it, and I believe that I've found one. You end up passing the entire 
function call as a string (preferably a WYSIWYG string). So, instead of

assertExcThrown!(Exception, myfunc)(LineInfo(), param1, param2);

you get the much shorter and cleaner

mixin(assertExcThrown!(Exception, `myfunc(param1, param2)`));


It mixes the whole thing in on that one line, so it doesn't affect the line
count 
and the call is actually done at the local scope, so it's exactly as if you'd 
written the function call directly in the function (since that's how it gets 
compiled in). It also manages to deal with __FILE__ and __LINE__  internally 
this way without even needing to pass them as default parameters. The one 
downside is that - being a string mixin - it does come with higher compilation 
overhead. But that should improve as the compiler improves ( 
http://d.puremagic.com/issues/show_bug.cgi?id=1382 is likely the main culprit).

The list of functions is unchanged, but a few of them became eponymous
templates 
to be mixed in as strings rather than being templated function calls. It is bit 
annoying to have to use mixin to do it, but the result is much cleaner on the 
whole, I believe. So, it's a huge useability change.

In any case, I'm presenting the updated for your review, so tell me what you 
think.

- Jonathan M Davis


P.S. My most recent update of std.datetime doesn't use the updated unit test 
functions yet, so the only examples of how to use them are in the docs and 
source of my unittest module - both of which are included in the link above. 
I'll likely post a version of std.datetime with the updated unit tests later 
today, so if you really want to see the functions used on a larger scale, you 
can check that out then.
Nov 19 2010
next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
Jonathan M Davis Wrote:
 
 In particular, needing to pass LineInfo() to assertExcThrown!() to know the
file 
 and line number was disliked (though it was by far the best solution that I'd 
 been able to come up with).

Not sure if this helps, but if you default-initialize template function parameters with __LINE__ and __FILE__ they get the line and file of where the template was instantiated.
Nov 19 2010
parent reply Sean Kelly <sean invisibleduck.org> writes:
Jonathan M Davis Wrote:

 On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:
 Jonathan M Davis Wrote:
 In particular, needing to pass LineInfo() to assertExcThrown!() to know
 the file and line number was disliked (though it was by far the best
 solution that I'd been able to come up with).

Not sure if this helps, but if you default-initialize template function parameters with __LINE__ and __FILE__ they get the line and file of where the template was instantiated.

Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.

This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.
Nov 19 2010
parent Sean Kelly <sean invisibleduck.org> writes:
Leandro Lucarella Wrote:

 Sean Kelly, el 19 de noviembre a las 14:59 me escribiste:
 
 This should work:
 
 void func(string x = __FILE__, T...)(T args);
 
 D allows defaulted template arguments to occur before non-defaulted ones.

And what is func!("blah")(); is supposed to do, make x = "blah"? args[0] = "blah"? both?

void func(string x = __FILE__, T...)(T args) { writeln( x, ' ', args.length ); } void main() { func!("hi")(); } prints: hi 0
Nov 19 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:
 Jonathan M Davis Wrote:
 In particular, needing to pass LineInfo() to assertExcThrown!() to know
 the file and line number was disliked (though it was by far the best
 solution that I'd been able to come up with).

Not sure if this helps, but if you default-initialize template function parameters with __LINE__ and __FILE__ they get the line and file of where the template was instantiated.

Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments. LineInfo had default arguments in its opCall() which were the file and line number, so you passed LineInfo() as the first argument to the function, thereby not having to pass __FILE__ and __LINE__, but ideally, they would be default arguments of the original function as you suggest. It's just that the variadic part got in the way. The new version is neither variadic (since whole call is a single string rather than passing the function name and arguments separately), and it doesn't even need the __FILE__ and __LINE__ number as default arguments, since they're just used directly in the string that's mixed in. - Jonathan M Davis
Nov 19 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday 19 November 2010 11:59:18 Sean Kelly wrote:
 Jonathan M Davis Wrote:
 On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:
 Jonathan M Davis Wrote:
 In particular, needing to pass LineInfo() to assertExcThrown!() to
 know the file and line number was disliked (though it was by far the
 best solution that I'd been able to come up with).

Not sure if this helps, but if you default-initialize template function parameters with __LINE__ and __FILE__ they get the line and file of where the template was instantiated.

Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.

This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.

If so, then I obviously assumed that that wouldn't work. You certainly can't do that with the function arguments. I think that the new solution is still better though, because it allows you to write the function call like you normally would. You just have to put it in string form. That, and there were problems with passing the function as an alias. Still, it's definitely good to know that you can have default template arguments before non-defaulted ones. Thanks. - Jonathan M Davis
Nov 19 2010
prev sibling next sibling parent Leandro Lucarella <luca llucax.com.ar> writes:
Sean Kelly, el 19 de noviembre a las 14:59 me escribiste:
 Jonathan M Davis Wrote:
 
 On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:
 Jonathan M Davis Wrote:
 In particular, needing to pass LineInfo() to assertExcThrown!() to know
 the file and line number was disliked (though it was by far the best
 solution that I'd been able to come up with).

Not sure if this helps, but if you default-initialize template function parameters with __LINE__ and __FILE__ they get the line and file of where the template was instantiated.

Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.

This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.

And what is func!("blah")(); is supposed to do, make x = "blah"? args[0] = "blah"? both? (I don't have a compiler at hand to try it =P) -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- He used to do surgery On girls in the eighties But gravity always wins
Nov 19 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday 19 November 2010 12:39:20 Leandro Lucarella wrote:
 Sean Kelly, el 19 de noviembre a las 14:59 me escribiste:
 Jonathan M Davis Wrote:
 On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:
 Jonathan M Davis Wrote:
 In particular, needing to pass LineInfo() to assertExcThrown!() to
 know the file and line number was disliked (though it was by far
 the best solution that I'd been able to come up with).

Not sure if this helps, but if you default-initialize template function parameters with __LINE__ and __FILE__ they get the line and file of where the template was instantiated.

Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.

This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.

And what is func!("blah")(); is supposed to do, make x = "blah"? args[0] = "blah"? both? (I don't have a compiler at hand to try it =P)

T... is filled in be args, so if there are no args, T is empty. It can't confuse x for T... - Jonathan M Davis
Nov 19 2010
prev sibling next sibling parent Leandro Lucarella <luca llucax.com.ar> writes:
Jonathan M Davis, el 19 de noviembre a las 13:24 me escribiste:
 On Friday 19 November 2010 12:39:20 Leandro Lucarella wrote:
 Sean Kelly, el 19 de noviembre a las 14:59 me escribiste:
 Jonathan M Davis Wrote:
 On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:
 Jonathan M Davis Wrote:
 In particular, needing to pass LineInfo() to assertExcThrown!() to
 know the file and line number was disliked (though it was by far
 the best solution that I'd been able to come up with).

Not sure if this helps, but if you default-initialize template function parameters with __LINE__ and __FILE__ they get the line and file of where the template was instantiated.

Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.

This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.

And what is func!("blah")(); is supposed to do, make x = "blah"? args[0] = "blah"? both? (I don't have a compiler at hand to try it =P)

T... is filled in be args, so if there are no args, T is empty. It can't confuse x for T...

Good point, thanks for the clarification. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Sometimes you got to suffer a little in your youth to motivate you to succeed later in life. Do you think if Bill Gates got laid in high school, do you think there'd be a Microsoft? Of course not. You gotta spend a lot of time stuffin your own locker with your underwear wedged up your arse before you think "I'm gona take over the world with computers! You'll see I'll show them."
Nov 19 2010
prev sibling next sibling parent Fawzi Mohamed <fawzi gmx.ch> writes:
On 19-nov-10, at 23:44, Sean Kelly wrote:

 Leandro Lucarella Wrote:

 Sean Kelly, el 19 de noviembre a las 14:59 me escribiste:
 This should work:

 void func(string x = __FILE__, T...)(T args);

 D allows defaulted template arguments to occur before non- 
 defaulted ones.



I wasn't aware that __FILE__ and __LINE__ did expand at the instantiation place, it is nice, but that seems to be the case only for D2 :(
Nov 20 2010
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2010-11-19 19:16, Jonathan M Davis wrote:
 Updated code: http://is.gd/hqPb2

 Okay. As mentioned before, I have helper unit test functions which I use
heavily
 in std.datetime and which are pretty much going to have to either end up as
 private helper functions in std.datetime or actually get added in a separate
 module for everyone to use (I'd prefer the latter). My last post on it seems to
 have met with positive feedback overall for the basic idea but not necessarily
 the execution.

 In particular, needing to pass LineInfo() to assertExcThrown!() to know the
file
 and line number was disliked (though it was by far the best solution that I'd
 been able to come up with). Overall, it's worked quite well, but periodically
 (for reasons that I _still_ don't understand) passing the function as an alias
 didn't work properly (sometimes the template constraint which checked that
 calling the function - exactly how it was called in the code - compiled failed,
 whereas the template would compile if the constraint was removed, which makes
no
 sense at all to me). So, there were enough problems in using it and enough
 dislike for how it worked, that I mucked around with it a bit to find a better
 way to do it, and I believe that I've found one. You end up passing the entire
 function call as a string (preferably a WYSIWYG string). So, instead of

 assertExcThrown!(Exception, myfunc)(LineInfo(), param1, param2);

 you get the much shorter and cleaner

 mixin(assertExcThrown!(Exception, `myfunc(param1, param2)`));


 It mixes the whole thing in on that one line, so it doesn't affect the line
count
 and the call is actually done at the local scope, so it's exactly as if you'd
 written the function call directly in the function (since that's how it gets
 compiled in). It also manages to deal with __FILE__ and __LINE__  internally
 this way without even needing to pass them as default parameters. The one
 downside is that - being a string mixin - it does come with higher compilation
 overhead. But that should improve as the compiler improves (
 http://d.puremagic.com/issues/show_bug.cgi?id=1382 is likely the main culprit).

 The list of functions is unchanged, but a few of them became eponymous
templates
 to be mixed in as strings rather than being templated function calls. It is bit
 annoying to have to use mixin to do it, but the result is much cleaner on the
 whole, I believe. So, it's a huge useability change.

 In any case, I'm presenting the updated for your review, so tell me what you
 think.

 - Jonathan M Davis


 P.S. My most recent update of std.datetime doesn't use the updated unit test
 functions yet, so the only examples of how to use them are in the docs and
 source of my unittest module - both of which are included in the link above.
 I'll likely post a version of std.datetime with the updated unit tests later
 today, so if you really want to see the functions used on a larger scale, you
 can check that out then.

Why don't you use delegates instead of string mixins? For example, assertExcThrown, could take a delegate which calls the function you want to test instead of a string that represents the call. The mixin want be needed as well. Am I missing something? -- /Jacob Carlborg
Nov 20 2010
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2010-11-21 01:23, Jonathan M Davis wrote:
 On Saturday 20 November 2010 08:03:52 Jacob Carlborg wrote:
 Why don't you use delegates instead of string mixins? For example,
 assertExcThrown, could take a delegate which calls the function you want
 to test instead of a string that represents the call. The mixin want be
 needed as well. Am I missing something?

Well, delegates wouldn't be a bad idea, but they're unwieldy too. Would you rather write assertExcThrown!Exception((){func(param1, param2);}); or mixin(assertExcThrown!(Exception, `func(param1, param2)`));

I would go with the delegate, but when you format the code like that it doesn't look any good (btw, no need for the extra pair of empty parentheses). I think this looks better: assertExcThrown!Exception({ func(param1, param2); }); And BTW, D needs a better way to pass a delegates to a function, something like the syntax that can be used in Scala: assertExcThrown!Exception { func(param1, param2); } I think that someone has talked about this syntax before on this mailing list.
 The delegate would be less efficient, but you could reduce code duplication by
 going that route, since then __FILE__ and __LINE__ could be default parameters
 to assertExcThrown!(). It would also make it so that __FILE__ and __LINE__
could
 be replace if you reall wanted to (which could be useful), but you could do
that
 by changing it so that the mixin version of assertExcThrown!() took __FILE__
and
 __LINE__ as default parameters instead of them being internal to the mixin.
 Neither is pretty, but they both have their pros and cons. It looks like if I
do
 keep the mixin version, I should probably move __FILE__ and __LINE__ to
 parameters instead of mixing them in directly, so that it's more flexible. I
 suppose that you could just make the function call parameter a lazy one and
 templatize assertExcThrown!() on the return type of the function (I _think_
that
 that would still work with void). That way, you could avoid having to type
(){;}
 around the function call. In a way though, it is a bit silly to declare it as
 lazy, since it _always_ gets called. That could be the best way to go though.

 Honestly, it never even occurred to me to make it a delegate. I was originally
 trying to pass the function and its arguments separately, and that didn't work
 as well as I'd have liked, and the mixin solution is what I arrived at. I do
find
 having to pass a delegate uglier than having to use a mixin, but that's
probably
 pretty subjective and debatable. Using delegates will make for less efficient
unit
 tests, but it would mean less code duplication, and thus faster compilation.
So,
 it's not entirely a straightforward choice, I think.

I guess you're right and I would choose the delegates.
 The lazy solution sounds pretty good actually. Can anyone think of any real
 downsides to that? So, it would look something like

 assertExcThrown(E : Throwable, T)(lazy T, string file = __FILE__, size_t line =
 __LINE__);

 - Jonathan M Davis

-- /Jacob Carlborg
Nov 21 2010
parent reply Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
Jacob Carlborg wrote:

 On 2010-11-21 01:23, Jonathan M Davis wrote:
 On Saturday 20 November 2010 08:03:52 Jacob Carlborg wrote:
 Why don't you use delegates instead of string mixins? For example,
 assertExcThrown, could take a delegate which calls the function you want
 to test instead of a string that represents the call. The mixin want be
 needed as well. Am I missing something?

Well, delegates wouldn't be a bad idea, but they're unwieldy too. Would you rather write assertExcThrown!Exception((){func(param1, param2);}); or mixin(assertExcThrown!(Exception, `func(param1, param2)`));

I would go with the delegate, but when you format the code like that it doesn't look any good (btw, no need for the extra pair of empty parentheses). I think this looks better: assertExcThrown!Exception({ func(param1, param2); }); And BTW, D needs a better way to pass a delegates to a function, something like the syntax that can be used in Scala: assertExcThrown!Exception { func(param1, param2); }

This is possible, but too surprising: assertExcThrown!Exception = { func(param1, param2); }; with lazy: assertExcThrown!Exception = func(param1, param2);
Nov 21 2010
parent Jacob Carlborg <doob me.com> writes:
On 2010-11-21 17:20, Lutger Blijdestijn wrote:
 Jacob Carlborg wrote:

 On 2010-11-21 01:23, Jonathan M Davis wrote:
 On Saturday 20 November 2010 08:03:52 Jacob Carlborg wrote:
 Why don't you use delegates instead of string mixins? For example,
 assertExcThrown, could take a delegate which calls the function you want
 to test instead of a string that represents the call. The mixin want be
 needed as well. Am I missing something?

Well, delegates wouldn't be a bad idea, but they're unwieldy too. Would you rather write assertExcThrown!Exception((){func(param1, param2);}); or mixin(assertExcThrown!(Exception, `func(param1, param2)`));

I would go with the delegate, but when you format the code like that it doesn't look any good (btw, no need for the extra pair of empty parentheses). I think this looks better: assertExcThrown!Exception({ func(param1, param2); }); And BTW, D needs a better way to pass a delegates to a function, something like the syntax that can be used in Scala: assertExcThrown!Exception { func(param1, param2); }

This is possible, but too surprising: assertExcThrown!Exception = { func(param1, param2); }; with lazy: assertExcThrown!Exception = func(param1, param2);

There's also the operator overload abuse which overloads opIn: assertExcThrown!Exception in { func(param1, param2); }; -- /Jacob Carlborg
Nov 21 2010
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2010-11-21 02:34, Jonathan M Davis wrote:
 On Saturday 20 November 2010 16:23:32 Jonathan M Davis wrote:
 The lazy solution sounds pretty good actually. Can anyone think of any real
 downsides to that? So, it would look something like

 assertExcThrown(E : Throwable, T)(lazy T, string file = __FILE__, size_t
 line = __LINE__);

Wait. No. I didn't think that one through enough. It doesn't work. You You don't actually get a delegate out of lazy, and what you need for assertExcThrown!() is a delegate. It would be great to be able to implicitly create a delegate like that, but lazy does it underneath the hood, so to speak, and when the function call is made, you still get the result of the function call, and by that point, it's too late, since to get the result of the function call, you obviously need to have called it. So, the question remains whether it would be better to pass a string which gets mixed in or a delegate which gets called.

Ok, now I'm not sure what you mean. If you don't get a delegate what do you get? And what to you mean by "and by that point, it's too late", do late for what?
 I think that the delegate solution is uglier to use, but that's debatable. It's
 less efficient at runtime, but it would be faster to compile and would result
in
 less code bloat (though how much code bloat matters for unit tests is
debatable,
 since they shouldn't end up in released binaries). I'm really not sure which is
 better. I like the mixin solution, but the delegate solution has its advantages
 as well.

 Anyone else have an opinion on whether (and why) the string mixin approach or
 the delegate approach is better?

 - Jonathan M Davis

This works according to the documentation of assertExcThrown: void foo (int, int) { throw new Throwable(""); } void assertExcThrown (E = Throwable, T) (lazy T dg, string file = __FILE__, size_t line = __LINE__) { bool thrown; try dg(); catch (E e) thrown = true; if (!thrown) throw new AssertError(format("assertExcThrown() failed: No %s was thrown.", E.stringof), file, line); } void main () { assertExcThrown(foo(3, 4)); } -- /Jacob Carlborg
Nov 21 2010
parent Jacob Carlborg <doob me.com> writes:
On 2010-11-21 14:06, Jonathan M Davis wrote:
 On Sunday 21 November 2010 04:19:51 Jacob Carlborg wrote:
 On 2010-11-21 02:34, Jonathan M Davis wrote:
 On Saturday 20 November 2010 16:23:32 Jonathan M Davis wrote:
 The lazy solution sounds pretty good actually. Can anyone think of any
 real downsides to that? So, it would look something like

 assertExcThrown(E : Throwable, T)(lazy T, string file = __FILE__, size_t
 line = __LINE__);

Wait. No. I didn't think that one through enough. It doesn't work. You You don't actually get a delegate out of lazy, and what you need for assertExcThrown!() is a delegate. It would be great to be able to implicitly create a delegate like that, but lazy does it underneath the hood, so to speak, and when the function call is made, you still get the result of the function call, and by that point, it's too late, since to get the result of the function call, you obviously need to have called it. So, the question remains whether it would be better to pass a string which gets mixed in or a delegate which gets called.

Ok, now I'm not sure what you mean. If you don't get a delegate what do you get? And what to you mean by "and by that point, it's too late", do late for what?
 I think that the delegate solution is uglier to use, but that's
 debatable. It's less efficient at runtime, but it would be faster to
 compile and would result in less code bloat (though how much code bloat
 matters for unit tests is debatable, since they shouldn't end up in
 released binaries). I'm really not sure which is better. I like the
 mixin solution, but the delegate solution has its advantages as well.

 Anyone else have an opinion on whether (and why) the string mixin
 approach or the delegate approach is better?

 - Jonathan M Davis

This works according to the documentation of assertExcThrown: void foo (int, int) { throw new Throwable(""); } void assertExcThrown (E = Throwable, T) (lazy T dg, string file = __FILE__, size_t line = __LINE__) { bool thrown; try dg(); catch (E e) thrown = true; if (!thrown) throw new AssertError(format("assertExcThrown() failed: No %s was thrown.", E.stringof), file, line); } void main () { assertExcThrown(foo(3, 4)); }

Okay. I obviously don't use lazy enough and am probably posting too often without enough sleep. I was thinking that with a lazy parameter, whatever delegate which was created for it was called and the value assigned to the function argument before the function body was actually entered. So, internally to the function, it didn't matter at all whether a parameter was lazy or not, as if the laziness of the parameters affected the outside of the function only and not the inside. But that would completely defeat the point of lazy and makes no sense at all. Now, the code that you give works, and I think that it shows that using lazy is exactly what assertExcThrown _should_ do. It allows for you to do a normal function call and then have it explicitly called within the function block without having to create a delegate or give the function call as a string. It annoys me a bit to have the extra overhead of the delegate, and part of me does think that it's silly to have a lazy parameter which is _always_ called, but it does the job, and it creates less code bloat than the mixin solution, and if speed is the main concern, the extra compilation overhead of the mixin solution is probably greater than the overhead of the delegate, so if you expect to compile and then immediately run the unit tests, the overall result of using a delegate is likely faster. And the lazy parameter gets rid of any ugliness that creating a delegate might have over the string mixin. So, I'll change it to work with a lazy parameter instead. Thanks for your help. - Jonathan M Davis

I don't know if the compiler can inline delegates or not but if it can I think this case would be very easy for the compiler to inline the delegate. -- /Jacob Carlborg
Nov 21 2010
prev sibling next sibling parent reply Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
I'm not particularly fond of this interface and think that a solution with a 
delegate / lazy or alias template parameter would be more convenient. 
However, until we have ast macros I do see the added value in this approach.

Some remarks about the api, not a proper review of the code itself:

- core.exception and std.string must be imported to use the module, relevant 
symbols should be selectively and publicy imported instead.

- exception would be better abbreviated as ex instead of exc, I believe this 
is more conventional. (assertExThrown instead of assertExcThrown)

- assertExcThrown should fail if an exception is thrown that is not T or a 
subclass of T. (catch and throw AssertError)

- assertEqual and assertNotEqual is inconsistent in naming with the 
assertOpFoo class of functions

I believe these assertions should be added:

- assertExcThrown and assertExcNotThrown with a default T (should be 
Exception, not Throwable)

- something equivalent to std.algorithm.equal, the latter is very useful in 
unittests

- assertOpBinary, assertTrue and perhaps assertPred (where the predicate is 
an template alias parameter)
Nov 20 2010
parent Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
Jonathan M Davis wrote:

 On Saturday 20 November 2010 10:16:55 Lutger Blijdestijn wrote:
 I'm not particularly fond of this interface and think that a solution
 with a delegate / lazy or alias template parameter would be more
 convenient. However, until we have ast macros I do see the added value in
 this approach.
 
 Some remarks about the api, not a proper review of the code itself:
 
 - core.exception and std.string must be imported to use the module,
 relevant symbols should be selectively and publicy imported instead.

I hadn't thought of that. Good idea.
 - exception would be better abbreviated as ex instead of exc, I believe
 this is more conventional. (assertExThrown instead of assertExcThrown)

I'll think about it. I don't think that I've often seen it abbreviated at all. But if Ex really is more common, then that would be better.

I know of std.exception.enforceEx, and catch(Exception ex) is also regularly used in examples.
 - assertExcThrown should fail if an exception is thrown that is not T or
 a subclass of T. (catch and throw AssertError)

It currently catches the exact exception (well, throwable) that you tell it to. The others are let through exactly like they would be with an assert. It also means that you get to see exactly what the erroneous exception was so that you can deal with it. An AssertError would be less informative, maybe even misleading. You get an AssertError because it didn't throw any exception, whereas you get the wrong exception if it threw the wrong one.

Suppose you want assert that a FileNotFoundException is thrown. Now if you get an Exception then: - technically the unittest has passed because no AssertError has been thrown (splitting hairs) - if for whatever reason you catch this error in the unittest, things will get screwed up - You (or possibly some other script) won' t get the same command line output, it's then harder to correlate the exception with the assertion I agree about wanting to know the original Exception though. I think this is possible by setting the .next field of AssertError with that exception. ...
 
 I believe these assertions should be added:
 
 - assertExcThrown and assertExcNotThrown with a default T (should be
 Exception, not Throwable)

That's not a bad idea, though personally, I tend to think that if someone used an Exception rather than a derived type that they aren't be specific enough (I'm sure that many would argue with me on that one though). It might work to just give it a default parameter (and I do agree that the default should be Exception rather than Throwable).

I agree but it doesn't matter for general use, people will want this and this is also practice in phobos (mostly through the use of enforce I think). ...
 
 - assertOpBinary, assertTrue and perhaps assertPred (where the predicate
 is an template alias parameter)

I decided that assertTrue() was a waste of time, because that's what a bare assert does. The same goes for assertFalse(), because all you have to do is add ! in front of the expression. All of the functions that I have are there to either improve the output (such as printing out the actual values of the two values being compared in assertEqual()), or they get rid of boilerplate code (such as the try-catch block and other details in assertExcThrown!()).

Except that assert does not print the expression which resulted in the assertion like all other functions do, so assertTrue does improve the output too.
 As for assertOpBinary, are you suggesting a function which does the given
 binary operation and then compares the result with the given expected
 result? That would be a good addition.
 
 As for assertPred. I don't know what that would do. Would that take a
 predicate and its parameters and then assert that the result was true? If
 that's what you're looking for, then assertPredTrue and assertPredFalse
 would be better. I think that you need to clarify quite what you meant
 though.

bool isSorted(); int[] numbers = getSortedNumbers(); assertPred!isSorted(numbers); It's the same as assert(isSorted(numbers)), except it allows for improved output. Not very important, but I find it common to use such predicates for testing so it might help. Alternatively assertTrue and assertFalse could take an optional predicate, defaulting to the identity and negation respectively: assertTrue!isSorted(numbers);
Nov 21 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
What about debug vs release compilation for this new module?

We know we have assert for debug mode, and enforce for release mode
(except the special assert false case). If I want assertExcThrown to
be compiled in release mode it seems I'd need an enforced version of
it, possibly called enforceExcThrown that uses enforce internally.

I'd prefer if we could somehow mark assert's to be compiled in release
mode instead of having to duplicate code for assert and enforce
versions of these new unittest functions.

On 11/20/10, Jacob Carlborg <doob me.com> wrote:
 On 2010-11-19 19:16, Jonathan M Davis wrote:
 Updated code: http://is.gd/hqPb2

 Okay. As mentioned before, I have helper unit test functions which I use
 heavily
 in std.datetime and which are pretty much going to have to either end up
 as
 private helper functions in std.datetime or actually get added in a
 separate
 module for everyone to use (I'd prefer the latter). My last post on it
 seems to
 have met with positive feedback overall for the basic idea but not
 necessarily
 the execution.

 In particular, needing to pass LineInfo() to assertExcThrown!() to know
 the file
 and line number was disliked (though it was by far the best solution that
 I'd
 been able to come up with). Overall, it's worked quite well, but
 periodically
 (for reasons that I _still_ don't understand) passing the function as an
 alias
 didn't work properly (sometimes the template constraint which checked that
 calling the function - exactly how it was called in the code - compiled
 failed,
 whereas the template would compile if the constraint was removed, which
 makes no
 sense at all to me). So, there were enough problems in using it and enough
 dislike for how it worked, that I mucked around with it a bit to find a
 better
 way to do it, and I believe that I've found one. You end up passing the
 entire
 function call as a string (preferably a WYSIWYG string). So, instead of

 assertExcThrown!(Exception, myfunc)(LineInfo(), param1, param2);

 you get the much shorter and cleaner

 mixin(assertExcThrown!(Exception, `myfunc(param1, param2)`));


 It mixes the whole thing in on that one line, so it doesn't affect the
 line count
 and the call is actually done at the local scope, so it's exactly as if
 you'd
 written the function call directly in the function (since that's how it
 gets
 compiled in). It also manages to deal with __FILE__ and __LINE__
 internally
 this way without even needing to pass them as default parameters. The one
 downside is that - being a string mixin - it does come with higher
 compilation
 overhead. But that should improve as the compiler improves (
 http://d.puremagic.com/issues/show_bug.cgi?id=1382 is likely the main
 culprit).

 The list of functions is unchanged, but a few of them became eponymous
 templates
 to be mixed in as strings rather than being templated function calls. It
 is bit
 annoying to have to use mixin to do it, but the result is much cleaner on
 the
 whole, I believe. So, it's a huge useability change.

 In any case, I'm presenting the updated for your review, so tell me what
 you
 think.

 - Jonathan M Davis


 P.S. My most recent update of std.datetime doesn't use the updated unit
 test
 functions yet, so the only examples of how to use them are in the docs and
 source of my unittest module - both of which are included in the link
 above.
 I'll likely post a version of std.datetime with the updated unit tests
 later
 today, so if you really want to see the functions used on a larger scale,
 you
 can check that out then.

Why don't you use delegates instead of string mixins? For example, assertExcThrown, could take a delegate which calls the function you want to test instead of a string that represents the call. The mixin want be needed as well. Am I missing something? -- /Jacob Carlborg

Nov 20 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 20 November 2010 08:03:52 Jacob Carlborg wrote:
 Why don't you use delegates instead of string mixins? For example,
 assertExcThrown, could take a delegate which calls the function you want
 to test instead of a string that represents the call. The mixin want be
 needed as well. Am I missing something?

Well, delegates wouldn't be a bad idea, but they're unwieldy too. Would you rather write assertExcThrown!Exception((){func(param1, param2);}); or mixin(assertExcThrown!(Exception, `func(param1, param2)`)); The delegate would be less efficient, but you could reduce code duplication by going that route, since then __FILE__ and __LINE__ could be default parameters to assertExcThrown!(). It would also make it so that __FILE__ and __LINE__ could be replace if you reall wanted to (which could be useful), but you could do that by changing it so that the mixin version of assertExcThrown!() took __FILE__ and __LINE__ as default parameters instead of them being internal to the mixin. Neither is pretty, but they both have their pros and cons. It looks like if I do keep the mixin version, I should probably move __FILE__ and __LINE__ to parameters instead of mixing them in directly, so that it's more flexible. I suppose that you could just make the function call parameter a lazy one and templatize assertExcThrown!() on the return type of the function (I _think_ that that would still work with void). That way, you could avoid having to type (){;} around the function call. In a way though, it is a bit silly to declare it as lazy, since it _always_ gets called. That could be the best way to go though. Honestly, it never even occurred to me to make it a delegate. I was originally trying to pass the function and its arguments separately, and that didn't work as well as I'd have liked, and the mixin solution is what I arrived at. I do find having to pass a delegate uglier than having to use a mixin, but that's probably pretty subjective and debatable. Using delegates will make for less efficient unit tests, but it would mean less code duplication, and thus faster compilation. So, it's not entirely a straightforward choice, I think. The lazy solution sounds pretty good actually. Can anyone think of any real downsides to that? So, it would look something like assertExcThrown(E : Throwable, T)(lazy T, string file = __FILE__, size_t line = __LINE__); - Jonathan M Davis
Nov 20 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 20 November 2010 10:16:55 Lutger Blijdestijn wrote:
 I'm not particularly fond of this interface and think that a solution with
 a delegate / lazy or alias template parameter would be more convenient.
 However, until we have ast macros I do see the added value in this
 approach.
 
 Some remarks about the api, not a proper review of the code itself:
 
 - core.exception and std.string must be imported to use the module,
 relevant symbols should be selectively and publicy imported instead.

I hadn't thought of that. Good idea.
 - exception would be better abbreviated as ex instead of exc, I believe
 this is more conventional. (assertExThrown instead of assertExcThrown)

I'll think about it. I don't think that I've often seen it abbreviated at all. But if Ex really is more common, then that would be better.
 - assertExcThrown should fail if an exception is thrown that is not T or a
 subclass of T. (catch and throw AssertError)

It currently catches the exact exception (well, throwable) that you tell it to. The others are let through exactly like they would be with an assert. It also means that you get to see exactly what the erroneous exception was so that you can deal with it. An AssertError would be less informative, maybe even misleading. You get an AssertError because it didn't throw any exception, whereas you get the wrong exception if it threw the wrong one. I suppose that I could see an argument that because it didn't throw that exact exception, it should throw an AssertError, but I think that it's worse, because you can't see what the unexpected exception was. It would be less helpful in debugging rather than more.
 - assertEqual and assertNotEqual is inconsistent in naming with the
 assertOpFoo class of functions

Not quite. assertEqual() doesn't explicitly test opEqual(). It uses ==. So, it works with primitives as well as structs that didn't declare an opEquals(). assertOpCmp() on the other hand, explicitly calls opCmp() - so, for instance, assertOpCmp!"=="() specifically tests that opCmp() returned zero rather than using ==. However, assertOpOpAssign doesn't actually call opOpAssign(), so there arguably is an inconsistency there. I'll have to think about how it would be best to adjust those names. I think that assertEqual() should definitely stay assertEqual() - it doesn't explicitly call opEquals() and other unit testing frameworks frequent have a function named assertEquals(), so it will be more familiar to many. I also think that assertOpCmp!() should stay as it is, since it is explicitly calling opCmp(). The really question is what to rename assertOpOpAssign!() to, since like with assertEqual(), it would be too restrictive to directly call opOpAssign(), and I can't rename it assertOpAssign() because opAssign() is something else entirely. So, if a different name would be better, I'm going to have to find one, since I don't know what else to name it.
 I believe these assertions should be added:
 
 - assertExcThrown and assertExcNotThrown with a default T (should be
 Exception, not Throwable)

That's not a bad idea, though personally, I tend to think that if someone used an Exception rather than a derived type that they aren't be specific enough (I'm sure that many would argue with me on that one though). It might work to just give it a default parameter (and I do agree that the default should be Exception rather than Throwable).
 - something equivalent to std.algorithm.equal, the latter is very useful in
 unittests

I'm not sure that I'd noticed std.algorithm.equal() before. That's a good idea. assertEqual() could simply be templatized in the same manner as std.algorithm.equal().
 - assertOpBinary, assertTrue and perhaps assertPred (where the predicate is
 an template alias parameter)

I decided that assertTrue() was a waste of time, because that's what a bare assert does. The same goes for assertFalse(), because all you have to do is add ! in front of the expression. All of the functions that I have are there to either improve the output (such as printing out the actual values of the two values being compared in assertEqual()), or they get rid of boilerplate code (such as the try-catch block and other details in assertExcThrown!()). As for assertOpBinary, are you suggesting a function which does the given binary operation and then compares the result with the given expected result? That would be a good addition. As for assertPred. I don't know what that would do. Would that take a predicate and its parameters and then assert that the result was true? If that's what you're looking for, then assertPredTrue and assertPredFalse would be better. I think that you need to clarify quite what you meant though. I pretty much created the unit test functions which I needed for std.datetime. So, I'm sure that there are other good ones which could be added. And I think that we should do so. My main concern though has been that because some version of these is going to have to go into Phobos when std.datetime does (be it as private in std.datetime or as its own module), I wanted to try and get the unit test functions themselves vetted and get them put in their own module. That way other people can use them, and more can be added later. So, I haven't been as concerned with finding every possibly useful unit testing function - particularly when for some of these, you really have to use them to see how useful they really are. But I'm definitely open to suggestions as to good unit testing functions to add. - Jonathan M Davis
Nov 20 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 20 November 2010 10:23:36 Andrej Mitrovic wrote:
 What about debug vs release compilation for this new module?
 
 We know we have assert for debug mode, and enforce for release mode
 (except the special assert false case). If I want assertExcThrown to
 be compiled in release mode it seems I'd need an enforced version of
 it, possibly called enforceExcThrown that uses enforce internally.
 
 I'd prefer if we could somehow mark assert's to be compiled in release
 mode instead of having to duplicate code for assert and enforce
 versions of these new unittest functions.

It's specifically versioned on unittest. So, if you have unit tests enable, they'll be compiled in. If you don't, then they won't. Their entire purpose is for improving unit tests, and they aren't really intended for use anywhere else. And since they all throw AssertError on failure, they would be exactly as useful as an assert would be if it were compiled in. Why would you be looking to use them outside of unit tests? What would you be trying to do with them? - Jonathan M Davis
Nov 20 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 20 November 2010 16:23:32 Jonathan M Davis wrote:
 The lazy solution sounds pretty good actually. Can anyone think of any real
 downsides to that? So, it would look something like
 
 assertExcThrown(E : Throwable, T)(lazy T, string file = __FILE__, size_t
 line = __LINE__);

Wait. No. I didn't think that one through enough. It doesn't work. You You don't actually get a delegate out of lazy, and what you need for assertExcThrown!() is a delegate. It would be great to be able to implicitly create a delegate like that, but lazy does it underneath the hood, so to speak, and when the function call is made, you still get the result of the function call, and by that point, it's too late, since to get the result of the function call, you obviously need to have called it. So, the question remains whether it would be better to pass a string which gets mixed in or a delegate which gets called. I think that the delegate solution is uglier to use, but that's debatable. It's less efficient at runtime, but it would be faster to compile and would result in less code bloat (though how much code bloat matters for unit tests is debatable, since they shouldn't end up in released binaries). I'm really not sure which is better. I like the mixin solution, but the delegate solution has its advantages as well. Anyone else have an opinion on whether (and why) the string mixin approach or the delegate approach is better? - Jonathan M Davis
Nov 20 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 21 November 2010 04:19:51 Jacob Carlborg wrote:
 On 2010-11-21 02:34, Jonathan M Davis wrote:
 On Saturday 20 November 2010 16:23:32 Jonathan M Davis wrote:
 The lazy solution sounds pretty good actually. Can anyone think of any
 real downsides to that? So, it would look something like
 
 assertExcThrown(E : Throwable, T)(lazy T, string file = __FILE__, size_t
 line = __LINE__);

Wait. No. I didn't think that one through enough. It doesn't work. You You don't actually get a delegate out of lazy, and what you need for assertExcThrown!() is a delegate. It would be great to be able to implicitly create a delegate like that, but lazy does it underneath the hood, so to speak, and when the function call is made, you still get the result of the function call, and by that point, it's too late, since to get the result of the function call, you obviously need to have called it. So, the question remains whether it would be better to pass a string which gets mixed in or a delegate which gets called.

Ok, now I'm not sure what you mean. If you don't get a delegate what do you get? And what to you mean by "and by that point, it's too late", do late for what?
 I think that the delegate solution is uglier to use, but that's
 debatable. It's less efficient at runtime, but it would be faster to
 compile and would result in less code bloat (though how much code bloat
 matters for unit tests is debatable, since they shouldn't end up in
 released binaries). I'm really not sure which is better. I like the
 mixin solution, but the delegate solution has its advantages as well.
 
 Anyone else have an opinion on whether (and why) the string mixin
 approach or the delegate approach is better?
 
 - Jonathan M Davis

This works according to the documentation of assertExcThrown: void foo (int, int) { throw new Throwable(""); } void assertExcThrown (E = Throwable, T) (lazy T dg, string file = __FILE__, size_t line = __LINE__) { bool thrown; try dg(); catch (E e) thrown = true; if (!thrown) throw new AssertError(format("assertExcThrown() failed: No %s was thrown.", E.stringof), file, line); } void main () { assertExcThrown(foo(3, 4)); }

Okay. I obviously don't use lazy enough and am probably posting too often without enough sleep. I was thinking that with a lazy parameter, whatever delegate which was created for it was called and the value assigned to the function argument before the function body was actually entered. So, internally to the function, it didn't matter at all whether a parameter was lazy or not, as if the laziness of the parameters affected the outside of the function only and not the inside. But that would completely defeat the point of lazy and makes no sense at all. Now, the code that you give works, and I think that it shows that using lazy is exactly what assertExcThrown _should_ do. It allows for you to do a normal function call and then have it explicitly called within the function block without having to create a delegate or give the function call as a string. It annoys me a bit to have the extra overhead of the delegate, and part of me does think that it's silly to have a lazy parameter which is _always_ called, but it does the job, and it creates less code bloat than the mixin solution, and if speed is the main concern, the extra compilation overhead of the mixin solution is probably greater than the overhead of the delegate, so if you expect to compile and then immediately run the unit tests, the overall result of using a delegate is likely faster. And the lazy parameter gets rid of any ugliness that creating a delegate might have over the string mixin. So, I'll change it to work with a lazy parameter instead. Thanks for your help. - Jonathan M Davis
Nov 21 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 21 November 2010 05:44:15 Jacob Carlborg wrote:
 I don't know if the compiler can inline delegates or not but if it can I
 think this case would be very easy for the compiler to inline the delegate.

It can't. That's one of the big issues with enforce. At the moment, it actually makes it so that if you use enforce in a function, that function can't be inlined. Assuming that that gets fixed, assertExcThrown!() might then be inlineable, depending on how picky the inliner is, at which point perhaps the delegate could be inlined, but I don't think that delegates will ever be inlineable unless the function they're called in is inlined, because the delegate to inline would change every time that the function is called. In any case, it's definitely true that the compiler may be able to better optimize assertExcThrown!(). But much as I'd like fast unit testing code, I'd much rather have useable and readily maintainable unit testing code than fast unit testing code. It would be nice to have both though. - Jonathan M Davis
Nov 21 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 21 November 2010 08:11:06 Lutger Blijdestijn wrote:
 Jonathan M Davis wrote:
 On Saturday 20 November 2010 10:16:55 Lutger Blijdestijn wrote:
 I'm not particularly fond of this interface and think that a solution
 with a delegate / lazy or alias template parameter would be more
 convenient. However, until we have ast macros I do see the added value
 in this approach.
 
 Some remarks about the api, not a proper review of the code itself:
 
 - core.exception and std.string must be imported to use the module,
 relevant symbols should be selectively and publicy imported instead.

I hadn't thought of that. Good idea.
 - exception would be better abbreviated as ex instead of exc, I believe
 this is more conventional. (assertExThrown instead of assertExcThrown)

I'll think about it. I don't think that I've often seen it abbreviated at all. But if Ex really is more common, then that would be better.

I know of std.exception.enforceEx, and catch(Exception ex) is also regularly used in examples.z

Then, I'll change it to assertExThrown.
 - assertExcThrown should fail if an exception is thrown that is not T or
 a subclass of T. (catch and throw AssertError)

It currently catches the exact exception (well, throwable) that you tell it to. The others are let through exactly like they would be with an assert. It also means that you get to see exactly what the erroneous exception was so that you can deal with it. An AssertError would be less informative, maybe even misleading. You get an AssertError because it didn't throw any exception, whereas you get the wrong exception if it threw the wrong one.

Suppose you want assert that a FileNotFoundException is thrown. Now if you get an Exception then: - technically the unittest has passed because no AssertError has been thrown (splitting hairs) - if for whatever reason you catch this error in the unittest, things will get screwed up - You (or possibly some other script) won' t get the same command line output, it's then harder to correlate the exception with the assertion I agree about wanting to know the original Exception though. I think this is possible by setting the .next field of AssertError with that exception.

Actually, not only does AssertError not take a next as one if its constructor parameters, but even if you set it directly before throwing it, it still doesn't print out the other exception. So, if you throw an AssertError in the case of the wrong exception, the best that you're going to get is a message saying what type of exception was thrown and maybe what it said, but you couldn't get its stack trace, which could be far more important.
 I believe these assertions should be added:
 
 - assertExcThrown and assertExcNotThrown with a default T (should be
 Exception, not Throwable)

That's not a bad idea, though personally, I tend to think that if someone used an Exception rather than a derived type that they aren't be specific enough (I'm sure that many would argue with me on that one though). It might work to just give it a default parameter (and I do agree that the default should be Exception rather than Throwable).

I agree but it doesn't matter for general use, people will want this and this is also practice in phobos (mostly through the use of enforce I think).

I'll do it, but I do think that it's generally bad practice to throw an Exception rather than a subtype of Exception (except perhaps in a script or other similarly short program).
 - assertOpBinary, assertTrue and perhaps assertPred (where the predicate
 is an template alias parameter)

I decided that assertTrue() was a waste of time, because that's what a bare assert does. The same goes for assertFalse(), because all you have to do is add ! in front of the expression. All of the functions that I have are there to either improve the output (such as printing out the actual values of the two values being compared in assertEqual()), or they get rid of boilerplate code (such as the try-catch block and other details in assertExcThrown!()).

Except that assert does not print the expression which resulted in the assertion like all other functions do, so assertTrue does improve the output too.

Neither do any of the others. They print the value. So if assertEqual(func("hello", "world"), 2); on line 115 of config.d failed because func("hello", "world") returned a 3, you'd get core.exception.AssertError config.d(115): assertEquals() failed: actual [3], expected [2]. whereas with assert(func("hello", "world") == 2); you'd get core.exception.AssertError config(115): unittest failure assertEqual() is way more informative, but it doesn't print the actual expression, just the result of the expression. Short of using string mixins, I don't know how to do any better than that. But I don't think that you need to either. The file and line number are plenty for you to be able to go and see what the actual expression was - even with just the assert. What assertEqual() does is give you what the actual values being compared was. That being the case, assertTrue() and assertFalse() wouldn't help you any. If assertTrue() failed, then the result was false. If assertFalse() failed, then the result was true.
 As for assertPred. I don't know what that would do. Would that take a
 predicate and its parameters and then assert that the result was true? If
 that's what you're looking for, then assertPredTrue and assertPredFalse
 would be better. I think that you need to clarify quite what you meant
 though.

bool isSorted(); int[] numbers = getSortedNumbers(); assertPred!isSorted(numbers); It's the same as assert(isSorted(numbers)), except it allows for improved output. Not very important, but I find it common to use such predicates for testing so it might help. Alternatively assertTrue and assertFalse could take an optional predicate, defaulting to the identity and negation respectively: assertTrue!isSorted(numbers);

I'd lean toward something like assertPred!() or assertPredTrue!(). Like I said, I see no point in a general assertTrue because it can't actually add anything over assert - at least not in any sane way that I can see. I'll look at adding something like assertPred!() though. - Jonathan M Davis
Nov 21 2010