lcov2.0分支覆盖率中关于std容器相关操作的分支未覆盖问题,如何解决?

我使用lcov2.0来检测分支覆盖率,但是会遇到很多和std容器相关的分支未覆盖的问题。

  • g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
  • gtest 1.12.x
  • lcov/genhtml: LCOV version 2.0-1

源码

// src/main.hpp
#include <cstdint>
#include <iostream>
#include <map>
#include <set>
#include <stdexcept>
using namespace std;

class mytestint
{
public:
    mytestint() = default;
    mytestint(uint16_t id, uint16_t pr) : _id(id), _pr(pr) {}

    uint16_t get_id() const
    {
        return _id;
    }

private:
    uint16_t _id;
    uint16_t _pr;
};

map<uint16_t, mytestint> _test_map;
map<uint16_t, uint16_t> _test_map_pod;

void test_emplace(uint16_t id, uint16_t pr)
{
    // map with class
    mytestint temp = {id, pr};
    auto t = _test_map.emplace(id, temp);
    auto k = _test_map.emplace(id, mytestint(id,pr));
    auto s = _test_map.insert({pr, temp});
    _test_map[id] = temp;
    auto h = _test_map[pr];
    // map with uint16_t
    _test_map_pod.emplace(id,pr);
    _test_map_pod.insert({pr,id});
    _test_map_pod[id] = pr;
}

gtest代码

#include "src/main.hpp"
#include <gtest/gtest.h>

TEST(MapTest, TestMap) {
    EXPECT_NO_THROW(test_emplace(1,1));
    EXPECT_NO_THROW(test_emplace(2,2));
    EXPECT_NO_THROW(test_emplace(3,3));
    EXPECT_NO_THROW(test_emplace(2,4));
}

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

lcov最终生成的报告长这样,所有insert和emplace相关的函数都会有一个未解决的分支

      92                 :           4 : void test_emplace(uint16_t id, uint16_t pr)
      93                 :             : {
      94                 :             :     // map with class
      95                 :           4 :     mytestint temp = {id, pr};
      96         [ +  - ]:           4 :     auto t = _test_map.emplace(id, temp);
      97         [ +  - ]:           4 :     auto k = _test_map.emplace(id, mytestint(id,pr));
      98         [ +  - ]:           4 :     auto s = _test_map.insert({pr, temp});
      99         [ +  - ]:           4 :     _test_map[id] = temp;
     100         [ +  - ]:           4 :     auto h = _test_map[pr];
     101                 :             :     // map with uint16_t
     102         [ +  - ]:           4 :     _test_map_pod.emplace(id,pr);
     103         [ +  - ]:           4 :     _test_map_pod.insert({pr,id});
     104         [ +  - ]:           4 :     _test_map_pod[id] = pr;
     105                 :           4 : }

贴出我用的lcov命令

    g++ -std=c++17 test.cpp -o test -lgtest -lgtest_main -pthread -fprofile-arcs -ftest-coverage -fprofile-update=atomic && \
    ./test && \
    gcov -o . test.cpp && \
    lcov --capture \
         --rc branch_coverage=1 \
         --directory . \
         --output-file coverage_all.info \
         --ignore-errors mismatch && \
    lcov --remove coverage_all.info '*/usr/include/*' '*/usr/lib/*' '*/usr/lib64/*' \
                                    '*/usr/local/include/*' '*/usr/local/lib/*'     \
                                    '*/usr/local/lib64/*'     \
        --rc branch_coverage=1 \
        --output-file coverage.info \
        --ignore-errors unused \
        --ignore-errors mismatch && \
    genhtml coverage.info \
            --rc branch_coverage=1 \
            --output-directory coverage_report 

我想知道要怎么覆盖上这些分支?因为现在要求分支覆盖率要达到某个百分比,但是代码里面有一大堆std库函数的操作而产生的分支,这些和自己实现的代码没有联系的分支很难被完全测试出来……

就想知道这里的分支未覆盖和这几个插入函数中的异常处理有关系吗?我看so上面一个远古帖子说在g++编译的时候屏蔽这些异常能解决一些分支。但是那样就完全没有办法使用try/catch等异常处理语句了,肯定不是个解决办法。

lcov用的人似乎太少了,国内和外网都没有找到一个很细致的分支覆盖率提高的教程(也有可能是我搜索的方式不对吧……很少上外网搜东西。)

关联问题 https://segmentfault.com/q/1010000044378276
阅读 967
avatarAI BotBETA

这个问题涉及到代码覆盖率和异常处理的问题。要解决这个问题,你需要明白lcov工具是如何工作的,以及它为什么将这些std库函数的分支视为未覆盖。

首先,lcov工具在计算代码覆盖率时,它将每个源文件编译为二进制的覆盖信息文件(.gcda文件)。然后,它使用这些.gcda文件来生成一个总的覆盖信息文件(coverage_all.info),其中包含了源代码中的所有分支和路径的覆盖情况。

然而,lcov工具在生成总的覆盖信息文件时,它默认会忽略一些被认为是库函数或系统调用的代码块。这些代码块通常不包含在源文件中,而是包含在标准库或操作系统提供的二进制文件中。由于这些代码块不是由你编写的,因此lcov工具默认认为它们不需要被测试。

在你的情况下,问题出在std容器相关的插入和emplace操作上。这些操作是在标准库中定义的,因此它们被视为忽略的代码块,从而导致了这些分支被视为未覆盖。

要解决这个问题,你有几个选择:

  1. 增加异常处理:异常处理是C++的一个重要特性,因此在某些情况下,你可能需要使用try/catch语句来捕获和处理可能的异常。然而,这并不是一个理想的解决方案,因为它会使代码变得复杂且难以维护。
  2. 自定义覆盖率工具:你可以编写自己的工具或脚本,来记录std容器相关操作的覆盖情况。然后,你可以使用这些数据来生成一个自定义的覆盖信息文件,该文件将包含这些操作的覆盖情况。这种方法需要一些编程技能,但它可以让你更精确地控制代码的覆盖率。
  3. 使用其他覆盖率工具:除了lcov之外,还有许多其他的覆盖率工具可供选择,例如gcov、cppcheck等。这些工具可能更适合你的需求,特别是当你需要测试std容器相关操作时。
  4. 修改编译选项:你可以尝试修改编译选项来改变lcov的行为。例如,你可以尝试添加-fprofile-arcs-ftest-coverage选项来启用覆盖率检测。然而,这种方法可能会影响代码的性能和稳定性,因此在使用之前需要谨慎考虑。

总的来说,要解决这个问题需要一些工作量,但并不是不可能的。你可以根据你的具体需求和资源来选择最合适的解决方案。

1 个回答
新手上路,请多包涵

C++的lcov隐藏未覆盖分支确实让人头疼,十分感谢指出了主要原因,最后我们使用了下述方法解决:

#ifndef UNIT_TEST
#define Try  try
#define Catch(aa)      catch(aa)  
#define Throw(ex)      throw(ex)
#define What(w)        ((w).what())
#else 
#define Try  
#define Catch(aa)  if(0) 
#define Throw(ex)
#define What(w)       ("")
#endif

然后在gtest的编译脚本中启用了 -fno-exceptions -DUNIT_TEST 编译选项解决了令人讨厌的未覆盖分支问题

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题