Mocking side effect with googlemock fails because of invalid cast - unit-testing

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:

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

Related

GMock removing implicit conversion warning when using ReturnArg

Given the following mock method
MOCK_METHOD2(send, int(const void* pBuffer, size_t szBuffer));
And the following usage
EXPECT_CALL(socketMock, send(_, _))
.Times(1)
.WillOnce(ReturnArg<1>());
Will generate a warning in ReturnArg due to the implicit conversion of szBuffer into the return type of int.
Here's an example of code:
#include <cstddef>
#include <cstdint>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
class ISocket {
public:
virtual int Send(const uint8_t* payload, const std::size_t payloadLength, std::size_t& sentLength) = 0;
};
class SocketMock : public ISocket {
public:
MOCK_METHOD3(Send, int(const uint8_t*, const std::size_t, std::size_t&));
};
class MySender
{
public:
static void Ping(ISocket& socket)
{
std::size_t bytesSent;
uint8_t payload[64];
memset(payload, 0, sizeof(payload));
socket.Send(payload, sizeof(payload), bytesSent);
if (bytesSent != sizeof(payload))
throw std::runtime_error("Socket transmission failed");
}
};
TEST(SocketSend, ReturnsBytesTransmitted)
{
SocketMock socket;
EXPECT_CALL(socket,
Send(
::testing::_, // payload
::testing::_, // payload length
::testing::_ // return by param, bytes sent
)).Times(1)
.WillOnce(::testing::ReturnArg<1>());
MySender::Ping(socket);
::testing::Mock::VerifyAndClearExpectations(&socket);
}
GTEST_API_ int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Here's the compiler warning:
googletest\googlemock\include\gmock/gmock-more-actions.h(165): warning C4267: 'return': conversion from 'size_t' to 'int', possible loss of data (compiling source file TestMain.cpp)
Is there anyway to make GMock use a static_cast to change the return type to eliminate the type conversion warning?
ReturnArg<k> is a Google Mock "action" (reference) that returns the kth argument of the function being mocked. Since in this case, the return type of the function is int and the first argument is of type size_t, a warning about a potentially narrowing warning is generated.
The easiest way to inject an explicit typecast is to write a custom action:
ACTION_TEMPLATE(ReturnArgWithTypeCast,
HAS_2_TEMPLATE_PARAMS(int, k, typename, return_type),
AND_0_VALUE_PARAMS()) {
return static_cast<return_type>(::testing::get<k>(args));
}
The action can be used this way:
...
.WillOnce(ReturnArgWithTypeCast<1, int>())

Error obtaining parameters of a structure pointer in c ++

I have defined the following structure:
struct com_NetworkStruct
{
enum com_NetworkStatus status;
char IPV4_Addr[COM_IPV4_ADDR_LENGTH];
char IPV4_Netmask[COM_IPV4_ADDR_LENGTH];
char IPV4_DNS1[COM_IPV4_ADDR_LENGTH];
char IPV4_DNS2[COM_IPV4_ADDR_LENGTH];
int IPV4_DHCP_Enabled;
int IPV6_Supported;
int IPV6_Enabled;
struct com_IPv6_Data *IPV6_Address_List;
char HW_Addr[COM_ETH_MAC_ADDR_LENGTH];
int MTU_Size;
int Link_Speed;
enum com_LinkType Link_Type;
};
And the following method definition:
int foo(struct com_NetworkStruct **netinfo);
This is the main function and how i call the function:
int main(int argc, char const *argv[])
{
int ret;
char data_aux[200];
struct com_NetworkStruct *netinfo = NULL;
ret = foo(&netinfo);
memset(data_aux, 0, sizeof(data_aux));
sprintf(data_aux, "%s", netinfo->IPV4_Addr);
cout<<string(data_aux)<<endl;
return 0;
}
The problem is when I try to read the data of the IPV4_Addr value since it sends me the following error:
Error: #289: no instance of constructor "std::basic_string<_CharT, Traits, Alloc>::basic_string [with CharT=char, Traits=std::char_traits<char>, _Alloc=std::allocator<char>]" matches the argument list
argument types are: (const char [20], char [16])
I already tried this, but nothing works:
&netinfo->IPV4_Addr
netinfo.IPV4_Addr
string(netinfo->IPV4_Addr)

pthread c++ invalid conversation from void * (*) to void* (*)(void*)

