C++ 代码覆盖率分析:使用 CMake + Gcovr 生成 HTML/XML/JSON 报告

使用 CMake 和 Gcovr 进行代码覆盖率分析

1. Gcovr 的原理

1.1 编译时插桩 (Instrumentation)

使用 -fprofile-arcs -ftest-coverage 选项时,GCC 会插入 探针 (probes) 以记录代码执行情况。

编译后,会生成:

  • .gcno 文件:代码块(Basic Block)信息。
  • 可执行文件(带有探针的代码)。

1.2 运行时记录 (Profiling Data)

运行可执行文件后,GCC 记录运行数据并生成:

  • .gcda 文件:存储实际执行路径。

1.3 解析覆盖率数据

  • gcovr 解析 .gcno.gcda 文件,提取覆盖率数据。
  • 格式化输出 HTML、XML、JSON 等报告。

1.4 过滤 & 报告生成

常见 gcovr 参数:

--root <path>      # 设置项目根目录
--exclude <pattern> # 排除测试代码
--html             # 生成 HTML 报告
--xml              # 生成 XML 报告 (CI/CD 用)
--json             # 生成 JSON 报告

1.5 调试 & 解决问题

如果覆盖率显示 0%,可以检查:

  1. 确保 -fprofile-arcs -ftest-coverage 选项已启用。
  2. 确认 .gcda 文件是否生成:
    find build -name "*.gcda"
    
  3. 手动运行 gcov 解析:
    gcov src/math_lib.cpp -o build/CMakeFiles/math_lib.dir/src
    
  4. 确保 gcovr --root 指定了正确的路径。

2. 环境准备

2.1 安装必要的软件包

在 Linux (Ubuntu) 下,可以使用以下命令安装相关工具:

sudo apt update
sudo apt install cmake g++ lcov gcovr libgtest-dev

3. 项目结构

project/
│── CMakeLists.txt
│── src/
│   ├── math_lib.cpp
│   ├── math_lib.h
│   ├── main.cpp
│── tests/
│   ├── test_math_lib.cpp
│── build/  (编译目录)
│── reports/  (存放覆盖率报告)

4. 代码示例

4.1 头文件 math_lib.h

#ifndef MATH_LIB_H
#define MATH_LIB_H

int add(int a, int b);
bool isEven(int n);
int if_add(int a, int b, bool added);

#endif

4.2 源文件 math_lib.cpp

#include "math_lib.h"

int add(int a, int b) {
    return a + b;
}

bool isEven(int n) {
    return n % 2 == 0;
}

int if_add(int a, int b, bool added) {
    if (added) {
        return a + b;
    }
    return a - b;
}

4.3 测试代码 test_math_lib.cpp

#include <gtest/gtest.h>
#include "../src/math_lib.h"

TEST(MathTest, AddFunction) {
    EXPECT_EQ(add(2, 3), 5);
    EXPECT_EQ(add(-1, 1), 0);
}

TEST(MathTest, IsEvenFunction) {
    EXPECT_TRUE(isEven(4));
    EXPECT_FALSE(isEven(7));
}

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

5. CMake 配置文件 CMakeLists.txt

#
cmake_minimum_required(VERSION 3.20)
project(CodeCoverageExample)

# 启用 C++ 标准
set(CMAKE_CXX_STANDARD 17)

# 启用代码覆盖率编译选项
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
# 添加 Google Test
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})

# 添加源文件
add_library(math_lib src/math_lib.cpp)

# 添加测试可执行文件
add_executable(test_math_lib tests/test_math_lib.cpp)
target_link_libraries(test_math_lib PRIVATE math_lib GTest::GTest GTest::Main pthread)

# 添加测试
enable_testing()
add_test(NAME RunTests COMMAND test_math_lib)

# 添加代码覆盖率目标
find_program(GCOVR gcovr REQUIRED)
set(GCOVR_COMMAND ${GCOVR_EXECUTABLE})

# 创建 reports 文件夹
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/reports)

if (GCOVR)
    execute_process(
        COMMAND ${CMAKE_COMMAND} -E echo "Cleaning *.gcno/*.gcda files"
        COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name '*.gcda' -delete
        COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name '*.gcno' -delete
    )
    message(STATUS "${GCOVR}")
    message(STATUS "${CMAKE_SOURCE_DIR}")

    add_custom_target(coverage
        COMMAND set -x
        COMMAND ${GCOVR} --root ${CMAKE_SOURCE_DIR}
            --html-details --html=${CMAKE_SOURCE_DIR}/reports/coverage.html
            --xml-pretty --xml=${CMAKE_SOURCE_DIR}/reports/coverage.xml
            --json -o ${CMAKE_SOURCE_DIR}/reports/coverage.json
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        COMMENT "Generating code coverage report..."
    )
endif()


6. 运行步骤

6.1 编译

# 生成构建目录并配置项目
cmake -S . -B build

# 构建项目
cmake --build build

6.2 运行测试

# 运行测试
ctest --test-dir build

6.3 生成覆盖率报告

cmake --build build --target coverage

生成的覆盖率报告coverage.html
在这里插入图片描述

7. 总结

完整代码覆盖率流程

  1. 编译:使用 -fprofile-arcs -ftest-coverage 选项,生成 .gcno 文件。
  2. 运行:运行测试,生成 .gcda 文件。
  3. 分析:使用 gcov 解析 .gcda.gcno 文件。
  4. 生成报告gcovr 读取 gcov 输出,并生成 HTML、XML、JSON 报告。

通过这套方法,我们可以高效地分析 C++ 代码的测试覆盖率,并在 CI/CD 中集成代码质量检查。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值