提问人:musnow 提问时间:11/10/2023 最后编辑:musnow 更新时间:11/10/2023 访问量:80
LCOV分支覆盖率,在std::map中放置/插入
lcov branch coverage with emplace/insert in std::map
问:
我正在使用 lcov 2.0 检查我的 gtest 分支覆盖率,但我遇到了许多由 STD 引起的分支,例如下面。emplace/insert/operator[]
std::map
- 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
;
我知道这是因为我曾经在 中构造一个匿名对象,而不是直接转发 emplace 的参数来构造 mytestint 对象。因此,此处的异常是在构造匿名 mytestint 对象期间引发的,而不是在 emplace 函数中引发的。mytestint(id,pr)
map.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[]
答:
0赞
Peng Guanwen
11/10/2023
#1
你是对的,未覆盖的树枝是投掷路径。运行 gcov
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
评论
0赞
musnow
11/10/2023
谢谢,我在带有 emplace 和 operator[] 的 main.hpp.gcov 文件中得到了这个结果,并带有插入branch 2 taken 0 (throw)
branch 3 taken 0 (throw)
0赞
musnow
11/10/2023
我试图在mytestint构造函数中添加一个抛掷,将_pr从uint16_t修改为int,抛出异常时。使用负数 pr 进行测试将添加另一个带有 to 行的分支_pr<0
[++]
auto k = _test_map.emplace(id, mytestint(id,pr));
评论
map::operator[]
将调用 If Key is not found 的默认构造函数。 如果传递 rValue,将调用 copy 构造函数,如果传递 rValue,则调用 Move 构造函数。因此,您可以在这些构造函数中添加异常。mytestint
map::insert