I encountered some odd behavior with ReturnRef that was causing seg faults or failing to return the correct value in tests that I'm updating to use gmock. I was able to workaround the issue by switching to ReturnRefOfCopy, but I would like to know why ReturnRef isn't working. A short example is included below. You can place these two files in the same directory and on Linux, build like: g++ test.cpp -lgtest -lgmock_main -lgmock -lrt -o test (and then of course, run using: ./test).
FIle 1: MockRunnable.h
#ifndef MOCKRUNNABLE_H_
#define MOCKRUNNABLE_H_
#include "gmock/gmock.h"
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include <string>
class Runnable
{
public :
virtual ~Runnable() {}
virtual uint8_t getPriority() const = 0;
virtual const std::string & getRunnableName() const = 0;
};
class MockRunnable : public Runnable {
public:
MOCK_CONST_METHOD0(getPriority, uint8_t());
MOCK_CONST_METHOD0(getRunnableName, const std::string&());
};
inline boost::shared_ptr<MockRunnable> CreateDefaultRunnable(std::string& name) {
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::NiceMock;
boost::shared_ptr<MockRunnable> runnable(new NiceMock<MockRunnable>);
ON_CALL(*runnable, getRunnableName()).WillByDefault(ReturnRef(name));
return runnable;
}
#endif /* MOCKRUNNABLE_H_ */
File 2: test.cpp
#include <vector>
using std::vector;
#include <string>
using std::string;
#include <iostream>
using std::cerr;
using std::endl;
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "MockRunnable.h"
using testing::Return;
TEST(MockTest, DunCrash) {
vector<string> names;
vector<boost::shared_ptr<MockRunnable> > runnables;
for(int i = 10; i > 0; --i) {
names.push_back("runnable");
runnables.push_back(CreateDefaultRunnable(names.back()));
ON_CALL(*runnables.back(), getPriority()).WillByDefault(Return(i));
cerr << "Front: " << runnables.front()->getRunnableName() << ": "
<< int(runnables.front()->getPriority()) << endl;
cerr << " Back: " << runnables.back()->getRunnableName() << ": "
<< int(runnables.back()->getPriority()) << endl;
}
}
int main(int argc, char** argv) {
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
So basically, the test is pushing back 10 MockRunnables and it's expecting that each Mock will print return the assigned name (ON_CALL in factory method) and priority. In this contrived example, the actual names contain duplicate test, but are unique references (stored in the vector of strings). Here is what prints out when running the test:
[ RUN ] MockTest.DunCrash
Front: runnable: 10
Back: runnable: 10
Front: : 10
Back: runnable: 9
Front: : 10
Back: runnable: 8
Front: : 10
Back: runnable: 7
Front: : 10
Back: runnable: 6
Front: : 10
Back: runnable: 5
Front: : 10
Back: runnable: 4
Front: : 10
Back: runnable: 3
Front: : 10
Back: runnable: 2
Front: : 10
Back: runnable: 1
As you can see, only the most recent mock pushed into the vector returns the correct reference. The others are garbage. Since the references still exist for the other elements in the vector, I would expect the call to getRunnableName to return "runnable". If I update the factory method to use ReturnRefOfCopy, everything works as intended. However, it doesn't appear that this test should require ReturnRefOfCopy.