Code coverage – pokrycie kodu
Transkrypt
Code coverage – pokrycie kodu
Piotr Dwieczkowski Code coverage Mierzenie pokrycia kodu, teoria oraz praktyka w C/C++ Plan Co to jest pokrycie kodu? Możliwe sposoby wykorzystania Rodzaje statystyk Wady i zalety mierzenia porycia kodu Narzędzia: gcov, lcov Bardzo prosty przykład Bardziej skomplikowane – CMAKE + gcov + lcov Alternatywy Co to jest pokrycie kodu? Miara wykorzystywana przy testowaniu oprogramowania Opisuje w jakim stopieniu program został sprawdzony przez testy Zazwyczaj wykorzystywane przy określaniu ‘skuteczności’ testów jednostkowych Dla większości języków istnieją zestawy narzędzi do testowania/pomiaru pokrycia Pokrycie możemy mierzyd według różnych kryteriów Pokrycie linii kodu ang. statement/line coverage Dla każdej linii kodu – czy instrukcje w danej linii zostały uruchomione? Zależne od struktury tekstowej kodu, np. : if( 1 == 0 ) printf(“hello world!”); Ta linia zawsze zostanie oznaczona jako uruchomiona Nie da nam to żadnej informacji o pokryciu printf Mało nam mówi o pętlach i instrukcjach warunkowcyh Pokrycie funkcji ang. function coverage Dla każdej funkcji – czy została wykonana? Pozwala szybko znaleźd funkcje, które nie są przetestowane Lub np. nie są już używane Może byd przydatne przy prostym profilowaniu jeśli zbieramy liczbę wywołao w tym wypadku najlepiej zbierad czas spędzony w funkcji (gprof) Pokrycie gałęzi ang. branch/decision coverage Wymaga analizy struktury kodu Mierzy czy instrukcje warunkowe wykonały obie ścieżki kodu (także pętle) Dokładniej – mierzy czy wartośd logiczna warunku została wyliczona podczas uruchomienia jako prawda i fałsz Nie bierze pod uwagę w jaki sposób została ona wyliczona (ang. condition coverage) Pokrycie ścieżek ang. path coverage Mierzy pokrycie wszystkich możliwych ścieżek wykonania funkcji Oczywiście pętle to utrudniają Możemy wprowadzid dodatkowe kryteria Zamiast nieskooczonej liczby ścieżek pętla dodaje dwie: Program nie wszedł do pętli Program wszedł do pętli (i uruchomił ją dowolną liczbę razy) Inne kryteria Race coverage Condition coverage (Multiple condition coverage) Loop coverage Data flow coverage Relational operator coverage Weak mutation coverage … Pokrycie kodu - wady, zalety Dobry sposób oceniania skuteczności testów Tworzenie testów dla pokrycia wymusza dodatkową analizę kodu Dużo (raczej łatwych w obsłudze) narzędzi Nie istnieje idealna miara pokrycia Nie można w 100% wierzyd, że wszystko zostało sprawdzone Wykonianie i analiza części z opisanych kryteriów może zająd dużo czasu Z drugiej strony – nie zawsze opłaca się dążyd do 100% pokrycia Prawo malejących przychodów (ang. dimnishing returns) gcov Pozwala analizowad pokrycie linii kodu, wykonywanych funkcji oraz gałęzi Wchodzi w skład pakietu gcc, działa tylko z programami skompilowanymi przez gcc (wymaga specjalnych flag) Dla podanego pliku źródłowego nazwa.cpp generuje tekstowy plik nazwa.cpp.gcov zawierający informacje o pokryciu dla każdej linii Jest raczej nieprzyjemny w obsłudze – trudno go ręcznie używad przy nieco większych projektach Pliki .gcov (wygenerowane przez gcov –b) -: 0:Source:tmp.c -: 0:Graph:tmp.gcno -: 0:Data:tmp.gcda -: 0:Runs:1 -: 0:Programs:1 -: 1:#include <stdio.h> -: 2: -: 3:int main (void) function main called 1 returned 1 blocks executed 75% 1: 4:{ 1: 5: int i, total; -: 6: 1: 7: total = 0; -: 8: 11: 9: for (i = 0; i < 10; i++) branch 0 taken 91% (fallthrough) branch 1 taken 9% 10: 10: total += i; -: 11: 1: 12: if (total != 45) branch 0 taken 0% (fallthrough) branch 1 taken 100% #####:13: printf ("Failure\n"); call 0 never executed -: 14: else 1: 15: printf ("Success\n"); call 0 called 1 returned 100% 1: 16: return 0; -: 17:} Jak to działa? Podczas kompilacji podajemy do gcc odpowiednie flagi: –fprofile–arcs –ftest–coverage Gcc generuje pliki .gcno dla każdego pliku źródłowego Oprócz tego do programu jest dołączany kod, który sprawia, że: Zawierają one informacje o strukturze kodu Podczas działania zbierane są statystyki pokrycia Przed zakooczeniem programu dane są zapisywane w plikach .gcda (w tym katalogu, w którym jest plik .gcno) Każdemu plikowi źródłowemu odpowiada po jednym .gcda i .gcno (ale nie plikom nagłówkowym) Pomimo tego przy uruchomieniu gcov generuje pokrycie dla plików nagłówkowych Pliki .gcda i .gcno są binarne i są wykorzystywane przez np. gcov, gprof i inne programy z gcc Problemy Nie możemy ustalid gdzie gcc zapisuje plik .gcno i .gcda (zapisuje tam gdzie plik wyjściowy) gcov musi zostad uruchomiony z tego samego katalogu co gcc podczas kompilacji Można podpowiedzied gdzie gcov ma szukad plików lcov Front-end do gcov Pozwala na generowanie stron html z informacjami o pokryciu dla poszczególnych plików Dużo łatwiejszy w obsłudze – wystarczy podad katalog, a lcov znajdzie pliki .gcda i .gcno (także w podkatalogach) i dla każdego pliku źródłowego przeanalizuje pokrycie lcov zapisuje pokrycie dla całego katalogu w pojedynczym pliku tracefile.info genhtml zamienia plik .info w pliki html lcov – możliwości W jednym pliku .info mogą byd przechowywane informacje o różnych uruchomieniach Można przeglądad pokrycie według oddzielnie uruchomionych testów lcov umożliwia np. porównywanie uruchomieo dodatkowo: lcov został stworzony w celu analizy pokrycia kodu uruchomionego jądra Linux (w czasie rzeczywistym) gcov+lcov - tutorial Kompilacja z flagami gcc/g++ g++ –fprofile–arcs –ftest–coverage main.cpp –o main Zauważ utworzone pliki .gcno Uruchom program tak jak zwykle ./main Zauważ pliki .gcds Jeśli używasz bezpośrednio gcov gcov plik_zrodlowy.cpp (albo *.cpp) Dla każdego pliku źródłowego otrzymujemy plik .cpp.gcov Jeśli używasz lcov uruchom lcov (najlepiej w oddzielnym katalogu) i wygeneruj html mkdir report cd report lcov --capture --directory ../ --output-file main.info c++filt < main.info > main.info (czytelne nazwy np. funkcji) genhtml main.info Otwórz index.html w ulubionej przeglądarce Złożony przykład – CMAKE Należy dodad flagi kompilatora – CMAKE tego nie ułatwia (ze względu na przenośnośd) Mamy zmienną CMAKE_CXX_COMPILER_FLAGS Możemy ją zmieniad w zależności od wartości opcji Wystarczy zrobid to raz dla całego projektu (zmienid flagi przed dodawaniem podfolderów) Wykonujemy make bez pokrycia, potem z pokryciem Nic się nie dzieje – CMAKE nie wie, że gcc powinien wygenerowad pliki .gcno – nie szuka ich i nie wie, że należy przebudowad projekt Złożony przykład – Testowanie UnitTest++ – wybrany na podstawie analizy dyskusji na SO i kilku artykułów w sieci W łatwy sposób można tworzyd całe zestawy testów Można też używad CTest Może byd to zbyt duże narzędzie dla niewielkich projektów Nie zdążyłem przetestowad tego rozwiązania Złożony przykład – Generowanie pokrycia Próby dodania gcov jako TARGET w Makefile żeby można było uruchomid make coverage taki TARGET można zdefiniowad jako obowiązkowy krok po testach zarówno add_custom_target jak i add_custom_command nie spełniają wszystkich wymagao trudno dostad się do plikó w.gcno powinny byd w katalogu CMakeFiles/target_name.dir/ ale nie jest to zdefiniowane w żadnej stałej co oznacza, że może się zmienid (znowu ze względu na przenośnośd) Generowanie pokrycia – rozwiązanie lcov służy właśnie do tego (i np. omija generację plików .gcov) Innym rozwiązaniem może byd CTest (w dokumentacji jest wspomniana opcja generowania pokrycia) Alternatywy Działające z gcc: ggcov Trucov gcc pod cygwin Visual Studio 2008/10 Testowanie/pokrycie wbudowane wersji Linux/Windows – BullseyeCoverage CTC++ TestCoverage Urządzenia wbudowane Jest to możliwe, ale nie ma dużo materiałów O gcov+arm jest trochę informacji na grupach dyskusyjnych Jest też praca “Gcov on an embedded systems Skupia się na architekturze PowerPC Pytania? Źródła gcov lcov http://www.verifysoft.com/en_ctcpp.html Różne rodzaje pokrycia z przykładami http://www.bullseye.com/ CTC++ TestCoverage http://code.google.com/p/trucov/ BullseyeCoverage http://ltp.sourceforge.net/coverage/lcov.php Trucov man gcov http://gcc.gnu.org/onlinedocs/gcc/Gcov.html http://www.javaranch.com/journal/2004/01/IntroToCodeCoverage.html http://www.bullseye.com/coverage.html Gcov on an embedded system (H.Blasum, F.Görgen, J.Urban) http://sysrun.haifa.il.ibm.com/hrl/greps2007/papers/gcov-on-an-embedded-system.pdf