2014-12-20 49 views
5

为什么单元测试对程序1有效,但对下面的程序2没有影响?为什么单元测试在这个D程序中不起作用?

计划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不显示。我错过了什么?

更新:重新提出的问题&增加了工作示例。

更新2:也与dmd -debug -unittest <program.d>编译,具有类似的结果。

+0

你是否也在调试模式下编译? –

+0

是的,我也直接用'dmd'('dmd -unittest winmain.d')在Visual Studio外编译 –

+0

用'dmd -debug -unittest winmain.d'试试 –

回答

15

答案很简单:在程序一中,unittests实际上是由运行程序在程序2中运行的,它们并不是因为单元测试函数从未被调用过,因为声明了自己的WinMain(甚至是extern C)main)绕过运行时初始化和设置,通常在它调用在C main中完成的D主代码之前自动完成。

打开您的dmd zip并获取文件dmd2/src/druntime/src/rt/dmain2.d。找到函数_d_run_main()。

当启动一个具有常规D main的D程序时,编译器会插入一个调用_d_run_main()的C main。这个功能,你可以看到翻翻源,做了一堆东西:

  • 它初始化浮点硬件模式d预计
  • 它格式化的命令行参数为d弦
  • 它初始化运行时间
  • 它运行单元测试< < ---对你来说非常重要!
  • 它运行d主裹在try/catch块为默认异常处理
  • 它终止运行时
  • 它刷新输出并返回

是,在线399(的版本我有,可能是你的druntime的源代码版本稍有不同),你会看到这几行:

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

是的,单元测试是从rt_init(又称Runtime.initialize单独运行)。编译器-unittest开关工作的方式是仅编译unittest函数,因此runModuleUnitTests会看到一堆空测试,它会跳过它。因此,您可以在自定义主体中调用该函数,而不用担心编译器开关。

既然你有一个自定义的主,并没有调用runModuleUnitTests(定义在core.runtime btw),单元测试从未发生。他们之前叫D主,但仍在c main或主。

我的建议是避免在D中使用WinMain,而宁愿编写常规D电源。您可以使用API​​函数GetCommandLineWGetModuleHandle传递给WinMain的参数。 (nCmdShow很少使用无论如何,我认为hPrevInstance是从16位天遗留残留物,所以我怀疑你会关心他们呢!)

WinMain存在也预示着你正在写一个GUI链接程序,因此应该使用Windows子系统 - 你不能获得控制台。您也可以通过在Windows 32位编译时将-L/SUBSYSTEM:WINDOWS:5.0传递给dmd来明确地执行此操作。 (/ SUBSYSTEM参数是optlink的开关之一。)在Windows 64上,我不确定,但如果不相同,它可能类似 - 请检查Microsoft链接器的文档以选择子系统,我相信它在那里。

在该链接器开关和两个API调用来获取参数之间,您不再需要WinMain,所以它可以为您节省重新实现运行时的函数自身的功能的麻烦。

如果您确实想要使用它,有两种选择:只需拨打_d_run_main - 查看它期望的签名的源代码。它需要一个指向主函数的指针,所以你可以重用所有这些。或者,您可以import core.runtime;并自己拨打Runtime.initialize(); runModuleUnitTests(); your main here... Runtime.terminate();。不要忘记检查返回值并处理异常!您需要按照正确的顺序执行并正确处理错误,否则您将看到崩溃。

所有这一切同样适用,如果你正在编写自己的extern(C) main以及你自己的WinMain

虽然如此,你可能会更好地避免它,只需编写一个常规的D主函数,并使用链接器开关关闭gui应用程序中的控制台。

相关问题