在写完C++/C代码后,一般会去写单元测试,而单元测试测完后,还要看下代码的覆盖率,即看下单元测试对代码的覆盖情况。这时候可以使用lcov或者gcovr来查看覆盖率。(单元测试是写出稳定代码的保证)
首先要厘清gcov,lcov和gcovr的关系:
只要用熟了,用哪一个都行!
安装lcov使用下面命令,
sudo apt install lcov
安装gcovr使用下面命令,
sudo apt install gcovr
首先要搭建一个工程,配合CMake使用更加方便,假设工程结构如下,
myfunc1和myfunc2会编译成库,最后和单元测试代码进行连接,myfunc3则是直接使用。
myfunc1.h,
#ifndef _MY_FUNC1_H_
#define _MY_FUNC1_H_
void myfunc1();
#endif
myfunc1.cpp,
#include
#include
#include "myfunc1.h"
void myfunc1()
{
std::string data = "hello world\n";
std::cout << __func__ << ": " << data;
}
CMakeLists.txt如下,
add_library(myfunc1 STATIC myfunc1.cpp)
myfunc2.h,
#ifndef _MY_FUNC2_H_
#define _MY_FUNC2_H_
void myfunc2(bool flag);
#endif
myfunc2.cpp,
#include
#include
#include "myfunc2.h"
void myfunc2(bool flag)
{
if (flag)
{
std::string data = "hello world\n";
std::cout << __func__ << ": " << data;
}
else
{
std::string data = "hello everyone\n";
std::cout << __func__ << ": " << data;
}
}
CMakeLists.txt如下,
add_library(myfunc2 STATIC myfunc2.cpp)
myfunc3.h,
#ifndef _MY_FUNC3_H_
#define _MY_FUNC3_H_
void myfunc3(bool flag);
#endif
myfunc3.cpp,
#include
#include
#include "myfunc3.h"
void myfunc3(bool flag)
{
if (flag)
{
std::string data = "hello world\n";
std::cout << __func__ << ": " << data;
}
else
{
std::string data = "hello everyone\n";
std::cout << __func__ << ": " << data;
}
}
test.cpp,
#include
#include "myfunc1.h"
#include "myfunc2.h"
#include "myfunc3.h"
int main(void)
{
std::cout << "start testing ...\n";
myfunc1();
myfunc2(true);
myfunc3(true);
myfunc3(false);
std::cout << "end testing ...\n";
return 0;
}
CMakeLists.txt,
cmake_minimum_required(VERSION 3.5)
project(demo)
option(COVERAGE "run unit test" OFF)
if(COVERAGE)
SET(GCC_COVERAGE_COMPILE_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
SET(GCC_COVERAGE_LINK_FLAGS "-lgcov")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
endif()
include_directories(myfunc1)
add_subdirectory(myfunc1)
include_directories(myfunc2)
add_subdirectory(myfunc2)
include_directories(myfunc3)
add_executable(test test.cpp myfunc3/myfunc3.cpp)
target_link_libraries(test myfunc1)
target_link_libraries(test myfunc2)
这个CMakeLists.txt掌控全局,内容也比较简单:
当开启COVERAGE后,会在编译flag里添加-g -O0 -fprofile-arcs -ftest-coverage
,在链接flag里添加-lgcov
。
gcov需要.gcno和.gcda文件来生成覆盖结果,添加以上这些选项后,经过编译就会产生.gcno文件,然后再执行一下测试程序,就会生成.gcda文件,具体的细节可以网上自行搜索。
cd到build目录,然后使用下面命令进行编译,
cmake -DCOVERAGE=ON .. && make
编译成功后,运行一下,
./test
可以使用lcov --help
查看lcov的帮助信息。
在前面编译运行后,就会生成.gcno和.gcda文件,可以使用如下命令在build目录下查看,
find . -type f \( -name "*.gcno" -o -name "*.gcda" \)
结果如下,
接着就是使用lcov来生成覆盖率结果,在build目录下操作,
lcov -c -i -d ./ -o init.info # 创建baseline coverage数据文件
lcov -c -d ./ -o tests.info # 创建coverage数据文件
lcov -a init.info -a tests.info -o all.info # 合并baseline and test coverage data
lcov -r all.info '/usr/include/*' -o final.info # 删除unnneeded files
lcov -r final.info '*.h' -o final.info # 删除头文件
genhtml --title "My unit test" --legend -o Coverage final.info # 生成html报告,目录名为Coverage
rm -rf init.info tests.info all.info final.info # 删除coverage数据文件
PS:这里还使用了一个命令genhtml,这个是安装lcov一起安装的
最后在build目录下生成Coverage目录,打开后可以看到网页文件,
使用浏览器打开index.html,
这里打开myfunc2.cpp,显示如下,红色部分表示没有覆盖到,
test.cpp里确实只调用myfunc2(true),所以反映的结果是正确的。
有时只想查看某些模块的覆盖率,这里假如是想查看myfunc2和myfunc3的情况,那么命令如下,
lcov -c -i -d ./myfunc2 -d ./myfunc3 -o init.info # 创建baseline coverage数据文件
lcov -c -d ./ -o tests.info # 创建coverage数据文件
lcov -a init.info -a tests.info -o all.info # 合并baseline and test coverage data
lcov -r all.info '/usr/include/*' -o final.info # 删除unnneeded files
lcov -r final.info '*.h' -o final.info # 删除头文件
genhtml --title "My unit test" --legend -o Coverage final.info # 生成html报告,目录名为Coverage
rm -rf init.info tests.info all.info final.info # 删除coverage数据文件
每次生成报告都要这样操作会很麻烦,可以把这些命令放到CMakeLists.txt里,
cmake_minimum_required(VERSION 3.5)
project(demo)
option(COVERAGE "run unit test" OFF)
if(COVERAGE)
SET(GCC_COVERAGE_COMPILE_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
SET(GCC_COVERAGE_LINK_FLAGS "-lgcov")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
endif()
include_directories(myfunc1)
add_subdirectory(myfunc1)
include_directories(myfunc2)
add_subdirectory(myfunc2)
include_directories(myfunc3)
ADD_CUSTOM_TARGET(lcov
COMMAND rm -rf Coverage
COMMAND lcov -c -i -d ./ -o init.info # 创建baseline coverage数据文件
COMMAND lcov -c -d ./ -o tests.info # 创建coverage数据文件
COMMAND lcov -a init.info -a tests.info -o all.info # 合并baseline and test coverage data
COMMAND lcov -r all.info '/usr/include/*' -o final.info # 删除unnneeded files
COMMAND lcov -r final.info '*.h' -o final.info # 删除头文件
COMMAND genhtml --title "My unit test" --legend -o Coverage final.info # 生成html报告,目录名为Coverage
COMMAND rm -rf init.info tests.info all.info final.info # 删除coverage数据文件
)
add_executable(test test.cpp myfunc3/myfunc3.cpp)
target_link_libraries(test myfunc1)
target_link_libraries(test myfunc2)
当编译运行后,可以使用下面命令来执行lcov,非常方便
make lcov
gcovr使用起来就比较方便了,同样在build目录下执行下面的命令,
mkdir -p CoverageGcovr
gcovr -r ../ --html --html-details -o CoverageGcovr/example-html-details.html
这样在build/CoverageGcovr/目录下就会生成html文件,
使用浏览器打开example-html-details.html,
如果只想查看myfunc1和myfunc2的覆盖情况,可以使用下面的命令去操作,
gcovr -r ../ -f ../myfunc1/ -f ../myfunc2/ --html --html-details -o CoverageGcovr/example-html-details.html
同样的,可以参考lcov把gcovr的命令也加到CMakeLists.txt里,
cmake_minimum_required(VERSION 3.5)
project(demo)
option(COVERAGE "run unit test" OFF)
if(COVERAGE)
SET(GCC_COVERAGE_COMPILE_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
SET(GCC_COVERAGE_LINK_FLAGS "-lgcov")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
endif()
include_directories(myfunc1)
add_subdirectory(myfunc1)
include_directories(myfunc2)
add_subdirectory(myfunc2)
include_directories(myfunc3)
ADD_CUSTOM_TARGET(lcov
COMMAND rm -rf Coverage
COMMAND lcov -c -i -d ./ -o init.info # 创建baseline coverage数据文件
COMMAND lcov -c -d ./ -o tests.info # 创建coverage数据文件
COMMAND lcov -a init.info -a tests.info -o all.info # 合并baseline and test coverage data
COMMAND lcov -r all.info '/usr/include/*' -o final.info # 删除unnneeded files
COMMAND lcov -r final.info '*.h' -o final.info # 删除头文件
COMMAND genhtml --title "My unit test" --legend -o Coverage final.info # 生成html报告,目录名为Coverage
COMMAND rm -rf init.info tests.info all.info final.info # 删除coverage数据文件
)
ADD_CUSTOM_TARGET(gcovr
COMMAND rm -rf CoverageGcovr
COMMAND mkdir -p CoverageGcovr
COMMAND gcovr -r ../ --html --html-details -o CoverageGcovr/example-html-details.html
)
add_executable(test test.cpp myfunc3/myfunc3.cpp)
target_link_libraries(test myfunc1)
target_link_libraries(test myfunc2)
编译运行后,可以使用下面的命令来调用gcovr,
make gcovr
其它帮助信息可以参考官方文档
本文讲述了lcov和gcov的基本使用,其它使用可以参考他们的帮助信息。配合CMake可以发现非常好用。