Come parte del test dell'unità, voglio garantire la copertura del codice dei test. L'obiettivo è di posizionare qualcosa come i macro REQUIRE_TEST
da qualche parte nel codice e controllare se tutti questi sono stati chiamati.Trova righe non eseguite del codice C++
void foo(bool b) {
if (b) {
REQUIRE_TEST
...
} else {
REQUIRE_TEST
...
}
}
void main() {
foo(true);
output_all_missed_REQUIRE_macros();
}
Idealmente l'uscita dovrebbe includere sorgente del file e la linea della macro.
La mia idea iniziale era di avere le macro creano oggetti statici che si registrerebbe in qualche mappa e poi verificare se tutti questi sono stati chiamati
#define REQUIRE_TEST \
do { \
static ___RequiredTest requiredTest(__func__, __FILE__, __LINE__);\
(void)requiredTest;\
___RequiredTest::increaseCounter(__func__, __FILE__, __LINE__);\
} while(false)
ma l'oggetto statico vengono creati solo quando il codice viene chiamato la prima volta. Quindi la mappa contiene solo funzioni che vengono conteggiate anche nella riga successiva - mancano i macro REQUIRE_TEST mancanti. lo __attribute__((used))
viene ignorato in questo caso.
Il gcc ha una bella attributo __attribute__((constructor))
, ma apparentemente sceglie di ignorarlo quando posto qui (codice seguente invece dell'oggetto statico)
struct teststruct { \
__attribute__((constructor)) static void bla() {\
___RequiredTest::register(__func__, __FILE__, __LINE__); \
} \
};\
nonché per
[]() __attribute__((constructor)) { \
___RequiredTest::register(__func__, __FILE__, __LINE__); \
};\
L'unica way out posso ora pensare a) manualmente (o tramite script) analizzare il codice al di fuori della normale compilation (uargh) o b) usare la macro __COUNTER__
per contare le macro - ma poi non saprei quali specifiche macro REQUIRE_TEST sono state non calle d ... (e si rompe tutto, se qualcun altro decide di utilizzare la macro __COUNTER__
così ...)
Esistono soluzioni decenti a questo problema? Cosa mi manca? E ' sarebbe bello avere una macro che aggiunge la riga corrente e il file quindi qualche variabile del preprocessore ogni volta che viene chiamata - ma non è possibile , giusto? Esistono altri modi per registrare qualcosa come eseguito prima dello
main()
che può essere eseguito all'interno di un corpo di una funzione?
Ricerca gli avvisi e le opzioni della riga di comando per il compilatore. Molti compilatori hanno la capacità di identificare "codice morto". –
TL; DR; Perché non usi qualche codice di profilazione del codice di copertura come ['gcov'] (https://gcc.gnu.org/onlinedocs/gcc/Gcov.html) e analizzi i risultati, ad es. usando ['lcov'] (http://ltp.sourceforge.net/coverage/lcov.php)? Questo potrebbe non necessariamente approvare veramente il codice _dead, se non viene eseguito dagli scenari di test. Uno strumento di analisi del codice statico potrebbe servire meglio a trovare cose del genere. –
@ πάνταῥεῖ 'gcov' è molto dettagliato - ma anche in parti di codice in cui non mi interessa davvero. Posizionare marcatori come "REQUIRE_TEST" mi permetterebbe di specificare quali parti mi interessano. Sarebbe anche un'altra dipendenza per tutti quelli a cui spedisco la mia libreria - e sembra che C++ sia _almost_ in grado di fare ciò che voglio ... Se uno qualsiasi dei miei tentativi funzionasse avrei tutto ciò che ho sempre desiderato. Forse qualcosa come 'gcov' sarà l'unica soluzione alla fine - ma mi piacerebbe sapere che ciò che sto cercando non è possibile prima di" rinunciare "e aggiungere un'altra dipendenza ... – example