Linux下lcov和gcovr的使用

在写完C++/C代码后,一般会去写单元测试,而单元测试测完后,还要看下代码的覆盖率,即看下单元测试对代码的覆盖情况。这时候可以使用lcov或者gcovr来查看覆盖率。(单元测试是写出稳定代码的保证)

文章目录

    • 一 厘清关系
    • 二 安装
    • 三 使用
      • 1. myfunc1内容
      • 2. myfunc2内容
      • 3. myfunc3内容
      • 4. 外层test.cpp和CMakeLists.txt
      • 5. 编译和运行
      • 6. 使用lcov
      • 7. 使用gcovr
    • 三 总结


一 厘清关系

首先要厘清gcov,lcov和gcovr的关系:

  1. gcov是安装GNU编译器时自带的,用于查看代码覆盖率,但是生成的结果难以阅读
  2. lcov和gcovr都是gcov的前端,运行时都会调用gcov,生成的结果都很方便阅读
  3. lcov和gcovr都可以生成html格式的报告,gcovr还可以生成xml格式的报告
  4. lcov比较老一点;gcovr比较新一点,使用python写的,所以系统里要安装python
  5. 使用的便捷性:gcovr比lcov稍微好点
  6. lcov可以查看内核代码的覆盖率,gcovr不行

只要用熟了,用哪一个都行!


二 安装

安装lcov使用下面命令,

sudo apt install lcov

安装gcovr使用下面命令,

sudo apt install gcovr

三 使用

首先要搭建一个工程,配合CMake使用更加方便,假设工程结构如下,
Linux下lcov和gcovr的使用_第1张图片
myfunc1和myfunc2会编译成库,最后和单元测试代码进行连接,myfunc3则是直接使用。

1. myfunc1内容

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)

2. myfunc2内容

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)

3. myfunc3内容

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;
    }
}

4. 外层test.cpp和CMakeLists.txt

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掌控全局,内容也比较简单:

  1. 先是添加一个选项COVERAGE 用来开启覆盖率测试
  2. 编译myfunc1和myfunc2生成对应的库
  3. 编译test.cpp并链接相关的库

当开启COVERAGE后,会在编译flag里添加-g -O0 -fprofile-arcs -ftest-coverage,在链接flag里添加-lgcov

gcov需要.gcno和.gcda文件来生成覆盖结果,添加以上这些选项后,经过编译就会产生.gcno文件,然后再执行一下测试程序,就会生成.gcda文件,具体的细节可以网上自行搜索。

5. 编译和运行

cd到build目录,然后使用下面命令进行编译,

cmake -DCOVERAGE=ON .. && make

编译成功后,运行一下,

./test

结果如下,
Linux下lcov和gcovr的使用_第2张图片

6. 使用lcov

可以使用lcov --help查看lcov的帮助信息。

在前面编译运行后,就会生成.gcno和.gcda文件,可以使用如下命令在build目录下查看,

find . -type f \( -name "*.gcno" -o -name "*.gcda" \)

结果如下,
Linux下lcov和gcovr的使用_第3张图片
接着就是使用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,
Linux下lcov和gcovr的使用_第4张图片
这里打开myfunc2.cpp,显示如下,红色部分表示没有覆盖到,
Linux下lcov和gcovr的使用_第5张图片
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

7. 使用gcovr

gcovr使用起来就比较方便了,同样在build目录下执行下面的命令,

mkdir -p CoverageGcovr
gcovr -r ../ --html --html-details -o CoverageGcovr/example-html-details.html

这样在build/CoverageGcovr/目录下就会生成html文件,
在这里插入图片描述
使用浏览器打开example-html-details.html,
Linux下lcov和gcovr的使用_第6张图片
如果只想查看myfunc1和myfunc2的覆盖情况,可以使用下面的命令去操作,

gcovr -r ../ -f ../myfunc1/ -f ../myfunc2/ --html --html-details -o CoverageGcovr/example-html-details.html

最后生成的html报告如下,
Linux下lcov和gcovr的使用_第7张图片

同样的,可以参考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可以发现非常好用。

你可能感兴趣的:(编程,linux,单元测试)