skip to Main Content

I have a function for example:

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

The man page tell me "The argument addr is a pointer to a sockaddr structure. This structure is filled in with the address of the peer socket, as known to the communications layer." So I would call the function with:

    struct sockaddr_storage clientAddr;
    struct sockaddr* sa{(sockaddr*)&clientAddr};
    clientLen = sizeof(clientAddr);;

    accept_sock = accept(listen_sock, (struct sockaddr*)&clientAddr, &clientLen);

With mocking accept I would like to fill clientAddr with:

    struct sockaddr_in* sa_in{(sockaddr_in*)&clientAddr};
    sa_in->sin_family = AF_INET;
    sa_in->sin_port = htons(50000);
    inet_pton(AF_INET, "192.168.1.2", &sa_in->sin_addr);

To return a mocked pointer to a filled structure I would use:

    EXPECT_CALL(mocked_sys_socket, accept(listen_sock, _, Pointee(clientLen)))
        .WillOnce(DoAll(SetArgPointee<1>(*sa), Return(accept_sock)));

But that isn’t what I need here. The clientAddr structure is already given.

What action instead of SetArgPointee<1>(*sa) I have to use to return with filling the provided structure clientAddr?

2

Answers


  1. Chosen as BEST ANSWER

    The answer of Ari shows the solution. To have a complete example here is the implementation I use now.

    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include "gmock/gmock.h"
    
    using ::testing::_;
    using ::testing::DoAll;
    using ::testing::Pointee;
    using ::testing::Return;
    using ::testing::SetArgPointee;
    
    //
    class SysSocketInterface {
      public:
        virtual ~SysSocketInterface() {}
        virtual int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) = 0;
    };
    
    // Version to call the real system function.
    class RealSysSocket : public SysSocketInterface {
      public:
        virtual ~RealSysSocket() override {}
        int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen) override {
            return ::accept(sockfd, addr, addrlen);
        }
    };
    
    // Version to call the mocked system function.
    class MockSysSocket : public SysSocketInterface {
      public:
        virtual ~MockSysSocket() override {}
        MOCK_METHOD(int, accept, (int, struct sockaddr*, socklen_t*), (override));
    };
    
    // Polymorphic class
    class SysSocket {
        SysSocketInterface* m_syssocket;
    
      public:
        SysSocket(SysSocketInterface* a_syssocket) : m_syssocket(a_syssocket) {}
    
        std::string get_addr(int a_listen_sock) {
            struct sockaddr_storage clientAddr;
            struct sockaddr_in* clientAddr_in{(sockaddr_in*)&clientAddr};
            socklen_t socklen = sizeof(sockaddr_storage);
    
            int accept_sock = m_syssocket->accept(a_listen_sock,
                                                  (sockaddr*)&clientAddr, &socklen);
            if (accept_sock == -1) {
                return "";
            }
    
            char text_addr[INET_ADDRSTRLEN];
            if (inet_ntop(AF_INET, &clientAddr_in->sin_addr, text_addr,
                          sizeof(text_addr)) == nullptr) {
                return "";
            }
    
            unsigned short int port = ntohs(clientAddr_in->sin_port);
            return std::string(text_addr) + ":" + std::to_string(port);
        }
    };
    
    //
    TEST(SysSocketTestSuite, call_real_sys_socket_accept) {
        RealSysSocket real_sys_socket;
        // Inject real sys_socket object
        SysSocket sys_socket(&real_sys_socket);
    
        int listen_sock = 0;
    
        // Test Unit
        // Listen_sock is not a valid socket
        EXPECT_EQ(sys_socket.get_addr(listen_sock), "");
    }
    
    TEST(SysSocketTestSuite, mock_sys_socket_accept) {
        // Provide a socket address
        struct sockaddr_storage clientAddrMock;
        struct sockaddr* sa{(sockaddr*)&clientAddrMock};
        struct sockaddr_in* sa_in{(sockaddr_in*)&clientAddrMock};
        sa_in->sin_family = AF_INET;
        sa_in->sin_port = htons(50000u);
        inet_pton(AF_INET, "192.168.1.2", &sa_in->sin_addr);
        socklen_t socklen = sizeof(sockaddr_storage);
    
        // Instantiate mocked sys_socket object
        MockSysSocket mock_sys_socket;
        // Inject mocked sys_socket object
        SysSocket sys_socket(&mock_sys_socket);
    
        int listen_sock = 3;
        int accept_sock = 4;
    
        EXPECT_CALL(mock_sys_socket, accept(listen_sock, _, Pointee(socklen)))
            .WillOnce(DoAll(SetArgPointee<1>(*sa), Return(accept_sock)));
    
        // Test Unit
        EXPECT_EQ(sys_socket.get_addr(listen_sock), "192.168.1.2:50000");
    }
    //-----------------------------------------------------------------------------
    
    int main(int argc, char** argv) {
        ::testing::InitGoogleMock(&argc, argv);
        return RUN_ALL_TESTS();
    }
    

    With executing the test I get:

    [==========] Running 2 tests from 1 test suite.
    [----------] Global test environment set-up.
    [----------] 2 tests from MockedSysSocketTest
    [ RUN      ] MockedSysSocketTest.call_real_sys_socket_accept
    [       OK ] MockedSysSocketTest.call_real_sys_socket_accept (0 ms)
    [ RUN      ] MockedSysSocketTest.mock_sys_socket_accept
    [       OK ] MockedSysSocketTest.mock_sys_socket_accept (0 ms)
    [----------] 2 tests from MockedSysSocketTest (0 ms total)
    
    [----------] Global test environment tear-down
    [==========] 2 tests from 1 test suite ran. (0 ms total)
    [  PASSED  ] 2 tests.
    

    Reference:
    Practical example for Dependency Injection and mocking with Googlemock


  2. IIUC, you want that as a result of a call to accept, the second parameter of accept, which is currently sa_int to be set to the value of sa_next.

    If that’s the case, I can think of two options:

    struct sockaddr {
      std::string name;
    };
    
    class MockedSysSocket {
     public:
      MOCK_METHOD(int, accept, (int, sockaddr *, socklen_t *addrlen), ());
    };
    
    TEST(MockedSysSocketTest, SideEffect1) {
      MockedSysSocket mocked_sys_socket;
    
      int accept_sock = 1;
      int listen_sock = 2;
    
      sockaddr sa_in{"initial"};
      sockaddr sa_next{"next"};
    
      socklen_t clientLen = sizeof(sockaddr);
    
      EXPECT_CALL(mocked_sys_socket, accept(listen_sock, &sa_in, &clientLen))
          .WillOnce(DoAll(SetArgPointee<1>(sa_next), Return(accept_sock)));
    
      auto actual = mocked_sys_socket.accept(listen_sock, &sa_in, &clientLen);
      EXPECT_EQ(sa_in.name, sa_next.name);
      EXPECT_EQ(actual, accept_sock);
    }
    
    TEST(MockedSysSocketTest, SideEffect2) {
      MockedSysSocket mocked_sys_socket;
    
      int accept_sock = 1;
      int listen_sock = 2;
    
      sockaddr sa_in{"initial"};
      sockaddr sa_next{"next"};
    
      socklen_t clientLen = sizeof(sockaddr);
    
      EXPECT_CALL(mocked_sys_socket, accept(listen_sock, &sa_in, &clientLen))
          .WillOnce(WithArg<1>(Invoke([&sa_next, accept_sock](sockaddr *in) {
            *in = sa_next;
            return accept_sock;
          })));
    
      auto actual = mocked_sys_socket.accept(listen_sock, &sa_in, &clientLen);
      EXPECT_EQ(sa_in.name, sa_next.name);
      EXPECT_EQ(actual, accept_sock);
    }
    

    Live example: https://godbolt.org/z/3h4ffcnd7

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search