[phobos] Silent failure of std.container unittests
Michel Fortin
michel.fortin at michelf.com
Sat Jul 17 05:17:09 PDT 2010
Le 2010-07-17 à 1:07, Walter Bright a écrit :
> Yes, but it's not hard. For me, the issue is making things for the user
> more complicated. D is already a very large language, and adding more
> and more stuff like this makes it larger, buggier, and less
> approachable. For example, nothing stands out to say what "unittest
> assert" does vs "assert".
I just want to point out that "unittest assert(x);" would just be a shortcut for "unittest { assert(x); }", which is quite similar to how you can omit the braces for conditional and loop statements in regular code. This is only a small shift from the earlier concept of unittest in D: instead of having unittest blocks scattered in your module, you have unittest statements (which can be blocks) scattered around. It's not much of a change, it's just being more permissive.
module abc;
unittest assert(sqrt(4) == 2); // single-statement unit test
unittest assert(sqrt(16) == 4);
As for nesting unittests, I agree that it might get harder to conceptualize how it works. But still, there isn't anything really 'special' about it, they have the same semantics as the module-level ones. It's better explained with a concrete example. Say a module includes those unit tests:
module abc;
unittest assert(sqrt(4) == 2);
unittest assert(sqrt(16) == 4);
unittest {
int[] result = makeSomeTable(399, 383, 927);
assert(result.length == 3); // guard against invalid state
unittest assert(result[0] == 281);
unittest assert(result[1] == 284);
unittest assert(result[2] == 283);
unittest {
int[] derivedResult = derive(result);
assert(derivedResult.length == 2); // guard against invalid state
unittest assert(derivedResult[0] == 3);
unittest assert(derivedResult[1] == -1);
}
// ... other tests reusing 'result'
}
How do you translate this to a module-level unittest function? First define this function in the runtime:
void __doUnitTest(void delegate() test) {
try { test(); }
catch (AssertionError e) { __unitTestLogFailure(e); }
}
Then replace each unittest statement or block with a call to the above function, passing the unittest code as the argument. The three module-level unittests I wrote above would translate to this module unittest function:
void __module_abc_unittests() {
__doUnitTest({ assert(sqrt(4) == 2); });
__doUnitTest({ assert(sqrt(16) == 4); });
__doUnitTest({
int[] result = makeSomeTable(399, 383, 927);
assert(result.length == 3); // guard against invalid state
__doUnitTest({ assert(result[0] == 281); });
__doUnitTest({ assert(result[1] == 284); });
__doUnitTest({ assert(result[2] == 283); });
__doUnitTest({
int[] derivedResult = derive(result);
assert(derivedResult.length == 2); // guard against invalid state
__doUnitTest({ assert(derivedResult[0] == 3); });
__doUnitTest({ assert(derivedResult[1] == -1); });
});
// ... other tests reusing 'result'
});
}
As other mentioned, what I do here with nested unit tests could easily be implemented by offering the user a new library function too. I just happen to think what I'm proposing here is more elegant.
--
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/
More information about the phobos
mailing list