c++c++14googlemock

How to write a google mock matcher to match a void pointer argument?


I have a function that takes a const void* that I'm mocking. I want to have matchers based on the value of pointee that's being passed into the function.

For example:

#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"


class A
{
    public:
        A(std::vector<uint32_t> data) : m_data(data) {}
        void doSomething()
        {
            for (auto x : data)
                writeData(&x, sizeof(x))
                
        }
        void writeData(const void* val, size_t size)
        {
            //More complicated than that obviously
            std::cout << "Writing data" << std::endl;
        }
    private:
        std::vector<uint32_t> m_data;
    
};


class MockA : public A
{
    public: 
        MOCK_METHOD(void, writeData, (const void* val, size_t size));
        
}


TEST(TestA, writeData)
{
    std::vector<uint32_t> testData{1,2,3,4};
    MockA myMockObj (testData);
    
    uint32_t expVal1 = 1;
    uint32_t expVal2 = 2;
    uint32_t expVal3 = 3;
    uint32_t expVal4 = 4;
    
    //The problem is the matcher won't work because writeData takes const void*
    //Pointer comparison doesn't work because the pointers dont't point to the same obj
    //for example &testData[1] != m_data[1] and also (void*)&expVal2 != val
    
    EXPECT_CALL(myMockObj, writeData(expVal4, 4)).Times(1);
    EXPECT_CALL(myMockObj, writeData(expVal3, 4)).Times(1);
    EXPECT_CALL(myMockObj, writeData(expVal2, 4)).Times(1);
    EXPECT_CALL(myMockObj, writeData(expVal1, 4)).Times(1);
    
    
    
    
    myMockObj.doSomething();
}

I would like to have a matcher that matches based on the value of both arguments as shown in the example above. The problem is that the first argument is a void* and not a uint32_t.

I can't do pointer comparison because the argument and what I'm matching against are in different memory locations.

I found this discussion which seems to suggest I'm not going to be able to use SaveArgPointee side effect. I did try something along the lines of:

uint32_t testData = 50;
uint32_t actualData;
EXPECT_CALL(myMockObj, writeData(testing::_, 4)).Times(1).WillOnce(testing::SaveArgPontee<0>(&actualData));

That complained about const void* is not a pointer-to-oject type

I then tried writing my own action like this

ACITON_P(AssignVoidPtr, param) {param = * (const_cast<uint32_t*>(static_cast<const unit32_t*>(arg0))); }

Just to find that parameters are not writable

I tried using indirection an returning the value of the pointer with another custom action:

ACITON(AssignVoidPtr) { return * (const_cast<uint32_t*>(static_cast<const unit32_t*>(arg0))); }

class MockA : public A
{
    public: 
        MOCK_METHOD(uint32_t, dummy, (const void* val, size_t size));
        
        void writeData(conts void* val, size_t size)
        {
            dummy(val, size)
        }
        
        
}


TEST(TestA, writeData)
{
    std::vector<uint32_t> testData{1,2,3,4};
    MockA myMockObj (testData);
    
    uint32_t expVal1 = 1;
    uint32_t expVal2 = 2;
    uint32_t expVal3 = 3;
    uint32_t expVal4 = 4;
    
    uint32_t actVal1;
    uint32_t actVal2;
    uint32_t actVal3;
    uint32_t actVal4;
    
    //In the actual test I can differentiate between the expect calls so the correct value is assigned to each var
    actVal4 = EXPECT_CALL(myMockObj, dummy(testing::_, 4)).Times(4).WillOnce(AssignVoidPtr());
    actVal3 = EXPECT_CALL(myMockObj, dummy(testing::_, 4)).Times(1).WillOnce(AssignVoidPtr());
    actVal2 = EXPECT_CALL(myMockObj, dummy(testing::_, 4)).Times(1).WillOnce(AssignVoidPtr());
    actVal1 = EXPECT_CALL(myMockObj, dummy(testing::_, 4)).Times(1).WillOnce(AssignVoidPtr());
    
    ASSERT_EQ(data[0], actVal1);
    
    
    myMockObj.doSomething();
}

But the above gave an error that it can't convert between fake_type and uint32_t

Any idea how can I achieve this?


Solution

  • After fixing a few compiler errors in the classes definitions, the solution is:

    #include <vector>
    #include "gmock/gmock.h"
    #include "gtest/gtest.h"
    
    class A {
     public:
      A(std::vector<uint32_t> data) : m_data(data) {}
      void doSomething() {
        for (auto x : m_data)
          writeData(&x, sizeof(x));
      }
    
      virtual void writeData(const void* val, size_t size) {}
    
     private:
      std::vector<uint32_t> m_data;
    };
    
    class MockA : public A {
     public:
      using A::A;
      MOCK_METHOD(void, writeData, (const void* val, size_t size), (override));
    };
    
    TEST(TestA, writeData) {
      using ::testing::Eq;
      using ::testing::Pointee;
      using ::testing::SafeMatcherCast;
    
      std::vector<uint32_t> testData{1, 2};
      MockA myMockObj(testData);
    
      EXPECT_CALL(myMockObj,
                  writeData(MatcherCast<const void*>(
                                SafeMatcherCast<const uint32_t*>(Pointee(Eq(1)))),
                            sizeof(uint32_t)));
      EXPECT_CALL(myMockObj,
                  writeData(MatcherCast<const void*>(
                                SafeMatcherCast<const uint32_t*>(Pointee(Eq(2)))),
                            sizeof(uint32_t)));
    
      myMockObj.doSomething();
    }
    

    https://godbolt.org/z/KxjrTeW84