digitalmars.D - Review: A new stab at a potential std.unittests
- Jonathan M Davis (42/42) Nov 19 2010 Updated code: http://is.gd/hqPb2
- Sean Kelly (2/6) Nov 19 2010 Not sure if this helps, but if you default-initialize template function ...
- Jonathan M Davis (12/20) Nov 19 2010 Yes. The problem was that the function was a _variadic_ template. So, yo...
- Sean Kelly (4/16) Nov 19 2010 This should work:
- Jonathan M Davis (8/27) Nov 19 2010 If so, then I obviously assumed that that wouldn't work. You certainly c...
- Leandro Lucarella (12/32) Nov 19 2010 And what is func!("blah")(); is supposed to do, make x = "blah"? args[0]
- Jonathan M Davis (4/29) Nov 19 2010 T... is filled in be args, so if there are no args, T is empty. It can't...
- Leandro Lucarella (13/42) Nov 19 2010 Good point, thanks for the clarification.
- Sean Kelly (11/21) Nov 19 2010 void func(string x = __FILE__, T...)(T args)
- Fawzi Mohamed (4/13) Nov 20 2010 I wasn't aware that __FILE__ and __LINE__ did expand at the
- Jacob Carlborg (7/49) Nov 20 2010 Why don't you use delegates instead of string mixins? For example,
- Andrej Mitrovic (9/97) Nov 20 2010 What about debug vs release compilation for this new module?
- Jonathan M Davis (32/36) Nov 20 2010 Well, delegates wouldn't be a bad idea, but they're unwieldy too. Would ...
- Jacob Carlborg (17/53) Nov 21 2010 I would go with the delegate, but when you format the code like that it
- Lutger Blijdestijn (7/37) Nov 21 2010 This is possible, but too surprising:
- Jacob Carlborg (7/44) Nov 21 2010 There's also the operator overload abuse which overloads opIn:
- Jonathan M Davis (9/19) Nov 20 2010 It's specifically versioned on unittest. So, if you have unit tests enab...
- Jonathan M Davis (18/23) Nov 20 2010 Wait. No. I didn't think that one through enough. It doesn't work. You Y...
- Jacob Carlborg (27/50) Nov 21 2010 Ok, now I'm not sure what you mean. If you don't get a delegate what do
- Jonathan M Davis (23/84) Nov 21 2010 Okay. I obviously don't use lazy enough and am probably posting too ofte...
- Jacob Carlborg (5/89) Nov 21 2010 I don't know if the compiler can inline delegates or not but if it can I...
- Jonathan M Davis (13/15) Nov 21 2010 It can't. That's one of the big issues with enforce. At the moment, it a...
- Lutger Blijdestijn (19/19) Nov 20 2010 I'm not particularly fond of this interface and think that a solution wi...
- Jonathan M Davis (63/86) Nov 20 2010 I'll think about it. I don't think that I've often seen it abbreviated a...
- Lutger Blijdestijn (32/91) Nov 21 2010 I know of std.exception.enforceEx, and catch(Exception ex) is also regul...
- Jonathan M Davis (31/121) Nov 21 2010 Actually, not only does AssertError not take a next as one if its constr...
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
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
On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:Jonathan M Davis Wrote: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 DavisIn 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
Jonathan M Davis Wrote:On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.Jonathan M Davis Wrote:Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.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
On Friday 19 November 2010 11:59:18 Sean Kelly wrote:Jonathan M Davis Wrote: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 DavisOn Friday, November 19, 2010 11:37:16 Sean Kelly wrote:This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.Jonathan M Davis Wrote:Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.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
Sean Kelly, el 19 de noviembre a las 14:59 me escribiste:Jonathan M Davis Wrote: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 winsOn Friday, November 19, 2010 11:37:16 Sean Kelly wrote:This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.Jonathan M Davis Wrote:Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.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
On Friday 19 November 2010 12:39:20 Leandro Lucarella wrote:Sean Kelly, el 19 de noviembre a las 14:59 me escribiste:T... is filled in be args, so if there are no args, T is empty. It can't confuse x for T... - Jonathan M DavisJonathan M Davis Wrote: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)On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.Jonathan M Davis Wrote:Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.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
Jonathan M Davis, el 19 de noviembre a las 13:24 me escribiste:On Friday 19 November 2010 12:39:20 Leandro Lucarella wrote: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."Sean Kelly, el 19 de noviembre a las 14:59 me escribiste: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 Wrote: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)On Friday, November 19, 2010 11:37:16 Sean Kelly wrote:This should work: void func(string x = __FILE__, T...)(T args); D allows defaulted template arguments to occur before non-defaulted ones.Jonathan M Davis Wrote:Yes. The problem was that the function was a _variadic_ template. So, you couldn't have default arguments.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
Leandro Lucarella Wrote:Sean Kelly, el 19 de noviembre a las 14:59 me escribiste:void func(string x = __FILE__, T...)(T args) { writeln( x, ' ', args.length ); } void main() { func!("hi")(); } prints: hi 0This 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?
Nov 19 2010
On 19-nov-10, at 23:44, Sean Kelly wrote:Leandro Lucarella Wrote: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 :(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.
Nov 20 2010
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
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
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
On 2010-11-21 01:23, Jonathan M Davis wrote:On Saturday 20 November 2010 08:03:52 Jacob Carlborg wrote: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.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.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
Jacob Carlborg wrote:On 2010-11-21 01:23, Jonathan M Davis wrote:This is possible, but too surprising: assertExcThrown!Exception = { func(param1, param2); }; with lazy: assertExcThrown!Exception = func(param1, param2);On Saturday 20 November 2010 08:03:52 Jacob Carlborg wrote: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); }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)`));
Nov 21 2010
On 2010-11-21 17:20, Lutger Blijdestijn wrote:Jacob Carlborg wrote:There's also the operator overload abuse which overloads opIn: assertExcThrown!Exception in { func(param1, param2); }; -- /Jacob CarlborgOn 2010-11-21 01:23, Jonathan M Davis wrote:This is possible, but too surprising: assertExcThrown!Exception = { func(param1, param2); }; with lazy: assertExcThrown!Exception = func(param1, param2);On Saturday 20 November 2010 08:03:52 Jacob Carlborg wrote: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); }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)`));
Nov 21 2010
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
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
On 2010-11-21 02:34, Jonathan M Davis wrote:On Saturday 20 November 2010 16:23:32 Jonathan M Davis wrote: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?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 DavisThis 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
On Sunday 21 November 2010 04:19:51 Jacob Carlborg wrote:On 2010-11-21 02:34, Jonathan M Davis wrote: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 DavisOn Saturday 20 November 2010 16:23:32 Jonathan M Davis wrote: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?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 DavisThis 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)); }
Nov 21 2010
On 2010-11-21 14:06, Jonathan M Davis wrote:On Sunday 21 November 2010 04:19:51 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. -- /Jacob CarlborgOn 2010-11-21 02:34, Jonathan M Davis wrote: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 DavisOn Saturday 20 November 2010 16:23:32 Jonathan M Davis wrote: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?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 DavisThis 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)); }
Nov 21 2010
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
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
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 functionsNot 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 unittestsI'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
Jonathan M Davis wrote:On Saturday 20 November 2010 10:16:55 Lutger Blijdestijn wrote:I know of std.exception.enforceEx, and catch(Exception ex) is also regularly used in examples.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.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. ...- 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 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 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).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.- 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.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
On Sunday 21 November 2010 08:11:06 Lutger Blijdestijn wrote:Jonathan M Davis wrote:Then, I'll change it to assertExThrown.On Saturday 20 November 2010 10:16:55 Lutger Blijdestijn wrote:I know of std.exception.enforceEx, and catch(Exception ex) is also regularly used in examples.zI'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.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.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.- 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'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).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 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).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.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.- 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!()).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 DavisAs 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