c++ lcov分支覆盖,在std::map中使用emplace/insert

rbl8hiat  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(122)

我正在使用lcov 2.0检查gtest分支覆盖率,但是我遇到了许多由STD引起的分支,例如下面的std::map中的emplace/insert/operator[]

  • g++(Ubuntu 11.4.0-1ubuntu1~22.04)11.4.0
  • gtest 1.12.x
  • lcov/genhtml:LCOV版本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将显示对这些函数所有调用都有未处理的分支。

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


我想知道有没有办法解决这些裸露的树枝?
这里未覆盖的分支和emplace/insert/operator[]函数内部的异常之间有关系吗?我应该如何在这些函数中引发异常?

更新异常

我尝试在mytestint构造函数中抛出异常

// type of _pr already modify to int
    mytestint(uint16_t id, int pr) : _id(id), _pr(pr)
    {
        if (pr < 0)
        {
            throw std::runtime_error("pr < 0 ");
        }
    }


新的测试代码

TEST(MapTest, TestMap) {
    EXPECT_NO_THROW(test_emplace(1,1));

    EXPECT_ANY_THROW(test_emplace(2,-4));
    EXPECT_ANY_THROW(test_emplace(3,-50));
}

TEST(VetorTest, TestEmplace) {
    EXPECT_NO_THROW(test_vector_emplace(1,1));
    EXPECT_ANY_THROW(test_vector_emplace(2,-4));
    EXPECT_ANY_THROW(test_vector_emplace(3,-50));
}


lcov导致了这一点。

93                 :             : map<uint16_t, mytestint> _test_map;
      94                 :             : map<uint16_t, uint16_t> _test_map_pod;
      95                 :             : vector<mytestint> _test_vector;
      96                 :             : 
      97                 :             : 
      98                 :           3 : void test_emplace(uint16_t id, int pr)
      99                 :             : {
     100         [ +  - ]:           3 :     printf("%u | %d\n", id, pr);
     101                 :             :     // map with class
     102         [ +  - ]:           3 :     mytestint temp = {id, id};
     103         [ +  - ]:           3 :     auto t = _test_map.emplace(id, temp);
     104   [ +  +  +  - ]:           3 :     auto k = _test_map.emplace(id, mytestint(id, pr));
     105         [ +  - ]:           1 :     auto s = _test_map.insert({pr, temp});
     106         [ +  - ]:           1 :     _test_map[id] = temp;
     107         [ +  - ]:           1 :     auto h = _test_map[pr];
     108                 :             :     // map with uint16_t
     109         [ +  - ]:           1 :     _test_map_pod.emplace(id, pr);
     110         [ +  - ]:           1 :     _test_map_pod.insert({pr, id});
     111         [ +  - ]:           1 :     _test_map_pod[id] = pr;
     112                 :           1 : }
     113                 :             : 
     114                 :           3 : void test_vector_emplace(uint16_t id, int pr)
     115                 :             : {
     116         [ +  - ]:           3 :     mytestint temp = {id, id};
     117         [ +  - ]:           3 :     _test_vector.push_back(temp);
     118         [ +  + ]:           3 :     _test_vector.emplace_back(id, pr);
     119                 :           1 : }


在构造函数中抛出异常可以解决vector.emplace_back中的分支覆盖问题,但不能解决map.emplace中的问题;
我知道这是因为我在map.emplace中使用了mytestint(id,pr)来构造匿名对象,而不是直接转发emplace的参数来构造mytestint对象。因此,这里的异常是在构造匿名mytestint对象时抛出的,而不是在emplace函数中抛出的。
尝试在mytestint中添加另一个构造函数

mytestint(int pr) : _id(0), _pr(pr)
    {
        if (pr < 0)
        {
            throw std::runtime_error("pr < 0 ");
        }
    }


而这些分支与例外覆盖!

112         [ +  + ]:           3 :     auto kk = _test_map.emplace(id, pr); 
   129         [ +  + ]:           3 :     _test_vector.emplace_back(pr);


但是我不知道如何在insert/push_back/operator[]中抛出异常;似乎我只能忽略insert/push_back/operator[]中这些未覆盖的分支。

h7appiyu

h7appiyu1#

你是对的,未覆盖的分支是抛出路径。

gcov -b -c -o . test.cpp

字符串
您将在.gcov文件中看到如下内容

4:   12:    auto t = _test_map.emplace(id, temp);
call    0 returned 4
branch  1 taken 4 (fallthrough)
branch  2 taken 0 (throw)


这里很难手动抛出异常,一个可能的解决方案是在mytestint的构造函数中抛出异常。

相关问题