[phobos] Silent failure of std.container unittests

Walter Bright walter at digitalmars.com
Tue Jul 13 23:06:50 PDT 2010



Sean Kelly wrote:
> Walter's change fixed the issue where non-throwing asserts weren't triggering a unittest failure, but it also reverted my change to trap unhandled exceptions, set a "fail" flag, and continue with the next test.  I'd fix this, but what I'm confused about is why the support-non-throwing-asserts change was re-added in the first place.  So instead I'd like to ask what the eventual/intended/desired/whatever behavior is for unit tests so I can work towards that.  I'd like throwing asserts and for each unittest in a module to be run separately so recovery from an AssertError (which I'd like for unit tests) is at the granularity of the unittest block, not the module.  The best I can do without a compiler change is module-level granularity like we have now, but asserts could all throw again, etc.  Should this be an intermediate step?  Should we keep non-throwing asserts?  Halp plzz.
>
>   

Perhaps what will help is examining the output of the compiler. For the 
program:

import std.c.stdio;

unittest
{
    int x = printf("inside unittest\n");
    assert(x == 100000);
}

void main(string args[])
{
    printf("inside main\n");
}

The output is:

_TEXT    segment dword use32 public 'CODE'    ;size is 0
_TEXT    ends
_DATA    segment para use32 public 'DATA'    ;size is 64
_DATA    ends
CONST    segment para use32 public 'CONST'    ;size is 0
CONST    ends
_BSS    segment para use32 public 'BSS'    ;size is 0
_BSS    ends
FLAT    group   
    extrn    _D3foo11__unittest1FZv
includelib phobos.lib
    extrn    _main
    extrn    __acrtused_con
    extrn    __Dmain
    extrn    __D3foo9__modtestFZv
FMB    segment dword use32 public 'DATA'    ;size is 0
FMB    ends
FM    segment dword use32 public 'DATA'    ;size is 4
FM    ends
FME    segment dword use32 public 'DATA'    ;size is 0
FME    ends
    extrn    _D3foo15__unittest_failFiZv
    extrn    __d_unittestm
    extrn    _printf

    public    _D3foo12__ModuleInfoZ
_D3foo11__unittest1FZv    COMDAT flags=x0 attr=x0 align=x0
__Dmain    COMDAT flags=x0 attr=x0 align=x0
__D3foo9__modtestFZv    COMDAT flags=x0 attr=x0 align=x0
_D3foo15__unittest_failFiZv    COMDAT flags=x0 attr=x0 align=x0

_TEXT    segment
    assume    CS:_TEXT
_TEXT    ends
_DATA    segment
    db    069h,06eh,073h,069h,064h,065h,020h,075h    ;inside u
    db    06eh,069h,074h,074h,065h,073h,074h,00ah    ;nittest.
    db    000h,000h,000h,000h,069h,06eh,073h,069h    ;....insi
    db    064h,065h,020h,06dh,061h,069h,06eh,00ah    ;de main.
    db    000h,000h,000h,000h,000h,000h,000h,000h    ;........
    db    000h,000h,000h,000h,000h,000h,000h,000h    ;........
_D3foo12__ModuleInfoZ:
    db    004h,002h,000h,0ffffff80h,000h,000h,000h,000h    ;........
    dd    offset FLAT:__D3foo9__modtestFZv
    db    066h,06fh,06fh,000h    ;foo.
_DATA    ends
CONST    segment
CONST    ends
_BSS    segment
_BSS    ends
FMB    segment
FMB    ends
FM    segment
    dd    offset FLAT:_D3foo12__ModuleInfoZ
FM    ends
FME    segment
FME    ends
_D3foo11__unittest1FZv    comdat
    assume    CS:_D3foo11__unittest1FZv
L0:     mov    EAX,offset FLAT:_DATA
        push    EAX
        call    near ptr _printf
        cmp    EAX,0186A0h
        je    L1C
        mov    EAX,7
        call    near ptr _D3foo15__unittest_failFiZv
L1C:        add    ESP,4
        ret
_D3foo11__unittest1FZv    ends
__Dmain    comdat
    assume    CS:__Dmain
L0:     mov    EAX,offset FLAT:_DATA[014h]
        push    EAX
        call    near ptr _printf
        xor    EAX,EAX
        add    ESP,4
        ret
__Dmain    ends
__D3foo9__modtestFZv    comdat
    assume    CS:__D3foo9__modtestFZv
L0:     call    near ptr _D3foo11__unittest1FZv
        ret
__D3foo9__modtestFZv    ends
_D3foo15__unittest_failFiZv    comdat
    assume    CS:_D3foo15__unittest_failFiZv
L0:     enter    4,0
        push    EAX
        mov    ECX,offset FLAT:_D3foo12__ModuleInfoZ
        push    ECX
        call    near ptr __d_unittestm
        add    ESP,8
        leave
        ret
_D3foo15__unittest_failFiZv    ends
    end

What is happening is that the compiler inserts the address of 
__D3foo9__modtestFZv into the ModuleInfo record. The druntime calls this 
function. This function, in turn, runs the unittest code for that 
module. When a unittest fails, the function 
__d_unittestm(&__ModuleInfoZ, __LINE__) is called. This function is in 
the druntime.

Note that the compiler generated code does not throw any exceptions. 
What happens when __d_unittestm() is called is ENTIRELY up to druntime, 
which can be:

1. nothing
2. print a message
3. abort
4. throw an exception chosen by druntime
5. whatever druntime wants to

The important thing to note is that this is up to druntime, not the 
compiler. The part that is up to the compiler is the granularity of the 
unittests, which is set at the module, not the individual unittest blocks.



More information about the phobos mailing list