Ingo
Ingo

Reputation: 736

Mocking side effect with googlemock fails because of invalid cast

On Debian I try to mock system function bind to bind an interface address to a socket. To set an ip address I think I have to use side effects on a parameter of bind. I use this code:

extern "C" {
    int mysocket();
}

// --- mock bind -------------------------------------------
class BindInterface {
public:
    virtual ~BindInterface() {}
    virtual int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen) = 0;
};

class BindMock : public BindInterface {
public:
    virtual ~BindMock() {}
    MOCK_METHOD(int, bind, (int, const struct sockaddr*, socklen_t), (override));
};

BindInterface* ptrBindMockObj = nullptr;
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen) {
    return ptrBindMockObj->bind(sockfd, addr, addrlen);
}

TEST(MockTestSuite, bind_address_to_a_socket)
{
    using ::testing::_;
    using ::testing::Return;
    using ::testing::DoAll;
    using ::testing::SetArgPointee;
    using ::testing::SetArgReferee;

    struct sockaddr_in my_addr = {0};

    BindMock bindMockObj;
    ptrBindMockObj = &bindMockObj;

    my_addr.sin_family = AF_INET;
    EXPECT_EQ(inet_pton(AF_INET, "192.168.55.66", &my_addr.sin_addr.s_addr), 1);

    EXPECT_CALL(bindMockObj, bind(_, _, _))
        .WillOnce(DoAll(SetArgPointee<1>((struct sockaddr*)my_addr),
                        SetArgReferee<2>(sizeof(my_addr)),
                        Return(0)));

    EXPECT_EQ(mysocket(), EXIT_SUCCESS);
}


int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

It doesn't work because the compiler complains invalid cast as follows:

test_mylib.cpp: In member function ‘virtual void MockTestSuite_bind_address_to_a_socket_Test::TestBody()’:
test_mylib.cpp:52:42: error: invalid cast from type ‘sockaddr_in’ to type ‘sockaddr*’
   52 |         .WillOnce(DoAll(SetArgPointee<1>((struct sockaddr*)my_addr),
      |                                          ^~~~~~~~~~~~~~~~~~~~~~~~~

Seems I do not really understand what SaveArgPointee<N>(pointer) is doing. What I'm doing wrong with types and cast?

UPDATE:
With the suggestion from the answer of @Quarra to define the ACTION_P outside the TEST body I get two error messages within a bunch of stacked calls:

googlemock/include/gmock/gmock-actions.h:1116:56: error: static assertion failed: Argument must be a reference type.
 1116 |     static_assert(std::is_lvalue_reference<argk_type>::value,
      |                                                        ^~~~~^
--- snip ---
test_mylib.cpp:33:54: error: no match for ‘operator=’ (operand types are ‘sockaddr’ and ‘const sockaddr_in’)
   33 | ACTION_P(AssignSockAddr, param) { *(sockaddr*)(arg0) = param; }
      |                                   ~~~~~~~~~~~~~~~~~~~^~~~~~~
In file included from /usr/include/x86_64-linux-gnu/sys/socket.h:33,
                 from /usr/include/netinet/in.h:23,
                 from test_mylib.cpp:7:

Upvotes: 1

Views: 722

Answers (1)

Quarra
Quarra

Reputation: 2695

I agree that it's strange that your example doesn't compile: it's trying to change the pointer to const socaddr, not const pointer...

You can define your own action and use it like that:

ACTION_P(AssignSockAddr, param) { *(sockaddr*)(arg0) = param; }

TEST(MockTestSuite, bind_address_to_a_socket) {
    // [ rest of the test code here ]
    EXPECT_CALL(bindMockObj, bind(_, _, _)).WillOnce(
        DoAll(WithArg<1>(AssignSockAddr(my_addr)), 
              SetArgReferee<2>(sizeof(my_addr)), 
              Return(0)));

    EXPECT_EQ(mysocket(), EXIT_SUCCESS);
}

Upvotes: 1

Related Questions