Why unit testing does not work in this D program?

Why does unit testing work for program 1, but not for program 2 below?

Program 1

import std.stdio; unittest { assert(false); } void main() { writeln("Hello D-World!"); } 

Program 2

 module winmain; import core.sys.windows.windows; unittest { assert(false); } extern (Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { return 0; } 

Both programs were compiled with the -unittest option (by running dmd -unittest <program.d> ). At startup, program 1 displays a unit test failure, but program 2 does not. What am I missing?

Update : reformulated question and added working example.

Update 2 : also compiled with dmd -debug -unittest <program.d> , with similar results.

+5
source share
1 answer

The answer is simple: there is only one in the program, unittests actually runs at run time in the second program, this is not because the unit test function is never called, since declaring your own WinMain (or even extern (C) main) bypasses initialization and configuration runtimes that usually execute automatically before it calls your main D code, which runs mostly C.

Open your dmd inbox and go to dmd2 / src / druntime / src / rt / dmain2.d. Find the _d_run_main () function.

When the D program starts with the regular D main, the compiler inserts the main C, which calls _d_run_main (). This function, as you can see, looking at the source, does a bunch of things:

  • it initializes floating point hardware in D mode expects
  • it formats command line arguments to D strings
  • it initializes the runtime
  • he runs unit tests <--- very important to you!
  • it runs the main D wrapped in a try / catch block to handle default exceptions
  • it completes the runtime
  • it clears the output and returns

Yes, on line 399 (the version that I have may be slightly different in your version of the druntime source), you will see the following lines:

  if (rt_init() && runModuleUnitTests()) tryExec({ result = mainFunc(args); }); 

Yup, unit tests run separately from rt_init (also known as Runtime.initialize). The way the -unittest compiler works is simply not to compile unittest functions, so runModuleUnitTests sees a bunch of null tests that it skips. That way you can call the function in your custom main without worrying about the compiler.

Since you have a regular core and runModuleUnitTests (defined in core.runtime btw) is not called, unit tests never happen. They are called before D main, but still inside c main or Winmain.

My recommendation is to avoid using WinMain in D, instead preferring to write regular D networks. You can get the arguments passed to WinMain with API functions like GetCommandLineW and GetModuleHandle . ( nCmdShow rarely used anyway, and I think hPrevInstance remained the same of 16-bit days, so I doubt that you took care of them anyway!)

The presence of WinMain also signals to the linker that you are writing a GUI program, and therefore must use the Windows subsystem - you are not getting a console. You can do this explicitly by passing -L/SUBSYSTEM:WINDOWS:5.0 to dmd when compiling 32 bits on Windows. (The / SUBSYSTEM argument is one of the optlink switches.) On Windows 64, I’m not sure, but it’s probably, if not identical, check the Microsoft linker docs to select the subsystem, I’m sure it is.

Between this linker linker and the two API calls, you no longer need WinMain to retrieve the arguments, so it saves you from overriding the _d_run_main execution _d_run_main .

If you still want to use it, there are two options: just call _d_run_main - see the source code for the signature that it expects. It takes a pointer to the main function so you can reuse it all. Or you can import core.runtime; and call Runtime.initialize(); runModuleUnitTests(); your main here... Runtime.terminate(); Runtime.initialize(); runModuleUnitTests(); your main here... Runtime.terminate(); by yourself. Remember to also check the return values ​​and handle the exceptions! You need to do this in the correct order and handle errors correctly or you will see failures.

All this applies if you write your own extern(C) main , as well as your own WinMain .

Again, you are probably better off avoiding this and just writing the normal core D function, and the linker switch disables the console in your gui application.

+15
source

Source: https://habr.com/ru/post/1209604/


All Articles