Unit tests, asserts and contradictions in the spec

John Colvin john.loughran.colvin at gmail.com
Thu Feb 7 16:49:38 UTC 2019


On Thursday, 7 February 2019 at 15:43:34 UTC, H. S. Teoh wrote:
> On Thu, Feb 07, 2019 at 06:51:23AM +0000, Paul Backus via 
> Digitalmars-d wrote:
>> On Thursday, 7 February 2019 at 01:04:15 UTC, H. S. Teoh wrote:
> [...]
>> > Since the nested helper function is marked nothrow, a failed 
>> > assert throwing an AssertError or UnittestAssertError may 
>> > bypass the unittest's unwinding code (the compiler assumes 
>> > runTest never throws, so never installs stack unwinding code 
>> > around it), which means the scope(exit) never triggers and 
>> > the resource is leaked.
>> 
>> I guess the implication here is that assert shouldn't count as 
>> nothrow in unit tests? That won't help non-nested helper 
>> functions, of course, but they also won't throw 
>> UnittestAssertError (in this hypothetical), so that should be 
>> okay, from a safety perspective.
>> 
>> These don't seem like insurmountable problems, but the 
>> possibility that existing code might *rely* on AssertErrors 
>> from deep in the call stack being caught by the runtime is one 
>> I hadn't considered. Since there's no way to distinguish the 
>> legitimate uses of that "feature" from the actually-broken 
>> ones, it's hard to imagine a solution that fixes the latter 
>> without breaking the former.
>
> Yeah, this is why I said this was a can o' worms. There's 
> probably a solution lurking in there somewhere, but it's 
> complicated, has messy edge cases, and I'm not sure how likely 
> it will be adopted.  It's all not so bad when you follow the 
> "assert failure == immediately terminate program" paradigm 
> strictly; but once you step outside of that, all sorts of 
> problems await.
>
> Of course, on the flip side, unittests that acquire global / 
> external resources that need cleanup, etc., are IMNSHO a code 
> smell. The code should be structured in such a way that every 
> unittest is self-contained and does not affect global state, 
> and especially not external OS state. (There's a place for such 
> tests, but they're hardly *unittests* anymore.)  If the code 
> can't be tested that way, then IMO it's suffers from bad design 
> and needs to be refactored.
>
> If all unittests were written this way, then your proposed 
> solution would totally make sense.  Unfortunately, reality is a 
> lot messier than that.  :-(
>
>
> T

A fork-based unittest runner would solve some problems without 
having to restart the process (could be expensive startup) or 
have people re-write their tests to use a new type of assert.

The process is started, static constructors are run setting up 
anything needed, the process is then forked & the tests run in 
the fork until death from success or assert, somehow 
communicating the index of the last successful test to the runner 
(let's call that tests[i]). Then if i < test.length - 1 do 
another fork and start from tests[i + 2] to skip the one that 
failed.

There are probably corner cases where you wouldn't want this 
behavior, but I can't think of one off the top of my head.


More information about the Digitalmars-d mailing list