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.

_______________________________________________
phobos mailing list
[email protected]
http://lists.puremagic.com/mailman/listinfo/phobos

Reply via email to