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?
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();
}