Dzięki testom pokrycia dowiesz się, które linie Twojego kodu zostały wykonane i ile razy były wykonywane. Możesz w ten sposób wykryć martwe fragmenty kodu, dowiedzieć się, gdzie warto pomyśleć nad optymalizacją, oraz sprawdzić, w jakim stopniu Twoje testy jednostkowe pokrywają kod.
Testy pokrycia w 3 krokach
Załóżmy, że mamy program program.cpp.
Żeby zrobić testy pokrycia kodu, musimy wykonać trzy kroki:
> g++ -fprofile-arcs -ftest-coverage -o program program.cpp > ./program > gcov program.cpp
- Najpierw kompilujemy program z flagami -fprofile-arcs oraz -ftest-coverage, dzięki czemu program zostaje wzbogacony o instrukcje diagnozujące jego działanie.
- Następnie uruchamiamy program – wtedy zostają wygenerowane jego statystyki.
- Na koniec, odpalamy narzędzie gcov, które wygeneruje dla nas dwa raporty:
- podstawowy (podsumowanie testu) – na standardowe wyjście
- szczegółowy (które linie ile razy wykonano) – do pliku gcov
Dla każdego pliku źródłowego automatycznie generowany jest osobny raport (osobny plik). My mamy tylko jeden plik, wiec powstanie jeden raport – program.cpp.gcov. Zobaczmy, jak to wszystko działa na konkretnym przykładzie.
Szybki przykład
Mamy prosty programik liczący silnię:
// program.cpp long int factorial(unsigned short n) { if (n == 0) return 0; long int fact = 1; for (int i = 2; i <= n; i++) fact *= i; return fact; } int main() { return factorial(10); }
Wykonujemy 3 kroki do testów pokrycia:
> g++ -fprofile-arcs -ftest-coverage -o program program.cpp > ./program > gcov program.cpp
W wyniku tego, na standardowe wyjście dostajemy krótkie podsumowanie:
File 'program.cpp' Lines executed:88.89% of 9 program.cpp:creating 'program.cpp.gcov'
Mówi ono, że moduł program.cpp liczy 9 linii kodu, z czego 88.89% zostało wykonane. Mówi też, że raport został zapisany do pliku program.cpp.gcov. Zobaczmy więc, co on zawiera:
wywołania:linia:kod programu 1: 1:long int factorial(unsigned short n) -: 2:{ 1: 3: if (n == 0) #####: 4: return 0; -: 5: 1: 6: long int fact = 1; 10: 7: for (int i = 2; i <= n; i++) 9: 8: fact *= i; -: 9: 1: 10: return fact; -: 11:} -: 12: 1: 13:int main() -: 14:{ 1: 15: return factorial(10); -: 16:}
Pierwsza kolumna przedstawia liczbę wywołań, druga – nr linii.
Co nam mówi ten raport? Ano mówi na przykład, że funkcja factorial została wywołana 1 raz. Widać też, że pętla for wykonana została 9 razy, oraz że warunek if (n == 0) nigdy nie został spełniony. W ten sposób możemy sprawdzić, czy program wykonuje się zgodnie z naszymi założeniami!
Oznaczenia w kolumnie wywołania:
- ##### – linia nigdy nie wykonana
- “-” – linia nie zawiera kodu wykonywalnego
- liczba – liczba wykonań linii kodu
Przydatne flagi gcov:
- -help – bardzo krótka lista flag narzędzia gcov. Przydatne!
- -n – nie generuje plików gcov. Przydatne do sprawdzania pokrycia kodu testami, kiedy interesuje nas tylko ogólny procent pokrycia, a nie szczegóły która linia ile razy została wykonana
- -b – procentowe statystyki rozgałęzień kodu – do każdej instrukcji warunkowej dodana zostanie informacja, jaki procent testów wyszedł na true, jaki na false
- -b -c – liczbowe zamiast procentowe statystyki rozgałęzień kodu (ile razy wyszło true, ile false)
- -f – podsumowanie dla każdej funkcji – ile procent linii zostało wykonane w danej funkcji. Na standardowe wyjście, nie do pliku
Podsumowanie
Korzystając z narzędzia gcov jesteś w stanie dokładnie prześledzić wykonanie swojego programu. Możesz odnaleść martwe bloki kodu oraz zidentyfikować fragmenty intensywnie przetwarzane, którym optymalizacja przyniesie największą korzyść. Możesz też sprawdzić, w jakim stopniu testy jednostkowe pokrywają Twój kod.
Następnym razem pokażę na prościutkim przykładzie, jak taki unit test coverage można zrobić.