On 01/31/2010 09:39 PM, Trip Volpe wrote:
I recently began porting an existing C++ project of mine (a 
compiler/interpreter for a dynamic language) to D. In the process I found that 
the built-in unit testing support, while an awesome concept, was a little bit 
sparse. In particular, assert() is fairly useless for unit tests, since it 
throws on a failure and prevents all subsequent tests from running.

Unfortunately, it's the only facility that seems to give you access to the 
current file and line number, very important bits of info for unit tests in a 
any non-trivial project. I was contemplating just abandoning D and sticking 
with C++ and Googletest, but after doing a bit of digging through the source, I 
found setAssertHandler() in core.exception. Perfect! I could use my own assert 
handler that just records the error and allows future asserts to still be 
reached. So I wrote it up and gave it a try, but pretty quickly started getting 
access violation errors.

I did a bit of searching, and found a bug report on the issue:

http://d.puremagic.com/issues/show_bug.cgi?id=3208

In the comments it's indicated that this is intentional, that the compiler 
expects the assertion handler to throw. So I guess I have a few questions:

1. Why is this the expected behavior? It seems to me that there are relatively 
few useful things you can do with a custom assert handler unless it is possible 
to refrain from throwing.

2. Is this going to be fixed any time soon? It seems fairly important to me; it 
really kills the value of built-in unit tests to be forced to choose between 
not having any line number information and being able to recognize only one 
assert failure per test run(!!!).

3. Are there any current workarounds for the problem? I could just use my own assertion 
function, but 1) I can't call it "assert" because that's a reserved word, and 
2) most importantly, it won't have any way of indicating in its diagnostic output which 
source line the failure occurred on. In C++, of course, I could use preprocessor macros 
to do that, but D omits a preprocessor (for very good reasons). Unfortunately, this seems 
like a case where D hasn't provided replacement functionality.

But maybe there's a sneaky fix I haven't thought of? I also found 
Runtime.moduleUnitTester() in core.runtime, which is very useful in itself, but 
provides an _almost_ useful improvement: I can catch the AssertErrors on a 
module-by-module basis, which would at least allow the unit tests for all 
modules to be run regardless of a failure in one of them. However, a single 
module could easily have dozens of tests in it, each of which could contain 
many individual asserts. Stopping the test early because one assert failed 
makes no sense.

It would be _almost_ acceptable to at least be sure of running all the tests in 
each module; is there any way to poke inside or otherwise override ModuleInfo's 
unitTest() function?


Thanks in advance for any advice or comments! D is a completely awesome 
language, but I found this issue a bit strange.


You can use line and file info with default arguments, this works (special case). I just hack around the default unittest system, something like this:

void test(string testName)(void delegate () testClosure,
                           int line = __LINE__,
                           string file = __FILE__)
{
    // register test start, run testClosure, end test
}

void expectEquals(A, B)(A a, B b,
                        int line = __LINE__,
                        string file = __FILE__)
{
    // record outcome of assertion
}

unittest
{
    test!"truth of it"( {
        expectEquals(true, true);
    } );
}

or slightly abusive:

unittest
{
    test!"truth of it" =  {
        expectEquals(true, true);
    };
}

Reply via email to