I'm new in C++ and trying to create multiple threads with pthread.
typedef struct thread_args{
int &sockfd;
struct sockaddr_in &serv_addr;
int size_serv_addr;
socklen_t &clilen;
int &newsockfd;
};
void create_server(int &sockfd, struct sockaddr_in &serv_addr, int size_serv_addr, socklen_t &clilen, int &newsockfd){
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd;
socklen_t clilen;
pthread_t t1;
struct sockaddr_in serv_addr, cli_addr;
struct thread_args *args;
args->clilen = clilen;
args->newsockfd = newsockfd;
args->serv_addr = serv_addr;
args->size_serv_addr = sizeof(serv_addr);
args->sockfd = sockfd;
pthread_create(&t1, NULL, create_server, &args);
printf("hello abc");
return 0;
}
When I run this code, it has a message:
error:/bin/sh -c 'make -j 4 -e -f error: invalid conversion from 'void* (*)(int&, sockaddr_in&, int, socklen_t&, int&) {aka void* (*)(int&, sockaddr_in&, int, unsigned int&, int&)}' to 'void* (*)(void*)' [-fpermissive]
pthread_create(&t1, NULL, create_server, &args);
How can I fix this?
Signature for your thread function should be:
void *(*start_routine) (void *)
but you provide:
void create_server(int &sockfd, struct sockaddr_in &serv_addr, int size_serv_addr, socklen_t &clilen, int &newsockfd)
you should create a function like:
void* myThread(void *arg);
then args argument in pthread_create call will be passed as arg parameter to myThread, you can use its fields to call create_server
Your function definition does not match the type pthread_create wants to have. It requires only 1 arguments and that is a void* (so a function void function(void* args)).
you need to change create_server to
void create_server(void* voidArgs) {
thread_args* args = static_cast<thread_args*>(voidArgs);
//...
}
You will not have those issues though if you use the c++11 std::thread object. There you can directly use the argument type and with any number of arguments you like. So you don't have to define the thread_args struct.

Pointer to method

Anyone knows how to compile this example code under msvc2010 ? (supposedly compiles under GCC)
class cmdLine;
struct cmdList
{
const char *c;
const char *s;
const char *h;
void (cmdLine::*cmdFuncPtr)();
};
class cmdLine
{
public:
cmdLine();
static cmdList myCmdList[];
void test();
};
cmdLine::cmdLine()
{
}
void cmdLine::test()
{
}
cmdList cmdLine::myCmdList[] =
{
{"xxx", "yyy", "zzzzz", &cmdLine::test},
{"aaa", "bbb", "ccc", 0}
};
int _tmain(int argc, _TCHAR* argv[])
{
cmdLine c;
(c.myCmdList[0].*cmdFuncPtr) (); //error (why?)
}
I get error C2065: 'cmdFuncPtr' : undeclared identifier and dont know whats wrong ?
Use this syntax
(c.*c.myCmdList[0].cmdFuncPtr) ();
As cmdFuncPtr is a pointer to a method of cmdLine, it needs an instance of the class to be invoked on, which is c. At the same time, cmdFuncPtr is a member of cmdList, so it needs an instance of the class where it is stored, which is c.myCmdList[0]. That's why c shall be used twice in the expression.
The expression presented by OP parses as: "Invoke a method on an instance of a class in c.myCmdList[0] through a method pointer stored in a standalone variable cmdFuncPtr". Such variable doesn't exist, that's what the compiler complains about.

member function pointer problem

Works:
static void WINAPI ServiceStart(DWORD argc, LPTSTR* argv);
int main() {
SERVICE_TABLE_ENTRY DispatchTable[] = {
{"MyService", ServiceStart},
{NULL, NULL}
};
}
Doesn't work:
error C2440: 'initializing' : cannot convert from 'void (__stdcall Service::* )(DWORD,LPTSTR *)' to 'LPSERVICE_MAIN_FUNCTIONA'
class Service {
static void WINAPI ServiceStart(DWORD argc, LPTSTR* argv);
};
int main() {
SERVICE_TABLE_ENTRY DispatchTable[] = {
{"MyService", Service::ServiceStart},
{NULL, NULL}
};
}
You must use & in that context to obtain a correct pointer.
Use &Service::ServiceStart, not Service::ServiceStart.
One piece of advice: since you made it a static function of Service class, rename it as well. ServiceStart is unnecessarily long. I think Service is implied here. If so, then make it just Start:
class Service
{
static void WINAPI Start(DWORD argc, LPTSTR *argv);
};
And then use &Service::Start.