2014-12-20 86 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應用程序中的控制檯。

相關問題