Dan*_*tor 6 unit-testing d visual-d
为什么单元测试适用于程序1,但不适用于下面的程序2?
计划1
import std.stdio;
unittest
{
assert(false);
}
void main()
{
writeln("Hello D-World!");
}
Run Code Online (Sandbox Code Playgroud)
计划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;
}
Run Code Online (Sandbox Code Playgroud)
两个程序都使用-unittest选项编译(通过运行dmd -unittest <program.d>).运行时,程序1显示单元测试失败,但程序2不显示.我错过了什么?
更新:重新制定的问题和增加的工作实例.
更新2:也编译dmd -debug -unittest <program.d>,具有类似的结果.
Ada*_*ppe 15
答案有点简单:在程序1中,单元测试实际上是由程序二中的运行时运行的,它们不是因为单元测试函数从未被调用,因为声明自己的WinMain(甚至extern(C)main)绕过运行时初始化和设置,这通常是在调用D main之前自动完成的 - 在C main中完成的代码.
打开你的dmd zip并转到dmd2/src/druntime/src/rt/dmain2.d文件.找到函数_d_run_main().
当启动具有常规D main的D程序时,编译器将插入一个调用_d_run_main()的C main.正如您可以看到的那样,通过源代码查看此函数可以执行以下操作:
是的,在第399行(我的版本,你的druntime源版本可能有点不同),你会看到这些行:
if (rt_init() && runModuleUnitTests())
tryExec({ result = mainFunc(args); });
Run Code Online (Sandbox Code Playgroud)
是的,单元测试与rt_init(也称为Runtime.initialize)分开运行.编译器-unittest开关的工作方式是它不会编译unittest函数,因此runModuleUnitTests会看到一堆空测试,它会跳过它.因此,您可以在自定义main中调用该函数,而无需担心编译器开关.
由于你有一个自定义main并且没有调用runModuleUnitTests(在core.runtimebtw中定义),所以单元测试永远不会发生.它们在D main 之前调用,但仍在c main或Win main之内.
我的建议是避免WinMain在D中使用,而是更喜欢编写常规D电源.您可以WinMain使用GetCommandLineW和等函数获取传递给API函数的参数GetModuleHandle.(nCmdShow很少使用,我认为hPrevInstance是16位日遗留下来的遗留物,所以我怀疑你无论如何都要关心它们!)
存在WinMain也向链接器发出信号,表示您正在编写GUI程序,因此应该使用Windows子系统 - 您没有控制台.您也可以通过-L/SUBSYSTEM:WINDOWS:5.0在Windows 32位上编译时传递给dmd来明确地执行此操作.(/ SUBSYSTEM参数是optlink的开关之一.)在Windows 64上,我不确定,但它可能是相似的,如果不相同 - 检查Microsoft链接器的文档选择子系统,我确定它在那里.
在该链接器开关和两个用于获取参数的API调用之间,您不再需要WinMain,因此它可以省去重新实现运行时_d_run_main函数自己执行的操作的麻烦.
如果你确实想要使用它,有两种选择:只需调用_d_run_main- 查看它所期望的签名的源代码.它需要一个指向main函数的指针,因此您可以重用所有这些.或者,你可以import core.runtime;打电话给Runtime.initialize(); runModuleUnitTests(); your main here... Runtime.terminate();自己.不要忘记检查返回值并处理异常!您需要按正确的顺序执行此操作并正确处理错误,否则您将看到崩溃.
如果你自己编写自己的extern(C) main,那么这一切都适用WinMain.
尽管如此,你可能最好避免它,只是编写一个常规的D main函数,使用链接器开关来关闭你的gui应用程序上的控制台.