统计C/C++代码覆盖率的工具很多,比如OpenCppCoverage可以与VS工具配合,获取并展示代码覆盖率简单直观,但是在Linux、Mac等系统该如何统计呢?一般的持续集成工具(Jenkins、gitlab-ci等)中又该如何统计呢?

准备工具,请参考教程安装即可:

GCC
CMake
Google Test
gcov
lcov
gcovr

代码覆盖率

代码覆盖率一般包含以下几种类型:

函数覆盖率:描述有多少比例的函数经过了测试。
语句覆盖率:描述有多少比例的语句经过了测试。
分支覆盖率:描述有多少比例的分支(例如:if-else,case语句)经过了测试。
条件覆盖率:描述有多少比例的可能性经过了测试。
因此一般的覆盖率结果也分为几种不同的类型。

gcov

gcov是由gcc工具链提供的代码覆盖率生成工具,可以很方便的和GCC编译器配合使用,通常情况下,直接安装gcc工具链,也就同时包含了gcov命令行工具。

对于代码覆盖率工具所做的工作,可以简单的理解为:标记一次运行过程中,哪些代码被执行过,哪些没有执行。
因此,即便没有测试代码,直接运行编译产物也可以得到代码的覆盖率。只不过,通常情况下这样得到的覆盖率较低罢了。
在项目中我们使用cmake编译,因此在CMakeLists.txt文件中设置覆盖率相关参数。

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

将-fprofile-arcs -ftest-coverage添加到编译器flag中,这个参数是很重要的,是生成代码覆盖率所必须的,对于该参数的说明可以参考:https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#Instrumentation-Options

执行cmake、make命令编译之后生成单测可执行文件Test。执行单测:

./Test --gtest_filter=ClassName.CaseName

单测执行之后,我们会得到每个源码文件对应的gcda和gcno后缀文件,比如main函数所在的主文件TestMain.cpp,通过TestMain.cpp.gcda和TestMain.cpp.gcno两个文件,便可以得到代码TestMain.cpp的覆盖率结果了。

通过gcov指定源码文件的名称,便可以得到该源码文件的覆盖率结果:

gcov TestMain.cpp.gcno
lcov

gcov得到的结果是文本形式的,而且不同的源码文件需要一一执行gcov命令,对于大工程是不方便的,我们希望得到更加美观和便于浏览的结果。

lcov

lcov是gcov工具的图形前端,收集多个源文件的gcov数据,生成描述覆盖率的HTML页面。生成的结果中会包含概述页面,方面浏览。

lcov有很多参数配合使用可以满足各种需求,lcov的使用方法可以通过以下这条命令查询:

lcov --help

我们一般关注以下这几个参数:

-c 或者 --capture 指定从编译产物中收集覆盖率信息。
-d DIR 或者 --directory DIR 指定编译产物的路径。
-e FILE PATTERN 或者 --extract FILE PATTERN 从指定的文件中根据PATTERN过滤结果。
-o FILENAME 或者 --output-file FILENAME 指定覆盖率输出的文件名称。

此外,特殊说明:

lcov默认不会打开分支覆盖率,因此我们还需要增加这个参数来打开分支覆盖率的计算:

--rc lcov_branch_coverage=1

lcov输出的仍然是一个中间产物,我们还需要通过lcov软件包提供的另外一个命令genhtml来生成最终需要的html格式的覆盖率报告文件。
同样的,为了打开分支覆盖率的计算,我们也要为这个命令增加–rc lcov_branch_coverage=1参数
最后,我们编辑一个make_all.sh脚本执行lcov相关操作:

COVERAGE_FILE=coverage.info
REPORT_FOLDER=coverage_report
lcov --rc lcov_branch_coverage=1 -c -d . -o ${COVERAGE_FILE}_tmp
lcov --rc lcov_branch_coverage=1 -e ${COVERAGE_FILE}_tmp "*src*" -o ${COVERAGE_FILE}
genhtml --rc genhtml_branch_coverage=1 ${COVERAGE_FILE} -o ${REPORT_FOLDER}

代码解析:
我们从前面的编译结果中收集覆盖率结果,并将结果输出到coverage.info_tmp文件中,该文件存储在当前目录下。但是这里面会包含非项目源码的覆盖率(例如GoogleTest),所以我们通过另外一条命令指定"src"文件夹进行过滤。最后,通过genhtml得到HTML格式的报告,报告结果存储在文件夹coverage_report中。

gcovr

一般场景下使用gcov和lcov能满足代码覆盖率的获取和展示工作,lcov和genhtml配合生成的HTML报告内容详尽,简洁直观,行覆盖率、分支覆盖率都有,但是HTML文件在常用的持续集成工具(比如Jenkins、gitlab-ci)中均无法集成,因此我们需要其他的工具用于覆盖率结果的持续集成展示。

gcovr是一款针对C/C++代码覆盖率并支持以多种方式(包括列表方式、XML文件方式、HTML网页方式等)展示出来的工具,而XML文件刚好是可以被持续集成工具解析的。

gcovr有很多参数配合使用可以满足各种需求,gcovr的使用方法可以通过以下这条命令查询:

gcovr --help

我们一般关注以下这几个参数:

-r ROOT 或者 --root ROOT 代码根目录,默认为'.',当前的路径。
-b 或者 --branches 以分支覆盖率形式报告。
-x 或者 --xml 指定报告的形式为XML。
-o OUTPUT 或者 --output OUTPUT 指定覆盖率输出的文件名称。
--html 指定报告的形式为HTML。

在项目的编译根目录下使用如下命令:

gcovr -r . --xml -o coverage.xml

当前目录下生成coverage.xml文件详细记录了所有源码文件的行覆盖率信息。

常见问题:gcovr得到的覆盖率为0%
解决:执行gcovr -r . 命令一般在编译路径下,cmake项目中我们一般习惯创建一个build文件夹编译源文件,测试执行之后,build路径下包含gcda和gcno、cpp.o,其实执行gcovr命令还需要源码文件,因此,需要在上层根目录下执行gcovr。

转载:https://www.sohu.com/a/329177032_120104204

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