Reputation: 13
I'm checking my gtest branch coverage with lcov 2.0, but I encountered many branchs caused by STD, such as emplace/insert/operator[]
in std::map
down below.
Here is my source code
// 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;
}
Here is my gtest code
#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 will display that all calls to these functions have an unprocessed branch.
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 : }
Here is my command to generate lcov report.
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
I would like to know is there any solution to these uncovered branches?
Is there a relationship between the uncovered branches here and the exceptions inside the emplace/insert/operator[]
function? How should I raise an exception in these function?
I try to thorw an exception in mytestint constructor
// 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 ");
}
}
with new test code
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 result in this.
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 : }
Throw an exception in constructor could solve branch coverage in vector.emplace_back
, but could not solve in map.emplace
;
I know this is because I used mytestint(id,pr)
to construct an anonymous object in map.emplace
, rather than directly forwarding the parameters of emplace to construct mytestint objects. Therefore, the exception here is thrown during the construction of anonymous mytestint objects, not in the emplace function.
Try to add another constructor in mytestint
mytestint(int pr) : _id(0), _pr(pr)
{
if (pr < 0)
{
throw std::runtime_error("pr < 0 ");
}
}
And these branchs with exception are covered!
112 [ + + ]: 3 : auto kk = _test_map.emplace(id, pr);
129 [ + + ]: 3 : _test_vector.emplace_back(pr);
But I don't know how to throw exception ininsert/push_back/operator[]
; It seems like I can only ignore these uncovered branch in insert/push_back/operator[]
.
Upvotes: 0
Views: 365
Reputation: 722
You are right that the uncovered branches are throw paths. Run gcov with
gcov -b -c -o . test.cpp
You will see something like the following in .gcov
file
4: 12: auto t = _test_map.emplace(id, temp);
call 0 returned 4
branch 1 taken 4 (fallthrough)
branch 2 taken 0 (throw)
It's hard to raise an exception here manually. One possible solution is to throw an exception in the constructors of mytestint
.
Upvotes: 0