C++从零开始实现LSM-Tree-KV存储-22-代码覆盖率

我们的主线内容其实都已经结束了, 这里先补充一些项目建设性方面的内容, 首先就是根据单元测试生成代码覆盖率的测试报告。

代码仓库:Vanilla-Beauty/tiny-lsm: A KV storage engine based on LSM Tree, supporting Redis RESP 感谢您的 Star!

欢迎支持本项目同步更新的从零开始实现的视频教程:https://avo6166ew2u.feishu.cn/docx/LXmVdezdsoTBRaxC97WcHwGunOc

欢迎加入讨论群:Tiny-LSM项目交流群

1 为什么需要代码覆盖率?

代码覆盖率测试是软件开发过程中验证测试完整性的重要手段,它通过统计程序中被执行的代码比例来评估测试用例的有效性。常见的代码覆盖率类型包括:

  • 行覆盖率(Line Coverage):衡量源文件中被测试执行的代码行数。
  • 函数覆盖率(Function Coverage):确认每个函数是否都被调用。
  • 分支覆盖率(Branch Coverage):检查程序中的条件判断是否都被覆盖。

常见工具

以下是几种主流的代码覆盖率测试工具,按语言和技术栈分类说明:

工具 语言/平台 简要说明
gcov / lcov C/C++ (GCC) gcov 是 GCC 自带的覆盖率分析工具,lcov 是其可视化前端,可生成 HTML 报告。
Valgrind + Gcov C/C++ 结合使用 Valgrind 的 callgrind 工具进行更精确的覆盖率分析。
Coverage.py Python 针对 Python 的代码覆盖率工具,支持分支覆盖率,并集成 unittest 和 pytest。
JaCoCo Java Java 平台的标准覆盖率工具,支持 Ant、Maven、Gradle 构建系统,能与 Jenkins 等 CI 工具集成。
Istanbul / nyc JavaScript 常用于 Node.js 项目,支持 ES6+,并能生成详细的 HTML 报告。
dotCover .NET JetBrains 提供的商业工具,用于 .NET 框架和 .NET Core 的单元测试覆盖率分析。
OpenCppCoverage C/C++ (Windows) 支持 Windows 平台的代码覆盖率工具,适合 MSVC 编译器环境。

常见的工作流

  1. 自动化测试流程整合

    • 可将覆盖率测试脚本集成到 CI/CD 流水线(如 GitHub Actions、GitLab CI、Jenkins)中,确保每次提交都自动运行并生成报告。
  2. 质量门禁设置

    • 利用工具提供的阈值配置功能(例如 lcov 配合 genhtmlcoverage.py),设定最低覆盖率要求,低于该值则构建失败。
  3. 持续改进

    • 定期查看覆盖率报告,针对未覆盖代码设计补充测试用例,提升整体测试质量和代码可靠性。

2 工具链使用介绍

2.1 工具链说明

本项目由于采用了 xmake 构建系统,因此我们将使用 xmakelcovgenhtml 等工具来生成代码覆盖率报告。以下是这些工具的简要说明:

工具 说明
xmake 一款现代构建系统,简洁灵活,支持多平台和构建模式(如覆盖率模式)。 (这个系列用了这么久就不再过多介绍了 )
lcov gcov 的图形化封装,生成 .info 格式的覆盖率数据文件。
genhtml 读取 .info 文件并生成 HTML 格式的可视化代码覆盖率报告。
gcov GCC 的代码覆盖率分析工具,生成 .gcno.gcda 数据文件供 lcov 使用。

2.2 工具链安装

为了顺利使用 xmakelcovgenhtml 工具生成代码覆盖率报告,需要在系统中安装这些工具。以下是各工具的安装说明,按不同操作系统分类。

2.2.1 lcov 安装

lcov 是基于 gcov 的前端工具,用于收集和处理覆盖率数据。

1
2
3
sudo apt-get install lcov # Ubuntu/Debian
sudo dnf install lcov # Fedora
brew install lcov # macOS(Homebrew)

验证是否安装成功:

1
lcov --version

2.2.2 genhtml 安装

genhtmllcov 的一部分,安装 lcov 后会自动包含 genhtml,无需单独安装。

验证是否可用:

1
genhtml --version

2.2.3 gcov 安装(GCC 工具链)

gcov 是 GCC 自带的覆盖率分析工具,通常随编译器一起安装。

Linux

确保已安装 GCC:

1
sudo apt-get install gcc g++

macOS

默认可能使用的是 Clang,需手动安装 GCC(例如通过 Homebrew):

1
brew install gcc

验证是否安装成功:

1
gcov --version

提示:

  • 若使用的是 CI 环境(如 GitHub Actions),可在 .yml 文件中添加上述安装命令。
  • 对于 Windows 平台若使用 MSVC 编译器,可考虑使用 OpenCppCoverage 替代方案。

3 获取代码覆盖率报告

3.1 配置文件修改

为了获取代码覆盖率测试, 我们在编译和链接文件时需要附上--coverage标志, 我们不妨再设置一个编译模式:

1
2
3
4
5
6
7
-- 在 coverage 模式下设置 flags
add_rules("mode.debug", "mode.release", "mode.coverage")

if is_mode("coverage") then
add_cxxflags("--coverage")
add_ldflags("--coverage")
end

coverage 模式下:

  • 使用 --coverage 编译器参数,以生成用于覆盖率分析的额外信息。
  • 使用 --coverage 链接器参数,以确保链接了必要的覆盖率运行库(如 gcov)。

