W ramach testów jednostkowych chcę zapewnić pokrycie kodu testów. Celem jest umieszczenie gdzieś w kodzie makr, takich jak REQUIRE_TEST
, i sprawdzenie, czy wszystkie zostały wywołane.Znajdź niezrealizowane wiersze kodu C++
void foo(bool b) {
if (b) {
REQUIRE_TEST
...
} else {
REQUIRE_TEST
...
}
}
void main() {
foo(true);
output_all_missed_REQUIRE_macros();
}
Najlepiej, gdyby dane wyjściowe zawierały plik źródłowy i linię makra.
Mój początkowy pomysł był aby mieć makra tworzyć obiekty statyczne, które będą rejestrować się w pewnym mapie, a następnie sprawdzić, czy wszystkie z nich były nazywane
#define REQUIRE_TEST \
do { \
static ___RequiredTest requiredTest(__func__, __FILE__, __LINE__);\
(void)requiredTest;\
___RequiredTest::increaseCounter(__func__, __FILE__, __LINE__);\
} while(false)
ale obiekt statyczne są tylko tworzone, gdy kod jest nazywany pierwszy raz. Mapa zawiera tylko funkcje, które są również zliczane w następnej linii - brakuje makr REQUIRE_TEST. __attribute__((used))
jest w tym przypadku ignorowany.
gcc ma ładny atrybut __attribute__((constructor))
, ale widocznie zdecyduje się go ignorować, gdy umieszczone tutaj (po kodzie zamiast obiektu statycznego)
struct teststruct { \
__attribute__((constructor)) static void bla() {\
___RequiredTest::register(__func__, __FILE__, __LINE__); \
} \
};\
jak również dla
[]() __attribute__((constructor)) { \
___RequiredTest::register(__func__, __FILE__, __LINE__); \
};\
Jedynym obecnie mogę myśleć teraz: a) ręcznie (lub za pomocą skryptu) analizować kod poza zwykłą kompilacją (uargh) lub b) używać makra __COUNTER__
do liczenia makr - ale wtedy nie wiedziałbym, które konkretne makra REQUIRE_TEST były nie calle d ... (i wszystko zepsuje się, jeśli ktoś inny zdecyduje się również użyć makra __COUNTER__
...)
Czy są jakieś przyzwoite rozwiązania tego problemu? czego mi brakuje? To byłoby miło mieć makro, które dołącza bieżącą linię i złożyć więc niektóre zmienna preprocesor kiedy to nazywa - ale to nie jest możliwe, prawda? Czy istnieją inne sposoby zarejestrowania czegoś, co ma być wykonane przed , wykonanym przed
main()
, które można wykonać w treści funkcji?
Badanie ostrzeżeń i opcji wiersza poleceń dla kompilatora. Wiele kompilatorów ma możliwość identyfikowania "martwego kodu". –
TL; DR; Dlaczego nie używasz wtyczki kodującej profilowanie zasięgu np. ['Gcov'] (https://gc.gnu.org/onlinedocs/gcc/Gcov.html) i analizujesz wyniki, np. przy użyciu ['lcov'] (http://ltp.sourceforge.net/coverage/lcov.php)? To niekoniecznie musi być zatwierdzone dla _dead code_, jeśli nie zostanie wykonane z twoich scenariuszy testowych. Narzędzie do statycznej analizy kodu może lepiej służyć do wyszukiwania takich martwych rzeczy. –
@ πάνταῥεῖ 'gcov' jest bardzo szczegółowy - ale także w częściach kodu, w których tak naprawdę mnie to nie obchodzi. Umieszczenie znaczników takich jak "REQUIRE_TEST" pozwoli mi określić, które części mnie interesują. Byłaby to także inna zależność dla każdego, do kogo wysyłam moją bibliotekę - i wydaje mi się, że C++ jest w stanie wykonywać to, co chcę ... Gdyby któraś z moich prób zadziałała, miałbym wszystko, czego chciałem. Może coś takiego jak 'gcov' będzie jedynym rozwiązaniem w końcu - ale chciałbym wiedzieć, że to, co próbuję, nie jest możliwe przed" rezygnacją "i dodaniem innej zależności ... – example