musnow
musnow

Reputation: 13

lcov branch coverage with emplace/insert in std::map

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?


update with exception

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

Answers (1)

Peng Guanwen
Peng Guanwen

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

Related Questions