Одним из популярных средств измерения покрытия кода тестами служит утилита gcov. Данная утилита используется совместно с gcc для анализа программ с целью достижения наибольшей производительности и эффективности кода, и выявления не покрываемых тестами участков кода. Предоставляемая статистика утилитой gcov, позволяет ответить на следующие вопросы:
- Как часто (количество прохождений) выполняется каждая строка кода.
- Какие строки кода не выполняются.
- Какой процент строк кода покрыт тестами для каждого файла.
- Какой процент ветвлений (условий) покрыт тестами для каждого файла.
Использование утилиты gcov для анализа кода, требует наличия при компиляции флагов -fprofile-arcs -ftest-coverage, которые сообщают компилятору о необходимости дополнительной информацию требуемой для gcov (граф прохождения программы), а также о необходимости включить дополнительные инструкции кода непосредственно в объектные файлы, также необходимых для gcov.
В общем и простейшем случае процедура запуска компиляции следующая:
$ g++ -fprofile-arcs -ftest-coverage main.cpp
Рассмотрим использование gcov на чуть более сложном примере. Пусть в нашем распоряжении имеется тестируемый проект, который состоит:
- main.cpp – в котором расположена инструкция запуска тестов.
- gtest/ – каталог, в котором находится исходный код тестовой среды Google Test.
- SRM204/ – каталог, в котором находятся заголовки с решениями и тестами задач.
В таком случае процесс запуска компиляции проекта с последующей возможностью анализа покрытия кода тестами будет выглядеть следующим образом:
$ mkdir Debug && cd Debug/ $ g++ -I.. -I../SRM204 -c -fprofile-arcs -ftest-coverage -o "main.o" "../main.cpp" $ g++ -I.. -I../SRM204 -c -fprofile-arcs -ftest-coverage -o "gtest-all.o" "../gtest/gtest-all.cc" $ g++ -fprofile-arcs -ftest-coverage -o "TopCoder.exe" ./gtest-all.o ./main.o
После успешной компиляции необходимо запустить полученный исполняемый файл, в ходе исполнения которого будут сгенерированы необходимые утилите gcov вспомогательные для последующего анализа:
$ ./TopCoder.exe
Далее, используя утилиту gcov, получим общие сведения о покрытии кода тестами, а также отчеты по покрытию кода для каждого файла проекта в отдельности, которые имеют расширение *.gcov:
$ gcov -b ../main.cpp
Фрагмент сведений выводимых, утилитой gcov в ходе формирования отчетов по каждому используемому файлу (с исходным кодом) в ходе исполнения работы тестируемой программы:
File '../SRM204/Apothecary.h' Lines executed:100.00% of 67 Branches executed:62.16% of 222 Taken at least once:36.94% of 222 Calls executed:49.14% of 175 ../SRM204/Apothecary.h:creating 'Apothecary.h.gcov'
Из представленного фрагмента, очевидно, что покрытие кода, в данном файле, составляет 100%, при этом вероятность прохождения всех веток составляет 62%.
Не стоит удивляться, если в ходе «профайлинга» будет представлена информация о покрытии кода подключаемых заголовков стандартной библиотеки C/C++:
$ ls . | grep ".gcov$" ... ... ... new.gcov new_allocator.h.gcov ostream.gcov sstream.gcov ... ... ...
Ниже представлен фрагмент сгенерированного отчета утилитой gcov по файлу с исходным кодом:
98: 77: for (int j = 0; j < (int) inputs[i].length(); j++) { 91: 78: iter = elem->find(inputs[i][j]); 91: 79: if (iter == elem->end()) { 73: 80: if (inputs[i][j] != ' ') { -: 81: // ... ... -: 82: } -: 83: } -: 84: else { 18: 85: iter->second++; -: 86: } -: 87: }
Значения, расположенные слева отражают сколько раз была исполнена та или иная строка кода. В данном конкретном случае, очевидно, что строка с номером 85 была выполнена в ходе тестирования 18 раз, а 81-ая строка – 65 раз соответственно.
Сама по себе утилита gcov не предоставляет никаких возможностей по фильтрации тех исходных файлов, которые не тестируются или вообще не относятся к проекту (например, исходные коды стандартной библиотеки C/C++), кроме того, нет никаких возможностей по резюмированию и протоколированию результатов. Однако существует утилита gcovr, которая расширяет возможности gcov и представляет собой скрипт на языке Python, который решает обозначенные выше проблемы утилиты gcov.
Как было упомянуто, утилита gcovr базируется на gcov, поэтому все действия при компиляции и требование как минимум одного запуска тестов остаются в прежними, дальнейшие же действия похожи, но имеют ряд расширенных возможностей.
Поскольку синтаксис использования утилиты gcov не представляет из себя что-то из ряда вон выходящего, то перейдем непосредственно к рассмотрению примера работы с рассматриваемой утилиты. Запросим резюме на покрытие тестами кода в исходных файлах корневого каталога данного проекта (опция -r позволяет указать каталог, исходные файлы которого, в том числе и в нижележащих каталогах, будут анализироваться на предмет покрытия), за исключением каталога gtest/, в котором расположены исходные коды Google Test (опция -e позволяет указать, исходные файлы каких каталогов не подвергать анализу):
$ gcovr -r ../ -e '..*/gtest/' ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ SRM204/Aaagmnrs.h 67 65 97% 44-45 SRM204/Apothecary.h 67 67 100% SRM204/Medici.h 58 58 100% main.cpp 4 4 100% ------------------------------------------------------------------------------ TOTAL 196 194 98% ------------------------------------------------------------------------------
Результат, выводимый утилитой на консоль, при отсутствии флага исключения (-e --exclude) включал бы в себя сведения о покрытии кода самой тестовой среды Google Test.
Для того, чтобы получить отчет по каждому файлу, как в gcov, необходимо использовать опцию -k --keep, поскольку по-умолчанию gcovr удаляет все отчеты:
$ gcovr -k -r ../ -e '..*/gtest/'
Если необходимо определить покрытие кода не по строкам, а по ветвям, достаточно указать соответствующую опцию -b --branches:
$ gcovr -b -r ../ -e '..*/gtest/' ------------------------------------------------------------------------------ File Branch Taken Cover Missing ------------------------------------------------------------------------------ SRM204/Aaagmnrs.h 180 99 55% 33,34, ... SRM204/Apothecary.h 138 82 59% 67,74, ... SRM204/Medici.h 138 76 55% 30,49, ... main.cpp 4 3 75% 31 ------------------------------------------------------------------------------ TOTAL 460 260 56% ------------------------------------------------------------------------------
Но наиболее яркой особенностью утилиты gcovr является генерация отчетов о покрытии кода тестами в формате XML, пригодных для отображения результатов покрытия в таких сервисах, как Jenkins и Hudson:
$ gcovr -r ../ -e '..*/gtest/' -x > coverage-report.xml
Таким образом, использование утилит gcov и gcovr позволяет в полном объеме оценить в процентном соотношении покрытие кода тестами, а при соответствующей интеграции с сервисами Jenkins и Hudson отследить динамику изменений.