2014-12-20 21 views
5

Perché i test delle unità funzionano per il programma 1, ma non per il programma 2 di seguito?Perché il test dell'unità non funziona in questo programma D?

Programma 1

import std.stdio; 

unittest 
{ 
    assert(false); 
} 

void main() 
{ 
    writeln("Hello D-World!"); 
} 

Programma 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; 
} 

Entrambi i programmi sono stati compilati con l'opzione -unittest (eseguendo dmd -unittest <program.d>). Durante l'esecuzione, il programma 1 visualizza l'errore del test dell'unità, ma il programma 2 no. Cosa mi manca?

Aggiornamento: domanda riformulata & aggiunto esempio di lavoro.

Aggiornamento 2: anche compilato con dmd -debug -unittest <program.d>, con risultati simili.

+0

hai compilato anche la modalità di debug? –

+0

sì, e ho compilato anche all'esterno di Visual Studio con 'dmd' direttamente (' dmd -unittest winmain.d') –

+0

prova con 'dmd -debug -unittest winmain.d' –

risposta

15

La risposta è piuttosto semplice: nel programma uno, le unittests sono effettivamente eseguite, dal runtime nel programma due, non lo sono perché la funzione di test unitario non viene mai chiamata, dal momento che dichiara il proprio WinMain (o anche extern (C) main) ignora l'inizializzazione e l'impostazione del runtime che di solito viene eseguita automaticamente prima di chiamare il codice principale D eseguito in una C main.

Aprire il file dmd zip e accedere al file dmd2/src/druntime/src/rt/dmain2.d. Trova la funzione _d_run_main().

Quando viene avviato un programma D con una normale D, il compilatore inserisce una C main che chiama _d_run_main(). Questa funzione, come si può vedere guardando attraverso la fonte, fa un sacco di cose:

  • inizializza l'hardware in virgola mobile al modo di D si aspetta
  • formatta gli argomenti della riga di comando in stringhe D
  • inizializza il runtime
  • esegue i test di unità < < --- super importante per voi!
  • si corre il principale D, avvolto in un blocco try/catch per la gestione delle eccezioni di default
  • termina il runtime
  • vampate di uscita e restituisce

Sì, sulla linea 399 (della versione ho, potrebbe essere un po 'diverso nella versione di fonte di druntime), vedrete queste righe:

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

Yup, unit test vengono eseguiti separatamente dai rt_init (noto anche come Runtime.initialize). Il modo in cui lo switch compilatore -unittest funziona è che semplicemente non compila le funzioni più semplici, quindi runModuleUnitTests vede un sacco di test nulli, che salta. In questo modo è possibile chiamare la funzione nel main personalizzato senza preoccuparsi dell'interruttore del compilatore.

Poiché si dispone di un main personalizzato e non si chiama runModuleUnitTests (definito in core.runtime btw), i test di unità non si verificano mai.Sono chiamati prima di D principale, ma ancora all'interno di c principale o Win principale.

La mia raccomandazione è di evitare l'uso di WinMain in D, preferendo invece scrivere normale rete D. È possibile ottenere gli argomenti passati a WinMain con funzioni API come GetCommandLineW e GetModuleHandle. (nCmdShow è usato raramente in ogni caso e penso hPrevInstance è legato avanzi dai giorni a 16 bit, quindi dubito che ci si cura di loro comunque!)

La presenza di WinMain segnala anche il linker che si sta scrivendo una GUI programma e quindi dovrebbe usare il sottosistema di Windows - non si ottiene una console. Puoi farlo anche esplicitamente passando a -L/SUBSYSTEM:WINDOWS:5.0 in dmd quando compili su Windows 32 bit. (L'argomento/SUBSYSTEM è uno degli switch di optlink.) Su Windows 64, non sono sicuro, ma probabilmente è simile se non identico - controlla i documenti del linker Microsoft per la scelta del sottosistema, sono sicuro che sia lì.

Tra lo switch del linker e le due chiamate API per recuperare gli argomenti, non è più necessario WinMain in modo da risparmiare la fatica di reimplementare ciò che la funzione _d_run_main del runtime esegue autonomamente.

Se si desidera utilizzarlo comunque, ci sono due opzioni: basta chiamare _d_run_main - guardare il codice sorgente per la firma che si aspetta. Serve un puntatore alla funzione principale in modo da poter riutilizzare tutto ciò. Oppure, è possibile import core.runtime; e chiamare il Runtime.initialize(); runModuleUnitTests(); your main here... Runtime.terminate(); da soli. Non dimenticare di controllare i valori di ritorno e gestire anche le eccezioni! Hai bisogno di farlo nell'ordine giusto e gestire gli errori correttamente o vedrai arresti anomali.

Tutto ciò vale anche se si scrive il proprio extern(C) main e il proprio WinMain.

Anche se, probabilmente, è meglio evitarlo e basta scrivere una normale funzione D principale, con l'interruttore linker per spegnere la console sulla tua app gui.

Problemi correlati