Покрытие кода (code coverage)

Покрытие кода – метрика ПО, показывающая процент исходного кода программы, который был выполнен в процессе тестирования.

Платформы тестирования С/C++ кода:

Платформа модульного тестирования Майкрософт для C++ (VS)

Google Test

Boost.Test

CTest (CMake)

[Catch2, [in CLion](https://www.jetbrains.com/help/clion/catch-tests-support.html]](https://github.com/catchorg/Catch2)

OpenCppCoverage (C++, Windows)

gcov(CLI) / lcov (GUI) (GCC)

llvm-cov (LLVM/Clang, CLI)

Code coverage с llvm-cov

Устанавливается вместе с LLVM.

Рассмотрим пример:

include/max.hpp

#pragma once

template<typename T>
T max(const T &a, const T &b)
{
	return (a >= b ? a : b);
}

struct Pixel
{
    char r, g, b;
};


#define Y(p) (0.299*(p.r) + 0.587*(p.g) + 0.114*(p.b))

template<>
Pixel max<Pixel>(const Pixel &a, const Pixel &b)
{  
	return (Y(a) > Y(b)) ? a : b;
}

tests.cpp

#include <gtest/gtest.h>

#include "max.hpp"

TEST(Max, Int) {
    EXPECT_EQ(max<int>(5, 3), 5);
}

TEST(Max, IntImplicit) {
    EXPECT_EQ(max(5, 3), 5);
}

TEST(Max, Float) {
    EXPECT_EQ(max<float>(5, 3), 5.0f);
}

int main(int argc, char **argv)
{
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}

Для того, чтобы покрытие кода можно было измерить, необходимо собрать программу с опциями -fprofile-instr-generate -fcoverage-mapping. Эти опции говорят компилятору о необходимости вставки дополнительных инструкций для сбора статистики.

В нашем случае (сборка из корня проекта):

clang -fprofile-instr-generate -fcoverage-mapping -O0 -Iinclude .\test\tests.cpp -Iinclude -Igtest\include -Lgtest\lib\ -lgtest -o tests.exe

Если запустить собранный файл:

.\tests.exe

то в результате запуска будет сгенерирован файл default.profraw – файл, содержащий данные профайлинга.

Если проект содержит несколько main-функций (main.cpp и tests.cpp), то можно сгенерировать разные profraw и затем объединить в единый отчёт:

llvm-profdata merge -sparse tests.profraw main.profraw -o tests.profdata                                   

profdata – промежуточный файл llvm-cov, используемый для генерации отчёта.

Для генерации отчёта можно использовать

llvm-cov show .\tests.exe -instr-profile tests.profdata -format html -output-dir coverage_report -ignore-filename-regex=".*[/\]gtest[/\].*" 

В результате исполнения данной команды будет создана директория coverage_report, в которой появится отчёт в формате HTML, в который не будут включены результаты, содержащие подстроку gtest (исключаем все логи про покрытие тестами самого gtest).

В случае приведённого выше кода статистика будет следующей:

Подробнее здесь.

Code coverage с gcov

изображение

Аналог: gcovr.

Установка: pip install gcovr

Идея аналогична llvm-cov – генерируются файлы профайлинга, а затем из них формируется отчёт.

Пример использования здесь.

Вариант описания CMakeLists.txt для автоматической сборки и формирования отчёта о покрытии кода:

cmake_minimum_required(VERSION 3.14)
project(example)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}     -fprofile-arcs -ftest-coverage")

# Specify the C++ standard
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED True)

add_subdirectory(lib)

include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)

set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(mainTest test/tests.cpp)

add_custom_target(cov 
COMMAND echo "=================== GCOVR ====================" 
COMMAND gcovr -r ${CMAKE_SOURCE_DIR} --exclude='${CMAKE_SOURCE_DIR}/build' --exclude='${CMAKE_SOURCE_DIR}/Eigen'
COMMAND open coverage.html)

add_dependencies(cov mainTest)

target_link_libraries(mainTest gtest gtest_main)

add_test(NAME mainTest COMMAND mainTest)

Code coverage в Visual Studio

изображение

Code coverage в CLion

изображение

Last updated