为什么单元测试适用于程序1,但不适用于下面的程序2?
计划1
import std.stdio;
unittest
{
assert(false);
}
void main()
{
writeln("Hello D-World!");
}
计划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;
}
这两个程序都是使用-unittest
选项编译的(通过运行dmd -unittest <program.d>
)。运行时,程序1显示单元测试失败,但程序2不显示。我错过了什么?
更新:重新制定的问题&amp;增加了工作实例。
更新2 :也使用dmd -debug -unittest <program.d>
编译,结果相似。
答案 0 :(得分:15)
答案有点简单:在程序一中,单元测试实际上是由程序二中的运行时运行的,它们不是因为单元测试函数从未被调用,因为声明自己的WinMain(甚至是extern(C) )main)绕过运行时初始化和设置,这通常是在调用D主代码之前自动完成的 - 在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); });
是的,单元测试与rt_init(也称为Runtime.initialize)分开运行。编译器-unittest开关的工作方式是它不会编译unittest函数,因此runModuleUnitTests会看到一堆空测试,它会跳过它。因此,您可以在自定义main中调用该函数,而无需担心编译器切换。
由于你有一个自定义main并且没有调用runModuleUnitTests
(在core.runtime
btw中定义),所以单元测试永远不会发生。它们在 D main之前调用,但仍在 c main或 Win main中。
我的建议是避免在D中使用WinMain
,而是更喜欢编写常规D电源。您可以使用WinMain
和GetCommandLineW
等API函数获取传递给GetModuleHandle
的参数。 (nCmdShow
很少使用,我认为hPrevInstance
是16位日遗留下来的遗留物,所以我怀疑你无论如何都要关心它们!)
WinMain
的存在也向链接器发出信号,表明您正在编写GUI程序,因此应该使用Windows子系统 - 您没有控制台。您也可以通过在Windows 32位上编译时将-L/SUBSYSTEM:WINDOWS:5.0
传递给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应用程序上的控制台。