3.2 测试脚本说明

脚本文件示例:generate_coverage.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/bash

# 清理旧的构建和覆盖率报告
rm -rf build/*
rm -rf coverage-report

# 配置为覆盖率构建模式
xmake f -m coverage

# 编译项目
xmake

# 执行所有测试(确保所有逻辑路径被触发)
xmake run-all-tests

# 收集覆盖率数据
lcov --capture --directory . --output-file coverage.info

# 去除无关路径(如系统库、xmake 缓存、测试代码等)
lcov --remove coverage.info '/usr/*' '*/.xmake/*' '*/test/*' -o coverage.cleaned.info

# 生成 HTML 格式的覆盖率报告
genhtml -o coverage-report coverage.cleaned.info

为了更好地理解 generate_coverage.sh, 以下是对整个流程中关键步骤的详细解释。

3.3 获取代码覆盖率的基本原理

3.3.1 编译阶段:插入覆盖率信息

在使用 GCC 编译器时,通过添加编译选项(如 -fprofile-arcs-ftest-coverage 或直接使用 --coverage),编译器会在生成的目标文件中插入额外的探针(instrumentation code):

  • .gcno 文件

    • 在编译阶段生成。
    • 包含程序结构信息(如函数、基本块、分支等)。
    • 用于将运行时数据映射回源码。
  • __gcov_init, __gcov_exit 等调用

    • 插入到函数入口和出口。
    • 用于记录执行路径。

3.3.2 运行阶段:收集执行路径数据

当程序运行时,这些探针会记录每条语句和分支是否被执行,并在程序正常退出时将结果写入 .gcda 文件:

  • .gcda 文件
    • 每个 .o 文件对应一个 .gcda 文件。
    • 包含该模块中各行代码/分支的实际执行次数。

⚠️ 注意:程序必须正常退出(如调用 exit() 或从 main 返回),否则可能无法正确生成 .gcda 文件。

3.3.3 数据采集与过滤

脚本使用 lcov 工具进行数据采集:

1
lcov --capture --directory . --output-file coverage.info
  • 遍历当前目录及其子目录,收集所有 .gcno.gcda 文件。
  • 将其转换为统一的 coverage.info 格式,便于后续处理。

随后通过 --remove 命令过滤掉不关心的路径(如系统头文件、构建缓存、测试代码):

1
lcov --remove coverage.info '/usr/*' '*/.xmake/*' '*/test/*' -o coverage.cleaned.info

这一步有助于聚焦项目核心代码,避免干扰。

3.3.4 报告生成

最后,使用 genhtml.info 文件转换为 HTML 可视化报告:

1
genhtml -o coverage-report coverage.cleaned.info

生成的 HTML 页面中:

  • 每个源文件都有对应的覆盖率统计。
  • 行号颜色区分已覆盖(绿色)、未覆盖(红色)。
  • 支持点击进入具体函数或文件查看细节。

4 gcov相关知识点

为了更清晰地理解代码覆盖率工具链中涉及的命令、参数和文件类型,以下是对 gcov 相关知识点的结构化整理,包括编译参数、生成文件、常用命令及其作用。


4.1 编译参数说明

参数 含义
-fprofile-arcs 记录程序执行路径(分支信息),用于分析控制流。
-ftest-coverage 生成 .gcno 文件,记录源码结构信息,供后续覆盖率分析使用。
--coverage GCC 提供的快捷选项,等价于同时启用 -fprofile-arcs-ftest-coverage,并链接 gcov 运行时库。

✅ 推荐在构建测试版本时使用 --coverage,确保编译器和链接器都正确插入探针。


4.2 生成文件说明

文件类型 来源阶段 描述
.gcno 编译阶段 包含函数、基本块、分支等源码结构信息,用于将运行时数据映射回源文件。
.gcda 运行阶段 程序执行后生成,记录每条语句和分支的实际执行次数。
.info lcov 输出 lcov 收集 .gcno.gcda 生成的中间格式文件,便于后续处理。

4.3 常用命令说明

4.3.1 lcov 命令

命令 功能描述
lcov --capture --directory . --output-file coverage.info 收集当前目录下所有模块的 .gcno.gcda 文件,合并为一个 .info 文件。
lcov --remove coverage.info '/usr/*' '*/.xmake/*' '*/test/*' -o coverage.cleaned.info 去除与项目无关的路径(如系统库、缓存、测试代码),提升报告准确性。
lcov --list coverage.info 查看 .info 文件中的覆盖信息内容。
lcov --extract coverage.info 'src/*' -o filtered.info 提取特定路径下的覆盖率数据。

4.3.2 genhtml 命令

命令 功能描述
genhtml -o coverage-report coverage.cleaned.info .info 文件转换为 HTML 格式报告,支持浏览器查看。
genhtml --title "My Project Coverage" -o coverage-report coverage.cleaned.info 自定义报告标题。
genhtml --legend -o coverage-report coverage.cleaned.info 显示详细图例,包括未覆盖行数和百分比。

4.4 流程简要回顾

  1. 编译阶段:使用 --coverage 编译,生成 .gcno
  2. 运行阶段:执行测试用例,生成 .gcda
  3. 采集阶段:使用 lcov --capture 收集 .gcno.gcda
  4. 过滤阶段:使用 lcov --remove 移除不关心的路径。
  5. 生成阶段:使用 genhtml 生成 HTML 报告,可视化展示。