C++ Google Mock SaveArg : how to save a pointer argument
Asked Answered
C

2

5

I having difficulty saving a pointer argument that my mock receives.

#define SIZE_OF_DATA

typedef struct {
    uint32_t someValue1;
    uint16_t someValue2;
    // other values here
} LargeStruct;


class SomeClass {
public:
    // assume sendData is a generic function where data is actually pointer to a LargeStruct
    void sendData(const uint8_t* data, const uint16_t size);
}

class MockClass : public SomeClass {
public:
    MOCK_METHOD2(sendData, void(const uint8_t*, const uint16_t));
};

I want to save the first argument to sendData (the pointer) and look at the data it points to (it points to a large struct, so I don't want to copy by value):

TEST(SomeFixture, sendData_checkSentDataIsValid) {
   MockClass mock;

  const uint8_t *pData;
  EXPECT_CALL(mock, sendData(_, SIZE_OF_DATA)).WillOnce(SaveArg<0>(&pData));

  // do something here that calls sendData()

  // hopefully data should point to the same data that was passed in to the method
  LargeStruct *ls = (LargeStruct *)pData;

  // now verify that the data is ok...
  // some expectations here
  EXPECT_EQ(SOMEVALUEIWANT, ls->someValue1); 

}

However, the data pointed to by pData is wrong - I think I appear to be saving the pointer value into the struct, rather than saving the pointer.

I think the problem lies in the variable I pass to SaveArg, but I can't seem to get it in a version that compiles and gives me the correct answer. Any pointers please?

Cisco answered 17/9, 2014 at 12:0 Comment(0)
F
8

I just ran into the same situation, and in my case, I had to make sure that the pointer passed into the equivalent of your sendData() function was not pointing to an automatic variable on the stack. Otherwise, by the time you access the pointer, the contents will have changed. I found that less than helpful, so I decided to define a customized alternative to SaveArg like this:

ACTION_TEMPLATE(SaveSomeValue1,
                HAS_1_TEMPLATE_PARAMS(int, k),
                AND_1_VALUE_PARAMS(pointer))
{
    const void * data = ::std::tr1::get<k>(args);
    const LargeStruct* ls = (const LargeStruct *)data;
    *pointer = ls->someValue1;
}

You can then use it like this:

uint32_t someValue1;
EXPECT_CALL(mock, sendData(_, SIZE_OF_DATA))
    .WillOnce(SaveSomeValue1<0>(&someValue1));
//...
EXPECT_EQ(SOMEVALUEIWANT, someValue1);
Farnham answered 28/4, 2015 at 15:45 Comment(2)
I'm currently seeing a compiler error (MSVS 2017) with this solution, indicating that the actions "must return a value". I added a return 0; to the end of the statement block and that fixed the problem, allows compilation, but safely? (GMock 1.7.0)Underwater
Following up my own comment: "must return a value" means the action is expected to return the same type value as the function being mocked. Using @dalestewart's technique or the default SaveArg() action, the DoAll() action is required to chain the side-effect action (returning void) with an explicit Return() action.Underwater
C
1

You can create one void pointer as a buffer and save argument inside that buffer. After that you should cast the buffer to your large structure.

 TEST(SomeFixture, sendData_checkSentDataIsValid) {
  MockClass mock;
  LargeStruct *ls;
  void *buffer;

  EXPECT_CALL(mock, sendData(_, SIZE_OF_DATA))
             .WillOnce(SaveArg<0>(&buffer));

 // do something here that calls sendData()

  ls = static_cast<LargeStruct  *>(buffer);

  EXPECT_EQ(SOMEVALUEIWANT, ls->someValue1); 

}
Catenane answered 24/5, 2016 at 9:9 Comment(1)
If LargeStruct data that sendData gets as a parameter is allocated on the stack, would void * buffer populated by SaveArg be pointing to a memory that is released after sendData returns?Galvanotropism

© 2022 - 2024 — McMap. All rights reserved.